diff --git a/apps/syndie/java/src/net/i2p/syndie/BlogManager.java b/apps/syndie/java/src/net/i2p/syndie/BlogManager.java
index b7c154f68..fc383c7b6 100644
--- a/apps/syndie/java/src/net/i2p/syndie/BlogManager.java
+++ b/apps/syndie/java/src/net/i2p/syndie/BlogManager.java
@@ -184,6 +184,7 @@ public class BlogManager {
}
public String login(User user, String login, String pass) {
+ if ( (login == null) || (pass == null) ) return "Login not specified";
Hash userHash = _context.sha().calculateHash(DataHelper.getUTF8(login));
Hash passHash = _context.sha().calculateHash(DataHelper.getUTF8(pass));
File userFile = new File(_userDir, Base64.encode(userHash.getData()));
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 a162d9426..cf9c8543b 100644
--- a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java
+++ b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java
@@ -83,6 +83,15 @@ public class HTMLRenderer extends EventReceiverImpl {
render(user, archive, entry, entry.getEntry().getText(), out, cutBody, showImages);
}
public void render(User user, Archive archive, EntryContainer entry, String rawSML, Writer out, boolean cutBody, boolean showImages) throws IOException {
+ prepare(user, archive, entry, rawSML, out, cutBody, showImages);
+
+ _out.write(_preBodyBuffer.toString());
+ _out.write(_bodyBuffer.toString());
+ _out.write(_postBodyBuffer.toString());
+ //int len = _preBodyBuffer.length() + _bodyBuffer.length() + _postBodyBuffer.length();
+ //System.out.println("Wrote " + len);
+ }
+ protected void prepare(User user, Archive archive, EntryContainer entry, String rawSML, Writer out, boolean cutBody, boolean showImages) throws IOException {
_user = user;
_archive = archive;
_entry = entry;
@@ -100,11 +109,6 @@ public class HTMLRenderer extends EventReceiverImpl {
_cutReached = false;
_cutSize = 1024;
_parser.parse(rawSML, this);
- _out.write(_preBodyBuffer.toString());
- _out.write(_bodyBuffer.toString());
- _out.write(_postBodyBuffer.toString());
- //int len = _preBodyBuffer.length() + _bodyBuffer.length() + _postBodyBuffer.length();
- //System.out.println("Wrote " + len);
}
public void receivePlain(String text) {
@@ -787,8 +791,30 @@ public class HTMLRenderer extends EventReceiverImpl {
return sanitizeString(str);
}
- private String getEntryURL() { return getEntryURL(_user != null ? _user.getShowImages() : false); }
- private String getEntryURL(boolean showImages) {
+ public static final String sanitizeXML(String orig) {
+ if (orig.indexOf('&') < 0) return orig;
+ StringBuffer rv = new StringBuffer(orig.length()+32);
+ for (int i = 0; i < orig.length(); i++) {
+ if (orig.charAt(i) == '&')
+ rv.append("&");
+ else
+ rv.append(orig.charAt(i));
+ }
+ return rv.toString();
+ }
+ public static final String sanitizeXML(StringBuffer orig) {
+ if (orig.indexOf("&") < 0) return orig.toString();
+ for (int i = 0; i < orig.length(); i++) {
+ if (orig.charAt(i) == '&') {
+ orig = orig.replace(i, i+1, "&");
+ i += "&".length();
+ }
+ }
+ return orig.toString();
+ }
+
+ protected String getEntryURL() { return getEntryURL(_user != null ? _user.getShowImages() : false); }
+ protected String getEntryURL(boolean showImages) {
if (_entry == null) return "unknown";
return "index.jsp?" + ArchiveViewerBean.PARAM_BLOG + "=" +
Base64.encode(_entry.getURI().getKeyHash().getData()) +
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/RSSRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/RSSRenderer.java
new file mode 100644
index 000000000..64bc6cf3c
--- /dev/null
+++ b/apps/syndie/java/src/net/i2p/syndie/sml/RSSRenderer.java
@@ -0,0 +1,305 @@
+package net.i2p.syndie.sml;
+
+import java.io.*;
+import java.util.*;
+import java.text.SimpleDateFormat;
+import net.i2p.data.*;
+import net.i2p.syndie.*;
+import net.i2p.syndie.data.*;
+
+/**
+ *
+ */
+public class RSSRenderer extends HTMLRenderer {
+
+ public void render(User user, Archive archive, EntryContainer entry, String urlPrefix, Writer out) throws IOException {
+ if (entry == null) return;
+ prepare(user, archive, entry, entry.getEntry().getText(), out, true, false);
+
+ out.write(" - \n");
+ out.write(" " + sanitizeXML(sanitizeString((String)_headers.get(HEADER_SUBJECT))) + "\n");
+ out.write(" " + urlPrefix + sanitizeXML(getEntryURL()) + "\n");
+ out.write(" " + urlPrefix + entry.getURI().toString() + "\n");
+ out.write(" " + getRFC822Date(entry.getURI().getEntryId()) + "\n");
+ String author = user.getPetNameDB().getNameByLocation(entry.getURI().getKeyHash().toBase64());
+ if (author == null) {
+ BlogInfo info = archive.getBlogInfo(entry.getURI());
+ if (info != null)
+ author = info.getProperty(BlogInfo.NAME);
+ }
+ if (author == null)
+ author = entry.getURI().getKeyHash().toBase64();
+ out.write(" " + sanitizeXML(sanitizeString(author)) + "@syndie.invalid\n");
+ String tags[] = entry.getTags();
+ if (tags != null)
+ for (int i = 0; i < tags.length; i++)
+ out.write(" " + sanitizeXML(sanitizeString(tags[i])) + "\n");
+
+ out.write(" " + sanitizeXML(_bodyBuffer.toString()) + "\n");
+
+ //renderEnclosures(user, entry, urlPrefix, out);
+
+ out.write("
\n");
+ }
+
+
+ public void receiveBold(String text) {
+ if (!continueBody()) { return; }
+ _bodyBuffer.append(sanitizeString(text));
+ }
+ public void receiveItalic(String text) {
+ if (!continueBody()) { return; }
+ _bodyBuffer.append(sanitizeString(text));
+ }
+ public void receiveUnderline(String text) {
+ if (!continueBody()) { return; }
+ _bodyBuffer.append(sanitizeString(text));
+ }
+ public void receiveHR() {
+ if (!continueBody()) { return; }
+ }
+ public void receiveH1(String body) {
+ if (!continueBody()) { return; }
+ _bodyBuffer.append(sanitizeString(body));
+ }
+ public void receiveH2(String body) {
+ if (!continueBody()) { return; }
+ _bodyBuffer.append(sanitizeString(body));
+ }
+ public void receiveH3(String body) {
+ if (!continueBody()) { return; }
+ _bodyBuffer.append(sanitizeString(body));
+ }
+ public void receiveH4(String body) {
+ if (!continueBody()) { return; }
+ _bodyBuffer.append(sanitizeString(body));
+ }
+ public void receiveH5(String body) {
+ if (!continueBody()) { return; }
+ _bodyBuffer.append(sanitizeString(body));
+ }
+ public void receivePre(String body) {
+ if (!continueBody()) { return; }
+ _bodyBuffer.append(sanitizeString(body));
+ }
+
+ public void receiveQuote(String text, String whoQuoted, String quoteLocationSchema, String quoteLocation) {
+ if (!continueBody()) { return; }
+ _bodyBuffer.append(sanitizeString(text));
+ }
+ public void receiveCode(String text, String codeLocationSchema, String codeLocation) {
+ if (!continueBody()) { return; }
+ _bodyBuffer.append(sanitizeString(text));
+ }
+ public void receiveImage(String alternateText, int attachmentId) {
+ if (!continueBody()) { return; }
+ _bodyBuffer.append(sanitizeString(alternateText));
+ }
+ public void receiveCut(String summaryText) {
+ if (!continueBody()) { return; }
+ _cutReached = true;
+ if (_cutBody) {
+ if ( (summaryText != null) && (summaryText.length() > 0) )
+ _bodyBuffer.append(sanitizeString(summaryText));
+ else
+ _bodyBuffer.append("more inside...");
+ } else {
+ if (summaryText != null)
+ _bodyBuffer.append(sanitizeString(summaryText));
+ }
+ }
+ /** are we either before the cut or rendering without cutting? */
+ protected boolean continueBody() {
+ boolean rv = ( (!_cutReached) && (_bodyBuffer.length() <= _cutSize) ) || (!_cutBody);
+ //if (!rv)
+ // System.out.println("rv: " + rv + " Cut reached: " + _cutReached + " bodyBufferSize: " + _bodyBuffer.length() + " cutBody? " + _cutBody);
+ if (!rv && !_cutReached) {
+ // exceeded the allowed size
+ _bodyBuffer.append("more inside...");
+ _cutReached = true;
+ }
+ return rv;
+ }
+ public void receiveNewline() {
+ if (!continueBody()) { return; }
+ if (true || (_lastNewlineAt >= _bodyBuffer.length()))
+ _bodyBuffer.append("\n");
+ else
+ _lastNewlineAt = _bodyBuffer.length();
+ }
+ public void receiveBlog(String name, String hash, String tag, long entryId, List locations, String description) {
+ byte blogData[] = Base64.decode(hash);
+ if ( (blogData == null) || (blogData.length != Hash.HASH_LENGTH) )
+ return;
+
+ Blog b = new Blog();
+ b.name = name;
+ b.hash = hash;
+ b.tag = tag;
+ b.entryId = entryId;
+ b.locations = locations;
+ if (!_blogs.contains(b))
+ _blogs.add(b);
+
+ if (!continueBody()) { return; }
+ if (hash == null) return;
+
+ Hash blog = new Hash(blogData);
+ if ( (description != null) && (description.trim().length() > 0) ) {
+ _bodyBuffer.append(sanitizeString(description));
+ } else if ( (name != null) && (name.trim().length() > 0) ) {
+ _bodyBuffer.append(sanitizeString(name));
+ } else {
+ _bodyBuffer.append("[view entry]");
+ }
+ }
+ public void receiveArchive(String name, String description, String locationSchema, String location,
+ String postingKey, String anchorText) {
+ ArchiveRef a = new ArchiveRef();
+ a.name = name;
+ a.description = description;
+ a.locationSchema = locationSchema;
+ a.location = location;
+ if (!_archives.contains(a))
+ _archives.add(a);
+
+ if (!continueBody()) { return; }
+
+ _bodyBuffer.append(sanitizeString(anchorText));
+ }
+ public void receiveLink(String schema, String location, String text) {
+ Link l = new Link();
+ l.schema = schema;
+ l.location = location;
+ if (!_links.contains(l))
+ _links.add(l);
+ if (!continueBody()) { return; }
+ if ( (schema == null) || (location == null) ) return;
+ _bodyBuffer.append(sanitizeString(text));
+ }
+ 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;
+ String knownName = null;
+ if (_user != null)
+ knownName = _user.getPetNameDB().getNameByLocation(location);
+ if (knownName != null) {
+ _bodyBuffer.append(sanitizeString(anchorText));
+ } else {
+ _bodyBuffer.append(sanitizeString(anchorText));
+ }
+ }
+ public void receiveAttachment(int id, String anchorText) {
+ if (!continueBody()) { return; }
+ _bodyBuffer.append(sanitizeString(anchorText));
+ }
+
+ // Mon, 03 Jun 2005 13:04:11 +0000
+ private static final SimpleDateFormat _rfc822Date = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
+ private static final String getRFC822Date(long when) {
+ synchronized (_rfc822Date) {
+ return _rfc822Date.format(new Date(when));
+ }
+ }
+
+ private void renderEnclosures(User user, EntryContainer entry, String urlPrefix, Writer out) throws IOException {
+ if (entry.getAttachments() != null) {
+ for (int i = 0; i < _entry.getAttachments().length; i++) {
+ Attachment a = _entry.getAttachments()[i];
+ out.write(" \n");
+ }
+ }
+
+ if (_blogs.size() > 0) {
+ for (int i = 0; i < _blogs.size(); i++) {
+ Blog b = (Blog)_blogs.get(i);
+ out.write(" \n");
+ }
+ }
+
+ if (_links.size() > 0) {
+ for (int i = 0; i < _links.size(); i++) {
+ Link l = (Link)_links.get(i);
+ StringBuffer url = new StringBuffer(128);
+ url.append("externallink.jsp?schema=");
+ url.append(sanitizeURL(l.schema)).append("&location=");
+ url.append(sanitizeURL(l.location));
+ out.write(" \n");
+ }
+ }
+
+ if (_addresses.size() > 0) {
+ for (int i = 0; i < _addresses.size(); i++) {
+ Address a = (Address)_addresses.get(i);
+
+ String knownName = null;
+ if (_user != null)
+ knownName = _user.getPetNameDB().getNameByLocation(a.location);
+ if (knownName == null) {
+ StringBuffer url = new StringBuffer(128);
+ url.append("addaddress.jsp?schema=");
+ url.append(sanitizeURL(a.schema)).append("&location=");
+ url.append(sanitizeURL(a.location)).append("&name=");
+ url.append(sanitizeURL(a.name)).append("&protocol=");
+ url.append(sanitizeURL(a.protocol));
+ out.write(" \n");
+ }
+ }
+ }
+
+ if (_archives.size() > 0) {
+ for (int i = 0; i < _archives.size(); i++) {
+ ArchiveRef a = (ArchiveRef)_archives.get(i);
+ String url = getArchiveURL(null, new SafeURL(a.locationSchema + "://" + a.location));
+ out.write(" \n");
+ }
+ }
+
+ if (_entry != null) {
+ List replies = _archive.getIndex().getReplies(_entry.getURI());
+ if ( (replies != null) && (replies.size() > 0) ) {
+ for (int i = 0; i < replies.size(); i++) {
+ BlogURI reply = (BlogURI)replies.get(i);
+ String url = getPageURL(reply.getKeyHash(), null, reply.getEntryId(), -1, -1, true, _user.getShowImages());
+ out.write(" \n");
+ }
+ }
+ }
+
+ String inReplyTo = (String)_headers.get(HEADER_IN_REPLY_TO);
+ if ( (inReplyTo != null) && (inReplyTo.trim().length() > 0) ) {
+ String url = getPageURL(sanitizeTagParam(inReplyTo));
+ out.write(" \n");
+ }
+ }
+
+ public void receiveHeaderEnd() {}
+ public void receiveEnd() {}
+
+ public static void main(String args[]) {
+ test("");
+ test("&");
+ test("a&");
+ test("&a");
+ test("a&a");
+ test("aa&aa");
+ }
+ private static final void test(String str) {
+ StringBuffer t = new StringBuffer(str);
+ String sanitized = sanitizeXML(t);
+ System.out.println("[" + str + "] --> [" + sanitized + "]");
+ }
+}
\ No newline at end of file
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 4b47fa7ac..93a0c2b20 100644
--- a/apps/syndie/java/src/net/i2p/syndie/web/ArchiveViewerBean.java
+++ b/apps/syndie/java/src/net/i2p/syndie/web/ArchiveViewerBean.java
@@ -489,7 +489,7 @@ public class ArchiveViewerBean {
}
}
- private static List pickEntryURIs(User user, ArchiveIndex index, Hash blog, String tag, long entryId, String group) {
+ public static List pickEntryURIs(User user, ArchiveIndex index, Hash blog, String tag, long entryId, String group) {
if ( (blog != null) && ( (blog.getData() == null) || (blog.getData().length != Hash.HASH_LENGTH) ) )
blog = null;
List rv = new ArrayList(16);
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/RSSServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/RSSServlet.java
new file mode 100644
index 000000000..3ca92e4f6
--- /dev/null
+++ b/apps/syndie/java/src/net/i2p/syndie/web/RSSServlet.java
@@ -0,0 +1,94 @@
+package net.i2p.syndie.web;
+
+import java.io.*;
+import java.util.*;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
+
+import net.i2p.data.*;
+import net.i2p.syndie.*;
+import net.i2p.syndie.data.*;
+import net.i2p.syndie.sml.*;
+
+/**
+ *
+ */
+public class RSSServlet extends HttpServlet {
+
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ req.setCharacterEncoding("UTF-8");
+ resp.setCharacterEncoding("UTF-8");
+ resp.setContentType("application/rss+xml");
+
+ User user = (User)req.getSession().getAttribute("user");
+ if (user == null) {
+ String login = req.getParameter("login");
+ String pass = req.getParameter("password");
+ user = new User();
+ BlogManager.instance().login(user, login, pass); // ignore failures - user will just be unauthorized
+ if (!user.getAuthenticated())
+ user.invalidate();
+ }
+
+ String selector = req.getParameter("selector");
+ if ( (selector == null) || (selector.length() <= 0) ) {
+ selector = getDefaultSelector(user);
+ }
+ ArchiveViewerBean.Selector sel = new ArchiveViewerBean.Selector(selector);
+
+ Archive archive = BlogManager.instance().getArchive();
+ ArchiveIndex index = archive.getIndex();
+ List entries = ArchiveViewerBean.pickEntryURIs(user, index, sel.blog, sel.tag, sel.entry, sel.group);
+
+ StringBuffer cur = new StringBuffer();
+ cur.append(req.getScheme());
+ cur.append("://");
+ cur.append(req.getServerName());
+ if (req.getServerPort() != 80)
+ cur.append(':').append(req.getServerPort());
+ cur.append(req.getContextPath()).append('/');
+ String urlPrefix = cur.toString();
+
+ Writer out = resp.getWriter();
+ out.write("\n");
+ out.write("\n");
+ out.write(" \n");
+ out.write(" Syndie feed\n");
+ out.write(" " + urlPrefix + HTMLRenderer.sanitizeXML(HTMLRenderer.getPageURL(sel.blog, sel.tag, sel.entry, sel.group, 5, 0, false, false)) +"\n");
+ out.write(" Summary of the latest Syndie posts\n");
+ out.write(" Syndie\n");
+
+ int count = 10;
+ String wanted = req.getParameter("wanted");
+ if (wanted != null) {
+ try {
+ count = Integer.parseInt(wanted);
+ } catch (NumberFormatException nfe) {
+ count = 10;
+ }
+ }
+ if (count < 0) count = 10;
+ if (count > 100) count = 100;
+
+ RSSRenderer r = new RSSRenderer();
+ for (int i = 0; i < count && i < entries.size(); i++) {
+ BlogURI uri = (BlogURI)entries.get(i);
+ EntryContainer entry = archive.getEntry(uri);
+ r.render(user, archive, entry, urlPrefix, out);
+ }
+
+ out.write(" \n");
+ out.write("\n");
+ out.close();
+ }
+
+ private static String getDefaultSelector(User user) {
+ if ( (user == null) || (user.getDefaultSelector() == null) )
+ return BlogManager.instance().getArchive().getDefaultSelector();
+ else
+ return user.getDefaultSelector();
+ }
+}
diff --git a/apps/syndie/jsp/index.jsp b/apps/syndie/jsp/index.jsp
index 9ce379788..b7a7aa1ef 100644
--- a/apps/syndie/jsp/index.jsp
+++ b/apps/syndie/jsp/index.jsp
@@ -4,6 +4,16 @@
SyndieMedia
+" rel="alternate" type="application/rss+xml" />
diff --git a/apps/syndie/jsp/web.xml b/apps/syndie/jsp/web.xml
index 9eb60866a..bb86268a5 100644
--- a/apps/syndie/jsp/web.xml
+++ b/apps/syndie/jsp/web.xml
@@ -9,6 +9,11 @@
net.i2p.syndie.web.ArchiveServlet
+
+ net.i2p.syndie.web.RSSServlet
+ net.i2p.syndie.web.RSSServlet
+
+
@@ -19,6 +24,10 @@
net.i2p.syndie.web.ArchiveServlet
/archive/*
+
+ net.i2p.syndie.web.RSSServlet
+ /rss.jsp
+