From ddd438de35fbe3d75565ba0a1660cac2e44a66b7 Mon Sep 17 00:00:00 2001
From: jrandom <jrandom>
Date: Sun, 13 Nov 2005 14:15:26 +0000
Subject: [PATCH] protect against spoofing in syndie with a per-jvm-instance
 nonce (which should prevent the spurious "are you being spoofed" things)
 fixed the syndie previewing

---
 .../java/src/net/i2p/syndie/BlogManager.java  |  2 +
 .../i2p/syndie/sml/ThreadedHTMLRenderer.java  | 32 ++++++++-
 .../net/i2p/syndie/web/AddressesServlet.java  | 12 ++++
 .../src/net/i2p/syndie/web/AdminServlet.java  |  3 +
 .../src/net/i2p/syndie/web/BaseServlet.java   | 68 +++++++++++++++++--
 .../src/net/i2p/syndie/web/PostServlet.java   | 25 +++++--
 .../net/i2p/syndie/web/ProfileServlet.java    |  3 +
 .../src/net/i2p/syndie/web/SwitchServlet.java | 45 ++++++------
 .../i2p/syndie/web/ViewThreadedServlet.java   |  5 +-
 9 files changed, 157 insertions(+), 38 deletions(-)

diff --git a/apps/syndie/java/src/net/i2p/syndie/BlogManager.java b/apps/syndie/java/src/net/i2p/syndie/BlogManager.java
index cc15b64509..d250ea9038 100644
--- a/apps/syndie/java/src/net/i2p/syndie/BlogManager.java
+++ b/apps/syndie/java/src/net/i2p/syndie/BlogManager.java
@@ -749,6 +749,8 @@ public class BlogManager {
                     int split = line.indexOf('=');
                     int split2 = line.indexOf(':');
                     if ( (split < 0) || ( (split2 > 0) && (split2 < split) ) ) split = split2;
+                    if ( (split < 0) && (split2 < 0) )
+                        continue;
                     String key = line.substring(0,split).trim();
                     String val = line.substring(split+1).trim();
                     raw.append(key).append(": ").append(val).append('\n');
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java
index 9c34631b8a..fe0da2216c 100644
--- a/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java
+++ b/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java
@@ -17,6 +17,7 @@ import net.i2p.util.Log;
 public class ThreadedHTMLRenderer extends HTMLRenderer {
     private Log _log;
     private String _baseURI;
+    private boolean _inlineReply;
     
     public ThreadedHTMLRenderer(I2PAppContext ctx) {
         super(ctx);
@@ -118,8 +119,11 @@ public class ThreadedHTMLRenderer extends HTMLRenderer {
     
     private static final boolean empty(String val) { return (val == null) || (val.trim().length() <= 0); }
     
+    /**
+     * @param replyHiddenFields HTML of hidden input fields necessary for the reply form to be honored
+     */
     public void render(User user, Writer out, Archive archive, BlogURI post, 
-                       boolean inlineReply, ThreadIndex index, String baseURI,
+                       boolean inlineReply, ThreadIndex index, String baseURI, String replyHiddenFields,
                        String offset, String requestTags, String filteredAuthor) throws IOException {
         EntryContainer entry = archive.getEntry(post);
         if (entry == null) return;
@@ -131,6 +135,7 @@ public class ThreadedHTMLRenderer extends HTMLRenderer {
         _archive = archive;
         _cutBody = false;
         _showImages = true;
+        _inlineReply = inlineReply;
         _headers = new HashMap();
         _bodyBuffer = new StringBuffer(1024);
         _postBodyBuffer = new StringBuffer(1024);
@@ -192,6 +197,30 @@ public class ThreadedHTMLRenderer extends HTMLRenderer {
         out.write("\n<a href=\"");
         out.write(getViewPostLink(baseURI, node, user, true, offset, requestTags, filteredAuthor));
         out.write("\" title=\"Select a shareable link directly to this post\">permalink</a>\n");
+
+
+        if (!inlineReply) {
+            String refuseReply = (String)_headers.get(HEADER_REFUSE_REPLIES);
+            boolean allowReply = false;
+            if ( (refuseReply != null) && (Boolean.valueOf(refuseReply).booleanValue()) ) {
+                if (_entry == null ) 
+                    allowReply = false;
+                else if ( (_user == null) || (_user.getBlog() == null) )
+                    allowReply = false;
+                else if (_entry.getURI().getKeyHash().equals(_user.getBlog()))
+                    allowReply = true;
+                else
+                    allowReply = false;
+            } else {
+                allowReply = true;
+            }
+            if (allowReply && (_entry != null) ) {
+                out.write("<a href=\"post.jsp?");
+                out.write(PostServlet.PARAM_PARENT + '=' + 
+                          Base64.encode(_entry.getURI().getKeyHash().toBase64() + '/' + _entry.getURI().getEntryId()));
+                out.write("\" title=\"Reply to the current post\" >Reply</a><br />\n");
+            }
+        }
         
         out.write("</td>\n</tr>\n");
         out.write("<!-- body meta end -->\n");
@@ -229,6 +258,7 @@ public class ThreadedHTMLRenderer extends HTMLRenderer {
                  (refuseReplies == null) || (!Boolean.valueOf(refuseReplies).booleanValue()) ) {
                 out.write("<!-- body reply begin -->\n");
                 out.write("<form action=\"post.jsp\" method=\"POST\" enctype=\"multipart/form-data\">\n");
+                out.write(replyHiddenFields);
                 out.write("<input type=\"hidden\" name=\"" + PostServlet.PARAM_PARENT + "\" value=\"");
                 out.write(Base64.encode(post.toString()));
                 out.write("\" />");
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/AddressesServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/AddressesServlet.java
index 56dfd7ded1..d7359b9f95 100644
--- a/apps/syndie/java/src/net/i2p/syndie/web/AddressesServlet.java
+++ b/apps/syndie/java/src/net/i2p/syndie/web/AddressesServlet.java
@@ -96,6 +96,7 @@ public class AddressesServlet extends BaseServlet {
             out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
             out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_BLOG + "\" />");
             out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_SYNDIE + "\" />");
+            writeAuthActionFields(out);
             out.write("<tr><td colspan=\"3\">");
             out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (pn.getIsPublic() ? " checked=\"true\" " : "") + " />\n");
             out.write("Name: <input type=\"hidden\" name=\"" + PARAM_NAME + "\" value=\"" + pn.getName() + "\" />" + pn.getName() + " ");
@@ -120,6 +121,7 @@ public class AddressesServlet extends BaseServlet {
         }
         
         out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
+        writeAuthActionFields(out);
         out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_BLOG + "\" />");
         out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_SYNDIE + "\" />");
         out.write("<tr><td colspan=\"3\">");
@@ -156,6 +158,7 @@ public class AddressesServlet extends BaseServlet {
         for (Iterator iter = names.iterator(); iter.hasNext(); ) {
             PetName pn = db.getByName((String)iter.next());
             out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
+            writeAuthActionFields(out);
             out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_ARCHIVE + "\" />");
             out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_SYNDIE + "\" />");
             out.write("<tr><td colspan=\"3\">");
@@ -177,6 +180,7 @@ public class AddressesServlet extends BaseServlet {
         }
 
         out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
+        writeAuthActionFields(out);
         out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_ARCHIVE + "\" />");
         out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_SYNDIE + "\" />");
         out.write("<tr><td colspan=\"3\">");
@@ -208,6 +212,7 @@ public class AddressesServlet extends BaseServlet {
         for (Iterator iter = names.iterator(); iter.hasNext(); ) {
             PetName pn = db.getByName((String)iter.next());
             out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
+            writeAuthActionFields(out);
             out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_I2PHEX + "\" />");
             out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_I2P + "\" />");
             out.write("<tr><td colspan=\"3\">");
@@ -222,6 +227,7 @@ public class AddressesServlet extends BaseServlet {
         }
         
         out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
+        writeAuthActionFields(out);
         out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_I2PHEX + "\" />");
         out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_I2P + "\" />");
         out.write("<tr><td colspan=\"3\">");
