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));
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();
}
@ -80,7 +88,10 @@ 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 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;
}
public static final String LOGIN_OK = "Logged in";
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,7 +381,40 @@ public class HTMLRenderer extends EventReceiverImpl {
public void receiveEnd() {
_postBodyBuffer.append("</td></tr>\n");
_postBodyBuffer.append("<tr>\n");
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=\"");
@ -397,7 +430,7 @@ public class HTMLRenderer extends EventReceiverImpl {
else
_postBodyBuffer.append("unknown");
_postBodyBuffer.append("\" />\n");
_postBodyBuffer.append("<td valign=\"top\" align=\"left\" style=\"entry.attachments.cell\" bgcolor=\"#77ff77\">\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> ");
@ -454,6 +487,7 @@ public class HTMLRenderer extends EventReceiverImpl {
}
_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,8 +540,9 @@ public class HTMLRenderer extends EventReceiverImpl {
} else {
_preBodyBuffer.append("[unknown blog]");
}
_preBodyBuffer.append("<br />\n");
String tags[] = (_entry != null ? _entry.getTags() : null);
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=\"");
@ -518,19 +553,19 @@ public class HTMLRenderer extends EventReceiverImpl {
if (i + 1 < tags.length)
_preBodyBuffer.append(", ");
}
_preBodyBuffer.append("</i><br /><font size=\"-1\">\n");
_preBodyBuffer.append("</i>");
}
_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;
}
}
}
}

View File

@ -2,7 +2,8 @@
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<form action="index.jsp">
<b>Blogs:</b> <%ArchiveViewerBean.renderBlogSelector(user, request.getParameterMap(), out);%>
<input type="submit" value="Refresh" /></form>
<input type="submit" value="Refresh" />
<input type="submit" name="action" value="<%=ArchiveViewerBean.SEL_ACTION_SET_AS_DEFAULT%>" /></form>
<hr />
<%ArchiveViewerBean.renderBlogs(user, request.getParameterMap(), out); out.flush(); %>

View File

@ -1,3 +1,3 @@
<td valign="top" align="left" bgcolor="#cccc88" height="10"><a href="index.jsp">Blogs</a></td>
<td valign="top" align="left" bgcolor="#cccc88" height="10">Remote archives</td>
<td valign="top" align="left" bgcolor="#cccc88" height="10">Manage</td>
<td valign="top" align="left" class="syndieTopNavBlogsCell" height="10"><a href="index.jsp">Blogs</a></td>
<td valign="top" align="left" class="syndieTopNavRemoteCell" height="10"><a href="remote.jsp">Remote archives</a></td>
<td valign="top" align="left" class="syndieTopNavManageCell" height="10">Manage</td>

View File

@ -3,6 +3,7 @@
<html>
<head>
<title>SyndieMedia</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">

View File

@ -2,6 +2,7 @@
<html>
<head>
<title>SyndieMedia</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">

View File

@ -0,0 +1,66 @@
<%@page import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.data.*, net.i2p.syndie.*, org.mortbay.servlet.MultiPartRequest, java.util.*, java.io.*" %>
<jsp:useBean scope="session" class="net.i2p.syndie.data.ArchiveIndex" id="archive" />
<html>
<head>
<title>SyndieMedia import</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">
<tr><td colspan="5" valign="top" align="left"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2"><jsp:include page="_leftnav.jsp" /></td>
<jsp:include page="_topnav.jsp" />
<td valign="top" align="left" rowspan="2"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr><td valign="top" align="left" colspan="3"><%
String contentType = request.getContentType();
if ((contentType != null) && (contentType.indexOf("boundary=") != -1) ) {
MultiPartRequest req = new MultiPartRequest(request);
int metaId = 0;
while (true) {
InputStream meta = req.getInputStream("blogmeta" + metaId);
if (meta == null)
break;
if (!BlogManager.instance().importBlogMetadata(meta)) {
System.err.println("blog meta " + metaId + " failed to be imported");
break;
}
metaId++;
}
int entryId = 0;
while (true) {
InputStream entry = req.getInputStream("blogpost" + entryId);
if (entry == null)
break;
if (!BlogManager.instance().importBlogEntry(entry)) {
System.err.println("blog entry " + entryId + " failed to be imported");
break;
}
entryId++;
}
if ( (entryId > 0) || (metaId > 0) ) {
BlogManager.instance().getArchive().regenerateIndex();
session.setAttribute("index", BlogManager.instance().getArchive().getIndex());
}
%>Imported <%=entryId%> posts and <%=metaId%> blog metadata files.
<%
} else { %><form action="import.jsp" method="POST" enctype="multipart/form-data">
Blog metadata 0: <input type="file" name="blogmeta0" /><br />
Blog metadata 1: <input type="file" name="blogmeta1" /><br />
Post 0: <input type="file" name="blogpost0" /><br />
Post 1: <input type="file" name="blogpost1" /><br />
Post 2: <input type="file" name="blogpost2" /><br />
Post 3: <input type="file" name="blogpost3" /><br />
Post 4: <input type="file" name="blogpost4" /><br />
Post 5: <input type="file" name="blogpost5" /><br />
Post 6: <input type="file" name="blogpost6" /><br />
Post 7: <input type="file" name="blogpost7" /><br />
Post 8: <input type="file" name="blogpost8" /><br />
Post 9: <input type="file" name="blogpost9" /><br />
<hr />
<input type="submit" name="Post" value="Post entry" /> <input type="reset" value="Cancel" />
<% } %>
</td></tr>
</table>
</body>

