From 8660cf0d74204760e169a1f7ad2462841f8a4b6b Mon Sep 17 00:00:00 2001 From: jrandom <jrandom> Date: Sat, 27 Aug 2005 22:15:35 +0000 Subject: [PATCH] 2005-08-27 jrandom * Minor logging and optimization tweaks in the router and SDK * Use ISO-8859-1 in the XML files (thanks redzara!) * The consolePassword config property can now be used to bypass the router console's nonce checking, allowing CLI restarts --- apps/i2ptunnel/jsp/web.xml | 4 +- .../src/net/i2p/router/web/FormHandler.java | 13 +- apps/routerconsole/jsp/web.xml | 4 +- .../java/src/net/i2p/syndie/BlogManager.java | 62 ++++++-- .../src/net/i2p/syndie/sml/HTMLRenderer.java | 35 +++-- .../net/i2p/syndie/web/ArchiveServlet.java | 2 + .../src/net/i2p/syndie/web/ExportServlet.java | 100 ++++++++++++ .../net/i2p/syndie/web/RemoteArchiveBean.java | 146 +++++++++++++++++- apps/syndie/jsp/remote.jsp | 17 +- apps/syndie/jsp/web.xml | 4 +- core/java/src/net/i2p/util/LogManager.java | 5 +- history.txt | 8 +- .../src/net/i2p/router/RouterVersion.java | 4 +- .../router/client/ClientConnectionRunner.java | 2 +- .../router/message/GarlicMessageReceiver.java | 3 - .../networkdb/kademlia/KBucketImpl.java | 26 ++-- .../KademliaNetworkDatabaseFacade.java | 2 +- .../kademlia/RepublishLeaseSetJob.java | 2 +- .../router/networkdb/kademlia/StoreJob.java | 23 ++- .../router/transport/udp/MessageReceiver.java | 4 +- .../i2p/router/tunnel/FragmentHandler.java | 4 +- 21 files changed, 400 insertions(+), 70 deletions(-) create mode 100644 apps/syndie/java/src/net/i2p/syndie/web/ExportServlet.java diff --git a/apps/i2ptunnel/jsp/web.xml b/apps/i2ptunnel/jsp/web.xml index b995397008..a814e9a87b 100644 --- a/apps/i2ptunnel/jsp/web.xml +++ b/apps/i2ptunnel/jsp/web.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?> +<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd"> @@ -14,4 +14,4 @@ <welcome-file>index.html</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list> -</web-app> \ No newline at end of file +</web-app> diff --git a/apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java index 1d66037afe..ac0e41d2aa 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java @@ -20,6 +20,7 @@ public class FormHandler { protected Log _log; private String _nonce; protected String _action; + protected String _passphrase; private List _errors; private List _notices; private boolean _processed; @@ -32,6 +33,7 @@ public class FormHandler { _processed = false; _valid = true; _nonce = null; + _passphrase = null; } /** @@ -51,6 +53,7 @@ public class FormHandler { public void setNonce(String val) { _nonce = val; } public void setAction(String val) { _action = val; } + public void setPassphrase(String val) { _passphrase = val; } /** * Override this to perform the final processing (in turn, adding formNotice @@ -119,8 +122,14 @@ public class FormHandler { String noncePrev = System.getProperty(getClass().getName() + ".noncePrev"); if ( ( (nonce == null) || (!_nonce.equals(nonce)) ) && ( (noncePrev == null) || (!_nonce.equals(noncePrev)) ) ) { - addFormError("Invalid nonce, are you being spoofed?"); - _valid = false; + + String expected = _context.getProperty("consolePassword"); + if ( (expected != null) && (expected.trim().length() > 0) && (expected.equals(_passphrase)) ) { + // ok + } else { + addFormError("Invalid nonce, are you being spoofed?"); + _valid = false; + } } } diff --git a/apps/routerconsole/jsp/web.xml b/apps/routerconsole/jsp/web.xml index b995397008..a814e9a87b 100644 --- a/apps/routerconsole/jsp/web.xml +++ b/apps/routerconsole/jsp/web.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?> +<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd"> @@ -14,4 +14,4 @@ <welcome-file>index.html</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list> -</web-app> \ No newline at end of file +</web-app> diff --git a/apps/syndie/java/src/net/i2p/syndie/BlogManager.java b/apps/syndie/java/src/net/i2p/syndie/BlogManager.java index 9bd4693bb0..591c00b74c 100644 --- a/apps/syndie/java/src/net/i2p/syndie/BlogManager.java +++ b/apps/syndie/java/src/net/i2p/syndie/BlogManager.java @@ -20,32 +20,39 @@ public class BlogManager { private File _userDir; private File _cacheDir; private File _tempDir; + private File _rootDir; private Archive _archive; static { TimeZone.setDefault(TimeZone.getTimeZone("GMT")); String rootDir = I2PAppContext.getGlobalContext().getProperty("syndie.rootDir"); - if (rootDir == null) - rootDir = System.getProperty("user.home"); - rootDir = rootDir + File.separatorChar + ".syndie"; + if (false) { + if (rootDir == null) + rootDir = System.getProperty("user.home"); + rootDir = rootDir + File.separatorChar + ".syndie"; + } else { + if (rootDir == null) + rootDir = "./syndie"; + } _instance = new BlogManager(I2PAppContext.getGlobalContext(), rootDir); } public static BlogManager instance() { return _instance; } public BlogManager(I2PAppContext ctx, String rootDir) { _context = ctx; - File root = new File(rootDir); - root.mkdirs(); - _blogKeyDir = new File(root, "blogkeys"); - _privKeyDir = new File(root, "privkeys"); + _rootDir = new File(rootDir); + _rootDir.mkdirs(); + readConfig(); + _blogKeyDir = new File(_rootDir, "blogkeys"); + _privKeyDir = new File(_rootDir, "privkeys"); String archiveDir = _context.getProperty("syndie.archiveDir"); if (archiveDir != null) _archiveDir = new File(archiveDir); else - _archiveDir = new File(root, "archive"); - _userDir = new File(root, "users"); - _cacheDir = new File(root, "cache"); - _tempDir = new File(root, "temp"); + _archiveDir = new File(_rootDir, "archive"); + _userDir = new File(_rootDir, "users"); + _cacheDir = new File(_rootDir, "cache"); + _tempDir = new File(_rootDir, "temp"); _blogKeyDir.mkdirs(); _privKeyDir.mkdirs(); _archiveDir.mkdirs(); @@ -56,6 +63,39 @@ public class BlogManager { _archive.regenerateIndex(); } + private void readConfig() { + File config = new File(_rootDir, "syndie.config"); + if (config.exists()) { + try { + Properties p = new Properties(); + DataHelper.loadProps(p, config); + for (Iterator iter = p.keySet().iterator(); iter.hasNext(); ) { + String key = (String)iter.next(); + System.setProperty(key, p.getProperty(key)); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + } + + public void writeConfig() { + File config = new File(_rootDir, "syndie.config"); + FileOutputStream out = null; + try { + out = new FileOutputStream(config); + for (Iterator iter = _context.getPropertyNames().iterator(); iter.hasNext(); ) { + String name = (String)iter.next(); + if (name.startsWith("syndie.")) + out.write((name + '=' + _context.getProperty(name) + '\n').getBytes()); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } finally { + if (out != null) try { out.close(); } catch (IOException ioe) {} + } + } + public BlogInfo createBlog(String name, String description, String contactURL, String archives[]) { return createBlog(name, null, description, contactURL, archives); } diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java index 883e620130..1c06060072 100644 --- a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java +++ b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java @@ -261,6 +261,14 @@ public class HTMLRenderer extends EventReceiverImpl { * */ public void receiveBlog(String name, String hash, String tag, long entryId, List locations, String description) { + if (!continueBody()) { return; } + if (hash == null) return; + + System.out.println("Receiving the blog: " + name + "/" + hash + "/" + tag + "/" + entryId +"/" + locations + ": "+ description); + byte blogData[] = Base64.decode(hash); + if ( (blogData == null) || (blogData.length != Hash.HASH_LENGTH) ) + return; + Blog b = new Blog(); b.name = name; b.hash = hash; @@ -270,13 +278,6 @@ public class HTMLRenderer extends EventReceiverImpl { if (!_blogs.contains(b)) _blogs.add(b); - if (!continueBody()) { return; } - if (hash == null) return; - - System.out.println("Receiving the blog: " + name + "/" + hash + "/" + tag + "/" + entryId +"/" + locations + ": "+ description); - byte blogData[] = Base64.decode(hash); - if ( (blogData == null) || (blogData.length != Hash.HASH_LENGTH) ) - return; Hash blog = new Hash(blogData); if (entryId > 0) { String pageURL = getPageURL(blog, tag, entryId, -1, -1, true, (_user != null ? _user.getShowImages() : false)); @@ -306,13 +307,14 @@ public class HTMLRenderer extends EventReceiverImpl { _bodyBuffer.append("\">Tag: ").append(sanitizeString(tag)).append("</a>"); } if ( (locations != null) && (locations.size() > 0) ) { - _bodyBuffer.append(" <select name=\"archiveLocation\">"); + _bodyBuffer.append(" Archives: "); for (int i = 0; i < locations.size(); i++) { SafeURL surl = (SafeURL)locations.get(i); - _bodyBuffer.append("<option value=\"").append(Base64.encode(surl.toString())).append("\">"); - _bodyBuffer.append(sanitizeString(surl.toString())).append("</option>\n"); + if (_user.getAuthenticated() && _user.getAllowAccessRemote()) + _bodyBuffer.append("<a href=\"").append(getArchiveURL(blog, surl)).append("\">").append(sanitizeString(surl.toString())).append("</a> "); + else + _bodyBuffer.append(sanitizeString(surl.toString())).append(' '); } - _bodyBuffer.append("</select>"); } _bodyBuffer.append("] "); } @@ -656,8 +658,8 @@ public class HTMLRenderer extends EventReceiverImpl { } if (!unsafe) return str; - str = str.replace('<', '_'); - str = str.replace('>', '-'); + str = str.replace('<', '_'); // this should be < + str = str.replace('>', '-'); // this should be > if (!allowNL) { str = str.replace('\n', ' '); str = str.replace('\r', ' '); @@ -668,6 +670,7 @@ public class HTMLRenderer extends EventReceiverImpl { public static final String sanitizeURL(String str) { return Base64.encode(str); } public static final String sanitizeTagParam(String str) { + str = str.replace('&', '_'); // this should be & if (str.indexOf('\"') < 0) return sanitizeString(str); str = str.replace('\"', '\''); @@ -753,4 +756,10 @@ public class HTMLRenderer extends EventReceiverImpl { buf.append(ArchiveViewerBean.PARAM_SHOW_IMAGES).append('=').append(showImages).append('&'); return buf.toString(); } + public static String getArchiveURL(Hash blog, SafeURL archiveLocation) { + return "remote.jsp?" + //+ "action=Continue..." // should this be the case? + + "&schema=" + sanitizeTagParam(archiveLocation.getSchema()) + + "&location=" + sanitizeTagParam(archiveLocation.getLocation()); + } } diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ArchiveServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ArchiveServlet.java index 28b5b9c7ba..20931fb183 100644 --- a/apps/syndie/java/src/net/i2p/syndie/web/ArchiveServlet.java +++ b/apps/syndie/java/src/net/i2p/syndie/web/ArchiveServlet.java @@ -24,6 +24,8 @@ public class ArchiveServlet extends HttpServlet { return; } else if (path.endsWith(Archive.INDEX_FILE)) { renderSummary(resp); + } else if (path.endsWith("export.zip")) { + ExportServlet.export(req, resp); } else { String blog = getBlog(path); if (path.endsWith(Archive.METADATA_FILE)) { diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ExportServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ExportServlet.java new file mode 100644 index 0000000000..6a0b40617d --- /dev/null +++ b/apps/syndie/java/src/net/i2p/syndie/web/ExportServlet.java @@ -0,0 +1,100 @@ +package net.i2p.syndie.web; + +import java.io.*; +import java.io.*; +import java.util.*; +import java.util.zip.*; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.ServletException; + +import net.i2p.data.*; +import net.i2p.syndie.*; +import net.i2p.syndie.data.*; + +/** + * Dump out a whole series of blog metadata and entries as a zip stream. All metadata + * is written before any entries, so it can be processed in order safely. + * + * HTTP parameters: + * = meta (multiple values): base64 hash of the blog for which metadata is requested + * = entry (multiple values): blog URI of an entry being requested + */ +public class ExportServlet extends HttpServlet { + + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + export(req, resp); + } + + public static void export(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String meta[] = req.getParameterValues("meta"); + String entries[] = req.getParameterValues("entry"); + resp.setContentType("application/x-syndie-zip"); + resp.setStatus(200); + OutputStream out = resp.getOutputStream(); + ZipOutputStream zo = new ZipOutputStream(out); + + List metaFiles = getMetaFiles(meta); + + ZipEntry ze = null; + byte buf[] = new byte[1024]; + int read = -1; + for (int i = 0; metaFiles != null && i < metaFiles.size(); i++) { + ze = new ZipEntry("meta" + i); + ze.setTime(0); + zo.putNextEntry(ze); + FileInputStream in = new FileInputStream((File)metaFiles.get(i)); + while ( (read = in.read(buf)) != -1) + zo.write(buf, 0, read); + zo.closeEntry(); + } + + List entryFiles = getEntryFiles(entries); + for (int i = 0; entryFiles != null && i < entryFiles.size(); i++) { + ze = new ZipEntry("entry" + i); + ze.setTime(0); + zo.putNextEntry(ze); + FileInputStream in = new FileInputStream((File)entryFiles.get(i)); + while ( (read = in.read(buf)) != -1) + zo.write(buf, 0, read); + zo.closeEntry(); + } + + zo.finish(); + zo.close(); + } + + private static List getMetaFiles(String blogHashes[]) { + if ( (blogHashes == null) || (blogHashes.length <= 0) ) return null; + File dir = BlogManager.instance().getArchive().getArchiveDir(); + List rv = new ArrayList(blogHashes.length); + for (int i = 0; i < blogHashes.length; i++) { + byte hv[] = Base64.decode(blogHashes[i]); + if ( (hv == null) || (hv.length != Hash.HASH_LENGTH) ) + continue; + File blogDir = new File(dir, blogHashes[i]); + File metaFile = new File(blogDir, Archive.METADATA_FILE); + if (metaFile.exists()) + rv.add(metaFile); + } + return rv; + } + + private static List getEntryFiles(String blogURIs[]) { + if ( (blogURIs == null) || (blogURIs.length <= 0) ) return null; + File dir = BlogManager.instance().getArchive().getArchiveDir(); + List rv = new ArrayList(blogURIs.length); + for (int i = 0; i < blogURIs.length; i++) { + BlogURI uri = new BlogURI(blogURIs[i]); + if (uri.getEntryId() < 0) + continue; + File blogDir = new File(dir, uri.getKeyHash().toBase64()); + File entryFile = new File(blogDir, uri.getEntryId() + ".snd"); + if (entryFile.exists()) + rv.add(entryFile); + } + return rv; + } +} diff --git a/apps/syndie/java/src/net/i2p/syndie/web/RemoteArchiveBean.java b/apps/syndie/java/src/net/i2p/syndie/web/RemoteArchiveBean.java index d2f0a64c94..f8965ba718 100644 --- a/apps/syndie/java/src/net/i2p/syndie/web/RemoteArchiveBean.java +++ b/apps/syndie/java/src/net/i2p/syndie/web/RemoteArchiveBean.java @@ -3,6 +3,7 @@ package net.i2p.syndie.web; import java.io.*; import java.text.*; import java.util.*; +import java.util.zip.*; import net.i2p.I2PAppContext; import net.i2p.data.*; import net.i2p.util.EepGet; @@ -108,6 +109,62 @@ public class RemoteArchiveBean { fetch(urls, tmpFiles, user, new BlogStatusListener()); } + public void fetchSelectedBulk(User user, Map parameters) { + String entries[] = ArchiveViewerBean.getStrings(parameters, "entry"); + String action = ArchiveViewerBean.getString(parameters, "action"); + if ("Fetch all new entries".equals(action)) { + ArchiveIndex localIndex = BlogManager.instance().getArchive().getIndex(); + List uris = new ArrayList(); + List matches = new ArrayList(); + for (Iterator iter = _remoteIndex.getUniqueBlogs().iterator(); iter.hasNext(); ) { + Hash blog = (Hash)iter.next(); + _remoteIndex.selectMatchesOrderByEntryId(matches, blog, null); + for (int i = 0; i < matches.size(); i++) { + BlogURI uri = (BlogURI)matches.get(i); + if (!localIndex.getEntryIsKnown(uri)) + uris.add(uri); + } + matches.clear(); + } + entries = new String[uris.size()]; + for (int i = 0; i < uris.size(); i++) + entries[i] = ((BlogURI)uris.get(i)).toString(); + } + if ( (entries == null) || (entries.length <= 0) ) return; + StringBuffer url = new StringBuffer(512); + url.append(buildExportURL()); + Set meta = new HashSet(); + for (int i = 0; i < entries.length; i++) { + BlogURI uri = new BlogURI(entries[i]); + if (uri.getEntryId() >= 0) { + url.append("entry=").append(uri.toString()).append('&'); + meta.add(uri.getKeyHash()); + _statusMessages.add("Scheduling blog post fetching for " + HTMLRenderer.sanitizeString(entries[i])); + } + } + for (Iterator iter = meta.iterator(); iter.hasNext(); ) { + Hash blog = (Hash)iter.next(); + url.append("meta=").append(blog.toBase64()).append('&'); + _statusMessages.add("Scheduling blog metadata fetching for " + blog.toBase64()); + } + List urls = new ArrayList(1); + urls.add(url.toString()); + List tmpFiles = new ArrayList(1); + try { + File tmp = File.createTempFile("fetchBulk", ".zip", BlogManager.instance().getTempDir()); + tmpFiles.add(tmp); + fetch(urls, tmpFiles, user, new BulkFetchListener(tmp)); + } catch (IOException ioe) { + _statusMessages.add("Internal error creating temporary file to fetch " + HTMLRenderer.sanitizeString(url.toString()) + ": " + ioe.getMessage()); + } + } + + private String buildExportURL() { + String loc = _remoteLocation.trim(); + int root = loc.lastIndexOf('/'); + return loc.substring(0, root + 1) + "export.zip?"; + } + private String buildEntryURL(BlogURI uri) { String loc = _remoteLocation.trim(); int root = loc.lastIndexOf('/'); @@ -300,6 +357,85 @@ public class RemoteArchiveBean { } } + /** + * Receive the status of a fetch for the zip containing blogs and metadata (as generated by + * the ExportServlet) + */ + private class BulkFetchListener implements EepGet.StatusListener { + private File _tmp; + public BulkFetchListener(File tmp) { + _tmp = tmp; + } + 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.substring(0, url.indexOf('?'))) + " successful, importing the data"); + File file = new File(outputFile); + ZipInputStream zi = null; + try { + zi = new ZipInputStream(new FileInputStream(file)); + + while (true) { + ZipEntry entry = zi.getNextEntry(); + if (entry == null) + break; + + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + byte buf[] = new byte[1024]; + int read = -1; + while ( (read = zi.read(buf)) != -1) + out.write(buf, 0, read); + + if (entry.getName().startsWith("meta")) { + BlogInfo i = new BlogInfo(); + i.load(new ByteArrayInputStream(out.toByteArray())); + boolean ok = BlogManager.instance().getArchive().storeBlogInfo(i); + if (ok) { + _statusMessages.add("Blog info for " + HTMLRenderer.sanitizeString(i.getProperty(BlogInfo.NAME)) + " imported"); + } else { + _statusMessages.add("Blog info at " + HTMLRenderer.sanitizeString(url) + " was corrupt / invalid / forged"); + } + } else if (entry.getName().startsWith("entry")) { + EntryContainer c = new EntryContainer(); + c.load(new ByteArrayInputStream(out.toByteArray())); + BlogURI uri = c.getURI(); + if ( (uri == null) || (uri.getKeyHash() == null) ) { + _statusMessages.add("Blog post " + HTMLRenderer.sanitizeString(entry.getName()) + " was corrupt - no URI"); + continue; + } + Archive a = BlogManager.instance().getArchive(); + BlogInfo info = a.getBlogInfo(uri); + if (info == null) { + _statusMessages.add("Blog post " + HTMLRenderer.sanitizeString(entry.getName()) + " cannot be imported, as we don't have their blog metadata"); + continue; + } + boolean ok = a.storeEntry(c); + if (!ok) { + _statusMessages.add("Blog post " + uri.toString() + " has an invalid signature"); + continue; + } else { + _statusMessages.add("Blog post " + uri.toString() + " imported"); + } + } + } + + BlogManager.instance().getArchive().regenerateIndex(); + } catch (IOException ioe) { + ioe.printStackTrace(); + _statusMessages.add("Error importing from " + HTMLRenderer.sanitizeString(url) + ": " + ioe.getMessage()); + } finally { + if (zi != null) try { zi.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); + _tmp.delete(); + } + } public void postSelectedEntries(User user, Map parameters) { String entries[] = ArchiveViewerBean.getStrings(parameters, "localentry"); @@ -366,7 +502,7 @@ public class RemoteArchiveBean { List entries = new ArrayList(); for (Iterator iter = remoteBlogs.iterator(); iter.hasNext(); ) { Hash blog = (Hash)iter.next(); - buf = new StringBuffer(1024); + buf.setLength(0); int shownEntries = 0; buf.append("<tr><td colspan=\"5\" align=\"left\" valign=\"top\">\n"); BlogInfo info = archive.getBlogInfo(blog); @@ -403,6 +539,11 @@ public class RemoteArchiveBean { buf.append("</td>\n"); buf.append("</tr>\n"); } + if (shownEntries > 0) { + out.write(buf.toString()); + buf.setLength(0); + } + int remote = shownEntries; // now for posts in known blogs that we have and they don't entries.clear(); @@ -429,7 +570,7 @@ public class RemoteArchiveBean { } } - if (shownEntries > 0) // skip blogs we have already syndicated + if (shownEntries > remote) // skip blogs we have already syndicated out.write(buf.toString()); } @@ -477,6 +618,7 @@ public class RemoteArchiveBean { if (localNew > 0) { out.write("<input type=\"submit\" name=\"action\" value=\"Post selected entries\" /> \n"); } + out.write("<hr />\n"); } private final SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy/MM/dd", Locale.UK); private String getDate(long when) { diff --git a/apps/syndie/jsp/remote.jsp b/apps/syndie/jsp/remote.jsp index 4720222d0d..e77487197a 100644 --- a/apps/syndie/jsp/remote.jsp +++ b/apps/syndie/jsp/remote.jsp @@ -19,13 +19,14 @@ 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">I2P/TOR/Freenet</option> - <option value="mnet">MNet</option> - <option value="feedspace">Feedspace</option> - <option value="usenet">Usenet</option> + <option value="web" <%=("web".equals(request.getParameter("schema")) ? "selected=\"true\"" : "")%>>I2P/TOR/Freenet</option> + <option value="mnet" <%=("mnet".equals(request.getParameter("schema")) ? "selected=\"true\"" : "")%>>MNet</option> + <option value="feedspace" <%=("feedspace".equals(request.getParameter("schema")) ? "selected=\"true\"" : "")%>>Feedspace</option> + <option value="usenet" <%=("usenet".equals(request.getParameter("schema")) ? "selected=\"true\"" : "")%>>Usenet</option> </select> Proxy <input type="text" size="10" name="proxyhost" value="localhost" />:<input type="text" size="4" name="proxyport" value="4444" /> -<input name="location" size="40" /> <input type="submit" name="action" value="Continue..." /><br /> +<input name="location" size="40" value="<%=(request.getParameter("location") != null ? request.getParameter("location") : "")%>" /> +<input type="submit" name="action" value="Continue..." /><br /> <% String action = request.getParameter("action"); if ("Continue...".equals(action)) { @@ -33,9 +34,11 @@ Proxy <input type="text" size="10" name="proxyhost" value="localhost" />:<input } else if ("Fetch metadata".equals(action)) { remote.fetchMetadata(user, request.getParameterMap()); } else if ("Fetch selected entries".equals(action)) { - remote.fetchSelectedEntries(user, request.getParameterMap()); + //remote.fetchSelectedEntries(user, request.getParameterMap()); + remote.fetchSelectedBulk(user, request.getParameterMap()); } else if ("Fetch all new entries".equals(action)) { - remote.fetchAllEntries(user, request.getParameterMap()); + //remote.fetchAllEntries(user, request.getParameterMap()); + remote.fetchSelectedBulk(user, request.getParameterMap()); } else if ("Post selected entries".equals(action)) { remote.postSelectedEntries(user, request.getParameterMap()); } diff --git a/apps/syndie/jsp/web.xml b/apps/syndie/jsp/web.xml index 2251d21077..9eb60866af 100644 --- a/apps/syndie/jsp/web.xml +++ b/apps/syndie/jsp/web.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?> +<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd"> @@ -29,4 +29,4 @@ <welcome-file>index.html</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list> -</web-app> \ No newline at end of file +</web-app> diff --git a/core/java/src/net/i2p/util/LogManager.java b/core/java/src/net/i2p/util/LogManager.java index 48f76efd17..b0bd46e008 100644 --- a/core/java/src/net/i2p/util/LogManager.java +++ b/core/java/src/net/i2p/util/LogManager.java @@ -141,14 +141,17 @@ public class LogManager { public Log getLog(Class cls, String name) { Log rv = null; String scope = Log.getScope(name, cls); + boolean isNew = false; synchronized (_logs) { rv = (Log)_logs.get(scope); if (rv == null) { rv = new Log(this, cls, name); _logs.put(scope, rv); + isNew = true; } } - updateLimit(rv); + if (isNew) + updateLimit(rv); return rv; } public List getLogs() { diff --git a/history.txt b/history.txt index 6108993756..da1e137095 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,10 @@ -$Id: history.txt,v 1.229 2005/08/23 16:25:49 jrandom Exp $ +$Id: history.txt,v 1.230 2005/08/24 17:55:27 jrandom Exp $ + +2005-08-27 jrandom + * Minor logging and optimization tweaks in the router and SDK + * Use ISO-8859-1 in the XML files (thanks redzara!) + * The consolePassword config property can now be used to bypass the router + console's nonce checking, allowing CLI restarts 2005-08-24 jrandom * Catch errors with corrupt tunnel messages more gracefully (no need to diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index b002dc5e58..d4211e2511 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -15,9 +15,9 @@ import net.i2p.CoreVersion; * */ public class RouterVersion { - public final static String ID = "$Revision: 1.218 $ $Date: 2005/08/23 16:25:49 $"; + public final static String ID = "$Revision: 1.219 $ $Date: 2005/08/24 17:55:26 $"; public final static String VERSION = "0.6.0.3"; - public final static long BUILD = 2; + public final static long BUILD = 3; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION); System.out.println("Router ID: " + RouterVersion.ID); diff --git a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java index 42a6fcb880..c0f711cf8d 100644 --- a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java +++ b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java @@ -146,7 +146,7 @@ public class ClientConnectionRunner { /** current client's sessionId */ SessionId getSessionId() { return _sessionId; } - void setSessionId(SessionId id) { _sessionId = id; } + void setSessionId(SessionId id) { if (id != null) _sessionId = id; } /** data for the current leaseRequest, or null if there is no active leaseSet request */ LeaseRequestState getLeaseRequest() { return _leaseRequest; } void setLeaseRequest(LeaseRequestState req) { _leaseRequest = req; } diff --git a/router/java/src/net/i2p/router/message/GarlicMessageReceiver.java b/router/java/src/net/i2p/router/message/GarlicMessageReceiver.java index 0e3182b9f9..fcc5bbddf8 100644 --- a/router/java/src/net/i2p/router/message/GarlicMessageReceiver.java +++ b/router/java/src/net/i2p/router/message/GarlicMessageReceiver.java @@ -71,9 +71,6 @@ public class GarlicMessageReceiver { handleClove(clove); } } else { - if (_log.shouldLog(Log.ERROR)) - _log.error("CloveMessageParser failed to decrypt the message [" + message.getUniqueId() - + "]"); if (_log.shouldLog(Log.WARN)) _log.warn("CloveMessageParser failed to decrypt the message [" + message.getUniqueId() + "]", new Exception("Decrypt garlic failed")); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KBucketImpl.java b/router/java/src/net/i2p/router/networkdb/kademlia/KBucketImpl.java index 0b8d944850..8de3448375 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KBucketImpl.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KBucketImpl.java @@ -12,6 +12,8 @@ import java.math.BigInteger; import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import java.util.ArrayList; +import java.util.List; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; @@ -22,7 +24,7 @@ import net.i2p.util.RandomSource; class KBucketImpl implements KBucket { private Log _log; /** set of Hash objects for the peers in the kbucket */ - private Set _entries; + private List _entries; /** we center the kbucket set on the given hash, and derive distances from this */ private Hash _local; /** include if any bits equal or higher to this bit (in big endian order) */ @@ -34,7 +36,7 @@ class KBucketImpl implements KBucket { public KBucketImpl(I2PAppContext context, Hash local) { _context = context; _log = context.logManager().getLog(KBucketImpl.class); - _entries = new HashSet(); + _entries = new ArrayList(64); //new HashSet(); setLocal(local); } @@ -193,14 +195,16 @@ class KBucketImpl implements KBucket { public Set getEntries() { Set entries = new HashSet(64); synchronized (_entries) { - entries.addAll(_entries); + for (int i = 0; i < _entries.size(); i++) + entries.add((Hash)_entries.get(i)); } return entries; } public Set getEntries(Set toIgnoreHashes) { Set entries = new HashSet(64); synchronized (_entries) { - entries.addAll(_entries); + for (int i = 0; i < _entries.size(); i++) + entries.add((Hash)_entries.get(i)); entries.removeAll(toIgnoreHashes); } return entries; @@ -208,22 +212,26 @@ class KBucketImpl implements KBucket { public void getEntries(SelectionCollector collector) { synchronized (_entries) { - for (Iterator iter = _entries.iterator(); iter.hasNext(); ) { - collector.add((Hash)iter.next()); - } + for (int i = 0; i < _entries.size(); i++) + collector.add((Hash)_entries.get(i)); } } public void setEntries(Set entries) { synchronized (_entries) { _entries.clear(); - _entries.addAll(entries); + for (Iterator iter = entries.iterator(); iter.hasNext(); ) { + Hash entry = (Hash)iter.next(); + if (!_entries.contains(entry)) + _entries.add(entry); + } } } public int add(Hash peer) { synchronized (_entries) { - _entries.add(peer); + if (!_entries.contains(peer)) + _entries.add(peer); return _entries.size(); } } diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index f4d3481eba..628fcfc41e 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -807,7 +807,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { } /** smallest allowed period */ - private static final int MIN_PER_PEER_TIMEOUT = 1*1000; + private static final int MIN_PER_PEER_TIMEOUT = 2*1000; private static final int MAX_PER_PEER_TIMEOUT = 5*1000; public int getPeerTimeout(Hash peer) { diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java index 7504121be1..daba22e15b 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java @@ -84,7 +84,7 @@ public class RepublishLeaseSetJob extends JobImpl { public void runJob() { if (_log.shouldLog(Log.WARN)) _log.warn("FAILED publishing of the leaseSet for " + _dest.toBase64()); - RepublishLeaseSetJob.this.requeue(30*1000); + RepublishLeaseSetJob.this.requeue(getContext().random().nextInt(60*1000)); } } } diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java index 5ef07d1a60..de1fba4e01 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java @@ -39,8 +39,8 @@ class StoreJob extends JobImpl { private long _expiration; private PeerSelector _peerSelector; - private final static int PARALLELIZATION = 3; // how many sent at a time - private final static int REDUNDANCY = 6; // we want the data sent to 6 peers + private final static int PARALLELIZATION = 4; // how many sent at a time + private final static int REDUNDANCY = 4; // we want the data sent to 6 peers /** * additionally send to 1 outlier(s), in case all of the routers chosen in our * REDUNDANCY set are attacking us by accepting DbStore messages but dropping @@ -75,6 +75,7 @@ class StoreJob extends JobImpl { getContext().statManager().createRateStat("netDb.storePeers", "How many peers each netDb must be sent to before success?", "NetworkDatabase", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); getContext().statManager().createRateStat("netDb.storeFailedPeers", "How many peers each netDb must be sent to before failing completely?", "NetworkDatabase", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); getContext().statManager().createRateStat("netDb.ackTime", "How long does it take for a peer to ack a netDb store?", "NetworkDatabase", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + getContext().statManager().createRateStat("netDb.replyTimeout", "How long after a netDb send does the timeout expire (when the peer doesn't reply in time)?", "NetworkDatabase", new long[] { 60*1000, 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); _facade = facade; _state = new StoreState(getContext(), key, data, toSkip); _onSuccess = onSuccess; @@ -154,8 +155,15 @@ class StoreJob extends JobImpl { _state.addSkipped(peer); } else { int peerTimeout = _facade.getPeerTimeout(peer); - //RateStat failing = prof.getDBHistory().getFailedLookupRate(); - //Rate failed = failing.getRate(60*60*1000); + PeerProfile prof = getContext().profileOrganizer().getProfile(peer); + RateStat failing = prof.getDBHistory().getFailedLookupRate(); + Rate failed = failing.getRate(60*60*1000); + long failedCount = failed.getCurrentEventCount()+failed.getLastEventCount(); + if (failedCount > 10) { + _state.addSkipped(peer); + continue; + } + // //if (failed.getCurrentEventCount() + failed.getLastEventCount() > avg) { // _state.addSkipped(peer); //} @@ -250,7 +258,7 @@ class StoreJob extends JobImpl { _state.addPending(peer.getIdentity().getHash()); SendSuccessJob onReply = new SendSuccessJob(getContext(), peer); - FailedJob onFail = new FailedJob(getContext(), peer); + FailedJob onFail = new FailedJob(getContext(), peer, getContext().clock().now()); StoreMessageSelector selector = new StoreMessageSelector(getContext(), getJobId(), peer, token, expiration); TunnelInfo outTunnel = selectOutboundTunnel(); @@ -321,10 +329,12 @@ class StoreJob extends JobImpl { */ private class FailedJob extends JobImpl { private RouterInfo _peer; + private long _sendOn; - public FailedJob(RouterContext enclosingContext, RouterInfo peer) { + public FailedJob(RouterContext enclosingContext, RouterInfo peer, long sendOn) { super(enclosingContext); _peer = peer; + _sendOn = sendOn; } public void runJob() { if (_log.shouldLog(Log.WARN)) @@ -332,6 +342,7 @@ class StoreJob extends JobImpl { + " timed out sending " + _state.getTarget()); _state.replyTimeout(_peer.getIdentity().getHash()); getContext().profileManager().dbStoreFailed(_peer.getIdentity().getHash()); + getContext().statManager().addRateData("netDb.replyTimeout", getContext().clock().now() - _sendOn, 0); sendNext(); } diff --git a/router/java/src/net/i2p/router/transport/udp/MessageReceiver.java b/router/java/src/net/i2p/router/transport/udp/MessageReceiver.java index 1176cf024b..e8501086c6 100644 --- a/router/java/src/net/i2p/router/transport/udp/MessageReceiver.java +++ b/router/java/src/net/i2p/router/transport/udp/MessageReceiver.java @@ -113,8 +113,8 @@ public class MessageReceiver implements Runnable { m.setUniqueId(state.getMessageId()); return m; } catch (I2NPMessageException ime) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Message invalid: " + state, ime); + if (_log.shouldLog(Log.WARN)) + _log.warn("Message invalid: " + state, ime); return null; } catch (Exception e) { _log.log(Log.CRIT, "Error dealing with a message: " + state, e); diff --git a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java index 61d8a08497..9aefba1f39 100644 --- a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java +++ b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java @@ -148,8 +148,8 @@ public class FragmentHandler { //Hash v = _context.sha().calculateHash(preV, 0, validLength); boolean eq = DataHelper.eq(v.getData(), 0, preprocessed, offset + HopProcessor.IV_LENGTH, 4); if (!eq) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Corrupt tunnel message - verification fails: \n" + Base64.encode(preprocessed, offset+HopProcessor.IV_LENGTH, 4) + if (_log.shouldLog(Log.WARN)) + _log.warn("Corrupt tunnel message - verification fails: \n" + Base64.encode(preprocessed, offset+HopProcessor.IV_LENGTH, 4) + "\n" + Base64.encode(v.getData(), 0, 4)); if (_log.shouldLog(Log.WARN)) _log.warn("nomatching endpoint: # pad bytes: " + (paddingEnd-(HopProcessor.IV_LENGTH+4)-1) + "\n" -- GitLab