@@ -248,6 +254,7 @@ public class AddressesServlet extends BaseServlet {
         for (Iterator iter = names.iterator(); iter.hasNext(); ) {
             PetName pn = db.getByName((String)iter.next());
             out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
+            writeAuthActionFields(out);
             out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_EEPSITE + "\" />");
             out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_I2P + "\" />");
             out.write("<tr><td colspan=\"3\">");
@@ -262,6 +269,7 @@ public class AddressesServlet extends BaseServlet {
         }
         
         out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
+        writeAuthActionFields(out);
         out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_EEPSITE + "\" />");
         out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_I2P + "\" />");
         out.write("<tr><td colspan=\"3\">");
@@ -288,6 +296,7 @@ public class AddressesServlet extends BaseServlet {
         for (Iterator iter = names.iterator(); iter.hasNext(); ) {
             PetName pn = db.getByName((String)iter.next());
             out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
+            writeAuthActionFields(out);
             out.write("<tr><td colspan=\"3\">");
             out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (pn.getIsPublic() ? " checked=\"true\" " : "") + " />\n");
             out.write("Network: <input type=\"text\" name=\"" + PARAM_NET + "\" value=\"" + pn.getNetwork() + "\" /> ");
@@ -302,6 +311,7 @@ public class AddressesServlet extends BaseServlet {
         }
         
         out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
+        writeAuthActionFields(out);
         
         out.write("<tr><td colspan=\"3\">");
         out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (newName.getIsPublic() ? " checked=\"true\" " : "") + " />\n");