View File

@ -2,6 +2,7 @@
<html>
<head>
<title>SyndieMedia</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">

View File

@ -1,8 +1,10 @@
<%@page import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.data.*, net.i2p.syndie.*, org.mortbay.servlet.MultiPartRequest, java.util.*" %>
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<jsp:useBean scope="session" class="net.i2p.syndie.web.PostBean" id="post" />
<html>
<head>
<title>SyndieMedia</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">
@ -12,10 +14,29 @@
<td valign="top" align="left" rowspan="2"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr><td valign="top" align="left" colspan="3"><%
if (!user.getAuthenticated()) {
%>You must be logged in to post<%
} else {
String confirm = request.getParameter("confirm");
if ( (confirm != null) && (confirm.equalsIgnoreCase("true")) ) {
BlogURI uri = post.postEntry();
if (uri != null) {
%>Blog entry <a href="<%=HTMLRenderer.getPageURL(user.getBlog(), null, uri.getEntryId(), -1, -1,
user.getShowExpanded(), user.getShowImages())%>">posted</a>!<%
} else {
%>There was an unknown error posting the entry...<%
}
post.reinitialize();
post.setUser(user);
} else {
// logged in but not confirmed...
String contentType = request.getContentType();
if ((contentType != null) && (contentType.indexOf("boundary=") != -1) ) {
if (!user.getAuthenticated()) { %>You must be logged in to post<%
} else {
// not confirmed but they posted stuff... gobble up what they give
// and display it as a preview (then we show the confirm form)
post.reinitialize();
post.setUser(user);
MultiPartRequest req = new MultiPartRequest(request);
String entrySubject = req.getString("entrysubject");
String entryTags = req.getString("entrytags");
@ -32,14 +53,14 @@ if ((contentType != null) && (contentType.indexOf("boundary=") != -1) ) {
}
}
List fileStreams = new ArrayList();
List fileNames = new ArrayList();
List fileTypes = new ArrayList();
post.setSubject(entrySubject);
post.setTags(entryTags);
post.setText(entryText);
post.setHeaders(entryHeaders);
for (int i = 0; i < 32; i++) {
String filename = req.getFilename("entryfile" + i);
if ( (filename != null) && (filename.trim().length() > 0) ) {
fileNames.add(filename.trim());
fileStreams.add(req.getInputStream("entryfile" + i));
Hashtable params = req.getParams("entryfile" + i);
String type = "application/octet-stream";
for (Iterator iter = params.keySet().iterator(); iter.hasNext(); ) {
@ -49,25 +70,20 @@ if ((contentType != null) && (contentType.indexOf("boundary=") != -1) ) {
break;
}
}
fileTypes.add(type);
post.addAttachment(filename.trim(), req.getInputStream("entryfile" + i), type);
}
}
BlogURI entry = BlogManager.instance().createBlogEntry(user, entrySubject, entryTags, entryHeaders, entryText, fileNames, fileStreams, fileTypes);
if (entry != null) {
// it has been rebuilt...
request.setAttribute("index", BlogManager.instance().getArchive().getIndex());
%>
Blog entry <a href="<%=HTMLRenderer.getPageURL(user.getBlog(), null, entry.getEntryId(), -1, -1, user.getShowExpanded(), user.getShowImages())%>">posted</a>!
<% } else { %>
There was an error posting... dunno what it was...
<% }
}
} else { %><form action="post.jsp" method="POST" enctype="multipart/form-data">
Post subject: <input type="text" size="80" name="entrysubject" /><br />
Post tags: <input type="text" size="20" name="entrytags" /><br />
post.renderPreview(out);
%><hr />Please <a href="post.jsp?confirm=true">confirm</a> that this is ok. Otherwise, just go back and make changes.<%
} else {
// logged in and not confirmed because they didn't send us anything!
// give 'em a new form
%><form action="post.jsp" method="POST" enctype="multipart/form-data">
Post subject: <input type="text" size="80" name="entrysubject" value="<%=post.getSubject()%>" /><br />
Post tags: <input type="text" size="20" name="entrytags" value="<%=post.getTags()%>" /><br />
Post content (in raw SML, no headers):<br />
<textarea rows="6" cols="80" name="entrytext"></textarea><br />
<textarea rows="6" cols="80" name="entrytext"><%=post.getText()%></textarea><br />
<b>SML cheatsheet:</b><br /><textarea rows="6" cols="80" readonly="true">
* newlines are newlines are newlines.
* all &lt; and &gt; are replaced with their &amp;symbol;
@ -88,7 +104,7 @@ SML headers are newline delimited key=value pairs. Example keys are:
* textfont = font to put most text into
</textarea><br />
SML post headers:<br />
<textarea rows="3" cols="80" name="entryheaders"></textarea><br /><%
<textarea rows="3" cols="80" name="entryheaders"><%=post.getHeaders()%></textarea><br /><%
String s = request.getParameter(ArchiveViewerBean.PARAM_IN_REPLY_TO);
if ( (s != null) && (s.trim().length() > 0) ) {%>
<input type="hidden" name="<%=ArchiveViewerBean.PARAM_IN_REPLY_TO%>" value="<%=request.getParameter(ArchiveViewerBean.PARAM_IN_REPLY_TO)%>" />
@ -105,8 +121,11 @@ Attachment 7: <input type="file" name="entryfile7" /><br />
Attachment 8: <input type="file" name="entryfile8" /><br />
Attachment 9: <input type="file" name="entryfile9" /><br />
<hr />
<input type="submit" name="Post" value="Post entry" /> <input type="reset" value="Cancel" />
<% } %>
</td></tr>
<input type="submit" name="Post" value="Preview..." /> <input type="reset" value="Cancel" />
<%
} // end of the 'logged in, not confirmed, nothing posted' section
} // end of the 'logged in, not confirmed' section
} // end of the 'logged in' section
%></td></tr>
</table>
</body>

