From 61b8e3598b91c651abee5ec523f4ea678b0fa789 Mon Sep 17 00:00:00 2001 From: jrandom Date: Mon, 5 Sep 2005 05:33:33 +0000 Subject: [PATCH] added rss2.0 support via rss.jsp rss.jsp can in turn receive all the filters that index.jsp can - e.g. ?blog=blah or ?selector=group://foo, and by default returns the latest 10 values (overridden with ?wanted=15). If you want it to pull with a user's blog's preferences (filters, groups, etc), you can specify ?login=user&password=password --- .../java/src/net/i2p/syndie/BlogManager.java | 1 + .../src/net/i2p/syndie/sml/HTMLRenderer.java | 40 ++- .../src/net/i2p/syndie/sml/RSSRenderer.java | 305 ++++++++++++++++++ .../net/i2p/syndie/web/ArchiveViewerBean.java | 2 +- .../src/net/i2p/syndie/web/RSSServlet.java | 94 ++++++ apps/syndie/jsp/index.jsp | 10 + apps/syndie/jsp/web.xml | 9 + 7 files changed, 453 insertions(+), 8 deletions(-) create mode 100644 apps/syndie/java/src/net/i2p/syndie/sml/RSSRenderer.java create mode 100644 apps/syndie/java/src/net/i2p/syndie/web/RSSServlet.java 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 +