@@ -372,4 +382,6 @@ public class AddressesServlet extends BaseServlet {
             return proto.equals(reqProto);
         }
     }
+    
+    protected String getTitle() { return "Syndie :: Addressbook"; }
 }
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/AdminServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/AdminServlet.java
index 7476550121..41a5c8fa0f 100644
--- a/apps/syndie/java/src/net/i2p/syndie/web/AdminServlet.java
+++ b/apps/syndie/java/src/net/i2p/syndie/web/AdminServlet.java
@@ -31,6 +31,7 @@ public class AdminServlet extends BaseServlet {
     
     private void displayForm(User user, HttpServletRequest req, PrintWriter out) throws IOException {
         out.write("<form action=\"" + req.getRequestURI() + "\" method=\"POST\">\n");
+        writeAuthActionFields(out);
         out.write("<tr><td colspan=\"3\">");
 
         out.write("<em class=\"b_adminField\">Single user?</em> <input type=\"checkbox\" class=\"b_adminField\" name=\"singleuser\" ");
@@ -71,4 +72,6 @@ public class AdminServlet extends BaseServlet {
         out.write("</td></tr>\n");
         out.write("</form>\n");
     }
+   
+    protected String getTitle() { return "Syndie :: Configuration"; }
 }
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java
index 51f006a495..f6387a2004 100644
--- a/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java
+++ b/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java
@@ -20,6 +20,53 @@ import net.i2p.syndie.sml.*;
  *
  */
 public abstract class BaseServlet extends HttpServlet {
+    protected static final String PARAM_AUTH_ACTION = "syndie.auth";
+    private static long _authNonce;
+    
+    public void init() throws ServletException { 
+        super.init();
+        _authNonce = I2PAppContext.getGlobalContext().random().nextLong();
+    }
+    
+    protected boolean authAction(HttpServletRequest req) {
+        return authAction(req.getParameter(PARAM_AUTH_ACTION));
+    }
+    protected boolean authAction(String auth) {
+        if (auth == null) {
+            return false;
+        } else {
+            try { 
+                boolean rv = (Long.valueOf(auth).longValue() == _authNonce); 
+                return rv;
+            } catch (NumberFormatException nfe) {
+                return false;
+            }
+        }
+    }
+    
+    /**
+     * write out hidden fields for params that need to be tacked onto an http request that updates 
+     * data, to prevent spoofing
+     */
+    protected void writeAuthActionFields(Writer out) throws IOException {
+        out.write("<input type=\"hidden\" name=\"" + PARAM_AUTH_ACTION + "\" value=\"" + _authNonce + "\" />");
+    }
+    protected String getAuthActionFields() throws IOException {
+        return "<input type=\"hidden\" name=\"" + PARAM_AUTH_ACTION + "\" value=\"" + _authNonce + "\" />";
+    }
+    /** 
+     * key=value& of params that need to be tacked onto an http request that updates data, to 
+     * prevent spoofing 
+     */
+    protected String getAuthActionParams() { return PARAM_AUTH_ACTION + '=' + _authNonce + '&'; }
+    /** 
+     * key=value& of params that need to be tacked onto an http request that updates data, to 
+     * prevent spoofing 
+     */
+    protected void addAuthActionParams(StringBuffer buf) { 
+        buf.append(PARAM_AUTH_ACTION).append('=').append(_authNonce).append('&'); 
+    }
+    
     public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         req.setCharacterEncoding("UTF-8");
         resp.setCharacterEncoding("UTF-8");
@@ -30,12 +77,15 @@ public abstract class BaseServlet extends HttpServlet {
         String pass = req.getParameter("password");
         String action = req.getParameter("action");
         boolean forceNewIndex = false;
+
+        boolean authAction = authAction(req);
         
         if (req.getParameter("regenerateIndex") != null)
             forceNewIndex = true;
 
         User oldUser = user;
-        user = handleRegister(user, req);
+        if (authAction)
+            user = handleRegister(user, req);
         if (oldUser != user)
             forceNewIndex = true;
         
@@ -48,23 +98,25 @@ public abstract class BaseServlet extends HttpServlet {
                 user = BlogManager.instance().getDefaultUser();
             }
             forceNewIndex = true;
-        } else if ("Login".equals(action)) {
+        } else if (authAction && "Login".equals(action)) {
             user = BlogManager.instance().login(login, pass); // ignore failures - user will just be unauthorized
             if (!user.getAuthenticated())
                 user = BlogManager.instance().getDefaultUser();
             forceNewIndex = true;
-        } else if ("Logout".equals(action)) {
+        } else if (authAction && "Logout".equals(action)) {
             user = BlogManager.instance().getDefaultUser();
             forceNewIndex = true;
         }
         
         req.getSession().setAttribute("user", user);
         
-        handleAdmin(user, req);
+        if (authAction) {
+            handleAdmin(user, req);
         
-        forceNewIndex = handleAddressbook(user, req) || forceNewIndex;
-        forceNewIndex = handleBookmarking(user, req) || forceNewIndex;
-        handleUpdateProfile(user, req);
+            forceNewIndex = handleAddressbook(user, req) || forceNewIndex;
+            forceNewIndex = handleBookmarking(user, req) || forceNewIndex;
+            handleUpdateProfile(user, req);
+        }
         
         FilteredThreadIndex index = (FilteredThreadIndex)req.getSession().getAttribute("threadIndex");
         
@@ -677,6 +729,7 @@ public abstract class BaseServlet extends HttpServlet {
         if (!empty(filteredAuthor))
             buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(filteredAuthor).append('&');
         
+        addAuthActionParams(buf);
         return buf.toString();
     }
     protected String getRemoveFromGroupLink(User user, String name, String group, String uri, String visible,
@@ -703,6 +756,7 @@ public abstract class BaseServlet extends HttpServlet {
         if (!empty(filteredAuthor))
             buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(filteredAuthor).append('&');
         
+        addAuthActionParams(buf);
         return buf.toString();
     }
     protected String getViewPostLink(HttpServletRequest req, ThreadNode node, User user, boolean isPermalink) {
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/PostServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/PostServlet.java
index 1a869728c8..9727a485cb 100644
--- a/apps/syndie/java/src/net/i2p/syndie/web/PostServlet.java
+++ b/apps/syndie/java/src/net/i2p/syndie/web/PostServlet.java
@@ -60,6 +60,13 @@ public class PostServlet extends BaseServlet {
     }
     
     private void previewPostedData(User user, HttpServletRequest rawRequest, Archive archive, String contentType, PostBean post, PrintWriter out) throws IOException {
+        MultiPartRequest req = new MultiPartRequest(rawRequest);
+        
+        if (!authAction(req.getString(PARAM_AUTH_ACTION))) {
+            out.write("<tr><td colspan=\"3\"><span class=\"b_postMsgErro\">Invalid form submission... stale data?</span></td></tr>");
+            return;
+        }
+        
         // not confirmed but they posted stuff... gobble up what they give
         // and display it as a prview (then we show the confirm form
         
@@ -68,8 +75,6 @@ public class PostServlet extends BaseServlet {
         post.reinitialize();
         post.setUser(user);
         
-        MultiPartRequest req = new MultiPartRequest(rawRequest);
-        
         boolean inNewThread = getInNewThread(req.getString(PARAM_IN_NEW_THREAD));
         boolean refuseReplies = getRefuseReplies(req.getString(PARAM_REFUSE_REPLIES));
         
@@ -86,8 +91,8 @@ public class PostServlet extends BaseServlet {
         if ( (replyTo != null) && (replyTo.trim().length() > 0) ) {
           byte r[] = Base64.decode(replyTo);
           if (r != null) {
-            if (entryHeaders == null) entryHeaders = HTMLRenderer.HEADER_IN_REPLY_TO + ": " + new String(r, "UTF-8");
-            else entryHeaders = entryHeaders + '\n' + HTMLRenderer.HEADER_IN_REPLY_TO + ": " + new String(r, "UTF-8");
+            if (entryHeaders == null) entryHeaders = HTMLRenderer.HEADER_IN_REPLY_TO + ": entry://" + new String(r, "UTF-8");
+            else entryHeaders = entryHeaders + '\n' + HTMLRenderer.HEADER_IN_REPLY_TO + ": entry://" + new String(r, "UTF-8");
           } else {
             replyTo = null;
           }
@@ -137,6 +142,7 @@ public class PostServlet extends BaseServlet {
 
         post.renderPreview(out);
         out.write("<hr /><span class=\"b_postConfirm\"><form action=\"" + getPostURI() + "\" method=\"POST\">\n");
+        writeAuthActionFields(out);
         out.write("Please confirm that the above is ok");
         if (BlogManager.instance().authorizeRemote(user)) { 
             out.write(", and select what additional archives you want the post transmitted to.");
@@ -166,6 +172,10 @@ public class PostServlet extends BaseServlet {
     }
     
     private void postEntry(User user, HttpServletRequest req, Archive archive, PostBean post, PrintWriter out) throws IOException {
+        if (!authAction(req)) {
+            out.write("<tr><td colspan=\"3\"><span class=\"b_postMsgErro\">Invalid form submission... stale data?</span></td></tr>");
+            return;
+        }
         String remArchive = req.getParameter(PARAM_REMOTE_ARCHIVE);
         post.setArchive(remArchive);
         BlogURI uri = post.postEntry(); 
@@ -185,6 +195,7 @@ public class PostServlet extends BaseServlet {
         post.setUser(user);
         
         out.write("<form action=\"" + getPostURI() + "\" method=\"POST\" enctype=\"multipart/form-data\">\n");
+        writeAuthActionFields(out);
         out.write("<tr><td colspan=\"3\">\n");
         out.write("<span class=\"b_postField\">Post subject:</span> ");
         out.write("<input type=\"text\" class=\"b_postSubject\" size=\"80\" name=\"" + PARAM_SUBJECT 
@@ -203,7 +214,7 @@ public class PostServlet extends BaseServlet {
         
         String parentURI = req.getParameter(PARAM_PARENT);
         if ( (parentURI != null) && (parentURI.trim().length() > 0) )
-            out.write("<input type=\"hidden\" name=\"" + PARAM_PARENT + " value=\"" + parentURI + "\" />\n");
+            out.write("<input type=\"hidden\" name=\"" + PARAM_PARENT + "\" value=\"" + parentURI + "\" />\n");
 
         out.write(" Tags: <input type=\"text\" size=\"10\" name=\"" + PARAM_TAGS + "\" value=\"" + getParam(req, PARAM_TAGS) + "\" />\n");
         
@@ -223,7 +234,8 @@ public class PostServlet extends BaseServlet {
         
         if (parentURI != null) {
             out.write("<hr /><span id=\"parentText\" class=\"b_postParent\">");
-            post.renderReplyPreview(out, DataHelper.getUTF8(Base64.decode(parentURI)));
+            String decoded = DataHelper.getUTF8(Base64.decode(parentURI));
+            post.renderReplyPreview(out, "entry://" + decoded);
             out.write("</span><hr/>\n");
         } 
         
@@ -273,4 +285,5 @@ public class PostServlet extends BaseServlet {
         + "<span class=\"b_postField\">Attachment 2:</span> <input class=\"b_postField\" type=\"file\" name=\"entryfile2\" /><br />"
         + "<span class=\"b_postField\">Attachment 3:</span> <input class=\"b_postField\" type=\"file\" name=\"entryfile3\" /><br />\n";
 
+    protected String getTitle() { return "Syndie :: Post new content"; }
 }
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java
index abb94154ff..0432298d35 100644
--- a/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java
+++ b/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java
@@ -57,6 +57,7 @@ public class ProfileServlet extends BaseServlet {
         
         out.write("<!-- " + info.toString() + "-->\n");
         out.write("<form action=\"" + baseURI + "\" method=\"POST\">\n");
+        writeAuthActionFields(out);
         // now add the form to update
         out.write("<tr><td colspan=\"3\">Your profile</td></tr>\n");
         out.write("<tr><td colspan=\"3\">Name: <input type=\"text\" name=\"" 
@@ -214,6 +215,8 @@ public class ProfileServlet extends BaseServlet {
             }
         }
     }
+
+    protected String getTitle() { return "Syndie :: View profile"; }
     
     private static final String INVALID_PROFILE = "<tr><td colspan=\"3\">The profile requested is invalid</td></tr>\n";
 }
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/SwitchServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/SwitchServlet.java
index 153c820f16..38b5fad1a0 100644
--- a/apps/syndie/java/src/net/i2p/syndie/web/SwitchServlet.java
+++ b/apps/syndie/java/src/net/i2p/syndie/web/SwitchServlet.java
@@ -19,30 +19,31 @@ import net.i2p.syndie.sml.*;
  * Login/register form
  *
  */
-public class SwitchServlet extends BaseServlet {   
-    private final String FORM = "<form action=\"" + getControlTarget() + "\" method=\"POST\">\n" +
-                                "<tr><td colspan=\"3\"><b>Log in to an existing account</b></td></tr>\n" +
-                                "<tr><td colspan=\"3\">Login: <input type=\"text\" name=\"login\" /></td></tr>\n" +
-                                "<tr><td colspan=\"3\">Password: <input type=\"password\" name=\"password\" /></td></tr>\n" +
-                                "<tr><td colspan=\"3\"><input type=\"submit\" name=\"action\" value=\"Login\" />\n" +
-                                "<input type=\"submit\" name=\"action\" value=\"Cancel\" />\n" +
-                                "<input type=\"submit\" name=\"action\" value=\"Logout\" /></td></tr>\n" +
-                                "</form>\n" +
-                                "<tr><td colspan=\"3\"><hr /></td></tr>\n" +
-                                "<form action=\"" + ThreadedHTMLRenderer.buildProfileURL(null) + "\" method=\"POST\">\n" +
-                                "<tr><td colspan=\"3\"><b>Register a new account</b></td></tr>\n" +
-                                "<tr><td colspan=\"3\">Login: <input type=\"text\" name=\"login\" /> (only known locally)</td></tr>\n" +
-                                "<tr><td colspan=\"3\">Password: <input type=\"password\" name=\"password\" /></td></tr>\n" +
-                                "<tr><td colspan=\"3\">Public name: <input type=\"text\" name=\"accountName\" /></td></tr>\n" +
-                                "<tr><td colspan=\"3\">Description: <input type=\"text\" name=\"description\" /></td></tr>\n" +
-                                "<tr><td colspan=\"3\">Contact URL: <input type=\"text\" name=\"contactURL\" /></td></tr>\n" +
-                                "<tr><td colspan=\"3\">Registration password: <input type=\"password\" name=\"registrationPass\" />" +
-                                " (only necessary if the Syndie administrator requires it)</td></tr>\n" +
-                                "<tr><td colspan=\"3\"><input type=\"submit\" name=\"action\" value=\"Register\" /></td></tr>\n" +
-                                "</form>\n";
+public class SwitchServlet extends BaseServlet { 
+    protected String getTitle() { return "Syndie :: Login/Register"; }
     
     protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
                                         int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
-        out.write(FORM);
+        out.write("<form action=\"" + getControlTarget() + "\" method=\"POST\">\n");
+        writeAuthActionFields(out);
+        out.write("<tr><td colspan=\"3\"><b>Log in to an existing account</b></td></tr>\n" +
+                  "<tr><td colspan=\"3\">Login: <input type=\"text\" name=\"login\" /></td></tr>\n" +
+                  "<tr><td colspan=\"3\">Password: <input type=\"password\" name=\"password\" /></td></tr>\n" +
+                  "<tr><td colspan=\"3\"><input type=\"submit\" name=\"action\" value=\"Login\" />\n" +
+                  "<input type=\"submit\" name=\"action\" value=\"Cancel\" />\n" +
+                  "<input type=\"submit\" name=\"action\" value=\"Logout\" /></td></tr>\n" +
+                  "</form>\n" +
+                  "<tr><td colspan=\"3\"><hr /></td></tr>\n" +
+                  "<form action=\"" + ThreadedHTMLRenderer.buildProfileURL(null) + "\" method=\"POST\">\n" +
+                  "<tr><td colspan=\"3\"><b>Register a new account</b></td></tr>\n" +
+                  "<tr><td colspan=\"3\">Login: <input type=\"text\" name=\"login\" /> (only known locally)</td></tr>\n" +
+                  "<tr><td colspan=\"3\">Password: <input type=\"password\" name=\"password\" /></td></tr>\n" +
+                  "<tr><td colspan=\"3\">Public name: <input type=\"text\" name=\"accountName\" /></td></tr>\n" +
+                  "<tr><td colspan=\"3\">Description: <input type=\"text\" name=\"description\" /></td></tr>\n" +
+                  "<tr><td colspan=\"3\">Contact URL: <input type=\"text\" name=\"contactURL\" /></td></tr>\n" +
+                  "<tr><td colspan=\"3\">Registration password: <input type=\"password\" name=\"registrationPass\" />" +
+                  " (only necessary if the Syndie administrator requires it)</td></tr>\n" +
+                  "<tr><td colspan=\"3\"><input type=\"submit\" name=\"action\" value=\"Register\" /></td></tr>\n" +
+                  "</form>\n");
     }
 }
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ViewThreadedServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ViewThreadedServlet.java
index da805d42f3..b3de4531bb 100644
--- a/apps/syndie/java/src/net/i2p/syndie/web/ViewThreadedServlet.java
+++ b/apps/syndie/java/src/net/i2p/syndie/web/ViewThreadedServlet.java
@@ -40,7 +40,7 @@ public class ViewThreadedServlet extends BaseServlet {
 
         for (int i = 0; i < posts.size(); i++) {
             BlogURI post = (BlogURI)posts.get(i);
-            renderer.render(user, out, archive, post, posts.size() == 1, index, uri, off, tags, author);
+            renderer.render(user, out, archive, post, posts.size() == 1, index, uri, getAuthActionFields(), off, tags, author);
         }
     }
     
@@ -136,7 +136,7 @@ public class ViewThreadedServlet extends BaseServlet {
         int written = 0;
         for (int curRoot = threadOffset; curRoot < numThreads + threadOffset; curRoot++) {
             ThreadNode node = index.getRoot(curRoot);
-            out.write("<!-- thread begin node=" + node + " curRoot=" + curRoot + " threadOffset=" + threadOffset + " -->\n");
+            out.write("<!-- thread begin curRoot=" + curRoot + " threadOffset=" + threadOffset + " -->\n");
             renderThread(user, out, index, archive, req, node, 0, visibleEntry, state);
             out.write("<!-- thread end -->\n");
             written++;
@@ -302,4 +302,5 @@ public class ViewThreadedServlet extends BaseServlet {
             return "&nbsp;"; 
     }
     
+    protected String getTitle() { return "Syndie :: View threads"; }
 }
-- 
GitLab