View File

@ -3,6 +3,7 @@
<html>
<head>
<title>SyndieMedia</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">

View File

@ -0,0 +1,58 @@
<%@page contentType="text/html" import="net.i2p.syndie.web.*" %>
<jsp:useBean scope="session" class="net.i2p.syndie.web.RemoteArchiveBean" id="remote" />
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<jsp:useBean scope="session" class="net.i2p.syndie.data.ArchiveIndex" id="archive" />
<html>
<head>
<title>SyndieMedia</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">
<tr><td colspan="5" valign="top" align="left"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2"><jsp:include page="_leftnav.jsp" /></td>
<jsp:include page="_topnav.jsp" />
<td valign="top" align="left" rowspan="2"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr><form action="remote.jsp" method="POST"><td valign="top" align="left" colspan="3">
<%
if (!user.getAuthenticated() || !user.getAllowAccessRemote()) {
%>Sorry, you are not allowed to access remote archives from here. Perhaps you should install Syndie yourself?<%
} else {
%>Import from:
<select name="schema">
<option value="web">Web</option>
<option value="eep">I2P</option>
<option value="tor">TOR</option>
<option value="freenet">Freenet</option>
<option value="mnet">MNet</option>
<option value="feedspace">Feedspace</option>
<option value="usenet">Usenet</option>
</select>
<input name="location" size="60" /> <input type="submit" name="action" value="Continue..." />
<%
String action = request.getParameter("action");
if ("Continue...".equals(action)) {
remote.fetchIndex(user, request.getParameter("schema"), request.getParameter("location"));
} else if ("Fetch metadata".equals(action)) {
remote.fetchMetadata(user, request.getParameterMap());
} else if ("Fetch selected entries".equals(action)) {
remote.fetchSelectedEntries(user, request.getParameterMap());
} else if ("Fetch all new entries".equals(action)) {
remote.fetchAllEntries(user, request.getParameterMap());
}
String msgs = remote.getStatus();
if ( (msgs != null) && (msgs.length() > 0) ) { %><pre><%=msgs%>
<a href="remote.jsp">Refresh</a></pre><br /><% }
if (remote.getFetchIndexInProgress()) { %><b>Please wait while the index is being fetched
from <%=remote.getRemoteLocation()%></b>. <%
} else if (remote.getRemoteIndex() != null) {
// remote index is NOT null!
%><b><%=remote.getRemoteLocation()%></b>:<br />
<%remote.renderDeltaForm(user, archive, out);%>
<textarea style="font-size:8pt" rows="5" cols="120"><%=remote.getRemoteIndex()%></textarea><%
}
}
%>
</td></form></tr>
</table>
</body>

View File

@ -0,0 +1,2 @@
<%@page contentType="text/css" %>
<%@include file="syndie.css" %>

View File

@ -0,0 +1,67 @@
.syndieEntrySubjectCell {
background-color: #999999;
font-size: 12px;
font-weight: bold;
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
border: 0px;
}
.syndieEntryMetaCell {
background-color: #888888;
font-size: 10px;
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
border: 0px;
}
.syndieEntryAttachmentsCell {
background-color: #aaaaaa;
font-size: 12px;
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
border: 0px;
}
.syndieEntrySummaryCell {
background-color: #eeeeee;
font-size: 12px;
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
border: 0px;
}
.syndieEntryBodyCell {
background-color: #eeeeee;
font-size: 12px;
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
border: 0px;
}
.syndieTopNavBlogsCell {
background-color: #888888;
font-size: 14px;
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
border: 0px;
}
.syndieTopNavRemoteCell {
background-color: #888888;
font-size: 14px;
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
border: 0px;
}
.syndieTopNavManageCell {
background-color: #888888;
font-size: 14px;
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
border: 0px;
}
body {
margin : 0px;
padding : 0px;
text-align : center;
font-family: Arial, Helvetica, sans-serif;
background-color : #FFFFFF;
color: #000000;
font-size: 12px;
}

View File

@ -0,0 +1 @@
<%response.sendRedirect("../index.jsp");%>

View File

@ -2,6 +2,7 @@
<html>
<head>
<title>SyndieMedia</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">

View File

