2005-01-09 jrandom

* Removed a longstanding bug that had caused unnecessary router identity
      churn due to clock skew
    * Temporarily sanity check within the streaming lib for long pending
      writes
    * Added support for a blog-wide logo to Syndie, and automated the pushing
      of updated extended blog info data along side the metadata.
This commit is contained in:
jrandom
2006-01-09 22:22:41 +00:00
committed by zzz
parent 002aed145f
commit 934f4082f1
11 changed files with 275 additions and 27 deletions

View File

@ -25,6 +25,8 @@ public class BlogInfoData {
/** identifies a post as being a blog info data, not a content bearing post */
public static final String TAG = "BlogInfoData";
public static final int MAX_LOGO_SIZE = 128*1024;
public BlogInfoData() {}
public BlogURI getEntryId() { return _dataEntryId; }

View File

@ -1026,7 +1026,7 @@ public class HTMLRenderer extends EventReceiverImpl {
"&" + ArchiveViewerBean.PARAM_ENTRY + "=" + _entry.getURI().getEntryId() +
"&" + ArchiveViewerBean.PARAM_ATTACHMENT + "=" + id;
}
public String getMetadataURL() {
if (_entry == null) return "unknown";
return getMetadataURL(_entry.getURI().getKeyHash());

View File

@ -140,10 +140,13 @@ public class BlogConfigBean {
}
public File getLogo() { return _logo; }
public void setLogo(File logo) {
if ( (logo != null) && (logo.length() > 128*1024) ) {
_log.error("Refusing a logo more than 128KB");
if ( (logo != null) && (logo.length() > BlogInfoData.MAX_LOGO_SIZE) ) {
_log.error("Refusing a logo of size " + logo.length());
logo.delete();
return;
}
if (_logo != null)
_logo.delete();
_logo = logo;
_updated = true;
}
@ -198,8 +201,10 @@ public class BlogConfigBean {
public boolean publishChanges() {
FileInputStream logo = null;
try {
if (_logo != null)
if (_logo != null) {
logo = new FileInputStream(_logo);
_log.debug("Logo file is: " + _logo.length() + "bytes @ " + _logo.getAbsolutePath());
}
InputStream styleStream = createStyleStream();
InputStream groupStream = createGroupStream();
@ -240,6 +245,7 @@ public class BlogConfigBean {
// ok great, published locally, though should we push it to others?
_log.info("Blog summary updated for " + _user + " in " + uri.toString());
setUser(_user);
_log.debug("Updated? " + _updated);
return true;
}
} else {
@ -255,6 +261,7 @@ public class BlogConfigBean {
} finally {
if (logo != null) try { logo.close(); } catch (IOException ioe) {}
// the other streams are in-memory, drop with the scope
if (_logo != null) _logo.delete();
}
return false;
}
@ -280,4 +287,8 @@ public class BlogConfigBean {
}
return new ByteArrayInputStream(DataHelper.getUTF8(buf));
}
protected void finalize() {
if (_logo != null) _logo.delete();
}
}

View File

@ -24,21 +24,28 @@ public class BlogConfigServlet extends BaseServlet {
public static final String PARAM_CONFIG_SCREEN = "screen";
public static final String SCREEN_REFERENCES = "references";
public static final String SCREEN_IMAGES = "images";
protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index,
int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
if ( (user == null) || (!user.getAuthenticated() && !BlogManager.instance().isSingleUser())) {
out.write("You must be logged in to edit your profile");
return;
}
public static BlogConfigBean getConfigBean(HttpServletRequest req, User user) {
BlogConfigBean bean = (BlogConfigBean)req.getSession().getAttribute(ATTR_CONFIG_BEAN);
if (bean == null) {
bean = new BlogConfigBean();
bean.setUser(user);
req.getSession().setAttribute(ATTR_CONFIG_BEAN, bean);
}
return bean;
}
public static BlogConfigBean getConfigBean(HttpServletRequest req) {
return (BlogConfigBean)req.getSession().getAttribute(ATTR_CONFIG_BEAN);
}
protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index,
int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
if ( (user == null) || (!user.getAuthenticated() && !BlogManager.instance().isSingleUser())) {
out.write("You must be logged in to edit your profile");
return;
}
// handle actions here...
// on done handling
BlogConfigBean bean = getConfigBean(req, user);
String screen = req.getParameter(PARAM_CONFIG_SCREEN);
if (screen == null)
@ -50,17 +57,70 @@ public class BlogConfigServlet extends BaseServlet {
StringBuffer buf = handleOtherAuthedActions(user, req, bean);
if (buf != null) out.write(buf.toString());
} else {
String contentType = req.getContentType();
if (!empty(contentType) && (contentType.indexOf("boundary=") != -1)) {
StringBuffer buf = handlePost(user, req, bean);
if (buf != null) out.write(buf.toString());
}
}
if (bean.isUpdated())
showCommitForm(req, out);
if (SCREEN_REFERENCES.equals(screen)) {
displayReferencesScreen(req, out, user, bean);
} else if (SCREEN_IMAGES.equals(screen)) {
displayImagesScreen(req, out, user, bean);
} else {
displayUnknownScreen(out, screen);
}
out.write("</td></tr>\n");
}
private StringBuffer handlePost(User user, HttpServletRequest rawRequest, BlogConfigBean bean) throws IOException {
StringBuffer rv = new StringBuffer(64);
MultiPartRequest req = new MultiPartRequest(rawRequest);
if (authAction(req.getString(PARAM_AUTH_ACTION))) {
// read in the logo if specified
String filename = req.getFilename("newLogo");
if ( (filename != null) && (filename.trim().length() > 0) ) {
Hashtable params = req.getParams("newLogo");
String type = "image/png";
for (Iterator iter = params.keySet().iterator(); iter.hasNext(); ) {
String cur = (String)iter.next();
if ("content-type".equalsIgnoreCase(cur)) {
type = (String)params.get(cur);
break;
}
}
InputStream logoSrc = req.getInputStream("newLogo");
File tmpLogo = File.createTempFile("blogLogo", ".png", BlogManager.instance().getTempDir());
FileOutputStream out = null;
try {
out = new FileOutputStream(tmpLogo);
byte buf[] = new byte[4096];
int read = 0;
while ( (read = logoSrc.read(buf)) != -1)
out.write(buf, 0, read);
} finally {
if (out != null) try { out.close(); } catch (IOException ioe) {}
}
long len = tmpLogo.length();
if (len > BlogInfoData.MAX_LOGO_SIZE) {
tmpLogo.delete();
rv.append("Proposed logo is too large (" + len + ", max of " + BlogInfoData.MAX_LOGO_SIZE + ")<br />\n");
} else {
bean.setLogo(tmpLogo);
rv.append("Logo updated<br />");
}
} else {
// logo not specified
}
} else {
// noop
}
return rv;
}
private void showCommitForm(HttpServletRequest req, PrintWriter out) throws IOException {
out.write("<form action=\"" + req.getRequestURI() + "\" method=\"GET\">\n");
@ -186,6 +246,18 @@ public class BlogConfigServlet extends BaseServlet {
}
}
private void displayImagesScreen(HttpServletRequest req, PrintWriter out, User user, BlogConfigBean bean) throws IOException {
out.write("<form action=\"" + getScreenURL(req, SCREEN_IMAGES, false) + "\" method=\"POST\" enctype=\"multipart/form-data\">\n");
writeAuthActionFields(out);
File logo = bean.getLogo();
if (logo != null)
out.write("Blog logo: <img src=\"" + ViewBlogServlet.getLogoURL(user.getBlog()) + "\" alt=\"Your blog's logo\" /><br />\n");
out.write("New logo: <input type=\"file\" name=\"newLogo\" title=\"PNG or JPG format logo\" /><br />\n");
out.write("<input type=\"submit\" name=\"action\" value=\"Save changes\">\n");
out.write("</form>\n");
}
protected StringBuffer handleOtherAuthedActions(User user, HttpServletRequest req, BlogConfigBean bean) {
StringBuffer buf = new StringBuffer();
req.setAttribute(getClass().getName() + ".output", buf);

View File

@ -5,8 +5,7 @@ import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.client.naming.PetName;
import net.i2p.syndie.*;
import net.i2p.syndie.data.BlogURI;
import net.i2p.syndie.data.EntryContainer;
import net.i2p.syndie.data.*;
import net.i2p.syndie.sml.HTMLPreviewRenderer;
import net.i2p.syndie.sml.HTMLRenderer;
import net.i2p.util.Log;
@ -112,7 +111,19 @@ public class PostBean {
if ( (pn != null) && ("syndiearchive".equals(pn.getProtocol())) ) {
RemoteArchiveBean r = new RemoteArchiveBean();
Map params = new HashMap();
params.put("localentry", new String[] { uri.toString() });
String entries[] = null;
BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(uri);
if (info != null) {
String str = info.getProperty(BlogInfo.SUMMARY_ENTRY_ID);
if (str != null) {
entries = new String[] { uri.toString(), str };
}
}
if (entries == null)
entries = new String[] { uri.toString() };
params.put("localentry", entries);
String proxyHost = BlogManager.instance().getDefaultProxyHost();
String port = BlogManager.instance().getDefaultProxyPort();
int proxyPort = 4444;

View File

@ -104,7 +104,6 @@ public class ProfileServlet extends BaseServlet {
out.write("<tr><td colspan=\"3\"><input type=\"submit\" name=\"action\" value=\"Update profile\" /></td></tr>\n");
out.write("</form>\n");
out.write("<tr><td colspan=\"3\"><a href=\"configblog.jsp\">Configure your blog</a></td></tr>\n");
}
private void renderProfile(User user, String baseURI, PrintWriter out, Hash author, Archive archive) throws IOException {

View File

@ -31,6 +31,8 @@ public class ViewBlogServlet extends BaseServlet {
public static final String PARAM_TAG = "tag";
/** $blogHash/$entryId/$attachmentId */
public static final String PARAM_ATTACHMENT = "attachment";
/** image within the BlogInfoData to load (e.g. logo.png, icon_$tagHash.png, etc) */
public static final String PARAM_IMAGE = "image";
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
@ -40,7 +42,15 @@ public class ViewBlogServlet extends BaseServlet {
if (renderAttachment(req, resp, attachment))
return;
}
//todo: take care of logo requests, etc
String img = req.getParameter(PARAM_IMAGE);
if (img != null) {
boolean rendered = renderUpdatedImage(img, req, resp);
if (!rendered)
rendered = renderPublishedImage(img, req, resp);
if (!rendered)
rendered = renderDefaultImage(img, req, resp);
if (rendered) return;
}
super.service(req, resp);
}
@ -179,16 +189,17 @@ public class ViewBlogServlet extends BaseServlet {
if (content != null) out.write(content);
}
public static String getLogoURL(Hash blog) {
return "blog.jsp?" + PARAM_BLOG + "=" + blog.toBase64() + "&amp;"
+ PARAM_IMAGE + "=" + BlogInfoData.ATTACHMENT_LOGO;
}
private void renderHeader(User user, HttpServletRequest req, PrintWriter out, BlogInfo info, BlogInfoData data, String title, String desc) throws IOException {
out.write("<body class=\"syndieBlog\">\n<span style=\"display: none\">" +
"<a href=\"#content\" title=\"Skip to the blog content\">Content</a></span>\n");
renderNavBar(user, req, out);
out.write("<div class=\"syndieBlogHeader\">\n");
if (data != null) {
if (data.isLogoSpecified()) {
out.write("<img src=\"logo.png\" alt=\"\" />\n");
}
}
out.write("<img class=\"syndieBlogLogo\" src=\"" + getLogoURL(info.getKey().calculateHash()) + "\" alt=\"\" />\n");
String name = desc;
if ( (name == null) || (name.trim().length() <= 0) )
name = title;
@ -454,6 +465,135 @@ public class ViewBlogServlet extends BaseServlet {
} catch (NumberFormatException nfe) {}
return false;
}
private boolean renderUpdatedImage(String requestedImage, HttpServletRequest req, HttpServletResponse resp) throws IOException {
BlogConfigBean bean = BlogConfigServlet.getConfigBean(req);
if ( (bean != null) && (bean.isUpdated()) && (bean.getLogo() != null) ) {
// the updated image only affects *our* blog...
User u = bean.getUser();
if (u != null) {
String reqBlog = req.getParameter(PARAM_BLOG);
if ( (reqBlog == null) || (u.getBlog().toBase64().equals(reqBlog)) ) {
if (BlogInfoData.ATTACHMENT_LOGO.equals(requestedImage)) {
File logo = bean.getLogo();
if (logo != null) {
byte buf[] = new byte[4096];
resp.setContentType("image/png");
resp.setContentLength((int)logo.length());
OutputStream out = resp.getOutputStream();
FileInputStream in = null;
try {
in = new FileInputStream(logo);
int read = 0;
while ( (read = in.read(buf)) != -1)
out.write(buf, 0, read);
_log.debug("Done writing the updated full length logo");
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
if (out != null) try { out.close(); } catch (IOException ioe) {}
}
_log.debug("Returning from writing the updated full length logo");
return true;
}
} else {
// ok, the blogConfigBean doesn't let people configure other things yet... fall through
}
}
}
}
return false;
}
private boolean renderPublishedImage(String requestedImage, HttpServletRequest req, HttpServletResponse resp) throws IOException {
// nothing matched in the updated config, lets look at the current published info
String blog = req.getParameter(PARAM_BLOG);
if (blog != null) {
Archive archive = BlogManager.instance().getArchive();
byte h[] = Base64.decode(blog);
if ( (h != null) && (h.length == Hash.HASH_LENGTH) ) {
Hash blogHash = new Hash(h);
BlogInfo info = archive.getBlogInfo(blogHash);
String entryId = info.getProperty(BlogInfo.SUMMARY_ENTRY_ID);
_log.debug("Author's entryId: " + entryId);
if (entryId != null) {
BlogURI dataURI = new BlogURI(entryId);
EntryContainer entry = archive.getEntry(dataURI);
if (entry != null) {
BlogInfoData data = new BlogInfoData();
try {
data.load(entry);
_log.debug("Blog info data loaded from: " + entryId);
Attachment toWrite = null;
if (BlogInfoData.ATTACHMENT_LOGO.equals(requestedImage)) {
toWrite = data.getLogo();
} else {
toWrite = data.getOtherAttachment(requestedImage);
}
if (toWrite != null) {
resp.setContentType("image/png");
resp.setContentLength(toWrite.getDataLength());
InputStream in = null;
OutputStream out = null;
try {
in = toWrite.getDataStream();
out = resp.getOutputStream();
byte buf[] = new byte[4096];
int read = -1;
while ( (read = in.read(buf)) != -1)
out.write(buf, 0, read);
_log.debug("Write image from: " + entryId);
return true;
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
if (out != null) try { out.close(); } catch (IOException ioe) {}
}
}
} catch (IOException ioe) {
_log.debug("Error reading/writing: " + entryId, ioe);
data = null;
}
}
}
}
}
return false;
}
/** 1px png, base64 encoded, used if they asked for an image that we dont know of */
private static final byte BLANK_IMAGE[] = Base64.decode("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQI12NgYAAAAAMAASDVlMcAAAAASUVORK5CYII=");
private boolean renderDefaultImage(String requestedImage, HttpServletRequest req, HttpServletResponse resp) throws IOException {
if (requestedImage.equals("logo.png")) {
InputStream in = req.getSession().getServletContext().getResourceAsStream("/images/default_blog_logo.png");
if (in != null) {
resp.setContentType("image/png");
OutputStream out = resp.getOutputStream();
try {
byte buf[] = new byte[4096];
int read = -1;
while ( (read = in.read(buf)) != -1)
out.write(buf, 0, read);
_log.debug("Done writing default logo");
} finally {
try { in.close(); } catch (IOException ioe) {}
try { out.close(); } catch (IOException ioe) {}
return true;
}
}
}
resp.setContentType("img.png");
resp.setContentLength(BLANK_IMAGE.length);
OutputStream out = resp.getOutputStream();
try {
out.write(BLANK_IMAGE);
} finally {
try { out.close(); } catch (IOException ioe) {}
}
_log.debug("Done writing default image");
return true;
}
private static final String CSS =
"<style>\n" +
@ -492,6 +632,10 @@ public class ViewBlogServlet extends BaseServlet {
" background-color: black;\n" +
" color: white;\n" +
"}\n" +
".syndieBlogLogo {\n" +
" float: left;\n" +
" display: inline;\n" +
"}\n" +
".syndieBlogLinks {\n" +
" width: 200px;\n" +
"}\n" +

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,4 +1,12 @@
$Id: history.txt,v 1.379 2006/01/08 15:54:39 jrandom Exp $
$Id: history.txt,v 1.380 2006/01/09 01:33:29 jrandom Exp $
2005-01-09 jrandom
* Removed a longstanding bug that had caused unnecessary router identity
churn due to clock skew
* Temporarily sanity check within the streaming lib for long pending
writes
* Added support for a blog-wide logo to Syndie, and automated the pushing
of updated extended blog info data along side the metadata.
2005-01-09 jrandom
* Bugfix for a rare SSU error (thanks cervantes!)

View File

@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
*
*/
public class RouterVersion {
public final static String ID = "$Revision: 1.325 $ $Date: 2006/01/04 21:48:17 $";
public final static String ID = "$Revision: 1.326 $ $Date: 2006/01/09 01:33:30 $";
public final static String VERSION = "0.6.1.8";
public final static long BUILD = 9;
public final static long BUILD = 10;
public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
System.out.println("Router ID: " + RouterVersion.ID);

View File

@ -303,8 +303,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
try {
publish(ri);
} catch (IllegalArgumentException iae) {
_log.log(Log.CRIT, "Our local router info is b0rked, clearing from scratch", iae);
_context.router().rebuildNewIdentity();
_context.router().rebuildRouterInfo(true);
//_log.log(Log.CRIT, "Our local router info is b0rked, clearing from scratch", iae);
//_context.router().rebuildNewIdentity();
}
}