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:
@ -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; }
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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() + "&"
|
||||
+ 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" +
|
||||
|
BIN
apps/syndie/jsp/images/default_blog_logo.png
Normal file
BIN
apps/syndie/jsp/images/default_blog_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
10
history.txt
10
history.txt
@ -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!)
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user