@ -0,0 +1,15 @@
<%@page import="net.i2p.syndie.web.ArchiveViewerBean" %><jsp:useBean
scope="session" class="net.i2p.syndie.web.PostBean" id="post" /><%
String id = request.getParameter(ArchiveViewerBean.PARAM_ATTACHMENT);
if (id != null) {
try {
int attachmentId = Integer.parseInt(id);
if ( (attachmentId < 0) || (attachmentId >= post.getAttachmentCount()) ) {
%>Attachment <%=attachmentId%> does not exist<%
} else {
response.setContentType(post.getContentType(attachmentId));
post.writeAttachmentData(attachmentId, response.getOutputStream());
}
} catch (NumberFormatException nfe) {}
}
%>

View File

@ -59,6 +59,7 @@
<copy file="installer/lib/jbigi/jbigi.jar" todir="build" />
<copy file="apps/addressbook/dist/addressbook.war" todir="build/" />
<copy file="apps/susimail/susimail.war" todir="build/" />
<copy file="apps/syndie/syndie.war" todir="build/" />
<copy file="apps/syndie/java/build/syndie.jar" todir="build/" />
<copy file="apps/syndie/syndie.war" todir="build/" />
</target>
@ -188,6 +189,7 @@
<copy file="build/routerconsole.war" todir="pkg-temp/webapps/" />
<copy file="build/addressbook.war" todir="pkg-temp/webapps/" />
<copy file="build/susimail.war" todir="pkg-temp/webapps/" />
<copy file="build/syndie.war" todir="pkg-temp/webapps/" />
<copy file="installer/resources/clients.config" todir="pkg-temp/" />
<copy file="installer/resources/eepget" todir="pkg-temp/" />
<copy file="installer/resources/i2prouter" todir="pkg-temp/" />
@ -286,6 +288,7 @@
<copy file="build/routerconsole.war" todir="pkg-temp/webapps/" />
<copy file="build/addressbook.war" todir="pkg-temp/webapps/" />
<copy file="build/susimail.war" todir="pkg-temp/webapps/" />
<copy file="build/syndie.war" todir="pkg-temp/webapps/" />
<copy file="history.txt" todir="pkg-temp/" />
<mkdir dir="pkg-temp/docs/" />
<copy file="news.xml" todir="pkg-temp/docs/" />

View File

