2005-08-23 jrandom

* Removed the concept of "no bandwidth limit" - if none is specified, its
      16KBps in/out.
    * Include ack packets in the per-peer cwin throttle (they were part of the
      bandwidth limit though).
    * Tweak the SSU cwin operation to get more accurrate estimates under
      congestions.
    * SSU improvements to resend more efficiently.
    * Added a basic scheduler to eepget to fetch multiple files sequentially.
This commit is contained in:
jrandom
2005-08-23 21:25:49 +00:00
committed by zzz
parent c7b75df390
commit 1a6b49cfb8
37 changed files with 1634 additions and 273 deletions

View File

@@ -30,6 +30,7 @@ public class Archive {
private Map _blogInfo;
private ArchiveIndex _index;
private EntryExtractor _extractor;
private String _defaultSelector;
public static final String METADATA_FILE = "meta.snm";
public static final String INDEX_FILE = "archive.txt";
@@ -50,6 +51,8 @@ public class Archive {
_blogInfo = new HashMap();
_index = null;
_extractor = new EntryExtractor(ctx);
_defaultSelector = ctx.getProperty("syndie.defaultSelector");
if (_defaultSelector == null) _defaultSelector = "";
reloadInfo();
}
@@ -63,7 +66,12 @@ public class Archive {
BlogInfo bi = new BlogInfo();
try {
bi.load(new FileInputStream(meta));
info.add(bi);
if (bi.verify(_context)) {
info.add(bi);
} else {
System.err.println("Invalid blog (but we're storing it anyway): " + bi);
info.add(bi);
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
@@ -79,8 +87,11 @@ public class Archive {
}
}
}
public String getDefaultSelector() { return _defaultSelector; }
public BlogInfo getBlogInfo(BlogURI uri) {
if (uri == null) return null;
synchronized (_blogInfo) {
return (BlogInfo)_blogInfo.get(uri.getKeyHash());
}
@@ -90,14 +101,20 @@ public class Archive {
return (BlogInfo)_blogInfo.get(key);
}
}
public void storeBlogInfo(BlogInfo info) {
public boolean storeBlogInfo(BlogInfo info) {
if (!info.verify(_context)) {
System.err.println("Not storing the invalid blog " + info);
return;
return false;
}
boolean isNew = true;
synchronized (_blogInfo) {
_blogInfo.put(info.getKey().calculateHash(), info);
BlogInfo old = (BlogInfo)_blogInfo.get(info.getKey().calculateHash());
if ( (old == null) || (old.getEdition() < info.getEdition()) )
_blogInfo.put(info.getKey().calculateHash(), info);
else
isNew = false;
}
if (!isNew) return true; // valid entry, but not stored, since its old
try {
File blogDir = new File(_rootDir, info.getKey().calculateHash().toBase64());
blogDir.mkdirs();
@@ -106,8 +123,10 @@ public class Archive {
info.write(out);
out.close();
System.out.println("Blog info written to " + blogFile.getPath());
return true;
} catch (IOException ioe) {
ioe.printStackTrace();
return false;
}
}
@@ -262,7 +281,16 @@ public class Archive {
}
public boolean storeEntry(EntryContainer container) {
if (container == null) return false;
BlogURI uri = container.getURI();
if (uri == null) return false;
File blogDir = new File(_rootDir, uri.getKeyHash().toBase64());
blogDir.mkdirs();
File entryFile = new File(blogDir, getEntryFilename(uri.getEntryId()));
if (entryFile.exists()) return true;
BlogInfo info = getBlogInfo(uri);
if (info == null) {
System.out.println("no blog metadata for the uri " + uri);
@@ -274,13 +302,10 @@ public class Archive {
} else {
//System.out.println("Signature is valid: " + container.getSignature() + " for info " + info);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
container.write(baos, true);
File blogDir = new File(_rootDir, uri.getKeyHash().toBase64());
blogDir.mkdirs();
byte data[] = baos.toByteArray();
File entryFile = new File(blogDir, getEntryFilename(uri.getEntryId()));
FileOutputStream out = new FileOutputStream(entryFile);
out.write(data);
out.close();

View File

@@ -18,6 +18,7 @@ public class BlogManager {
private File _archiveDir;
private File _userDir;
private File _cacheDir;
private File _tempDir;
private Archive _archive;
static {
@@ -42,11 +43,13 @@ public class BlogManager {
_archiveDir = new File(root, "archive");
_userDir = new File(root, "users");
_cacheDir = new File(root, "cache");
_tempDir = new File(root, "temp");
_blogKeyDir.mkdirs();
_privKeyDir.mkdirs();
_archiveDir.mkdirs();
_cacheDir.mkdirs();
_userDir.mkdirs();
_tempDir.mkdirs();
_archive = new Archive(ctx, _archiveDir.getAbsolutePath(), _cacheDir.getAbsolutePath());
_archive.regenerateIndex();
}
@@ -93,6 +96,7 @@ public class BlogManager {
}
public Archive getArchive() { return _archive; }
public File getTempDir() { return _tempDir; }
public List listMyBlogs() {
File files[] = _privKeyDir.listFiles();
@@ -175,45 +179,7 @@ public class BlogManager {
FileWriter out = null;
try {
out = new FileWriter(userFile);
out.write("password=" + user.getHashedPassword() + "\n");
out.write("blog=" + user.getBlog().toBase64() + "\n");
out.write("lastid=" + user.getMostRecentEntry() + "\n");
out.write("lastmetaedition=" + user.getLastMetaEntry() + "\n");
out.write("lastlogin=" + user.getLastLogin() + "\n");
out.write("addressbook=" + user.getAddressbookLocation() + "\n");
out.write("showimages=" + user.getShowImages() + "\n");
out.write("showexpanded=" + user.getShowExpanded() + "\n");
StringBuffer buf = new StringBuffer();
buf.append("groups=");
Map groups = user.getBlogGroups();
for (Iterator iter = groups.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
List selectors = (List)groups.get(name);
buf.append(name).append(':');
for (int i = 0; i < selectors.size(); i++) {
buf.append(selectors.get(i));
if (i + 1 < selectors.size())
buf.append(",");
}
if (iter.hasNext())
buf.append(' ');
}
buf.append('\n');
out.write(buf.toString());
// shitlist=hash,hash,hash
List shitlistedBlogs = user.getShitlistedBlogs();
if (shitlistedBlogs.size() > 0) {
buf.setLength(0);
buf.append("shitlistedblogs=");
for (int i = 0; i < shitlistedBlogs.size(); i++) {
Hash blog = (Hash)shitlistedBlogs.get(i);
buf.append(blog.toBase64());
if (i + 1 < shitlistedBlogs.size())
buf.append(',');
}
buf.append('\n');
out.write(buf.toString());
}
out.write(user.export());
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
@@ -347,6 +313,39 @@ public class BlogManager {
}
}
/**
* read in the syndie blog metadata file from the stream, verifying it and adding it to
* the archive if necessary
*
*/
public boolean importBlogMetadata(InputStream metadataStream) throws IOException {
try {
BlogInfo info = new BlogInfo();
info.load(metadataStream);
return _archive.storeBlogInfo(info);
} catch (IOException ioe) {
ioe.printStackTrace();
return false;
}
}
/**
* read in the syndie entry file from the stream, verifying it and adding it to
* the archive if necessary
*
*/
public boolean importBlogEntry(InputStream entryStream) throws IOException {
try {
EntryContainer c = new EntryContainer();
c.load(entryStream);
return _archive.storeEntry(c);
} catch (IOException ioe) {
ioe.printStackTrace();
return false;
}
}
public String addAddress(User user, String name, String location, String schema) {
if (!user.getAuthenticated()) return "Not logged in";
boolean ok = validateAddressName(name);

View File

@@ -24,9 +24,17 @@ public class User {
private String _addressbookLocation;
private boolean _showImagesByDefault;
private boolean _showExpandedByDefault;
private String _defaultSelector;
private long _lastLogin;
private long _lastMetaEntry;
private boolean _allowAccessRemote;
private boolean _authenticated;
private String _eepProxyHost;
private int _eepProxyPort;
private String _webProxyHost;
private int _webProxyPort;
private String _torProxyHost;
private int _torProxyPort;
public User() {
_context = I2PAppContext.getGlobalContext();
@@ -40,9 +48,17 @@ public class User {
_mostRecentEntry = -1;
_blogGroups = new HashMap();
_shitlistedBlogs = new ArrayList();
_defaultSelector = null;
_addressbookLocation = "userhosts.txt";
_showImagesByDefault = false;
_showExpandedByDefault = false;
_allowAccessRemote = false;
_eepProxyHost = null;
_webProxyHost = null;
_torProxyHost = null;
_eepProxyPort = -1;
_webProxyPort = -1;
_torProxyPort = -1;
_lastLogin = -1;
_lastMetaEntry = 0;
}
@@ -60,10 +76,21 @@ public class User {
public long getLastLogin() { return _lastLogin; }
public String getHashedPassword() { return _hashedPassword; }
public long getLastMetaEntry() { return _lastMetaEntry; }
public String getDefaultSelector() { return _defaultSelector; }
public void setDefaultSelector(String sel) { _defaultSelector = sel; }
public boolean getAllowAccessRemote() { return _allowAccessRemote; }
public void setAllowAccessRemote(boolean allow) { _allowAccessRemote = true; }
public void setMostRecentEntry(long id) { _mostRecentEntry = id; }
public void setLastMetaEntry(long id) { _lastMetaEntry = id; }
public String getEepProxyHost() { return _eepProxyHost; }
public int getEepProxyPort() { return _eepProxyPort; }
public String getWebProxyHost() { return _webProxyHost; }
public int getWebProxyPort() { return _webProxyPort; }
public String getTorProxyHost() { return _torProxyHost; }
public int getTorProxyPort() { return _torProxyPort; }
public void invalidate() {
BlogManager.instance().saveUser(this);
init();
@@ -135,11 +162,75 @@ public class User {
_showImagesByDefault = (show != null) && (show.equals("true"));
show = props.getProperty("showexpanded", "false");
_showExpandedByDefault = (show != null) && (show.equals("true"));
_defaultSelector = props.getProperty("defaultselector");
String allow = props.getProperty("allowaccessremote", "false");
_allowAccessRemote = (allow != null) && (allow.equals("true"));
_eepProxyPort = getInt(props.getProperty("eepproxyport"));
_webProxyPort = getInt(props.getProperty("webproxyport"));
_torProxyPort = getInt(props.getProperty("torproxyport"));
_eepProxyHost = props.getProperty("eepproxyhost");
_webProxyHost = props.getProperty("webproxyhost");
_torProxyHost = props.getProperty("torproxyhost");
_lastLogin = _context.clock().now();
_authenticated = true;
return LOGIN_OK;
}
private int getInt(String val) {
if (val == null) return -1;
try { return Integer.parseInt(val); } catch (NumberFormatException nfe) { return -1; }
}
public static final String LOGIN_OK = "Logged in";
public String export() {
StringBuffer buf = new StringBuffer(512);
buf.append("password=" + getHashedPassword() + "\n");
buf.append("blog=" + getBlog().toBase64() + "\n");
buf.append("lastid=" + getMostRecentEntry() + "\n");
buf.append("lastmetaedition=" + getLastMetaEntry() + "\n");
buf.append("lastlogin=" + getLastLogin() + "\n");
buf.append("addressbook=" + getAddressbookLocation() + "\n");
buf.append("showimages=" + getShowImages() + "\n");
buf.append("showexpanded=" + getShowExpanded() + "\n");
buf.append("defaultselector=" + getDefaultSelector() + "\n");
buf.append("allowaccessremote=" + _allowAccessRemote + "\n");
buf.append("eepproxyhost="+_eepProxyHost+"\n");
buf.append("eepproxyport="+_eepProxyPort+"\n");
buf.append("webproxyhost="+_webProxyHost+"\n");
buf.append("webproxyport="+_webProxyPort+"\n");
buf.append("torproxyhost="+_torProxyHost+"\n");
buf.append("torproxyport="+_torProxyPort+"\n");
buf.append("groups=");
Map groups = getBlogGroups();
for (Iterator iter = groups.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
List selectors = (List)groups.get(name);
buf.append(name).append(':');
for (int i = 0; i < selectors.size(); i++) {
buf.append(selectors.get(i));
if (i + 1 < selectors.size())
buf.append(",");
}
if (iter.hasNext())
buf.append(' ');
}
buf.append('\n');
// shitlist=hash,hash,hash
List shitlistedBlogs = getShitlistedBlogs();
if (shitlistedBlogs.size() > 0) {
buf.setLength(0);
buf.append("shitlistedblogs=");
for (int i = 0; i < shitlistedBlogs.size(); i++) {
Hash blog = (Hash)shitlistedBlogs.get(i);
buf.append(blog.toBase64());
if (i + 1 < shitlistedBlogs.size())
buf.append(',');
}
buf.append('\n');
}
return buf.toString();
}
}

View File

@@ -77,6 +77,44 @@ public class ArchiveIndex {
/** get the raw entry size (including attachments) from the given blog/tag pair */
public long getBlogEntrySizeKB(int index, int entryIndex) { return ((EntrySummary)((BlogSummary)_blogs.get(index)).entries.get(entryIndex)).size; }
public boolean getEntryIsKnown(BlogURI uri) { return getEntry(uri) != null; }
public long getBlogEntrySizeKB(BlogURI uri) {
EntrySummary entry = getEntry(uri);
if (entry == null) return -1;
return entry.size;
}
private EntrySummary getEntry(BlogURI uri) {
if ( (uri == null) || (uri.getKeyHash() == null) || (uri.getEntryId() < 0) ) return null;
for (int i = 0; i < _blogs.size(); i++) {
BlogSummary summary = (BlogSummary)_blogs.get(i);
if (summary.blog.equals(uri.getKeyHash())) {
for (int j = 0; j < summary.entries.size(); j++) {
EntrySummary entry = (EntrySummary)summary.entries.get(j);
if (entry.entry.equals(uri))
return entry;
}
}
}
return null;
}
public Set getBlogEntryTags(BlogURI uri) {
Set tags = new HashSet();
if ( (uri == null) || (uri.getKeyHash() == null) || (uri.getEntryId() < 0) ) return tags;
for (int i = 0; i < _blogs.size(); i++) {
BlogSummary summary = (BlogSummary)_blogs.get(i);
if (summary.blog.equals(uri.getKeyHash())) {
for (int j = 0; j < summary.entries.size(); j++) {
EntrySummary entry = (EntrySummary)summary.entries.get(j);
if (entry.entry.equals(uri)) {
tags.add(summary.tag);
break;
}
}
}
}
return tags;
}
/** how many 'new' blogs are listed */
public int getNewestBlogCount() { return _newestBlogs.size(); }
public Hash getNewestBlog(int index) { return (Hash)_newestBlogs.get(index); }

View File

@@ -13,6 +13,7 @@ import net.i2p.I2PAppContext;
* Required keys:
* Owner: base64 of their signing public key
* Signature: base64 of the DSA signature of the rest of the ordered metadata
* Edition: base10 unique identifier for this metadata (higher clobbers lower)
*
* Optional keys:
* Posters: comma delimited list of base64 signing public keys that
@@ -53,6 +54,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 EDITION = "Edition";
public void load(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
@@ -63,8 +65,13 @@ public class BlogInfo {
line = line.trim();
int len = line.length();
int split = line.indexOf(':');
if ( (len <= 0) || (split <= 0) || (split >= len - 2) )
if ( (len <= 0) || (split <= 0) ) {
continue;
} else if (split >= len - 1) {
names.add(line.substring(0, split).trim());
vals.add("");
continue;
}
String key = line.substring(0, split).trim();
String val = line.substring(split+1).trim();
@@ -102,7 +109,8 @@ public class BlogInfo {
if ( (includeRealSignature) || (!SIGNATURE.equals(_optionNames[i])) )
buf.append(_optionNames[i]).append(':').append(_optionValues[i]).append('\n');
}
out.write(buf.toString().getBytes());
String s = buf.toString();
out.write(s.getBytes());
}
public String getProperty(String name) {
@@ -133,6 +141,18 @@ public class BlogInfo {
_optionValues = values;
}
public int getEdition() {
String e = getProperty(EDITION);
if (e != null) {
try {
return Integer.parseInt(e);
} catch (NumberFormatException nfe) {
return 0;
}
}
return 0;
}
public String[] getProperties() { return _optionNames; }
public SigningPublicKey[] getPosters() { return _posters; }
@@ -151,7 +171,9 @@ public class BlogInfo {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream(512);
write(out, false);
return ctx.dsa().verifySignature(_signature, out.toByteArray(), _key);
out.close();
byte data[] = out.toByteArray();
return ctx.dsa().verifySignature(_signature, data, _key);
} catch (IOException ioe) {
return false;
}
@@ -192,4 +214,52 @@ public class BlogInfo {
}
return buf.toString();
}
public static void main(String args[]) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
/*
try {
Object keys[] = ctx.keyGenerator().generateSigningKeypair();
SigningPublicKey pub = (SigningPublicKey)keys[0];
SigningPrivateKey priv = (SigningPrivateKey)keys[1];
Properties opts = new Properties();
opts.setProperty("Name", "n4m3");
opts.setProperty("Description", "foo");
opts.setProperty("Edition", "0");
opts.setProperty("ContactURL", "u@h.org");
BlogInfo info = new BlogInfo(pub, null, opts);
System.err.println("\n");
System.err.println("\n");
info.sign(ctx, priv);
System.err.println("\n");
boolean ok = info.verify(ctx);
System.err.println("\n");
System.err.println("sign&verify: " + ok);
System.err.println("\n");
System.err.println("\n");
FileOutputStream o = new FileOutputStream("bloginfo-test.dat");
info.write(o, true);
o.close();
FileInputStream i = new FileInputStream("bloginfo-test.dat");
byte buf[] = new byte[4096];
int sz = DataHelper.read(i, buf);
BlogInfo read = new BlogInfo();
read.load(new ByteArrayInputStream(buf, 0, sz));
ok = read.verify(ctx);
System.err.println("write to disk, verify read: " + ok);
System.err.println("Data: " + Base64.encode(buf, 0, sz));
System.err.println("Str : " + new String(buf, 0, sz));
} catch (Exception e) { e.printStackTrace(); }
*/
try {
FileInputStream in = new FileInputStream(args[0]);
BlogInfo info = new BlogInfo();
info.load(in);
boolean ok = info.verify(I2PAppContext.getGlobalContext());
System.out.println("OK? " + ok + " :" + info);
} catch (Exception e) { e.printStackTrace(); }
}
}

View File

@@ -72,7 +72,9 @@ public class EntryContainer {
public int getFormat() { return _format; }
public void load(InputStream source) throws IOException {
String fmt = DataHelper.readLine(source).trim();
String line = DataHelper.readLine(source);
if (line == null) throw new IOException("No format line in the entry");
String fmt = line.trim();
if (FORMAT_ZIP_UNENCRYPTED_STR.equals(fmt)) {
_format = FORMAT_ZIP_UNENCRYPTED;
} else if (FORMAT_ZIP_ENCRYPTED_STR.equals(fmt)) {
@@ -81,7 +83,6 @@ public class EntryContainer {
throw new IOException("Unsupported entry format: " + fmt);
}
String line = null;
while ( (line = DataHelper.readLine(source)) != null) {
line = line.trim();
int len = line.length();
@@ -99,17 +100,24 @@ public class EntryContainer {
parseHeaders();
String sigStr = DataHelper.readLine(source);
if ( (sigStr == null) || (sigStr.indexOf("Signature:") == -1) )
throw new IOException("No signature line");
sigStr = sigStr.substring("Signature:".length()+1).trim();
_signature = new Signature(Base64.decode(sigStr));
//System.out.println("Sig: " + _signature.toBase64());
line = DataHelper.readLine(source).trim();
line = DataHelper.readLine(source);
if (line == null)
throw new IOException("No size line");
line = line.trim();
int dataSize = -1;
try {
int index = line.indexOf("Size:");
if (index == 0)
dataSize = Integer.parseInt(line.substring("Size:".length()+1).trim());
else
throw new IOException("Invalid size line");
} catch (NumberFormatException nfe) {
throw new IOException("Invalid entry size: " + line);
}

View File

@@ -0,0 +1,109 @@
package net.i2p.syndie.sml;
import java.io.*;
import java.text.*;
import java.util.*;
import net.i2p.data.*;
import net.i2p.syndie.*;
import net.i2p.syndie.data.*;
import net.i2p.syndie.web.*;
/**
*
*/
public class HTMLPreviewRenderer extends HTMLRenderer {
private List _filenames;
private List _fileTypes;
private List _files;
public HTMLPreviewRenderer(List filenames, List fileTypes, List files) {
super();
_filenames = filenames;
_fileTypes = fileTypes;
_files = files;
}
protected String getAttachmentURLBase() { return "viewtempattachment.jsp"; }
protected String getAttachmentURL(int id) {
return getAttachmentURLBase() + "?" +
ArchiveViewerBean.PARAM_ATTACHMENT + "=" + id;
}
public void receiveAttachment(int id, String anchorText) {
if (!continueBody()) { return; }
if ( (id < 0) || (_files == null) || (id >= _files.size()) ) {
_bodyBuffer.append(sanitizeString(anchorText));
} else {
File f = (File)_files.get(id);
String name = (String)_filenames.get(id);
String type = (String)_fileTypes.get(id);
_bodyBuffer.append("<a href=\"").append(getAttachmentURL(id)).append("\">");
_bodyBuffer.append(sanitizeString(anchorText)).append("</a>");
_bodyBuffer.append(" (").append(f.length()/1024).append("KB, ");
_bodyBuffer.append(" \"").append(sanitizeString(name)).append("\", ");
_bodyBuffer.append(sanitizeString(type)).append(")");
}
}
public void receiveEnd() {
_postBodyBuffer.append("</td></tr>\n");
_postBodyBuffer.append("<tr>\n");
_postBodyBuffer.append("<form action=\"").append(getAttachmentURLBase()).append("\">\n");
_postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" class=\"syndieEntryAttachmentsCell\"\n");
if (_files.size() > 0) {
_postBodyBuffer.append("<b>Attachments:</b> ");
_postBodyBuffer.append("<select name=\"").append(ArchiveViewerBean.PARAM_ATTACHMENT).append("\">\n");
for (int i = 0; i < _files.size(); i++) {
_postBodyBuffer.append("<option value=\"").append(i).append("\">");
File f = (File)_files.get(i);
String name = (String)_filenames.get(i);
String type = (String)_fileTypes.get(i);
_postBodyBuffer.append(sanitizeString(name));
_postBodyBuffer.append(" (").append(f.length()/1024).append("KB");
_postBodyBuffer.append(", type ").append(sanitizeString(type)).append(")</option>\n");
}
_postBodyBuffer.append("</select>\n");
_postBodyBuffer.append("<input type=\"submit\" value=\"Download\" name=\"Download\" /><br />\n");
}
if (_blogs.size() > 0) {
_postBodyBuffer.append("<b>Blog references:</b> ");
for (int i = 0; i < _blogs.size(); i++) {
Blog b = (Blog)_blogs.get(i);
_postBodyBuffer.append("<a href=\"").append(getPageURL(new Hash(Base64.decode(b.hash)), b.tag, b.entryId, -1, -1, (_user != null ? _user.getShowExpanded() : false), (_user != null ? _user.getShowImages() : false)));
_postBodyBuffer.append("\">").append(sanitizeString(b.name)).append("</a> ");
}
_postBodyBuffer.append("<br />\n");
}
if (_links.size() > 0) {
_postBodyBuffer.append("<b>External links:</b> ");
for (int i = 0; i < _links.size(); i++) {
Link l = (Link)_links.get(i);
_postBodyBuffer.append("<a href=\"externallink.jsp?schema=");
_postBodyBuffer.append(sanitizeURL(l.schema)).append("&location=");
_postBodyBuffer.append(sanitizeURL(l.location));
_postBodyBuffer.append("\">").append(sanitizeString(l.location));
_postBodyBuffer.append(" (").append(sanitizeString(l.schema)).append(")</a> ");
}
_postBodyBuffer.append("<br />\n");
}
if (_addresses.size() > 0) {
_postBodyBuffer.append("<b>Addresses:</b> ");
for (int i = 0; i < _addresses.size(); i++) {
Address a = (Address)_addresses.get(i);
_postBodyBuffer.append("<a href=\"addaddress.jsp?schema=");
_postBodyBuffer.append(sanitizeURL(a.schema)).append("&location=");
_postBodyBuffer.append(sanitizeURL(a.location)).append("&name=");
_postBodyBuffer.append(sanitizeURL(a.name));
_postBodyBuffer.append("\">").append(sanitizeString(a.name));
}
_postBodyBuffer.append("<br />\n");
}
_postBodyBuffer.append("</td>\n</form>\n</tr>\n");
_postBodyBuffer.append("</table>\n");
}
}

View File

@@ -12,23 +12,23 @@ import net.i2p.syndie.web.*;
*
*/
public class HTMLRenderer extends EventReceiverImpl {
private SMLParser _parser;
private Writer _out;
private User _user;
private Archive _archive;
private EntryContainer _entry;
private boolean _showImages;
private boolean _cutBody;
private boolean _cutReached;
private int _cutSize;
private int _lastNewlineAt;
private Map _headers;
private List _addresses;
private List _links;
private List _blogs;
private StringBuffer _preBodyBuffer;
private StringBuffer _bodyBuffer;
private StringBuffer _postBodyBuffer;
protected SMLParser _parser;
protected Writer _out;
protected User _user;
protected Archive _archive;
protected EntryContainer _entry;
protected boolean _showImages;
protected boolean _cutBody;
protected boolean _cutReached;
protected int _cutSize;
protected int _lastNewlineAt;
protected Map _headers;
protected List _addresses;
protected List _links;
protected List _blogs;
protected StringBuffer _preBodyBuffer;
protected StringBuffer _bodyBuffer;
protected StringBuffer _postBodyBuffer;
public HTMLRenderer() {
_parser = new SMLParser();
@@ -190,7 +190,7 @@ public class HTMLRenderer extends EventReceiverImpl {
}
/** are we either before the cut or rendering without cutting? */
private boolean continueBody() {
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);
@@ -227,7 +227,7 @@ public class HTMLRenderer extends EventReceiverImpl {
_bodyBuffer.append(']');
}
private static class Blog {
protected static class Blog {
public String name;
public String hash;
public String tag;
@@ -317,7 +317,7 @@ public class HTMLRenderer extends EventReceiverImpl {
_bodyBuffer.append("] ");
}
private static class Link {
protected static class Link {
public String schema;
public String location;
public int hashCode() { return -1; }
@@ -340,7 +340,7 @@ public class HTMLRenderer extends EventReceiverImpl {
_bodyBuffer.append(sanitizeURL(text)).append("\">").append(sanitizeString(text)).append("</a>");
}
private static class Address {
protected static class Address {
public String name;
public String schema;
public String location;
@@ -381,79 +381,113 @@ public class HTMLRenderer extends EventReceiverImpl {
public void receiveEnd() {
_postBodyBuffer.append("</td></tr>\n");
_postBodyBuffer.append("<tr>\n");
_postBodyBuffer.append("<form action=\"").append(getAttachmentURLBase()).append("\">\n");
_postBodyBuffer.append("<input type=\"hidden\" name=\"").append(ArchiveViewerBean.PARAM_BLOG);
_postBodyBuffer.append("\" value=\"");
if (_entry != null)
_postBodyBuffer.append(Base64.encode(_entry.getURI().getKeyHash().getData()));
else
_postBodyBuffer.append("unknown");
_postBodyBuffer.append("\" />\n");
_postBodyBuffer.append("<input type=\"hidden\" name=\"").append(ArchiveViewerBean.PARAM_ENTRY);
_postBodyBuffer.append("\" value=\"");
if (_entry != null)
_postBodyBuffer.append(_entry.getURI().getEntryId());
else
_postBodyBuffer.append("unknown");
_postBodyBuffer.append("\" />\n");
_postBodyBuffer.append("<td valign=\"top\" align=\"left\" style=\"entry.attachments.cell\" bgcolor=\"#77ff77\">\n");
if ( (_entry != null) && (_entry.getAttachments() != null) && (_entry.getAttachments().length > 0) ) {
_postBodyBuffer.append("<b>Attachments:</b> ");
_postBodyBuffer.append("<select name=\"").append(ArchiveViewerBean.PARAM_ATTACHMENT).append("\">\n");
for (int i = 0; i < _entry.getAttachments().length; i++) {
_postBodyBuffer.append("<option value=\"").append(i).append("\">");
Attachment a = _entry.getAttachments()[i];
_postBodyBuffer.append(sanitizeString(a.getName()));
if ( (a.getDescription() != null) && (a.getDescription().trim().length() > 0) ) {
_postBodyBuffer.append(": ");
_postBodyBuffer.append(sanitizeString(a.getDescription()));
if (_cutBody) {
_postBodyBuffer.append("<tr class=\"syndieEntryAttachmentsCell\">\n");
_postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" class=\"syndieEntryAttachmentsCell\">");
_postBodyBuffer.append("<a href=\"").append(getEntryURL()).append("\">View details...</a> ");
if ( (_entry != null) && (_entry.getAttachments() != null) && (_entry.getAttachments().length > 0) ) {
int num = _entry.getAttachments().length;
if (num == 1)
_postBodyBuffer.append("1 attachment ");
else
_postBodyBuffer.append(num + " attachments ");
}
int blogs = _blogs.size();
if (blogs == 1)
_postBodyBuffer.append("1 blog reference ");
else if (blogs > 1)
_postBodyBuffer.append(blogs).append(" blog references ");
int links = _links.size();
if (links == 1)
_postBodyBuffer.append("1 external link ");
else if (links > 1)
_postBodyBuffer.append(links).append(" external links");
int addrs = _addresses.size();
if (addrs == 1)
_postBodyBuffer.append("1 address ");
else if (addrs > 1)
_postBodyBuffer.append(addrs).append(" addresses ");
_postBodyBuffer.append("</td></tr>\n");
} else {
_postBodyBuffer.append("<tr class=\"syndieEntryAttachmentsCell\">\n");
_postBodyBuffer.append("<form action=\"").append(getAttachmentURLBase()).append("\">\n");
_postBodyBuffer.append("<input type=\"hidden\" name=\"").append(ArchiveViewerBean.PARAM_BLOG);
_postBodyBuffer.append("\" value=\"");
if (_entry != null)
_postBodyBuffer.append(Base64.encode(_entry.getURI().getKeyHash().getData()));
else
_postBodyBuffer.append("unknown");
_postBodyBuffer.append("\" />\n");
_postBodyBuffer.append("<input type=\"hidden\" name=\"").append(ArchiveViewerBean.PARAM_ENTRY);
_postBodyBuffer.append("\" value=\"");
if (_entry != null)
_postBodyBuffer.append(_entry.getURI().getEntryId());
else
_postBodyBuffer.append("unknown");
_postBodyBuffer.append("\" />\n");
_postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" class=\"syndieEntryAttachmentsCell\">\n");
if ( (_entry != null) && (_entry.getAttachments() != null) && (_entry.getAttachments().length > 0) ) {
_postBodyBuffer.append("<b>Attachments:</b> ");
_postBodyBuffer.append("<select name=\"").append(ArchiveViewerBean.PARAM_ATTACHMENT).append("\">\n");
for (int i = 0; i < _entry.getAttachments().length; i++) {
_postBodyBuffer.append("<option value=\"").append(i).append("\">");
Attachment a = _entry.getAttachments()[i];
_postBodyBuffer.append(sanitizeString(a.getName()));
if ( (a.getDescription() != null) && (a.getDescription().trim().length() > 0) ) {
_postBodyBuffer.append(": ");
_postBodyBuffer.append(sanitizeString(a.getDescription()));
}
_postBodyBuffer.append(" (").append(a.getDataLength()/1024).append("KB");
_postBodyBuffer.append(", type ").append(sanitizeString(a.getMimeType())).append(")</option>\n");
}
_postBodyBuffer.append(" (").append(a.getDataLength()/1024).append("KB");
_postBodyBuffer.append(", type ").append(sanitizeString(a.getMimeType())).append(")</option>\n");
_postBodyBuffer.append("</select>\n");
_postBodyBuffer.append("<input type=\"submit\" value=\"Download\" name=\"Download\" /><br />\n");
}
_postBodyBuffer.append("</select>\n");
_postBodyBuffer.append("<input type=\"submit\" value=\"Download\" name=\"Download\" /><br />\n");
}
if (_blogs.size() > 0) {
_postBodyBuffer.append("<b>Blog references:</b> ");
for (int i = 0; i < _blogs.size(); i++) {
Blog b = (Blog)_blogs.get(i);
_postBodyBuffer.append("<a href=\"").append(getPageURL(new Hash(Base64.decode(b.hash)), b.tag, b.entryId, -1, -1, (_user != null ? _user.getShowExpanded() : false), (_user != null ? _user.getShowImages() : false)));
_postBodyBuffer.append("\">").append(sanitizeString(b.name)).append("</a> ");
if (_blogs.size() > 0) {
_postBodyBuffer.append("<b>Blog references:</b> ");
for (int i = 0; i < _blogs.size(); i++) {
Blog b = (Blog)_blogs.get(i);
_postBodyBuffer.append("<a href=\"").append(getPageURL(new Hash(Base64.decode(b.hash)), b.tag, b.entryId, -1, -1, (_user != null ? _user.getShowExpanded() : false), (_user != null ? _user.getShowImages() : false)));
_postBodyBuffer.append("\">").append(sanitizeString(b.name)).append("</a> ");
}
_postBodyBuffer.append("<br />\n");
}
_postBodyBuffer.append("<br />\n");
}
if (_links.size() > 0) {
_postBodyBuffer.append("<b>External links:</b> ");
for (int i = 0; i < _links.size(); i++) {
Link l = (Link)_links.get(i);
_postBodyBuffer.append("<a href=\"externallink.jsp?schema=");
_postBodyBuffer.append(sanitizeURL(l.schema)).append("&location=");
_postBodyBuffer.append(sanitizeURL(l.location));
_postBodyBuffer.append("\">").append(sanitizeString(l.location));
_postBodyBuffer.append(" (").append(sanitizeString(l.schema)).append(")</a> ");
if (_links.size() > 0) {
_postBodyBuffer.append("<b>External links:</b> ");
for (int i = 0; i < _links.size(); i++) {
Link l = (Link)_links.get(i);
_postBodyBuffer.append("<a href=\"externallink.jsp?schema=");
_postBodyBuffer.append(sanitizeURL(l.schema)).append("&location=");
_postBodyBuffer.append(sanitizeURL(l.location));
_postBodyBuffer.append("\">").append(sanitizeString(l.location));
_postBodyBuffer.append(" (").append(sanitizeString(l.schema)).append(")</a> ");
}
_postBodyBuffer.append("<br />\n");
}
_postBodyBuffer.append("<br />\n");
}
if (_addresses.size() > 0) {
_postBodyBuffer.append("<b>Addresses:</b> ");
for (int i = 0; i < _addresses.size(); i++) {
Address a = (Address)_addresses.get(i);
_postBodyBuffer.append("<a href=\"addaddress.jsp?schema=");
_postBodyBuffer.append(sanitizeURL(a.schema)).append("&location=");
_postBodyBuffer.append(sanitizeURL(a.location)).append("&name=");
_postBodyBuffer.append(sanitizeURL(a.name));
_postBodyBuffer.append("\">").append(sanitizeString(a.name));
if (_addresses.size() > 0) {
_postBodyBuffer.append("<b>Addresses:</b> ");
for (int i = 0; i < _addresses.size(); i++) {
Address a = (Address)_addresses.get(i);
_postBodyBuffer.append("<a href=\"addaddress.jsp?schema=");
_postBodyBuffer.append(sanitizeURL(a.schema)).append("&location=");
_postBodyBuffer.append(sanitizeURL(a.location)).append("&name=");
_postBodyBuffer.append(sanitizeURL(a.name));
_postBodyBuffer.append("\">").append(sanitizeString(a.name));
}
_postBodyBuffer.append("<br />\n");
}
_postBodyBuffer.append("<br />\n");
_postBodyBuffer.append("</td>\n</form>\n</tr>\n");
}
_postBodyBuffer.append("</td>\n</form>\n</tr>\n");
_postBodyBuffer.append("</table>\n");
}
@@ -463,8 +497,9 @@ public class HTMLRenderer extends EventReceiverImpl {
}
public void receiveHeaderEnd() {
renderMetaCell();
_preBodyBuffer.append("<table width=\"100%\" border=\"0\">\n");
renderSubjectCell();
renderMetaCell();
renderPreBodyCell();
}
@@ -473,25 +508,24 @@ public class HTMLRenderer extends EventReceiverImpl {
public static final String HEADER_IN_REPLY_TO = "InReplyTo";
private void renderSubjectCell() {
_preBodyBuffer.append("<td align=\"left\" valign=\"top\" style=\"entry.subject.cell\" bgcolor=\"#3355ff\">");
_preBodyBuffer.append("<tr class=\"syndieEntrySubjectCell\"><td align=\"left\" valign=\"top\" class=\"syndieEntrySubjectCell\" width=\"400\"> ");
String subject = (String)_headers.get(HEADER_SUBJECT);
if (subject == null)
subject = "[no subject]";
_preBodyBuffer.append(sanitizeString(subject));
_preBodyBuffer.append("</td></tr>\n");
_preBodyBuffer.append("</td>\n");
}
private void renderPreBodyCell() {
String bgcolor = (String)_headers.get(HEADER_BGCOLOR);
if (_cutBody)
_preBodyBuffer.append("<tr><td align=\"left\" valign=\"top\" style=\"entry.summary.cell\" bgcolor=\"" + (bgcolor == null ? "#33ffff" : sanitizeTagParam(bgcolor)) + "\">");
_preBodyBuffer.append("<tr class=\"syndieEntrySummaryCell\"><td colspan=\"2\" align=\"left\" valign=\"top\" class=\"syndieEntrySummaryCell\" " + (bgcolor != null ? "bgcolor=\"" + sanitizeTagParam(bgcolor) + "\"" : "") + "\">");
else
_preBodyBuffer.append("<tr><td align=\"left\" valign=\"top\" style=\"entry.body.cell\" bgcolor=\"" + (bgcolor == null ? "#33ffff" : sanitizeTagParam(bgcolor)) + "\">");
_preBodyBuffer.append("<tr class=\"syndieEntryBodyCell\"><td colspan=\"2\" align=\"left\" valign=\"top\" class=\"syndieEntryBodyCell\" " + (bgcolor != null ? "bgcolor=\"" + sanitizeTagParam(bgcolor) + "\"" : "") + "\">");
}
private void renderMetaCell() {
_preBodyBuffer.append("<table width=\"100%\" border=\"0\">\n");
_preBodyBuffer.append("<tr><td align=\"left\" valign=\"top\" rowspan=\"3\" style=\"entry.meta.cell\" bgcolor=\"#33ccff\">\n");
_preBodyBuffer.append("<td nowrap=\"true\" align=\"right\" valign=\"top\" class=\"syndieEntryMetaCell\">\n");
BlogInfo info = null;
if (_entry != null)
info = _archive.getBlogInfo(_entry.getURI());
@@ -506,31 +540,32 @@ public class HTMLRenderer extends EventReceiverImpl {
} else {
_preBodyBuffer.append("[unknown blog]");
}
_preBodyBuffer.append("<br />\n");
String tags[] = (_entry != null ? _entry.getTags() : null);
_preBodyBuffer.append("<i>");
for (int i = 0; tags != null && i < tags.length; i++) {
_preBodyBuffer.append("<a href=\"");
_preBodyBuffer.append(getPageURL(_entry.getURI().getKeyHash(), tags[i], -1, -1, -1, (_user != null ? _user.getShowExpanded() : false), (_user != null ? _user.getShowImages() : false)));
_preBodyBuffer.append("\">");
_preBodyBuffer.append(sanitizeString(tags[i]));
_preBodyBuffer.append("</a>");
if (i + 1 < tags.length)
_preBodyBuffer.append(", ");
if ( (tags != null) && (tags.length > 0) ) {
_preBodyBuffer.append(" Tags: ");
_preBodyBuffer.append("<i>");
for (int i = 0; tags != null && i < tags.length; i++) {
_preBodyBuffer.append("<a href=\"");
_preBodyBuffer.append(getPageURL(_entry.getURI().getKeyHash(), tags[i], -1, -1, -1, (_user != null ? _user.getShowExpanded() : false), (_user != null ? _user.getShowImages() : false)));
_preBodyBuffer.append("\">");
_preBodyBuffer.append(sanitizeString(tags[i]));
_preBodyBuffer.append("</a>");
if (i + 1 < tags.length)
_preBodyBuffer.append(", ");
}
_preBodyBuffer.append("</i>");
}
_preBodyBuffer.append("</i><br /><font size=\"-1\">\n");
_preBodyBuffer.append(" ");
if (_entry != null)
_preBodyBuffer.append(getEntryDate(_entry.getURI().getEntryId()));
else
_preBodyBuffer.append(getEntryDate(new Date().getTime()));
_preBodyBuffer.append("</font><br />");
String inReplyTo = (String)_headers.get(HEADER_IN_REPLY_TO);
System.err.println("In reply to: [" + inReplyTo + "]");
if ( (inReplyTo != null) && (inReplyTo.trim().length() > 0) )
_preBodyBuffer.append("<a href=\"").append(getPageURL(sanitizeTagParam(inReplyTo))).append("\">In reply to</a><br />\n");
_preBodyBuffer.append(" <a href=\"").append(getPageURL(sanitizeTagParam(inReplyTo))).append("\">In reply to</a>\n");
if ( (_user != null) && (_user.getAuthenticated()) )
_preBodyBuffer.append("<a href=\"").append(getPostURL(_user.getBlog(), true)).append("\">Reply</a><br />\n");
_preBodyBuffer.append("\n</td>\n");
_preBodyBuffer.append(" <a href=\"").append(getPostURL(_user.getBlog(), true)).append("\">Reply</a>\n");
_preBodyBuffer.append("\n</td></tr>\n");
}
private final SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy/MM/dd");
@@ -539,7 +574,7 @@ public class HTMLRenderer extends EventReceiverImpl {
try {
String str = _dateFormat.format(new Date(when));
long dayBegin = _dateFormat.parse(str).getTime();
return str + "<br />" + (when - dayBegin);
return str + "." + (when - dayBegin);
} catch (ParseException pe) {
pe.printStackTrace();
// wtf
@@ -548,12 +583,26 @@ public class HTMLRenderer extends EventReceiverImpl {
}
}
public static final String sanitizeString(String str) {
public static final String sanitizeString(String str) { return sanitizeString(str, true); }
public static final String sanitizeString(String str, boolean allowNL) {
if (str == null) return null;
if ( (str.indexOf('<') < 0) && (str.indexOf('>') < 0) )
return str;
boolean unsafe = false;
unsafe = unsafe || str.indexOf('<') >= 0;
unsafe = unsafe || str.indexOf('>') >= 0;
if (!allowNL) {
unsafe = unsafe || str.indexOf('\n') >= 0;
unsafe = unsafe || str.indexOf('\r') >= 0;
unsafe = unsafe || str.indexOf('\f') >= 0;
}
if (!unsafe) return str;
str = str.replace('<', '_');
str = str.replace('>', '-');
if (!allowNL) {
str = str.replace('\n', ' ');
str = str.replace('\r', ' ');
str = str.replace('\f', ' ');
}
return str;
}
@@ -575,8 +624,8 @@ public class HTMLRenderer extends EventReceiverImpl {
"&" + ArchiveViewerBean.PARAM_EXPAND_ENTRIES + "=true";
}
private String getAttachmentURLBase() { return "viewattachment.jsp"; }
private String getAttachmentURL(int id) {
protected String getAttachmentURLBase() { return "viewattachment.jsp"; }
protected String getAttachmentURL(int id) {
if (_entry == null) return "unknown";
return getAttachmentURLBase() + "?" +
ArchiveViewerBean.PARAM_BLOG + "=" +

View File

@@ -79,12 +79,26 @@ public class ArchiveViewerBean {
public static final String SEL_BLOGTAG = "blogtag://";
public static final String SEL_ENTRY = "entry://";
public static final String SEL_GROUP = "group://";
/** submit field for the selector form */
public static final String PARAM_SELECTOR_ACTION = "action";
public static final String SEL_ACTION_SET_AS_DEFAULT = "Set as default";
public static void renderBlogSelector(User user, Map parameters, Writer out) throws IOException {
String sel = getString(parameters, PARAM_SELECTOR);
String action = getString(parameters, PARAM_SELECTOR_ACTION);
if ( (sel != null) && (action != null) && (SEL_ACTION_SET_AS_DEFAULT.equals(action)) ) {
user.setDefaultSelector(HTMLRenderer.sanitizeString(sel, false));
BlogManager.instance().saveUser(user);
}
out.write("<select name=\"");
out.write(PARAM_SELECTOR);
out.write("\">");
out.write("<option value=\"");
out.write(getDefaultSelector(user, parameters));
out.write("\">Default blog filter</option>\n");
out.write("\">");
out.write("<option value=\"");
out.write(SEL_ALL);
out.write("\">All posts from all blogs</option>\n");
@@ -157,6 +171,13 @@ public class ArchiveViewerBean {
}
private static String getDefaultSelector(User user, Map parameters) {
if ( (user == null) || (user.getDefaultSelector() == null) )
return BlogManager.instance().getArchive().getDefaultSelector();
else
return user.getDefaultSelector();
}
public static void renderBlogs(User user, Map parameters, Writer out) throws IOException {
String blogStr = getString(parameters, PARAM_BLOG);
Hash blog = null;
@@ -174,6 +195,8 @@ public class ArchiveViewerBean {
if (group != null) group = new String(Base64.decode(group));
String sel = getString(parameters, PARAM_SELECTOR);
if ( (sel == null) && (blog == null) && (group == null) && (tag == null) )
sel = getDefaultSelector(user, parameters);
if (sel != null) {
Selector s = new Selector(sel);
blog = s.blog;
@@ -349,7 +372,7 @@ public class ArchiveViewerBean {
return rv;
}
private static final String getString(Map parameters, String param) {
public static final String getString(Map parameters, String param) {
if ( (parameters == null) || (parameters.get(param) == null) )
return null;
Object vals = parameters.get(param);
@@ -369,6 +392,24 @@ public class ArchiveViewerBean {
return null;
}
}
public static final String[] getStrings(Map parameters, String param) {
if ( (parameters == null) || (parameters.get(param) == null) )
return null;
Object vals = parameters.get(param);
if (vals.getClass().isArray()) {
return (String[])vals;
} else if (vals instanceof Collection) {
Collection c = (Collection)vals;
if (c.size() <= 0) return null;
String rv[] = new String[c.size()];
int i = 0;
for (Iterator iter = c.iterator(); iter.hasNext(); i++)
rv[i] = (String)iter.next();
return rv;
} else {
return null;
}
}
private static final int getInt(Map param, String key, int defaultVal) {
String val = getString(param, key);

View File

@@ -0,0 +1,136 @@
package net.i2p.syndie.web;
import java.io.*;
import java.util.*;
import net.i2p.syndie.*;
import net.i2p.syndie.data.BlogURI;
import net.i2p.syndie.sml.HTMLPreviewRenderer;
/**
*
*/
public class PostBean {
private User _user;
private String _subject;
private String _tags;
private String _headers;
private String _text;
private List _filenames;
private List _fileStreams;
private List _localFiles;
private List _fileTypes;
private boolean _previewed;
public PostBean() { reinitialize(); }
public void reinitialize() {
System.out.println("Reinitializing " + (_text != null ? "(with " + _text.length() + " bytes of sml!)" : ""));
_user = null;
_subject = null;
_tags = null;
_text = null;
_headers = null;
_filenames = new ArrayList();
_fileStreams = new ArrayList();
_fileTypes = new ArrayList();
if (_localFiles != null)
for (int i = 0; i < _localFiles.size(); i++)
((File)_localFiles.get(i)).delete();
_localFiles = new ArrayList();
_previewed = false;
}
public User getUser() { return _user; }
public String getSubject() { return (_subject != null ? _subject : ""); }
public String getTags() { return (_tags != null ? _tags : ""); }
public String getText() { return (_text != null ? _text : ""); }
public String getHeaders() { return (_headers != null ? _headers : ""); }
public void setUser(User user) { _user = user; }
public void setSubject(String subject) { _subject = subject; }
public void setTags(String tags) { _tags = tags; }
public void setText(String text) { _text = text; }
public void setHeaders(String headers) { _headers = headers; }
public String getContentType(int id) {
if ( (id >= 0) && (id < _fileTypes.size()) )
return (String)_fileTypes.get(id);
return "application/octet-stream";
}
public void writeAttachmentData(int id, OutputStream out) throws IOException {
FileInputStream in = new FileInputStream((File)_localFiles.get(id));
byte buf[] = new byte[1024];
int read = 0;
while ( (read = in.read(buf)) != -1)
out.write(buf, 0, read);
out.close();
}
public void addAttachment(String filename, InputStream fileStream, String mimeType) {
_filenames.add(filename);
_fileStreams.add(fileStream);
_fileTypes.add(mimeType);
}
public int getAttachmentCount() { return (_filenames != null ? _filenames.size() : 0); }
public BlogURI postEntry() throws IOException {
if (!_previewed) return null;
List localStreams = new ArrayList(_localFiles.size());
for (int i = 0; i < _localFiles.size(); i++) {
File f = (File)_localFiles.get(i);
localStreams.add(new FileInputStream(f));
}
return BlogManager.instance().createBlogEntry(_user, _subject, _tags, _headers, _text,
_filenames, localStreams, _fileTypes);
}
public void renderPreview(Writer out) throws IOException {
System.out.println("Subject: " + _subject);
System.out.println("Text: " + _text);
System.out.println("Headers: " + _headers);
// cache all the _fileStreams into temporary files, storing those files in _localFiles
// then render the page accordingly with an HTMLRenderer, altered to use a different
// 'view attachment'
cacheAttachments();
String smlContent = renderSMLContent();
HTMLPreviewRenderer r = new HTMLPreviewRenderer(_filenames, _fileTypes, _localFiles);
r.render(_user, BlogManager.instance().getArchive(), null, smlContent, out, false, true);
_previewed = true;
}
private String renderSMLContent() {
StringBuffer raw = new StringBuffer();
raw.append("Subject: ").append(_subject).append('\n');
raw.append("Tags: ");
StringTokenizer tok = new StringTokenizer(_tags, " \t\n");
while (tok.hasMoreTokens())
raw.append(tok.nextToken()).append('\t');
raw.append('\n');
raw.append(_headers.trim());
raw.append("\n\n");
raw.append(_text.trim());
return raw.toString();
}
private void cacheAttachments() throws IOException {
File postCacheDir = new File(BlogManager.instance().getTempDir(), _user.getBlog().toBase64());
if (!postCacheDir.exists())
postCacheDir.mkdirs();
for (int i = 0; i < _fileStreams.size(); i++) {
InputStream in = (InputStream)_fileStreams.get(i);
File f = File.createTempFile("attachment", ".dat", postCacheDir);
FileOutputStream o = new FileOutputStream(f);
byte buf[] = new byte[1024];
int read = 0;
while ( (read = in.read(buf)) != -1)
o.write(buf, 0, read);
o.close();
in.close();
_localFiles.add(f);
System.out.println("Caching attachment " + i + " temporarily in "
+ f.getAbsolutePath() + " w/ " + f.length() + "bytes");
}
_fileStreams.clear();
}
}

View File

@@ -0,0 +1,379 @@
package net.i2p.syndie.web;
import java.io.*;
import java.text.*;
import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
import net.i2p.util.EepGet;
import net.i2p.util.EepGetScheduler;
import net.i2p.syndie.data.*;
import net.i2p.syndie.sml.*;
import net.i2p.syndie.*;
/**
*
*/
public class RemoteArchiveBean {
private String _remoteSchema;
private String _remoteLocation;
private String _proxyHost;
private int _proxyPort;
private ArchiveIndex _remoteIndex;
private List _statusMessages;
private boolean _fetchIndexInProgress;
public RemoteArchiveBean() {
reinitialize();
}
public void reinitialize() {
_remoteSchema = null;
_remoteLocation = null;
_remoteIndex = null;
_fetchIndexInProgress = false;
_proxyHost = null;
_proxyPort = -1;
_statusMessages = new ArrayList();
}
public String getRemoteSchema() { return _remoteSchema; }
public String getRemoteLocation() { return _remoteLocation; }
public ArchiveIndex getRemoteIndex() { return _remoteIndex; }
public boolean getFetchIndexInProgress() { return _fetchIndexInProgress; }
public String getStatus() {
StringBuffer buf = new StringBuffer();
while (_statusMessages.size() > 0)
buf.append(_statusMessages.remove(0)).append("\n");
return buf.toString();
}
public void fetchMetadata(User user, Map parameters) {
String meta = ArchiveViewerBean.getString(parameters, "blog");
if (meta == null) return;
Set blogs = new HashSet();
if ("ALL".equals(meta)) {
Set localBlogs = BlogManager.instance().getArchive().getIndex().getUniqueBlogs();
Set remoteBlogs = _remoteIndex.getUniqueBlogs();
for (Iterator iter = remoteBlogs.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
if (!localBlogs.contains(blog)) {
blogs.add(blog);
}
}
} else {
blogs.add(new Hash(Base64.decode(meta.trim())));
}
List urls = new ArrayList(blogs.size());
List tmpFiles = new ArrayList(blogs.size());
for (Iterator iter = blogs.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
urls.add(buildMetaURL(blog));
try {
tmpFiles.add(File.createTempFile("fetchMeta", ".txt", BlogManager.instance().getTempDir()));
} catch (IOException ioe) {
_statusMessages.add("Internal error creating temporary file to fetch " + blog.toBase64() + ": " + ioe.getMessage());
}
}
for (int i = 0; i < urls.size(); i++)
_statusMessages.add("Scheduling up metadata fetches for " + HTMLRenderer.sanitizeString((String)urls.get(i)));
fetch(urls, tmpFiles, user, new MetadataStatusListener());
}
private String buildMetaURL(Hash blog) {
String loc = _remoteLocation.trim();
int root = loc.lastIndexOf('/');
return loc.substring(0, root + 1) + blog.toBase64() + "/" + Archive.METADATA_FILE;
}
public void fetchSelectedEntries(User user, Map parameters) {
String entries[] = ArchiveViewerBean.getStrings(parameters, "entry");
if ( (entries == null) || (entries.length <= 0) ) return;
List urls = new ArrayList(entries.length);
List tmpFiles = new ArrayList(entries.length);
for (int i = 0; i < entries.length; i++) {
urls.add(buildEntryURL(new BlogURI(entries[i])));
try {
tmpFiles.add(File.createTempFile("fetchBlog", ".txt", BlogManager.instance().getTempDir()));
} catch (IOException ioe) {
_statusMessages.add("Internal error creating temporary file to fetch " + HTMLRenderer.sanitizeString(entries[i]) + ": " + ioe.getMessage());
}
}
for (int i = 0; i < urls.size(); i++)
_statusMessages.add("Scheduling blog post fetching for " + HTMLRenderer.sanitizeString(entries[i]));
fetch(urls, tmpFiles, user, new BlogStatusListener());
}
private String buildEntryURL(BlogURI uri) {
String loc = _remoteLocation.trim();
int root = loc.lastIndexOf('/');
return loc.substring(0, root + 1) + uri.getKeyHash().toBase64() + "/" + uri.getEntryId() + ".snd";
}
public void fetchAllEntries(User user, Map parameters) {
ArchiveIndex localIndex = BlogManager.instance().getArchive().getIndex();
List uris = new ArrayList();
List entries = new ArrayList();
for (Iterator iter = _remoteIndex.getUniqueBlogs().iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
_remoteIndex.selectMatchesOrderByEntryId(entries, blog, null);
for (int i = 0; i < entries.size(); i++) {
BlogURI uri = (BlogURI)entries.get(i);
if (!localIndex.getEntryIsKnown(uri))
uris.add(uri);
}
entries.clear();
}
List urls = new ArrayList(uris.size());
List tmpFiles = new ArrayList(uris.size());
for (int i = 0; i < uris.size(); i++) {
urls.add(buildEntryURL((BlogURI)uris.get(i)));
try {
tmpFiles.add(File.createTempFile("fetchBlog", ".txt", BlogManager.instance().getTempDir()));
} catch (IOException ioe) {
_statusMessages.add("Internal error creating temporary file to fetch " + HTMLRenderer.sanitizeString(uris.get(i).toString()) + ": " + ioe.getMessage());
}
}
for (int i = 0; i < urls.size(); i++)
_statusMessages.add("Fetch all entries: " + HTMLRenderer.sanitizeString((String)urls.get(i)));
fetch(urls, tmpFiles, user, new BlogStatusListener());
}
private void fetch(List urls, List tmpFiles, User user, EepGet.StatusListener lsnr) {
EepGetScheduler scheduler = new EepGetScheduler(I2PAppContext.getGlobalContext(), urls, tmpFiles, _proxyHost, _proxyPort, lsnr);
scheduler.fetch();
}
public void fetchIndex(User user, String schema, String location) {
_fetchIndexInProgress = true;
_remoteIndex = null;
_remoteLocation = location;
_remoteSchema = schema;
_proxyHost = null;
_proxyPort = -1;
if ("eep".equals(_remoteSchema)) {
_proxyHost = user.getEepProxyHost();
_proxyPort = user.getEepProxyPort();
} else if ("web".equals(_remoteSchema)) {
_proxyHost = user.getWebProxyHost();
_proxyPort = user.getWebProxyPort();
} else if ("tor".equals(_remoteSchema)) {
_proxyHost = user.getTorProxyHost();
_proxyPort = user.getTorProxyPort();
} else {
_statusMessages.add(new String("Remote schema " + HTMLRenderer.sanitizeString(schema) + " currently not supported"));
_fetchIndexInProgress = false;
return;
}
_statusMessages.add("Fetching index from " + HTMLRenderer.sanitizeString(_remoteLocation));
File archiveFile = new File(BlogManager.instance().getTempDir(), user.getBlog().toBase64() + "_remoteArchive.txt");
archiveFile.delete();
EepGet eep = new EepGet(I2PAppContext.getGlobalContext(), ((_proxyHost != null) && (_proxyPort > 0)),
_proxyHost, _proxyPort, 0, archiveFile.getAbsolutePath(), location);
eep.addStatusListener(new IndexFetcherStatusListener(archiveFile));
eep.fetch();
}
private class IndexFetcherStatusListener implements EepGet.StatusListener {
private File _archiveFile;
public IndexFetcherStatusListener(File file) {
_archiveFile = file;
}
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
_statusMessages.add("Attempt " + currentAttempt + " failed after " + bytesTransferred + (cause != null ? cause.getMessage() : ""));
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " successful");
_fetchIndexInProgress = false;
ArchiveIndex i = new ArchiveIndex(false);
try {
i.load(_archiveFile);
_statusMessages.add("Archive fetched and loaded");
_remoteIndex = i;
} catch (IOException ioe) {
_statusMessages.add("Archive is corrupt: " + ioe.getMessage());
}
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " failed after " + bytesTransferred);
_fetchIndexInProgress = false;
}
}
private class MetadataStatusListener implements EepGet.StatusListener {
public MetadataStatusListener() {}
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
_statusMessages.add("Attempt " + currentAttempt + " failed after " + bytesTransferred + (cause != null ? cause.getMessage() : ""));
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " successful");
File info = new File(outputFile);
FileInputStream in = null;
try {
BlogInfo i = new BlogInfo();
in = new FileInputStream(info);
i.load(in);
boolean ok = BlogManager.instance().getArchive().storeBlogInfo(i);
if (ok) {
_statusMessages.add("Blog info for " + HTMLRenderer.sanitizeString(i.getProperty(BlogInfo.NAME)) + " imported");
BlogManager.instance().getArchive().reloadInfo();
} else {
_statusMessages.add("Blog info at " + HTMLRenderer.sanitizeString(url) + " was corrupt / invalid / forged");
}
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
info.delete();
}
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " failed after " + bytesTransferred);;
}
}
private class BlogStatusListener implements EepGet.StatusListener {
public BlogStatusListener() {}
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
_statusMessages.add("Attempt " + currentAttempt + " failed after " + bytesTransferred + (cause != null ? cause.getMessage() : ""));
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " successful");
File file = new File(outputFile);
FileInputStream in = null;
try {
EntryContainer c = new EntryContainer();
in = new FileInputStream(file);
c.load(in);
BlogURI uri = c.getURI();
if ( (uri == null) || (uri.getKeyHash() == null) ) {
_statusMessages.add("Blog post at " + HTMLRenderer.sanitizeString(url) + " was corrupt - no URI");
return;
}
Archive a = BlogManager.instance().getArchive();
BlogInfo info = a.getBlogInfo(uri);
if (info == null) {
_statusMessages.add("Blog post " + uri.toString() + " cannot be imported, as we don't have their blog metadata");
return;
}
boolean ok = a.storeEntry(c);
if (!ok) {
_statusMessages.add("Blog post at " + url + ": " + uri.toString() + " has an invalid signature");
return;
} else {
_statusMessages.add("Blog post " + uri.toString() + " imported");
BlogManager.instance().getArchive().regenerateIndex();
}
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
file.delete();
}
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " failed after " + bytesTransferred);
}
}
public void renderDeltaForm(User user, ArchiveIndex localIndex, Writer out) throws IOException {
Archive archive = BlogManager.instance().getArchive();
StringBuffer buf = new StringBuffer(512);
buf.append("<b>New blogs:</b> <select name=\"blog\"><option value=\"ALL\">All</option>\n");
Set localBlogs = archive.getIndex().getUniqueBlogs();
Set remoteBlogs = _remoteIndex.getUniqueBlogs();
int newBlogs = 0;
for (Iterator iter = remoteBlogs.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
if (!localBlogs.contains(blog)) {
buf.append("<option value=\"" + blog.toBase64() + "\">" + blog.toBase64() + "</option>\n");
newBlogs++;
}
}
if (newBlogs > 0) {
out.write(buf.toString());
out.write("</select> <input type=\"submit\" name=\"action\" value=\"Fetch metadata\" /><br />\n");
}
int newEntries = 0;
out.write("<table border=\"1\" width=\"100%\">\n");
for (Iterator iter = remoteBlogs.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
buf = new StringBuffer(1024);
int shownEntries = 0;
buf.append("<tr><td colspan=\"5\" align=\"left\" valign=\"top\">\n");
BlogInfo info = archive.getBlogInfo(blog);
if (info != null) {
buf.append("<a href=\"" + HTMLRenderer.getPageURL(blog, null, -1, -1, -1, user.getShowExpanded(), user.getShowImages()) + "\"><b>" + HTMLRenderer.sanitizeString(info.getProperty(BlogInfo.NAME)) + "</b></a>: " +
HTMLRenderer.sanitizeString(info.getProperty(BlogInfo.DESCRIPTION)) + "\n");
} else {
buf.append("<b>" + blog.toBase64() + "</b>\n");
}
buf.append("</td></tr>\n");
buf.append("<tr><td>&nbsp;</td><td nowrap=\"true\"><b>Posted on</b></td><td nowrap=\"true\"><b>#</b></td><td nowrap=\"true\"><b>Size</b></td><td width=\"90%\" nowrap=\"true\"><b>Tags</b></td></tr>\n");
List entries = new ArrayList();
_remoteIndex.selectMatchesOrderByEntryId(entries, blog, null);
for (int i = 0; i < entries.size(); i++) {
BlogURI uri = (BlogURI)entries.get(i);
buf.append("<tr>\n");
if (!archive.getIndex().getEntryIsKnown(uri)) {
buf.append("<td><input type=\"checkbox\" name=\"entry\" value=\"" + uri.toString() + "\" /></td>\n");
newEntries++;
shownEntries++;
} else {
String page = HTMLRenderer.getPageURL(blog, null, uri.getEntryId(), -1, -1,
user.getShowExpanded(), user.getShowImages());
buf.append("<td><a href=\"" + page + "\">(local)</a></td>\n");
}
buf.append("<td>" + getDate(uri.getEntryId()) + "</td>\n");
buf.append("<td>" + getId(uri.getEntryId()) + "</td>\n");
buf.append("<td>" + _remoteIndex.getBlogEntrySizeKB(uri) + "KB</td>\n");
buf.append("<td>");
for (Iterator titer = new TreeSet(_remoteIndex.getBlogEntryTags(uri)).iterator(); titer.hasNext(); ) {
String tag = (String)titer.next();
buf.append("<a href=\"" + HTMLRenderer.getPageURL(blog, tag, -1, -1, -1, user.getShowExpanded(), user.getShowImages()) + "\">" + tag + "</a> \n");
}
buf.append("</td>\n");
buf.append("</tr>\n");
}
if (shownEntries > 0) // skip blogs we have already syndicated
out.write(buf.toString());
}
out.write("</table>\n");
if (newEntries > 0) {
out.write("<input type=\"submit\" name=\"action\" value=\"Fetch selected entries\" /> \n");
out.write("<input type=\"submit\" name=\"action\" value=\"Fetch all new entries\" /> \n");
} else {
out.write(HTMLRenderer.sanitizeString(_remoteLocation) + " has no new posts to offer us\n");
}
}
private final SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy/MM/dd");
private String getDate(long when) {
synchronized (_dateFormat) {
return _dateFormat.format(new Date(when));
}
}
private long getId(long id) {
synchronized (_dateFormat) {
try {
String str = _dateFormat.format(new Date(id));
long dayBegin = _dateFormat.parse(str).getTime();
return (id - dayBegin);
} catch (ParseException pe) {
pe.printStackTrace();
// wtf
return id;
}
}
}
}