diff --git a/apps/syndie/java/src/net/i2p/syndie/data/BlogInfo.java b/apps/syndie/java/src/net/i2p/syndie/data/BlogInfo.java
index 0d6d858c7..6ad973cd0 100644
--- a/apps/syndie/java/src/net/i2p/syndie/data/BlogInfo.java
+++ b/apps/syndie/java/src/net/i2p/syndie/data/BlogInfo.java
@@ -55,6 +55,7 @@ public class BlogInfo {
public static final String SIGNATURE = "Signature";
public static final String NAME = "Name";
public static final String DESCRIPTION = "Description";
+ public static final String CONTACT_URL = "ContactURL";
public static final String EDITION = "Edition";
public void load(InputStream in) throws IOException {
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 3496690ef..847947b5e 100644
--- a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java
+++ b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java
@@ -400,8 +400,12 @@ public class HTMLRenderer extends EventReceiverImpl {
if (location != null) {
_bodyBuffer.append(" at ");
SafeURL surl = new SafeURL(locationSchema + "://" + location);
- _bodyBuffer.append("").append(sanitizeString(surl.toString())).append("");
+ if (BlogManager.instance().authorizeRemote(_user)) {
+ _bodyBuffer.append("").append(sanitizeString(surl.toString())).append("");
+ } else {
+ _bodyBuffer.append(sanitizeString(surl.getLocation()));
+ }
if (_user.getAuthenticated()) {
_bodyBuffer.append(" 0) ) {
+ try {
+ Hash key = new Hash();
+ key.fromBase64(loc);
+ PetNameDB db = user.getPetNameDB();
+ PetName pn = db.getByLocation(loc);
+ boolean isNew = false;
+ if (pn == null) {
+ isNew = true;
+ BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(key);
+ String name = null;
+ if (info != null)
+ name = info.getProperty(BlogInfo.NAME);
+ else
+ name = loc.substring(0,6);
+
+ if (db.containsName(name)) {
+ int i = 0;
+ while (db.containsName(name + i))
+ i++;
+ name = name + i;
+ }
+
+ pn = new PetName(name, "syndie", "syndieblog", loc);
+ }
+ pn.addGroup(group);
+ if (isNew)
+ db.add(pn);
+ BlogManager.instance().saveUser(user);
+ // if we are ignoring someone, we need to recalculate the filters
+ if (FilteredThreadIndex.GROUP_IGNORE.equals(group))
+ rv = true;
+ } catch (DataFormatException dfe) {
+ // bad loc, ignore
+ }
+ }
+
+ String name = req.getParameter(ThreadedHTMLRenderer.PARAM_REMOVE_FROM_GROUP_NAME);
+ group = req.getParameter(ThreadedHTMLRenderer.PARAM_REMOVE_FROM_GROUP);
+ if ( (name != null) && (name.trim().length() > 0) ) {
+ PetNameDB db = user.getPetNameDB();
+ PetName pn = db.getByName(name);
+ boolean changed = false;
+ if (pn != null) {
+ if ( (group != null) && (group.trim().length() > 0) ) {
+ // just remove them from the group
+ changed = pn.isMember(group);
+ pn.removeGroup(group);
+ if ( (changed) && (FilteredThreadIndex.GROUP_IGNORE.equals(group)) )
+ rv = true;
+ } else {
+ // remove it completely
+ if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE))
+ rv = true;
+ db.remove(pn);
+ changed = true;
+ }
+ }
+ if (changed)
+ BlogManager.instance().saveUser(user);
+ }
+
+ return rv;
+ }
+
+ protected void handleUpdateProfile(User user, HttpServletRequest req) {
+ if ( (user == null) || (!user.getAuthenticated()) || (user.getBlog() == null) )
+ return;
+
+ String action = req.getParameter("action");
+ if ( (action == null) || !("Update profile".equals(action)) )
+ return;
+
+ String name = req.getParameter(ThreadedHTMLRenderer.PARAM_PROFILE_NAME);
+ String desc = req.getParameter(ThreadedHTMLRenderer.PARAM_PROFILE_DESC);
+ String url = req.getParameter(ThreadedHTMLRenderer.PARAM_PROFILE_URL);
+ String other = req.getParameter(ThreadedHTMLRenderer.PARAM_PROFILE_OTHER);
+
+ Properties opts = new Properties();
+ if (!empty(name))
+ opts.setProperty(BlogInfo.NAME, name.trim());
+ if (!empty(desc))
+ opts.setProperty(BlogInfo.DESCRIPTION, desc.trim());
+ if (!empty(url))
+ opts.setProperty(BlogInfo.CONTACT_URL, url.trim());
+ if (!empty(other)) {
+ StringBuffer key = new StringBuffer();
+ StringBuffer val = null;
+ for (int i = 0; i < other.length(); i++) {
+ char c = other.charAt(i);
+ if ( (c == ':') || (c == '=') ) {
+ if (val != null) {
+ val.append(c);
+ } else {
+ val = new StringBuffer();
+ }
+ } else if ( (c == '\n') || (c == '\r') ) {
+ String k = key.toString().trim();
+ String v = (val != null ? val.toString().trim() : "");
+ if ( (k.length() > 0) && (v.length() > 0) ) {
+ opts.setProperty(k, v);
+ }
+ key.setLength(0);
+ val = null;
+ } else if (val != null) {
+ val.append(c);
+ } else {
+ key.append(c);
+ }
+ }
+ // now finish the last of it
+ String k = key.toString().trim();
+ String v = (val != null ? val.toString().trim() : "");
+ if ( (k.length() > 0) && (v.length() > 0) ) {
+ opts.setProperty(k, v);
+ }
+ }
+
+ boolean updated = BlogManager.instance().updateMetadata(user, user.getBlog(), opts);
+ }
+
+ protected void render(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws ServletException, IOException {
+ Archive archive = BlogManager.instance().getArchive();
+ int numThreads = 10;
+ int threadOffset = getOffset(req);
+ if (threadOffset == -1) {
+ threadOffset = index.getRootCount() - numThreads;
+ }
+ if (threadOffset < 0) {
+ threadOffset = 0;
+ }
+
+ BlogURI visibleEntry = getVisible(req);
+
+ int offset = 0;
+ if ( empty(req, ThreadedHTMLRenderer.PARAM_OFFSET) && (visibleEntry != null) ) {
+ // we're on a permalink, so jump the tree to the given thread
+ threadOffset = index.getRoot(visibleEntry);
+ if (threadOffset < 0)
+ threadOffset = 0;
+ }
+
+ renderBegin(user, req, out, index);
+ renderNavBar(user, req, out, index);
+ renderControlBar(user, req, out, index);
+ renderServletDetails(user, req, out, index, threadOffset, visibleEntry, archive);
+ renderEnd(user, req, out, index);
+ }
+
+ protected void renderBegin(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
+ out.write(BEGIN_HTML);
+ }
+ protected void renderNavBar(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
+ //out.write("\n");
+ out.write(" \n");
+ }
+
+ protected static final ArrayList SKIP_TAGS = new ArrayList();
+ static {
+ SKIP_TAGS.add("action");
+ SKIP_TAGS.add("filter");
+ // post and visible are skipped since we aren't good at filtering by tag when the offset will
+ // skip around randomly. at least, not yet.
+ SKIP_TAGS.add("visible");
+ //SKIP_TAGS.add("post");
+ //SKIP_TAGS.add("thread");
+ SKIP_TAGS.add("offset"); // if we are adjusting the filter, ignore the previous offset
+ SKIP_TAGS.add("login");
+ SKIP_TAGS.add("password");
+ }
+
+ private static final String CONTROL_TARGET = "threads.jsp";
+ protected String getControlTarget() { return CONTROL_TARGET; }
+
+ protected void renderControlBar(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
+ out.write("\n");
+ }
+
+ protected abstract void renderServletDetails(User user, HttpServletRequest req, PrintWriter out,
+ ThreadIndex index, int threadOffset, BlogURI visibleEntry,
+ Archive archive) throws IOException;
+
+ protected static final int getOffset(HttpServletRequest req) {
+ String off = req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET);
+ try {
+ return Integer.parseInt(off);
+ } catch (NumberFormatException nfe) {
+ return 0;
+ }
+ }
+ protected static final BlogURI getVisible(HttpServletRequest req) {
+ return getAsBlogURI(req.getParameter(ThreadedHTMLRenderer.PARAM_VISIBLE));
+ }
+ protected static final BlogURI getAsBlogURI(String uri) {
+ if (uri != null) {
+ int split = uri.indexOf('/');
+ if ( (split <= 0) || (split + 1 >= uri.length()) )
+ return null;
+ String blog = uri.substring(0, split);
+ String id = uri.substring(split+1);
+ try {
+ Hash hash = new Hash();
+ hash.fromBase64(blog);
+ long msgId = Long.parseLong(id);
+ if (msgId > 0)
+ return new BlogURI(hash, msgId);
+ } catch (DataFormatException dfe) {
+ return null;
+ } catch (NumberFormatException nfe) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+
+ protected String trim(String orig, int maxLen) {
+ if ( (orig == null) || (orig.length() <= maxLen) )
+ return orig;
+ return orig.substring(0, maxLen) + "...";
+ }
+
+ protected static final boolean empty(HttpServletRequest req, String param) {
+ String val = req.getParameter(param);
+ return (val == null) || (val.trim().length() <= 0);
+ }
+
+ protected static final boolean empty(String val) {
+ return (val == null) || (val.trim().length() <= 0);
+ }
+
+ protected String getExpandLink(HttpServletRequest req, ThreadNode node) {
+ return getExpandLink(node, req.getRequestURI(), req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
+ }
+ protected static String getExpandLink(ThreadNode node, String uri, String viewPost, String viewThread,
+ String offset, String tags, String author) {
+ StringBuffer buf = new StringBuffer(64);
+ buf.append(uri);
+ buf.append('?');
+ // expand node == let one of node's children be visible
+ if (node.getChildCount() > 0) {
+ ThreadNode child = node.getChild(0);
+ buf.append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
+ buf.append(child.getEntry().getKeyHash().toBase64()).append('/');
+ buf.append(child.getEntry().getEntryId()).append('&');
+ }
+
+ if (!empty(viewPost))
+ buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append('&');
+ else if (!empty(viewThread))
+ buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
+
+ if (!empty(offset))
+ buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
+
+ if (!empty(tags))
+ buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
+
+ if (!empty(author))
+ buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(author).append('&');
+
+ return buf.toString();
+ }
+ protected String getCollapseLink(HttpServletRequest req, ThreadNode node) {
+ return getCollapseLink(node, req.getRequestURI(),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
+ }
+
+ protected String getCollapseLink(ThreadNode node, String uri, String viewPost, String viewThread,
+ String offset, String tags, String author) {
+ StringBuffer buf = new StringBuffer(64);
+ buf.append(uri);
+ // collapse node == let the node be visible
+ buf.append('?').append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
+ buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
+ buf.append(node.getEntry().getEntryId()).append('&');
+
+ if (!empty(viewPost))
+ buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append('&');
+ else if (!empty(viewThread))
+ buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
+
+ if (!empty(offset))
+ buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
+
+ if (!empty(tags))
+ buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
+
+ if (!empty(author))
+ buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(author).append('&');
+
+ return buf.toString();
+ }
+ protected String getProfileLink(HttpServletRequest req, Hash author) {
+ return getProfileLink(author);
+ }
+ protected String getProfileLink(Hash author) { return ThreadedHTMLRenderer.buildProfileURL(author); }
+
+ protected String getAddToGroupLink(HttpServletRequest req, Hash author, User user, String group) {
+ return getAddToGroupLink(user, author, group, req.getRequestURI(),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_VISIBLE),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
+ }
+ protected String getAddToGroupLink(User user, Hash author, String group, String uri, String visible,
+ String viewPost, String viewThread, String offset, String tags, String filteredAuthor) {
+ StringBuffer buf = new StringBuffer(64);
+ buf.append(uri);
+ buf.append('?');
+ if (!empty(visible))
+ buf.append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=').append(visible).append('&');
+ buf.append(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_LOCATION).append('=').append(author.toBase64()).append('&');
+ buf.append(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_NAME).append('=').append(group).append('&');
+
+ if (!empty(viewPost))
+ buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append('&');
+ else if (!empty(viewThread))
+ buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
+
+ if (!empty(offset))
+ buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
+
+ if (!empty(tags))
+ buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
+
+ if (!empty(filteredAuthor))
+ buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(filteredAuthor).append('&');
+
+ return buf.toString();
+ }
+ protected String getRemoveFromGroupLink(User user, String name, String group, String uri, String visible,
+ String viewPost, String viewThread, String offset, String tags, String filteredAuthor) {
+ StringBuffer buf = new StringBuffer(64);
+ buf.append(uri);
+ buf.append('?');
+ if (!empty(visible))
+ buf.append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=').append(visible).append('&');
+ buf.append(ThreadedHTMLRenderer.PARAM_REMOVE_FROM_GROUP_NAME).append('=').append(name).append('&');
+ buf.append(ThreadedHTMLRenderer.PARAM_REMOVE_FROM_GROUP).append('=').append(group).append('&');
+
+ if (!empty(viewPost))
+ buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append('&');
+ else if (!empty(viewThread))
+ buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
+
+ if (!empty(offset))
+ buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
+
+ if (!empty(tags))
+ buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
+
+ if (!empty(filteredAuthor))
+ buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(filteredAuthor).append('&');
+
+ return buf.toString();
+ }
+ protected String getViewPostLink(HttpServletRequest req, ThreadNode node, User user, boolean isPermalink) {
+ return ThreadedHTMLRenderer.getViewPostLink(req.getRequestURI(), node, user, isPermalink,
+ req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
+ }
+ protected String getViewThreadLink(HttpServletRequest req, ThreadNode node, User user) {
+ return getViewThreadLink(req.getRequestURI(), node, user,
+ req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
+ }
+ protected static String getViewThreadLink(String uri, ThreadNode node, User user, String offset,
+ String tags, String author) {
+ StringBuffer buf = new StringBuffer(64);
+ buf.append(uri);
+ if (node.getChildCount() > 0) {
+ buf.append('?').append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
+ ThreadNode child = node.getChild(0);
+ buf.append(child.getEntry().getKeyHash().toBase64()).append('/');
+ buf.append(child.getEntry().getEntryId()).append('&');
+ } else {
+ buf.append('?').append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
+ buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
+ buf.append(node.getEntry().getEntryId()).append('&');
+ }
+ buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=');
+ buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
+ buf.append(node.getEntry().getEntryId()).append('&');
+
+ if (!empty(offset))
+ buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
+
+ if (!empty(tags))
+ buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
+
+ if (!empty(author))
+ buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(author).append('&');
+
+ buf.append("#").append(node.getEntry().toString());
+ return buf.toString();
+ }
+ protected String getFilterByTagLink(HttpServletRequest req, ThreadNode node, User user, String tag, String author) {
+ return ThreadedHTMLRenderer.getFilterByTagLink(req.getRequestURI(), node, user, tag, author);
+ }
+ protected String getNavLink(HttpServletRequest req, int offset) {
+ return ThreadedHTMLRenderer.getNavLink(req.getRequestURI(),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
+ req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR),
+ offset);
+ }
+
+ protected void renderEnd(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
+ out.write(END_HTML);
+ }
+
+ protected Collection getFilteredTags(HttpServletRequest req) {
+ String tags = req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS);
+ if (tags != null) {
+ StringTokenizer tok = new StringTokenizer(tags, "\n\t ");
+ ArrayList rv = new ArrayList();
+ while (tok.hasMoreTokens()) {
+ String tag = tok.nextToken().trim();
+ if (tag.length() > 0)
+ rv.add(tag);
+ }
+ return rv;
+ } else {
+ return Collections.EMPTY_LIST;
+ }
+ }
+
+ protected Collection getFilteredAuthors(HttpServletRequest req) {
+ String authors = req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR);
+ if (authors != null) {
+ StringTokenizer tok = new StringTokenizer(authors, "\n\t ");
+ ArrayList rv = new ArrayList();
+ while (tok.hasMoreTokens()) {
+ try {
+ Hash h = new Hash();
+ h.fromBase64(tok.nextToken().trim());
+ rv.add(h);
+ } catch (DataFormatException dfe) {}
+ }
+ return rv;
+ } else {
+ return Collections.EMPTY_LIST;
+ }
+ }
+
+ private static final String BEGIN_HTML = "\n" +
+"\n" +
+"\n");
+ out.write("\n");
+ if (user.getAuthenticated() && (user.getBlog() != null) ) {
+ out.write("Logged in as ");
+ out.write(user.getUsername());
+ out.write("\n");
+ out.write("(switch)\n");
+ out.write("Post a new thread\n");
+ } else {
+ out.write("\n");
+ }
+ //out.write(" \n");
+ out.write("\n");
+ if (user.getAuthenticated() && user.getAllowAccessRemote()) {
+ out.write("Syndicate\n");
+ out.write("Import RSS/Atom\n");
+ out.write("Admin\n");
+ }
+ out.write("\n