@ -152,6 +152,10 @@ public class Base64 {
private static void runApp(String args[]) {
try {
if ("encodestring".equalsIgnoreCase(args[0])) {
System.out.println(encode(args[1].getBytes()));
return;
}
InputStream in = System.in;
OutputStream out = System.out;
if (args.length >= 3) {

View File

@ -0,0 +1,72 @@
package net.i2p.util;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import net.i2p.I2PAppContext;
/**
*
*/
public class EepGetScheduler implements EepGet.StatusListener {
private I2PAppContext _context;
private List _urls;
private List _localFiles;
private String _proxyHost;
private int _proxyPort;
private int _curURL;
private EepGet.StatusListener _listener;
public EepGetScheduler(I2PAppContext ctx, List urls, List localFiles, String proxyHost, int proxyPort, EepGet.StatusListener lsnr) {
_context = ctx;
_urls = urls;
_localFiles = localFiles;
_proxyHost = proxyHost;
_proxyPort = proxyPort;
_curURL = -1;
_listener = lsnr;
}
public void fetch() {
I2PThread t = new I2PThread(new Runnable() { public void run() { fetchNext(); } }, "EepGetScheduler");
t.setDaemon(true);
t.start();
}
private void fetchNext() {
_curURL++;
if (_curURL >= _urls.size()) return;
String url = (String)_urls.get(_curURL);
String out = EepGet.suggestName(url);
if ( (_localFiles != null) && (_localFiles.size() > _curURL) ) {
File f = (File)_localFiles.get(_curURL);
out = f.getAbsolutePath();
} else {
if (_localFiles == null)
_localFiles = new ArrayList(_urls.size());
_localFiles.add(new File(out));
}
EepGet get = new EepGet(_context, ((_proxyHost != null) && (_proxyPort > 0)), _proxyHost, _proxyPort, 0, out, url);
get.addStatusListener(this);
get.fetch();
}
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
_listener.attemptFailed(url, bytesTransferred, bytesRemaining, currentAttempt, numRetries, cause);
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
_listener.bytesTransferred(alreadyTransferred, currentWrite, bytesTransferred, bytesRemaining, url);
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
_listener.transferComplete(alreadyTransferred, bytesTransferred, bytesRemaining, url, outputFile);
fetchNext();
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
_listener.transferFailed(url, bytesTransferred, bytesRemaining, currentAttempt);
fetchNext();
}
}

View File

@ -1,4 +1,14 @@
$Id: history.txt,v 1.227 2005/08/17 15:05:03 jrandom Exp $
$Id: history.txt,v 1.228 2005/08/21 13:39:06 jrandom Exp $
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.
* 2005-08-21 0.6.0.3 released

View File

@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
*
*/
public class RouterVersion {
public final static String ID = "$Revision: 1.216 $ $Date: 2005/08/17 15:05:03 $";
public final static String ID = "$Revision: 1.217 $ $Date: 2005/08/21 13:39:05 $";
public final static String VERSION = "0.6.0.3";
public final static long BUILD = 0;
public final static long BUILD = 1;
public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION);
System.out.println("Router ID: " + RouterVersion.ID);

View File

@ -119,7 +119,9 @@ public class FIFOBandwidthLimiter {
*/
final void refillBandwidthQueues(long bytesInbound, long bytesOutbound) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Refilling the queues with " + bytesInbound + "/" + bytesOutbound);
_log.debug("Refilling the queues with " + bytesInbound + "/" + bytesOutbound + ", available " +
_availableInboundBytes + '/' + _availableOutboundBytes + ", max " +
_maxInboundBytes + '/' + _maxOutboundBytes);
_availableInboundBytes += bytesInbound;
_availableOutboundBytes += bytesOutbound;
if (_availableInboundBytes > _maxInboundBytes) {

View File

@ -26,14 +26,20 @@ class FIFOBandwidthRefiller implements Runnable {
public static final String PROP_OUTBOUND_BANDWIDTH_PEAK = "i2np.bandwidth.outboundBurstKBytes";
//public static final String PROP_REPLENISH_FREQUENCY = "i2np.bandwidth.replenishFrequencyMs";
// no longer allow unlimited bandwidth - the user must specify a value, and if they do not, it is 16KBps
public static final int DEFAULT_INBOUND_BANDWIDTH = 16;
public static final int DEFAULT_OUTBOUND_BANDWIDTH = 16;
public static final int DEFAULT_BURST_SECONDS = 60;
/** For now, until there is some tuning and safe throttling, we set the floor at 6KBps inbound */
public static final int MIN_INBOUND_BANDWIDTH = 1;
public static final int MIN_INBOUND_BANDWIDTH = 5;
/** For now, until there is some tuning and safe throttling, we set the floor at 6KBps outbound */
public static final int MIN_OUTBOUND_BANDWIDTH = 1;
public static final int MIN_OUTBOUND_BANDWIDTH = 5;
/** For now, until there is some tuning and safe throttling, we set the floor at a 10 second burst */
public static final int MIN_INBOUND_BANDWIDTH_PEAK = 1;
public static final int MIN_INBOUND_BANDWIDTH_PEAK = 10;
/** For now, until there is some tuning and safe throttling, we set the floor at a 10 second burst */
public static final int MIN_OUTBOUND_BANDWIDTH_PEAK = 1;
public static final int MIN_OUTBOUND_BANDWIDTH_PEAK = 10;
/** Updating the bandwidth more than once a second is silly. once every 2 or 5 seconds is less so. */
public static final long MIN_REPLENISH_FREQUENCY = 100;
@ -146,6 +152,8 @@ class FIFOBandwidthRefiller implements Runnable {
_inboundKBytesPerSecond = in;
else
_inboundKBytesPerSecond = MIN_INBOUND_BANDWIDTH;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Updating inbound rate to " + _inboundKBytesPerSecond);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Invalid inbound bandwidth limit [" + inBwStr
@ -155,6 +163,9 @@ class FIFOBandwidthRefiller implements Runnable {
if ( (inBwStr == null) && (_log.shouldLog(Log.DEBUG)) )
_log.debug("Inbound bandwidth limits not specified in the config via " + PROP_INBOUND_BANDWIDTH);
}
if (_inboundKBytesPerSecond <= 0)
_inboundKBytesPerSecond = DEFAULT_INBOUND_BANDWIDTH;
}
private void updateOutboundRate() {
String outBwStr = _context.getProperty(PROP_OUTBOUND_BANDWIDTH);
@ -169,6 +180,8 @@ class FIFOBandwidthRefiller implements Runnable {
_outboundKBytesPerSecond = out;
else
_outboundKBytesPerSecond = MIN_OUTBOUND_BANDWIDTH;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Updating outbound rate to " + _outboundKBytesPerSecond);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Invalid outbound bandwidth limit [" + outBwStr
@ -178,6 +191,9 @@ class FIFOBandwidthRefiller implements Runnable {
if ( (outBwStr == null) && (_log.shouldLog(Log.DEBUG)) )
_log.debug("Outbound bandwidth limits not specified in the config via " + PROP_OUTBOUND_BANDWIDTH);
}
if (_outboundKBytesPerSecond <= 0)
_outboundKBytesPerSecond = DEFAULT_OUTBOUND_BANDWIDTH;
}
private void updateInboundPeak() {
@ -203,11 +219,13 @@ class FIFOBandwidthRefiller implements Runnable {
if (_log.shouldLog(Log.WARN))
_log.warn("Invalid inbound bandwidth burst limit [" + inBwStr
+ "]");
_limiter.setMaxInboundBytes(DEFAULT_BURST_SECONDS * _inboundKBytesPerSecond * 1024);
}
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Inbound bandwidth burst limits not specified in the config via "
+ PROP_INBOUND_BANDWIDTH_PEAK);
_limiter.setMaxInboundBytes(DEFAULT_BURST_SECONDS * _inboundKBytesPerSecond * 1024);
}
}
private void updateOutboundPeak() {
@ -233,11 +251,13 @@ class FIFOBandwidthRefiller implements Runnable {
if (_log.shouldLog(Log.WARN))
_log.warn("Invalid outbound bandwidth burst limit [" + outBwStr
+ "]");
_limiter.setMaxOutboundBytes(DEFAULT_BURST_SECONDS * _outboundKBytesPerSecond * 1024);
}
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Outbound bandwidth burst limits not specified in the config via "
+ PROP_OUTBOUND_BANDWIDTH_PEAK);
_limiter.setMaxOutboundBytes(DEFAULT_BURST_SECONDS * _outboundKBytesPerSecond * 1024);
}
}

View File

@ -575,6 +575,8 @@ public class EstablishmentManager {
if (outboundState != null) {
if (outboundState.getLifetime() > MAX_ESTABLISH_TIME) {
if (outboundState.getState() != OutboundEstablishState.STATE_CONFIRMED_COMPLETELY) {
if (_log.shouldLog(Log.WARN))
_log.warn("Lifetime of expired outbound establish: " + outboundState.getLifetime());
while (true) {
OutNetMessage msg = outboundState.getNextQueuedMessage();
if (msg == null)

View File

@ -326,7 +326,7 @@ public class OutboundMessageFragments {
state.push();
int rto = peer.getRTO() * state.getPushCount();
int rto = peer.getRTO();// * state.getPushCount();
state.setNextSendTime(now + rto);
if (peer.getSendWindowBytesRemaining() > 0)
@ -338,7 +338,7 @@ public class OutboundMessageFragments {
_log.warn("Allocation of " + size + " rejected w/ wsize=" + peer.getSendWindowBytes()
+ " available=" + peer.getSendWindowBytesRemaining()
+ " for message " + state.getMessageId() + ": " + state);
state.setNextSendTime((now + 1024) & ~SECOND_MASK);
state.setNextSendTime(now+(_context.random().nextInt(2*ACKSender.ACK_FREQUENCY))); //(now + 1024) & ~SECOND_MASK);
if (_log.shouldLog(Log.WARN))
_log.warn("Retransmit after choke for next send time in " + (state.getNextSendTime()-now) + "ms");
_throttle.choke(peer.getRemotePeer());
@ -435,7 +435,7 @@ public class OutboundMessageFragments {
PeerState peer = state.getPeer();
if (peer != null) {
// this adjusts the rtt/rto/window/etc
peer.messageACKed(numFragments*state.getFragmentSize(), state.getLifetime(), state.getMaxSends());
peer.messageACKed(numFragments*state.getFragmentSize(), state.getLifetime(), numSends);
if (peer.getSendWindowBytesRemaining() > 0)
_throttle.unchoke(peer.getRemotePeer());
} else {

View File

@ -93,6 +93,10 @@ public class PeerState {
private int _sendBytes;
private int _receiveBps;
private int _receiveBytes;
private int _sendACKBps;
private int _sendACKBytes;
private int _receiveACKBps;
private int _receiveACKBytes;
private long _receivePeriodBegin;
private volatile long _lastCongestionOccurred;
/**
@ -141,8 +145,11 @@ public class PeerState {
private long _packetsTransmitted;
/** how many packets were retransmitted within the last RETRANSMISSION_PERIOD_WIDTH packets */
private long _packetsRetransmitted;
/** how many packets were transmitted within the last RETRANSMISSION_PERIOD_WIDTH packets */
private long _packetsPeriodTransmitted;
private int _packetsPeriodRetransmitted;
private int _packetRetransmissionRate;
/** what was the $packetsTransmitted when the current RETRANSMISSION_PERIOD_WIDTH began */
/** at what time did we last break off the retransmission counter period */
private long _retransmissionPeriodStart;
/** how many dup packets were received within the last RETRANSMISSION_PERIOD_WIDTH packets */
private long _packetsReceivedDuplicate;
@ -163,7 +170,7 @@ public class PeerState {
* of 608
*/
private static final int DEFAULT_MTU = 608;//600; //1500;
private static final int MIN_RTO = 1000 + ACKSender.ACK_FREQUENCY;
private static final int MIN_RTO = 500 + ACKSender.ACK_FREQUENCY;
private static final int MAX_RTO = 3000; // 5000;
public PeerState(I2PAppContext ctx) {
@ -373,6 +380,10 @@ public class PeerState {
return _consecutiveFailedSends;
}
/** how fast we are sending *ack* packets */
public int getSendACKBps() { return _sendACKBps; }
public int getReceiveACKBps() { return _receiveACKBps; }
/**
* have all of the packets received in the current second requested that
* the previous second's ACKs be sent?
@ -384,14 +395,20 @@ public class PeerState {
* cannot. If it is not decremented, the window size remaining is
* not adjusted at all.
*/
public boolean allocateSendingBytes(int size) {
public boolean allocateSendingBytes(int size) { return allocateSendingBytes(size, false); }
public boolean allocateSendingBytes(int size, boolean isForACK) {
long now = _context.clock().now();
long duration = now - _lastSendRefill;
if (duration >= 1000) {
_sendWindowBytesRemaining = _sendWindowBytes;
_sendBytes += size;
_sendBps = (int)(0.9f*(float)_sendBps + 0.1f*((float)_sendBytes * (1000f/(float)duration)));
if (isForACK) {
_sendACKBytes += size;
_sendACKBps = (int)(0.9f*(float)_sendACKBps + 0.1f*((float)_sendACKBytes * (1000f/(float)duration)));
}
_sendBytes = 0;
_sendACKBytes = 0;
_lastSendRefill = now;
}
//if (true) return true;
@ -399,6 +416,8 @@ public class PeerState {
_sendWindowBytesRemaining -= size;
_sendBytes += size;
_lastSendTime = now;
if (isForACK)
_sendACKBytes += size;
return true;
} else {
return false;
@ -432,14 +451,17 @@ public class PeerState {
public int getSlowStartThreshold() { return _slowStartThreshold; }
/** we received the message specified completely */
public void messageFullyReceived(Long messageId, int bytes) {
if (bytes > 0)
public void messageFullyReceived(Long messageId, int bytes) { messageFullyReceived(messageId, bytes, false); }
public void messageFullyReceived(Long messageId, int bytes, boolean isForACK) {
if (bytes > 0) {
_receiveBytes += bytes;
else {
if (_retransmissionPeriodStart + RETRANSMISSION_PERIOD_WIDTH < _packetsReceived) {
if (isForACK)
_receiveACKBytes += bytes;
} else {
if (_retransmissionPeriodStart + 1000 < _context.clock().now()) {
_packetsReceivedDuplicate++;
} else {
_retransmissionPeriodStart = _packetsReceived;
_retransmissionPeriodStart = _context.clock().now();
_packetsReceivedDuplicate = 1;
}
}
@ -448,6 +470,9 @@ public class PeerState {
long duration = now - _receivePeriodBegin;
if (duration >= 1000) {
_receiveBps = (int)(0.9f*(float)_receiveBps + 0.1f*((float)_receiveBytes * (1000f/(float)duration)));
if (isForACK)
_receiveACKBps = (int)(0.9f*(float)_receiveACKBps + 0.1f*((float)_receiveACKBytes * (1000f/(float)duration)));
_receiveACKBytes = 0;
_receiveBytes = 0;
_receivePeriodBegin = now;
_context.statManager().addRateData("udp.receiveBps", _receiveBps, 0);
@ -480,20 +505,21 @@ public class PeerState {
*/
private boolean congestionOccurred() {
long now = _context.clock().now();
if (_lastCongestionOccurred + 10*1000 > now)
return false; // only shrink once every 10 seconds
if (_lastCongestionOccurred + 5*1000 > now)
return false; // only shrink once every 5 seconds
_lastCongestionOccurred = now;
_context.statManager().addRateData("udp.congestionOccurred", _sendWindowBytes, _sendBps);
int congestionAt = _sendWindowBytes;
//if (true)
// _sendWindowBytes -= 10000;
//else
_sendWindowBytes = (_sendWindowBytes*2) / 3;
_sendWindowBytes = _sendWindowBytes/4; //(_sendWindowBytes*2) / 3;
if (_sendWindowBytes < MINIMUM_WINDOW_BYTES)
_sendWindowBytes = MINIMUM_WINDOW_BYTES;
if (_sendWindowBytes < _slowStartThreshold)
_slowStartThreshold = _sendWindowBytes;
//if (congestionAt/2 < _slowStartThreshold)
_slowStartThreshold = congestionAt/2;
return true;
}
@ -595,24 +621,34 @@ public class PeerState {
public void messageACKed(int bytesACKed, long lifetime, int numSends) {
_consecutiveFailedSends = 0;
_lastFailedSendPeriod = -1;
if (numSends < 2) {
if (_sendWindowBytes <= _slowStartThreshold) {
_sendWindowBytes += bytesACKed;
} else {
double prob = ((double)bytesACKed) / ((double)_sendWindowBytes);
if (_context.random().nextDouble() <= prob)
if (false) {
_sendWindowBytes += 16; // why 16?
} else {
float prob = ((float)bytesACKed) / ((float)_sendWindowBytes);
float v = _context.random().nextFloat();
if (v < 0) v = 0-v;
if (v <= prob)
_sendWindowBytes += bytesACKed;
}
}
}
if (_sendWindowBytes > MAX_SEND_WINDOW_BYTES)
_sendWindowBytes = MAX_SEND_WINDOW_BYTES;
_lastReceiveTime = _context.clock().now();
if (false) {
if (_sendWindowBytesRemaining + bytesACKed <= _sendWindowBytes)
_sendWindowBytesRemaining += bytesACKed;
else
_sendWindowBytesRemaining = _sendWindowBytes;
}
_messagesSent++;
if (numSends <= 2)
if (numSends < 2)
recalculateTimeouts(lifetime);
else
_log.warn("acked after numSends=" + numSends + " w/ lifetime=" + lifetime + " and size=" + bytesACKed);
@ -643,11 +679,14 @@ public class PeerState {
/** we are resending a packet, so lets jack up the rto */
public void messageRetransmitted(int packets) {
if (_retransmissionPeriodStart + RETRANSMISSION_PERIOD_WIDTH < _packetsTransmitted) {
long now = _context.clock().now();
if (_retransmissionPeriodStart + 1000 <= now) {
_packetsRetransmitted += packets;
} else {
_packetRetransmissionRate = (int)((float)(0.9f*_packetRetransmissionRate) + (float)(0.1f*_packetsRetransmitted));
_retransmissionPeriodStart = _packetsTransmitted;
//_packetsPeriodTransmitted = _packetsTransmitted - _retransmissionPeriodStart;
_packetsPeriodRetransmitted = (int)_packetsRetransmitted;
_retransmissionPeriodStart = now;
_packetsRetransmitted = packets;
}
congestionOccurred();
@ -655,10 +694,13 @@ public class PeerState {
//_rto *= 2;
}
public void packetsTransmitted(int packets) {
long now = _context.clock().now();
_packetsTransmitted += packets;
if (_retransmissionPeriodStart + RETRANSMISSION_PERIOD_WIDTH > _packetsTransmitted) {
//_packetsPeriodTransmitted += packets;
if (_retransmissionPeriodStart + 1000 <= now) {
_packetRetransmissionRate = (int)((float)(0.9f*_packetRetransmissionRate) + (float)(0.1f*_packetsRetransmitted));
_retransmissionPeriodStart = _packetsTransmitted;
_retransmissionPeriodStart = 0;
_packetsPeriodRetransmitted = (int)_packetsRetransmitted;
_packetsRetransmitted = 0;
}
}
@ -673,6 +715,8 @@ public class PeerState {
public long getMessagesReceived() { return _messagesReceived; }
public long getPacketsTransmitted() { return _packetsTransmitted; }
public long getPacketsRetransmitted() { return _packetsRetransmitted; }
public long getPacketsPeriodTransmitted() { return _packetsPeriodTransmitted; }
public int getPacketsPeriodRetransmitted() { return _packetsPeriodRetransmitted; }
/** avg number of packets retransmitted for every 100 packets */
public long getPacketRetransmissionRate() { return _packetRetransmissionRate; }
public long getPacketsReceived() { return _packetsReceived; }

View File

@ -132,6 +132,7 @@ class PeerTestManager {
*/
private void receiveTestReply(RemoteHostId from, UDPPacketReader.PeerTestReader testInfo) {
PeerTestState test = _currentTest;
if (test == null) return;
if ( (DataHelper.eq(from.getIP(), test.getBobIP().getAddress())) && (from.getPort() == test.getBobPort()) ) {
byte ip[] = new byte[testInfo.readIPSize()];
testInfo.readIP(ip, 0);

View File

@ -40,9 +40,12 @@ public class UDPReceiver {
_runner = new Runner();
_context.statManager().createRateStat("udp.receivePacketSize", "How large packets received are", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("udp.droppedInbound", "How many packet are queued up but not yet received when we drop", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("udp.droppedInboundProbabalistically", "How many packet we drop probabalistically (to simulate failures)", "udp", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("udp.acceptedInboundProbabalistically", "How many packet we accept probabalistically (to simulate failures)", "udp", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 });
}
public void startup() {
adjustDropProbability();
_keepRunning = true;
I2PThread t = new I2PThread(_runner, _name);
t.setDaemon(true);
@ -57,6 +60,18 @@ public class UDPReceiver {
}
}
private void adjustDropProbability() {
String p = _context.getProperty("i2np.udp.dropProbability");
if (p != null) {
try {
ARTIFICIAL_DROP_PROBABILITY = Float.parseFloat(p);
} catch (NumberFormatException nfe) {}
if (ARTIFICIAL_DROP_PROBABILITY < 0) ARTIFICIAL_DROP_PROBABILITY = 0;
} else {
ARTIFICIAL_DROP_PROBABILITY = 0;
}
}
/**
* Replace the old listen port with the new one, returning the old.
* NOTE: this closes the old socket so that blocking calls unblock!
@ -69,17 +84,26 @@ public class UDPReceiver {
/** if a packet been sitting in the queue for a full second (meaning the handlers are overwhelmed), drop subsequent packets */
private static final long MAX_QUEUE_PERIOD = 1*1000;
private static final float ARTIFICIAL_DROP_PROBABILITY = 0.0f; // 0.02f; // 0.0f;
private static float ARTIFICIAL_DROP_PROBABILITY = 0.0f; // 0.02f; // 0.0f;
private static final int ARTIFICIAL_DELAY = 0; // 100;
private static final int ARTIFICIAL_DELAY_BASE = 0; //100;
private int receive(UDPPacket packet) {
//adjustDropProbability();
if (ARTIFICIAL_DROP_PROBABILITY > 0) {
// the first check is to let the compiler optimize away this
// random block on the live system when the probability is == 0
if (_context.random().nextFloat() <= ARTIFICIAL_DROP_PROBABILITY)
int v = _context.random().nextInt(1000);
if (v < ARTIFICIAL_DROP_PROBABILITY*1000) {
if (_log.shouldLog(Log.ERROR))
_log.error("Drop with v=" + v + " p=" + ARTIFICIAL_DROP_PROBABILITY + " packet size: " + packet.getPacket().getLength());
_context.statManager().addRateData("udp.droppedInboundProbabalistically", 1, 0);
return -1;
} else {
_context.statManager().addRateData("udp.acceptedInboundProbabalistically", 1, 0);
}
}
if ( (ARTIFICIAL_DELAY > 0) || (ARTIFICIAL_DELAY_BASE > 0) ) {