diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
index cadcd544a640612c56749e73f70dc9d38b003947..702c24bf50e36c5408b6a95726809f066c88aaa3 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
@@ -479,9 +479,12 @@ public class IndexBean {
     public void setStartOnLoad(String moo) {
         _startOnLoad = true;
     }
-    public void setSharedClient(String moo) {
+    public void setShared(String moo) {
     	_sharedClient=true;
     }
+    public void setShared(boolean val) {
+    	_sharedClient=val;
+    }
     public void setConnectDelay(String moo) {
         _connectDelay = true;
     }
diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp
index 31516add16a005bccb2cc7e9cf1c5c8fad769715..dd6de657b98fdea565ace35ccc44675c85fae709 100644
--- a/apps/i2ptunnel/jsp/editClient.jsp
+++ b/apps/i2ptunnel/jsp/editClient.jsp
@@ -166,9 +166,9 @@ if (curTunnel >= 0) {
 </td>
 <td>
 <% if (editBean.isSharedClient(curTunnel)) { %>
-<input type="checkbox" value="true" name="sharedClient" checked="true" />
+<input type="checkbox" value="true" name="shared" checked="true" />
 <% } else { %>
-<input type="checkbox" value="true" name="sharedClient" />
+<input type="checkbox" value="true" name="shared" />
 <% } %>
 <i>(Share tunnels with other clients and httpclients? Change requires restart of client proxy)</i>
 </td>
diff --git a/apps/syndie/java/src/net/i2p/syndie/ArchiveIndexer.java b/apps/syndie/java/src/net/i2p/syndie/ArchiveIndexer.java
index bec41742735c56050dbb2e7c5da68cd17cd08eee..09e65683124298aea0730ff0069bc2b7f20e4490 100644
--- a/apps/syndie/java/src/net/i2p/syndie/ArchiveIndexer.java
+++ b/apps/syndie/java/src/net/i2p/syndie/ArchiveIndexer.java
@@ -158,7 +158,7 @@ class ArchiveIndexer {
             _headers.setProperty(header, value);
         }
         
-        public void receiveAddress(String name, String schema, String location, String anchorText) {}
+        public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) {}
         public void receiveArchive(String name, String description, String locationSchema, String location, String postingKey, String anchorText) {}
         public void receiveAttachment(int id, String anchorText) {}
         public void receiveBegin() {}
diff --git a/apps/syndie/java/src/net/i2p/syndie/BlogManager.java b/apps/syndie/java/src/net/i2p/syndie/BlogManager.java
index e6f04a19827a969149eb5645b35bdc242f0b5c29..c0c2d13b45a738372c245e397c3dc08d975e5054 100644
--- a/apps/syndie/java/src/net/i2p/syndie/BlogManager.java
+++ b/apps/syndie/java/src/net/i2p/syndie/BlogManager.java
@@ -225,6 +225,7 @@ public class BlogManager {
         try {
             out = new FileOutputStream(userFile);
             out.write(DataHelper.getUTF8(user.export()));
+            user.getPetNameDB().store(user.getAddressbookLocation());
         } catch (IOException ioe) {
             ioe.printStackTrace();
         } finally {
@@ -399,7 +400,7 @@ public class BlogManager {
     }
     
     
-    public String addAddress(User user, String name, String location, String schema) {
+    public String addAddress(User user, String name, String protocol, String location, String schema) {
         if (!user.getAuthenticated()) return "Not logged in";
         boolean ok = validateAddressName(name);
         if (!ok) return "Invalid name: " + HTMLRenderer.sanitizeString(name);
@@ -408,19 +409,17 @@ public class BlogManager {
         if (!validateAddressSchema(schema)) return "Unsupported schema: " + HTMLRenderer.sanitizeString(schema);
         // no need to quote user/location further, as they've been sanitized
         
-        FileOutputStream out = null;
-        try {
-            File userHostsFile = new File(user.getAddressbookLocation());
-            Properties knownHosts = getKnownHosts(user, true);
-            if (knownHosts.containsKey(name)) return "Name is already in use";
+        PetNameDB names = user.getPetNameDB();
+        if (names.exists(name))
+            return "Name is already in use";
+        PetName pn = new PetName(name, schema, protocol, location);
+        names.set(name, pn);
         
-            out = new FileOutputStream(userHostsFile, true);
-            out.write(DataHelper.getUTF8(name + "=" + location + '\n'));
-            return "Address " + name + " written to your hosts file (" + userHostsFile.getName() + ")";
+        try {
+            names.store(user.getAddressbookLocation());
+            return "Address " + name + " written to your addressbook";
         } catch (IOException ioe) {
-            return "Error writing out host entry: " + ioe.getMessage();
-        } finally {
-            if (out != null) try { out.close(); } catch (IOException ioe) {}
+            return "Error writing out the name: " + ioe.getMessage();
         }
     }
     
@@ -455,12 +454,17 @@ public class BlogManager {
 
     private boolean validateAddressLocation(String location) {
         if ( (location == null) || (location.trim().length() <= 0) ) return false;
-        try {
-            Destination d = new Destination(location);
-            return (d.getPublicKey() != null);
-        } catch (DataFormatException dfe) {
-            dfe.printStackTrace();
-            return false;
+        if (false) {
+            try {
+                Destination d = new Destination(location);
+                return (d.getPublicKey() != null);
+            } catch (DataFormatException dfe) {
+                dfe.printStackTrace();
+                return false;
+            }
+        } else {
+            // not everything is an i2p destination...
+            return true;
         }
     }
 
diff --git a/apps/syndie/java/src/net/i2p/syndie/PetName.java b/apps/syndie/java/src/net/i2p/syndie/PetName.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6c42e9da5000be7b5e65b7fb996ba3ec9a53b1c
--- /dev/null
+++ b/apps/syndie/java/src/net/i2p/syndie/PetName.java
@@ -0,0 +1,175 @@
+package net.i2p.syndie;
+
+import java.util.*;
+import net.i2p.data.DataHelper;
+
+/**
+ *
+ */
+public class PetName {
+    private String _name;
+    private String _network;
+    private String _protocol;
+    private List _groups;
+    private boolean _isPublic;
+    private String _location;
+    
+    public PetName() {
+        this(null, null, null, null);
+    }
+    public PetName(String name, String network, String protocol, String location) {
+        _name = name;
+        _network = network;
+        _protocol = protocol;
+        _location = location;
+        _groups = new ArrayList();
+        _isPublic = false;
+    }
+    /**
+     * @param dbLine name:network:protocol:isPublic:group1,group2,group3:location
+     */
+    public PetName(String dbLine) {
+        _groups = new ArrayList();
+        StringTokenizer tok = new StringTokenizer(dbLine, ":\n", true);
+        int tokens = tok.countTokens();
+        System.out.println("Tokens: " + tokens);
+        if (tokens < 7) {
+            return;
+        }
+        String s = tok.nextToken();
+        if (":".equals(s)) {
+            _name = null;
+        } else {
+            _name = s;
+            s = tok.nextToken(); // skip past the :
+        }
+        s = tok.nextToken();
+        if (":".equals(s)) {
+            _network = null;
+        } else {
+            _network = s;
+            s = tok.nextToken(); // skip past the :
+        }
+        s = tok.nextToken();
+        if (":".equals(s)) {
+            _protocol = null;
+        } else {
+            _protocol = s;
+            s = tok.nextToken(); // skip past the :
+        }
+        s = tok.nextToken();
+        if (":".equals(s)) {
+            _isPublic = false;
+        } else {
+            if ("true".equals(s))
+                _isPublic = true;
+            else
+                _isPublic = false;
+            s = tok.nextToken(); // skip past the :
+        }
+        s = tok.nextToken();
+        if (":".equals(s)) {
+            // noop
+        } else {
+            StringTokenizer gtok = new StringTokenizer(s, ",");
+            while (gtok.hasMoreTokens())
+                _groups.add(gtok.nextToken().trim());
+            s = tok.nextToken(); // skip past the :
+        }
+        if (tok.hasMoreTokens()) {
+            s = tok.nextToken();
+            if (":".equals(s)) {
+                _location = null;
+            } else {
+                _location = s;
+            }
+        } else {
+            _location = null;
+        }
+    }
+    
+    public String getName() { return _name; }
+    public String getNetwork() { return _network; }
+    public String getProtocol() { return _protocol; }
+    public String getLocation() { return _location; }
+    public boolean getIsPublic() { return _isPublic; }
+    public int getGroupCount() { return _groups.size(); }
+    public String getGroup(int i) { return (String)_groups.get(i); }
+    
+    public void setName(String name) { _name = name; }
+    public void setNetwork(String network) { _network = network; }
+    public void setProtocol(String protocol) { _protocol = protocol; }
+    public void setLocation(String location) { _location = location; }
+    public void setIsPublic(boolean pub) { _isPublic = pub; }
+    public void addGroup(String name) { 
+        if ( (name != null) && (name.length() > 0) && (!_groups.contains(name)) )
+            _groups.add(name);
+    }
+    public void removeGroup(String name) { _groups.remove(name); }
+    public void setGroups(String groups) {
+        if (groups != null) {
+            _groups.clear();
+            StringTokenizer tok = new StringTokenizer(groups, ", \t");
+            while (tok.hasMoreTokens())
+                addGroup(tok.nextToken().trim());
+        } else {
+            _groups.clear();
+        }
+    }
+    public boolean isMember(String group) {
+        for (int i = 0; i < getGroupCount(); i++)
+            if (getGroup(i).equals(group))
+                return true;
+        return false;
+    }
+    
+    public String toString() {
+        StringBuffer buf = new StringBuffer(256);
+        if (_name != null) buf.append(_name.trim());
+        buf.append(':');
+        if (_network != null) buf.append(_network.trim());
+        buf.append(':');
+        if (_protocol != null) buf.append(_protocol.trim());
+        buf.append(':').append(_isPublic).append(':');
+        if (_groups != null) {
+            for (int i = 0; i < _groups.size(); i++) {
+                buf.append(((String)_groups.get(i)).trim());
+                if (i + 1 < _groups.size())
+                    buf.append(',');
+            }
+        }
+        buf.append(':');
+        if (_location != null) buf.append(_location.trim());
+        return buf.toString();
+    }
+    
+    public boolean equals(Object obj) {
+        if ( (obj == null) || !(obj instanceof PetName) ) return false;
+        PetName pn = (PetName)obj;
+        return DataHelper.eq(_name, pn._name) &&
+               DataHelper.eq(_location, pn._location) &&
+               DataHelper.eq(_network, pn._network) &&
+               DataHelper.eq(_protocol, pn._protocol);
+    }
+    public int hashCode() {
+        int rv = 0;
+        rv += DataHelper.hashCode(_name);
+        rv += DataHelper.hashCode(_location);
+        rv += DataHelper.hashCode(_network);
+        rv += DataHelper.hashCode(_protocol);
+        return rv;
+    }
+    
+    public static void main(String args[]) {
+        test("a:b:c:d:e:f");
+        test("a:::::d");
+        test("a:::::");
+        test("a:b::::");
+        test(":::::");
+    }
+    private static void test(String line) {
+        PetName pn = new PetName(line);
+        String val = pn.toString();
+        System.out.println("OK? " + val.equals(line) + ": " + line);
+    }
+}
diff --git a/apps/syndie/java/src/net/i2p/syndie/PetNameDB.java b/apps/syndie/java/src/net/i2p/syndie/PetNameDB.java
new file mode 100644
index 0000000000000000000000000000000000000000..700464924c2e950e8ffbb098940ec8532599a935
--- /dev/null
+++ b/apps/syndie/java/src/net/i2p/syndie/PetNameDB.java
@@ -0,0 +1,74 @@
+package net.i2p.syndie;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ *
+ */
+public class PetNameDB {
+    /** name (String) to PetName mapping */
+    private Map _names;
+    
+    public PetNameDB() {
+        _names = Collections.synchronizedMap(new HashMap());
+    }
+
+    public PetName get(String name) { return (PetName)_names.get(name); } 
+    public boolean exists(String name) { return _names.containsKey(name); }
+    public void set(String name, PetName pn) { _names.put(name, pn); }
+    public void remove(String name) { _names.remove(name); }
+    public Set getNames() { return new HashSet(_names.keySet()); }
+    public List getGroups() {
+        List rv = new ArrayList();
+        for (Iterator iter = new HashSet(_names.values()).iterator(); iter.hasNext(); ) {
+            PetName name = (PetName)iter.next();
+            for (int i = 0; i < name.getGroupCount(); i++)
+                if (!rv.contains(name.getGroup(i)))
+                    rv.add(name.getGroup(i));
+        }
+        return rv;
+    }
+    
+    public String getNameByLocation(String location) { 
+        if (location == null) return null;
+        synchronized (_names) {
+            for (Iterator iter = _names.values().iterator(); iter.hasNext(); ) {
+                PetName name = (PetName)iter.next();
+                if ( (name.getLocation() != null) && (name.getLocation().trim().equals(location.trim())) )
+                    return name.getName();
+            }
+        }
+        return null;
+    }
+    
+    public void load(String location) throws IOException {
+        File f = new File(location);
+        if (!f.exists()) return;
+        BufferedReader in = null;
+        try {
+            in = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8"));
+            String line = null;
+            while ( (line = in.readLine()) != null) {
+                PetName name = new PetName(line);
+                if (name.getName() != null)
+                    _names.put(name.getName(), name);
+            }
+        } finally {
+            in.close();
+        }
+    }
+    public void store(String location) throws IOException {
+        Writer out = null;
+        try {
+            out = new OutputStreamWriter(new FileOutputStream(location), "UTF-8");
+            for (Iterator names = getNames().iterator(); names.hasNext(); ) {
+                PetName name = get((String)names.next());
+                if (name != null)
+                    out.write(name.toString() + "\n");
+            }
+        } finally {
+            out.close();
+        }
+    }
+}
diff --git a/apps/syndie/java/src/net/i2p/syndie/User.java b/apps/syndie/java/src/net/i2p/syndie/User.java
index c75e951e556eba42dc545ce0973fe1324a4ae2f8..b4f435c50c7b31b85767332e179ece8d81be16e5 100644
--- a/apps/syndie/java/src/net/i2p/syndie/User.java
+++ b/apps/syndie/java/src/net/i2p/syndie/User.java
@@ -1,6 +1,7 @@
 package net.i2p.syndie;
 
 import java.io.UnsupportedEncodingException;
+import java.io.IOException;
 import java.util.*;
 import net.i2p.I2PAppContext;
 import net.i2p.data.*;
@@ -36,6 +37,7 @@ public class User {
     private int _webProxyPort;
     private String _torProxyHost;
     private int _torProxyPort;
+    private PetNameDB _petnames;
     
     public User() {
         _context = I2PAppContext.getGlobalContext();
@@ -62,6 +64,7 @@ public class User {
         _torProxyPort = -1;
         _lastLogin = -1;
         _lastMetaEntry = 0;
+        _petnames = new PetNameDB();
     }
     
     public boolean getAuthenticated() { return _authenticated; }
@@ -92,6 +95,8 @@ public class User {
     public String getTorProxyHost() { return _torProxyHost; }
     public int getTorProxyPort() { return _torProxyPort; }
     
+    public PetNameDB getPetNameDB() { return _petnames; }
+    
     public void invalidate() { 
         BlogManager.instance().saveUser(this);
         init(); 
@@ -156,8 +161,14 @@ public class User {
         }
         
         String addr = props.getProperty("addressbook", "userhosts.txt");
-        if (addr != null)
+        if (addr != null) {
             _addressbookLocation = addr;
+            try {
+                _petnames.load(addr);
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+            }
+        }
         
         String show = props.getProperty("showimages", "false");
         _showImagesByDefault = (show != null) && (show.equals("true"));
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/EventReceiverImpl.java b/apps/syndie/java/src/net/i2p/syndie/sml/EventReceiverImpl.java
index e00fb3ccef9ce837507e2cbbc64e67a93b5bb28c..3b8bfb5b6a775bf7dfba9d304c8e4b26195e2585 100644
--- a/apps/syndie/java/src/net/i2p/syndie/sml/EventReceiverImpl.java
+++ b/apps/syndie/java/src/net/i2p/syndie/sml/EventReceiverImpl.java
@@ -25,7 +25,7 @@ public class EventReceiverImpl implements SMLParser.EventReceiver {
     public void receiveImage(String alternateText, int attachmentId) {
         System.out.println("Receive image [" + alternateText + "]/[" + attachmentId + "]");
     }
-    public void receiveAddress(String name, String schema, String location, String anchorText) {
+    public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) {
         System.out.println("Receive address [" + name + "]/[" + schema + "]/[" + location + "]/[" + anchorText+ "]");
     }
     public void receiveBold(String text) { System.out.println("Receive bold [" + text+ "]"); }
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLPreviewRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLPreviewRenderer.java
index 8a9ad82fddd8318167f919bffbbf7745a17a3244..21274d79cde871abf812ac8fc673c50abb74dbdf 100644
--- a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLPreviewRenderer.java
+++ b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLPreviewRenderer.java
@@ -94,11 +94,20 @@ public class HTMLPreviewRenderer extends HTMLRenderer {
             _postBodyBuffer.append("<b>Addresses:</b> ");
             for (int i = 0; i < _addresses.size(); i++) {
                 Address a = (Address)_addresses.get(i);
-                _postBodyBuffer.append("<a href=\"addaddress.jsp?schema=");
-                _postBodyBuffer.append(sanitizeURL(a.schema)).append("&location=");
-                _postBodyBuffer.append(sanitizeURL(a.location)).append("&name=");
-                _postBodyBuffer.append(sanitizeURL(a.name));
-                _postBodyBuffer.append("\">").append(sanitizeString(a.name));
+                
+                String knownName = null;
+                if (_user != null)
+                    knownName = _user.getPetNameDB().getNameByLocation(a.location);
+                if (knownName != null) {
+                    _postBodyBuffer.append(' ').append(sanitizeString(knownName));
+                } else {
+                    _postBodyBuffer.append(" <a href=\"addaddress.jsp?schema=");
+                    _postBodyBuffer.append(sanitizeURL(a.schema)).append("&location=");
+                    _postBodyBuffer.append(sanitizeURL(a.location)).append("&name=");
+                    _postBodyBuffer.append(sanitizeURL(a.protocol)).append("&protocol=");
+                    _postBodyBuffer.append(sanitizeURL(a.name));
+                    _postBodyBuffer.append("\">").append(sanitizeString(a.name));
+                }
             }
             _postBodyBuffer.append("<br />\n");
         }
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java
index b9a957441e732960103691d23624844809b87c88..57032b5c46992e1dadbd7d8cd6b3f59908529802 100644
--- a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java
+++ b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java
@@ -387,25 +387,37 @@ public class HTMLRenderer extends EventReceiverImpl {
         public String name;
         public String schema;
         public String location;
+        public String protocol;
         public int hashCode() { return -1; }
         public boolean equals(Object o) {
             Address a = (Address)o;
-            return DataHelper.eq(schema, a.schema) && DataHelper.eq(location, a.location) && DataHelper.eq(name, a.name);
+            return DataHelper.eq(schema, a.schema) && DataHelper.eq(location, a.location) && DataHelper.eq(protocol, a.protocol) && DataHelper.eq(name, a.name);
         }
     }
-    public void receiveAddress(String name, String schema, String location, String anchorText) {
+    public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) {
         Address a = new Address();
         a.name = name;
         a.schema = schema;
         a.location = location;
+        a.protocol = protocol;
         if (!_addresses.contains(a))
             _addresses.add(a);
         if (!continueBody()) { return; }
         if ( (schema == null) || (location == null) ) return;
-        _bodyBuffer.append("<a href=\"addaddress.jsp?schema=");
-        _bodyBuffer.append(sanitizeURL(schema)).append("&name=");
-        _bodyBuffer.append(sanitizeURL(name)).append("&location=");
-        _bodyBuffer.append(sanitizeURL(location)).append("\">").append(sanitizeString(anchorText)).append("</a>");
+        String knownName = null;
+        if (_user != null)
+            knownName = _user.getPetNameDB().getNameByLocation(location);
+        if (knownName != null) {
+            _bodyBuffer.append(sanitizeString(anchorText));
+            _bodyBuffer.append(" <i>(").append(sanitizeString(knownName)).append(")</i>");
+        } else {
+            System.err.println("Receiving address [" + location + "]");
+            _bodyBuffer.append("<a href=\"addaddress.jsp?schema=");
+            _bodyBuffer.append(sanitizeURL(schema)).append("&name=");
+            _bodyBuffer.append(sanitizeURL(name)).append("&protocol=");
+            _bodyBuffer.append(sanitizeURL(protocol)).append("&location=");
+            _bodyBuffer.append(sanitizeURL(location)).append("\">").append(sanitizeString(anchorText)).append("</a>");
+        }
     }
     
     public void receiveAttachment(int id, String anchorText) {
@@ -540,11 +552,20 @@ public class HTMLRenderer extends EventReceiverImpl {
                 _postBodyBuffer.append("<b>Addresses:</b>");
                 for (int i = 0; i < _addresses.size(); i++) {
                     Address a = (Address)_addresses.get(i);
-                    _postBodyBuffer.append(" <a href=\"addaddress.jsp?schema=");
-                    _postBodyBuffer.append(sanitizeURL(a.schema)).append("&location=");
-                    _postBodyBuffer.append(sanitizeURL(a.location)).append("&name=");
-                    _postBodyBuffer.append(sanitizeURL(a.name));
-                    _postBodyBuffer.append("\">").append(sanitizeString(a.name));
+                    
+                    String knownName = null;
+                    if (_user != null)
+                        knownName = _user.getPetNameDB().getNameByLocation(a.location);
+                    if (knownName != null) {
+                        _postBodyBuffer.append(' ').append(sanitizeString(knownName));
+                    } else {
+                        _postBodyBuffer.append(" <a href=\"addaddress.jsp?schema=");
+                        _postBodyBuffer.append(sanitizeURL(a.schema)).append("&location=");
+                        _postBodyBuffer.append(sanitizeURL(a.location)).append("&name=");
+                        _postBodyBuffer.append(sanitizeURL(a.name)).append("&protocol=");
+                        _postBodyBuffer.append(sanitizeURL(a.protocol));
+                        _postBodyBuffer.append("\">").append(sanitizeString(a.name));
+                    }
                 }
                 _postBodyBuffer.append("<br />\n");
             }
@@ -632,20 +653,47 @@ public class HTMLRenderer extends EventReceiverImpl {
         if ( (tags != null) && (tags.length > 0) )
             _preBodyBuffer.append("<form action=\"index.jsp\">");
         _preBodyBuffer.append("<td nowrap=\"true\" align=\"right\" valign=\"top\" class=\"syndieEntryMetaCell\">\n");
+        
+        String knownName = null;
+        if ( (_entry != null) && (_user != null) )
+            knownName = _user.getPetNameDB().getNameByLocation(_entry.getURI().getKeyHash().toBase64());
+        //if (knownName != null)
+        //    _preBodyBuffer.append("Pet name: ").append(sanitizeString(knownName)).append(" ");
+
         BlogInfo info = null;
         if (_entry != null) 
             info = _archive.getBlogInfo(_entry.getURI());
         if (info != null) {
             _preBodyBuffer.append("<a href=\"").append(getMetadataURL()).append("\">");
-            String nameStr = info.getProperty("Name");
-            if (nameStr == null)
-                _preBodyBuffer.append("[no name]");
-            else
-                _preBodyBuffer.append(sanitizeString(nameStr));
+            if (knownName != null) {
+                _preBodyBuffer.append(sanitizeString(knownName));
+            } else {
+                String nameStr = info.getProperty("Name");
+                if (nameStr == null)
+                    _preBodyBuffer.append("[no name]");
+                else
+                    _preBodyBuffer.append(sanitizeString(nameStr));
+            }
             _preBodyBuffer.append("</a>");
         } else {
             _preBodyBuffer.append("[unknown blog]");
         }
+
+        
+        if ( (_user != null) && (_user.getAuthenticated()) && (_entry != null) ) {
+            PetName pn = _user.getPetNameDB().get(knownName);
+            if ( (pn == null) || (!pn.isMember("Favorites")) )
+                _preBodyBuffer.append(" <input type=\"submit\" name=\"action\" value=\"Bookmark blog\" />");
+            if ( (pn == null) || (!pn.isMember("Ignore")) )
+                _preBodyBuffer.append(" <input type=\"submit\" name=\"action\" value=\"Ignore blog\" />");
+            else
+                _preBodyBuffer.append(" <input type=\"submit\" name=\"action\" value=\"Unignore blog\" />");
+            _preBodyBuffer.append(" <input type=\"hidden\" name=\"blog\" value=\"").append(_entry.getURI().getKeyHash().toBase64()).append("\" />");
+            if (info != null)
+                _preBodyBuffer.append(" <input type=\"hidden\" name=\"name\" value=\"").append(sanitizeTagParam(info.getProperty("Name"))).append("\" />");
+        }
+
+        
         if ( (tags != null) && (tags.length > 0) ) {
             _preBodyBuffer.append(" Tags: ");
             _preBodyBuffer.append("<select name=\"selector\">");
@@ -680,8 +728,9 @@ public class HTMLRenderer extends EventReceiverImpl {
             _preBodyBuffer.append(getEntryDate(_entry.getURI().getEntryId()));
         else
             _preBodyBuffer.append(getEntryDate(new Date().getTime()));
-        if ( (_user != null) && (_user.getAuthenticated()) )
+        if ( (_user != null) && (_user.getAuthenticated()) ) {
             _preBodyBuffer.append(" <a href=\"").append(getPostURL(_user.getBlog(), true)).append("\">Reply</a>\n");
+        }
         _preBodyBuffer.append("\n</td>");
         if ( (tags != null) && (tags.length > 0) )
             _preBodyBuffer.append("</form>");
@@ -694,7 +743,7 @@ public class HTMLRenderer extends EventReceiverImpl {
             try {
                 String str = _dateFormat.format(new Date(when));
                 long dayBegin = _dateFormat.parse(str).getTime();
-                return str + "." + (when - dayBegin);
+                return str + " [" + (when - dayBegin) + "]";
             } catch (ParseException pe) {
                 pe.printStackTrace();
                 // wtf
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/SMLParser.java b/apps/syndie/java/src/net/i2p/syndie/sml/SMLParser.java
index 1560fb276bdf5d82be82ad9b1535cdefbb4bc20b..d9d3be9ad6c7dcc010d3ccc2230788c7150bd48b 100644
--- a/apps/syndie/java/src/net/i2p/syndie/sml/SMLParser.java
+++ b/apps/syndie/java/src/net/i2p/syndie/sml/SMLParser.java
@@ -211,6 +211,7 @@ public class SMLParser {
     private static final String P_ADDRESS_NAME = "name";
     private static final String P_ADDRESS_LOCATION = "location";
     private static final String P_ADDRESS_SCHEMA = "schema";
+    private static final String P_ADDRESS_PROTOCOL = "proto";
     private static final String P_ATTACHMENT_ID = "id";
     private static final String P_ARCHIVE_NAME = "name";
     private static final String P_ARCHIVE_DESCRIPTION = "description";
@@ -254,7 +255,7 @@ public class SMLParser {
         } else if (T_LINK.equals(tagName)) {
             receiver.receiveLink(getString(P_LINK_SCHEMA, attr), getString(P_LINK_LOCATION, attr), body);
         } else if (T_ADDRESS.equals(tagName)) {
-            receiver.receiveAddress(getString(P_ADDRESS_NAME, attr), getString(P_ADDRESS_SCHEMA, attr), getString(P_ADDRESS_LOCATION, attr), body);
+            receiver.receiveAddress(getString(P_ADDRESS_NAME, attr), getString(P_ADDRESS_SCHEMA, attr), getString(P_ADDRESS_PROTOCOL, attr), getString(P_ADDRESS_LOCATION, attr), body);
         } else if (T_H1.equals(tagName)) {
             receiver.receiveH1(body);
         } else if (T_H2.equals(tagName)) {
@@ -381,7 +382,7 @@ public class SMLParser {
         public void receiveArchive(String name, String description, String locationSchema, String location, 
                                    String postingKey, String anchorText);
         public void receiveImage(String alternateText, int attachmentId);
-        public void receiveAddress(String name, String schema, String location, String anchorText);
+        public void receiveAddress(String name, String schema, String protocol, String location, String anchorText);
         public void receiveAttachment(int id, String anchorText);
         public void receiveBold(String text);
         public void receiveItalic(String text);
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ArchiveViewerBean.java b/apps/syndie/java/src/net/i2p/syndie/web/ArchiveViewerBean.java
index 3cdfc609a4c723c302fd938a534cfd38f511a2ea..7d56d035e37f01a7cbc061def5c27f087bfc9f32 100644
--- a/apps/syndie/java/src/net/i2p/syndie/web/ArchiveViewerBean.java
+++ b/apps/syndie/java/src/net/i2p/syndie/web/ArchiveViewerBean.java
@@ -102,12 +102,12 @@ public class ArchiveViewerBean {
         out.write(SEL_ALL);
         out.write("\">All posts from all blogs</option>\n");
         
-        Map groups = null;
+        List groups = null;
         if (user != null)
-            groups = user.getBlogGroups();
+            groups = user.getPetNameDB().getGroups();
         if (groups != null) {
-            for (Iterator iter = groups.keySet().iterator(); iter.hasNext(); ) {
-                String name = (String)iter.next();
+            for (int i = 0; i < groups.size(); i++) {
+                String name = (String)groups.get(i);
                 out.write("<option value=\"group://" + Base64.encode(DataHelper.getUTF8(name)) + "\">" +
                           "Group: " + HTMLRenderer.sanitizeString(name) + "</option>\n");
             }
@@ -226,6 +226,7 @@ public class ArchiveViewerBean {
         if (blogStr != null) blog = new Hash(Base64.decode(blogStr));
         String tag = getString(parameters, PARAM_TAG);
         if (tag != null) tag = DataHelper.getUTF8(Base64.decode(tag));
+        
         long entryId = -1;
         if (blogStr != null) {
             String entryIdStr = getString(parameters, PARAM_ENTRY);
@@ -237,6 +238,14 @@ public class ArchiveViewerBean {
         if (group != null) group = DataHelper.getUTF8(Base64.decode(group));
         
         String sel = getString(parameters, PARAM_SELECTOR);
+        
+        if (getString(parameters, "action") != null) {
+            tag = null;
+            blog = null;
+            sel = null;
+            group = null;
+        }
+        
         if ( (sel == null) && (blog == null) && (group == null) && (tag == null) )
             sel = getDefaultSelector(user, parameters);
         if (sel != null) {
@@ -453,13 +462,43 @@ public class ArchiveViewerBean {
                     else
                         index.selectMatchesOrderByEntryId(rv, s.blog, s.tag);
                 }
-                return rv;
             }
+            PetNameDB db = user.getPetNameDB();
+            for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
+                String name = (String)iter.next();
+                PetName pn = db.get(name);
+                if ("syndie".equals(pn.getNetwork()) && "syndieblog".equals(pn.getProtocol()) && pn.isMember(group)) {
+                    byte pnLoc[] = Base64.decode(pn.getLocation());
+                    if (pnLoc != null) {
+                        Hash pnHash = new Hash(pnLoc);
+                        index.selectMatchesOrderByEntryId(rv, pnHash, null);
+                    }
+                }
+            }
+            if (rv.size() > 0)
+                return rv;
         }
         index.selectMatchesOrderByEntryId(rv, blog, tag);
+        filterIgnored(user, rv);
         return rv;
     }
     
+    private static void filterIgnored(User user, List uris) {
+        for (int i = 0; i < uris.size(); i++) {
+            BlogURI uri = (BlogURI)uris.get(i);
+            Hash k = uri.getKeyHash();
+            if (k == null) continue;
+            String pname = user.getPetNameDB().getNameByLocation(k.toBase64());
+            if (pname != null) {
+                PetName pn = user.getPetNameDB().get(pname);
+                if ( (pn != null) && (pn.isMember("Ignore")) ) {
+                    uris.remove(i);
+                    i--;
+                }
+            }
+        }
+    }
+    
     public static final String getString(Map parameters, String param) {
         if ( (parameters == null) || (parameters.get(param) == null) )
             return null;
diff --git a/apps/syndie/jsp/_bodyindex.jsp b/apps/syndie/jsp/_bodyindex.jsp
index adafede9d12f74f361fbab593dfc6e52213b9087..e31810486bfa0f2e03a6c8d867acff876b1f8dcd 100644
--- a/apps/syndie/jsp/_bodyindex.jsp
+++ b/apps/syndie/jsp/_bodyindex.jsp
@@ -1,6 +1,37 @@
 <%@page contentType="text/html; charset=UTF-8" import="net.i2p.syndie.web.ArchiveViewerBean, net.i2p.syndie.*" %>
 <% request.setCharacterEncoding("UTF-8"); %>
-<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" /><table border="0" width="100%">
+<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" /><%
+if (user.getAuthenticated() && (null != request.getParameter("action")) ) {
+  %><!-- <%=request.getParameterMap()%> --><%
+  String blog = request.getParameter("blog");
+  String group = null;
+  if (request.getParameter("action").equals("Bookmark blog"))
+    group = "Favorites";
+  else if (request.getParameter("action").equals("Ignore blog"))
+    group = "Ignore";
+  boolean unignore = ("Unignore blog".equals(request.getParameter("action")));
+
+  String name = user.getPetNameDB().getNameByLocation(blog);
+  if (name == null)
+    name = request.getParameter("name");
+  if (name == null)
+    name = blog;
+  if ( (name != null) && (blog != null) && ( (group != null) || (unignore) ) ) {
+    PetName pn = user.getPetNameDB().get(name);
+    if (pn != null) {
+      if (unignore)
+        pn.removeGroup("Ignore");
+      else
+        pn.addGroup(group);
+    } else {
+      pn = new PetName(name, "syndie", "syndieblog", blog);
+      pn.addGroup(group);
+      user.getPetNameDB().set(name, pn);
+    }
+    BlogManager.instance().saveUser(user);
+  }
+}
+%><table border="0" width="100%">
 <tr><form action="index.jsp"><td nowrap="true">
 <b>Blogs:</b> <%ArchiveViewerBean.renderBlogSelector(user, request.getParameterMap(), out);%>
 <input type="submit" value="Refresh" />
diff --git a/apps/syndie/jsp/_topnav.jsp b/apps/syndie/jsp/_topnav.jsp
index 43a2055d2d2497455581e064ed3b94e6a5b8735e..66c1ac764638243a21facf1df3d3a91b433e8fff 100644
--- a/apps/syndie/jsp/_topnav.jsp
+++ b/apps/syndie/jsp/_topnav.jsp
@@ -24,6 +24,7 @@ Logged in as: <b><jsp:getProperty property="username" name="user" />:</b>
 <a href="<%=HTMLRenderer.getPageURL(user.getBlog(), null, -1, -1, -1, user.getShowExpanded(), user.getShowImages())%>"><%=HTMLRenderer.sanitizeString(ArchiveViewerBean.getBlogName(user.getBlogStr()))%></a>
 <a href="<%=HTMLRenderer.getPostURL(user.getBlog())%>">Post</a>
 <a href="<%=HTMLRenderer.getMetadataURL(user.getBlog())%>">Metadata</a>
+<a href="addresses.jsp">Addressbook</a>
 <a href="index.jsp?logout=true">Logout</a><br />
 <%} else {%>
 Login: <input type="text" name="login" size="8" />
diff --git a/apps/syndie/jsp/addaddress.jsp b/apps/syndie/jsp/addaddress.jsp
index 681e186dc48df11b37219af13f14a34c28d57ac1..9a63e9774d9664b51ad0346ea9f80c461b5d199f 100644
--- a/apps/syndie/jsp/addaddress.jsp
+++ b/apps/syndie/jsp/addaddress.jsp
@@ -14,13 +14,17 @@
     <td valign="top" align="left" rowspan="2"><jsp:include page="_rightnav.jsp" /></td></tr>
 <tr><td valign="top" align="left" colspan="3"><%
 String nameStr = request.getParameter("name");
+String protoStr = request.getParameter("proto");
 String locStr = request.getParameter("location");
 String schemaStr = request.getParameter("schema");
 String name = null;
+String proto = null;
 String location = null;
 String schema = null;
 try {
     name = DataHelper.getUTF8(Base64.decode(nameStr));
+    if ( (protoStr != null) && (protoStr.trim().length() > 0) )
+      proto = DataHelper.getUTF8(Base64.decode(protoStr));
     location = DataHelper.getUTF8(Base64.decode(locStr));
     schema = DataHelper.getUTF8(Base64.decode(schemaStr));
 } catch (NullPointerException npe) {
@@ -30,7 +34,7 @@ try {
 if ( (name == null) || (location == null) || (schema == null) ) {
   out.write("<b>No location specified</b>");
 } else if (user.getAuthenticated() && ("Add".equals(request.getParameter("action"))) ) {
-  out.write("<b>" + BlogManager.instance().addAddress(user, name, location, schema) + "</b>");
+  out.write("<b>" + BlogManager.instance().addAddress(user, name, proto, location, schema) + "</b>");
 } else { %>Are you sure you really want to add the
 addressbook mapping of <%=HTMLRenderer.sanitizeString(name)%> to
 <input type="text" size="20" value="<%=HTMLRenderer.sanitizeString(location)%>" />, applicable within the
diff --git a/apps/syndie/jsp/addresses.jsp b/apps/syndie/jsp/addresses.jsp
new file mode 100644
index 0000000000000000000000000000000000000000..e591dcad1cc11409ca3e1291d3514520bcbdff73
--- /dev/null
+++ b/apps/syndie/jsp/addresses.jsp
@@ -0,0 +1,158 @@
+<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.data.*, net.i2p.syndie.*, org.mortbay.servlet.MultiPartRequest, java.util.*, java.io.*" %>
+<% request.setCharacterEncoding("UTF-8"); %>
+<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
+<html>
+<head>
+<title>SyndieMedia addressbook</title>
+<link href="style.jsp" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<table border="1" cellpadding="0" cellspacing="0" width="100%">
+<tr><td colspan="5" valign="top" align="left"><jsp:include page="_toplogo.jsp" /></td></tr>
+<tr><td valign="top" align="left" rowspan="2"><jsp:include page="_leftnav.jsp" /></td>
+    <jsp:include page="_topnav.jsp" />
+    <td valign="top" align="left" rowspan="2"><jsp:include page="_rightnav.jsp" /></td></tr>
+<tr><td valign="top" align="left" colspan="3"><%
+if (!user.getAuthenticated()) { 
+    %>You must log in to view your addressbook<% 
+} else {
+    PetNameDB names = user.getPetNameDB();
+    String action = request.getParameter("action");
+    if ( (action != null) && ("Change".equals(action)) ) {
+        String oldPetname = request.getParameter("petname");
+        PetName cur = names.get(oldPetname);
+        if (cur != null) {
+          cur.setName(request.getParameter("name"));
+          cur.setNetwork(request.getParameter("network"));
+          cur.setProtocol(request.getParameter("protocol"));
+          cur.setIsPublic(null != request.getParameter("isPublic"));
+          cur.setLocation(request.getParameter("location"));
+          cur.setGroups(request.getParameter("groups"));
+          names.store(user.getAddressbookLocation());
+          %><b>Address updated</b><%
+        }
+    } else if ( (action != null) && ("Add".equals(action)) ) {
+        PetName cur = names.get(request.getParameter("name"));
+        if (cur != null) { %><b>Address already exists</b><% } else {
+          cur = new PetName();
+          cur.setName(request.getParameter("name"));
+          cur.setNetwork(request.getParameter("network"));
+          cur.setProtocol(request.getParameter("protocol"));
+          cur.setIsPublic(null != request.getParameter("isPublic"));
+          cur.setLocation(request.getParameter("location"));
+          cur.setGroups(request.getParameter("groups"));
+          names.set(cur.getName(), cur);
+          names.store(user.getAddressbookLocation());
+          %><b>Address added</b><%
+        }
+    } else if ( (action != null) && ("Delete".equals(action)) ) {
+        PetName cur = names.get(request.getParameter("name"));
+        if (cur != null) { 
+          names.remove(cur.getName());
+          names.store(user.getAddressbookLocation());
+          %><b>Address removed</b><%
+        }
+    }
+    TreeSet sorted = new TreeSet(names.getNames());
+    %><table border="0" width="100%">
+<tr><td><b>Name</b></td><td><b>Network</b></td><td><b>Protocol</b></td><td><b>Location</b></td><td><b>Public?</b></td><td><b>Groups</b><td>&nbsp;</td></tr>
+<%
+    StringBuffer buf = new StringBuffer(128);
+    for (Iterator iter = sorted.iterator(); iter.hasNext(); ) {
+        PetName name = names.get((String)iter.next());
+        buf.append("<tr><form action=\"addresses.jsp\" method=\"POST\">");
+        buf.append("<input type=\"hidden\" name=\"petname\" value=\"").append(name.getName()).append("\" />");
+        buf.append("<td><input type=\"text\" size=\"20\" name=\"name\" value=\"").append(name.getName()).append("\" /></td><td>");
+        buf.append("<select name=\"network\">");
+        String net = name.getNetwork();
+        if (net == null) net = "";
+        buf.append("<option value=\"i2p\" ");
+        if ("i2p".equals(net))
+            buf.append("selected=\"true\" ");
+        buf.append("/>I2P</option>");
+
+        buf.append("<option value=\"syndie\" ");
+        if ( ("syndie".equals(net)) || ("".equals(net)) )
+            buf.append("selected=\"true\" ");
+        buf.append("/>Syndie</option>");
+
+        buf.append("<option value=\"tor\" ");
+        if ("tor".equals(net))
+            buf.append("selected=\"true\" ");
+        buf.append("/>TOR</option>");
+
+        buf.append("<option value=\"freenet\" ");
+        if ("freenet".equals(net))
+            buf.append("selected=\"true\" ");
+        buf.append("/>Freenet</option>");
+
+        buf.append("<option value=\"internet\" ");
+        if ("internet".equals(net))
+            buf.append("selected=\"true\" ");
+        buf.append("/>Internet</option>");
+
+        buf.append("</select></td><td><select name=\"protocol\">");
+        String proto = name.getProtocol();
+        if (proto == null) proto = "";
+
+        buf.append("<option value=\"http\" ");
+        if ("http".equals(proto))
+            buf.append("selected=\"true\" ");
+        buf.append("/>HTTP</option>");
+
+        buf.append("<option value=\"irc\" ");
+        if ("irc".equals(proto))
+            buf.append("selected=\"true\" ");
+        buf.append("/>IRC</option>");
+
+        buf.append("<option value=\"i2phex\" ");
+        if ("i2phex".equals(proto))
+            buf.append("selected=\"true\" ");
+        buf.append("/>I2Phex</option>");
+
+        buf.append("<option value=\"syndiearchive\" ");
+        if ("syndiearchive".equals(proto))
+            buf.append("selected=\"true\" ");
+        buf.append("/>Syndie archive</option>");
+
+        buf.append("<option value=\"syndieblog\" ");
+        if ("syndieblog".equals(proto))
+            buf.append("selected=\"true\" ");
+        buf.append("/>Syndie blog</option>");
+
+        buf.append("</select></td><td>");
+        if (name.getLocation() != null)
+            buf.append("<input name=\"location\" size=\"50\" value=\"").append(name.getLocation()).append("\" />");
+        else
+            buf.append("<input name=\"location\" size=\"50\" value=\"\" />");
+
+        buf.append("</td><td><input type=\"checkbox\" name=\"isPublic\" ");
+        if (name.getIsPublic())
+            buf.append("checked=\"true\" ");
+        buf.append(" /></td><td><input type=\"text\" name=\"groups\" size=\"10\" value=\"");
+        for (int j = 0; j < name.getGroupCount(); j++) {
+            buf.append(HTMLRenderer.sanitizeTagParam(name.getGroup(j)));
+            if (j + 1 < name.getGroupCount()) 
+                buf.append(',');
+        }
+        buf.append("\" /></td><td nowrap=\"true\">");
+        buf.append("<input type=\"submit\" name=\"action\" value=\"Change\" /> <input type=\"submit\" name=\"action\" value=\"Delete\" />");
+        buf.append("</td></form></tr>");
+        out.write(buf.toString());
+        buf.setLength(0);
+    }
+    %>
+    <tr><form action="addresses.jsp" method="POST"><td><input type="text" name="name" size="20" /></td>
+        <td><select name="network"><option value="i2p">I2P</option><option value="syndie">Syndie</option><option value="tor">Tor</option><option value="freenet">Freenet</option><option value="internet">Internet</option></select></td>
+        <td><select name="protocol"><option value="http">HTTP</option><option value="irc">IRC</option><option value="i2phex">I2Phex</option><option value="syndiearchive">Syndie archive</option><option value="syndieblog">Syndie blog</option></select></td>
+        <td><input type="text" size="50" name="location" /></td>
+        <td><input type="checkbox" name="isPublic" /></td>
+        <td><input type="text" name="groups" size="10" /></td>
+        <td><input type="submit" name="action" value="Add" /></td>
+    </form></tr>
+    </table><%
+}
+%>
+</td></tr>
+</table>
+</body>
diff --git a/history.txt b/history.txt
index ef2770b47b96b01f14934d5df18f1faf20778ed6..393a46bf24e5a020b53c14a6cf6c47ec16d56e74 100644
--- a/history.txt
+++ b/history.txt
@@ -1,4 +1,12 @@
-$Id: history.txt,v 1.236 2005/09/02 13:34:14 jrandom Exp $
+$Id: history.txt,v 1.237 2005/09/02 14:10:05 jrandom Exp $
+
+2005-09-04  jrandom
+    * Don't persist peer profiles until we are shutting down, as the 
+      persistence process gobbles RAM and wall time.
+    * Bugfix to allow you to check/uncheck the sharedClient setting on the
+      I2PTunnel web interface.
+    * Be more careful when expiring a failed tunnel message fragment so we 
+      don't drop the data while attempting to read it.
 
 * 2005-09-02  0.6.0.5 released
 
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 02d92e0b7be14aca33bae3c2fcef781bf33ba9cb..7ac878557595a23a4a75a769ab928d9c6731054a 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
  *
  */
 public class RouterVersion {
-    public final static String ID = "$Revision: 1.225 $ $Date: 2005/09/02 13:34:15 $";
+    public final static String ID = "$Revision: 1.226 $ $Date: 2005/09/02 14:10:06 $";
     public final static String VERSION = "0.6.0.5";
-    public final static long BUILD = 0;
+    public final static long BUILD = 1;
     public static void main(String args[]) {
         System.out.println("I2P Router version: " + VERSION);
         System.out.println("Router ID: " + RouterVersion.ID);
diff --git a/router/java/src/net/i2p/router/peermanager/PeerManager.java b/router/java/src/net/i2p/router/peermanager/PeerManager.java
index 68aa11e9b7ec241a4d3de7e8188c9dae92963abd..9eae7279156c4b3e403fdd530b1352659d30024c 100644
--- a/router/java/src/net/i2p/router/peermanager/PeerManager.java
+++ b/router/java/src/net/i2p/router/peermanager/PeerManager.java
@@ -39,7 +39,7 @@ class PeerManager {
         _organizer.setUs(context.routerHash());
         loadProfiles();
         _context.jobQueue().addJob(new EvaluateProfilesJob(_context));
-        _context.jobQueue().addJob(new PersistProfilesJob(_context, this));
+        //_context.jobQueue().addJob(new PersistProfilesJob(_context, this));
     }
     
     void storeProfiles() {
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
index 4b80c32e12e66d8b450ff1d07f2092077f7c1aea..b3ea7dac9788cf3e0584032bb1fab9321eb3d34b 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
@@ -408,7 +408,7 @@ class PeerTestManager {
                 charlieInfo = _context.netDb().lookupRouterInfoLocally(charlie.getRemotePeer());
         }
         
-        if (charlie == null) {
+        if ( (charlie == null) || (charlieInfo == null) ) {
             if (_log.shouldLog(Log.WARN))
                 _log.warn("Unable to pick a charlie");
             return;
diff --git a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java
index 9aefba1f39553062311aaefe4545971357be2be9..e8f81d50b494fb488055bd320120c9e0e206def7 100644
--- a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java
+++ b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java
@@ -353,6 +353,8 @@ public class FragmentHandler {
         _completed++;
         try {
             byte data[] = msg.toByteArray();
+            if (msg == null)
+                return;
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("RECV(" + data.length + "): " + Base64.encode(data)  
                            + " " + _context.sha().calculateHash(data).toBase64());
diff --git a/router/java/src/net/i2p/router/tunnel/FragmentedMessage.java b/router/java/src/net/i2p/router/tunnel/FragmentedMessage.java
index d39a5e56b2b950c86f1c0f516db6620b27083366..186843d815512fa7f5518553b267d091cfcb3d80 100644
--- a/router/java/src/net/i2p/router/tunnel/FragmentedMessage.java
+++ b/router/java/src/net/i2p/router/tunnel/FragmentedMessage.java
@@ -214,15 +214,20 @@ public class FragmentedMessage {
         _completed = true;
     }
     public byte[] toByteArray() {
-        byte rv[] = new byte[getCompleteSize()];
-        writeComplete(rv, 0);
-        releaseFragments();
-        return rv;
+        synchronized (this) {
+            if (_releasedAfter > 0) return null;
+            byte rv[] = new byte[getCompleteSize()];
+            writeComplete(rv, 0);
+            releaseFragments();
+            return rv;
+        }
     }
     
     public long getReleasedAfter() { return _releasedAfter; }
     public void failed() {
-        releaseFragments();
+        synchronized (this) {
+            releaseFragments();
+        }
     }
 
     /**