forked from I2P_Developers/i2p.i2p
Reseed:
- Add form to manually reseed from zip or su3 URL (result status not yet working) - Add form to manually reseed from local zip or su3 file (not yet working, needs multipart/form-date moved from susimail) - Add form to create reseed zip file to share (working) - Backend support and refactoring in reseed code
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.router.networkdb.reseed.Reseeder;
|
||||
|
||||
/**
|
||||
@@ -25,11 +31,72 @@ public class ConfigReseedHandler extends FormHandler {
|
||||
// skip the nonce checking in ReseedHandler
|
||||
addFormNotice(_("Starting reseed process"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (_action.equals(_("Save changes"))) {
|
||||
} else if (_action.equals(_("Reseed from URL"))) {
|
||||
String val = getJettyString("url");
|
||||
if (val != null)
|
||||
val = val.trim();
|
||||
if (val == null || val.length() == 0) {
|
||||
addFormError(_("You must enter a URL"));
|
||||
return;
|
||||
}
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(val);
|
||||
} catch (MalformedURLException mue) {
|
||||
addFormError(_("Bad URL {0}", val));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!_context.netDb().reseedChecker().requestReseed(url)) {
|
||||
addFormError(_("Reseeding is already in progress"));
|
||||
} else {
|
||||
// wait a while for completion but not forever
|
||||
for (int i = 0; i < 20; i++) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
if (!_context.netDb().reseedChecker().inProgress()) {
|
||||
String status = _context.netDb().reseedChecker().getStatus();
|
||||
if (status.length() > 0)
|
||||
addFormNotice(status);
|
||||
else
|
||||
addFormNotice(_("Ressed complete, check summary bar for status"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (_context.netDb().reseedChecker().inProgress()) {
|
||||
String status = _context.netDb().reseedChecker().getStatus();
|
||||
if (status.length() > 0)
|
||||
addFormNotice(status);
|
||||
else
|
||||
addFormNotice(_("Ressed in progress, check summary bar for status"));
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException iae) {
|
||||
addFormError(_("Bad URL {0}", val) + " - " + iae.getMessage());
|
||||
}
|
||||
} else if (_action.equals(_("Reseed from file"))) {
|
||||
//////// FIXME multipart
|
||||
String val = getJettyString("file");
|
||||
if (val == null || val.length() == 0) {
|
||||
addFormError(_("You must enter a file"));
|
||||
return;
|
||||
}
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(DataHelper.getASCII(val));
|
||||
try {
|
||||
int count = _context.netDb().reseedChecker().requestReseed(bais);
|
||||
if (count <= 0) {
|
||||
addFormError(_("Reseed from file failed"));
|
||||
} else {
|
||||
addFormNotice(ngettext("Reseed successful, loaded {0} router info from file",
|
||||
"Reseed successful, loaded {0} router infos from file",
|
||||
count));
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
addFormError(_("Reseed from file failed") + " - " + ioe);
|
||||
}
|
||||
} else if (_action.equals(_("Save changes"))) {
|
||||
saveChanges();
|
||||
return;
|
||||
}
|
||||
//addFormError(_("Unsupported") + ' ' + _action + '.');
|
||||
}
|
||||
@@ -84,4 +151,9 @@ public class ConfigReseedHandler extends FormHandler {
|
||||
else
|
||||
addFormError(_("Error saving the configuration (applied but not saved) - please see the error logs"));
|
||||
}
|
||||
|
||||
/** translate (ngettext) @since 0.9.19 */
|
||||
public String ngettext(String s, String p, int n) {
|
||||
return Messages.getString(n, s, p, _context);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,163 @@
|
||||
package net.i2p.router.web;
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by jrandom in 2003 and released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
* It probably won't make your computer catch on fire, or eat
|
||||
* your children, but it might. Use at your own risk.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.RouterContext;
|
||||
|
||||
/**
|
||||
* Copy a random selection of 'count' router infos from configDir/netDb
|
||||
* to 'toDir'. Skip your own router info, and old, hidden, unreachable, and
|
||||
* introduced routers, and those from bad countries.
|
||||
*
|
||||
* Much easier than the one in installer/tools since we have a running router.
|
||||
*
|
||||
* Caller must delete file when done.
|
||||
*
|
||||
* @since 0.9.19 modified from BundleRouterInfos in installer/tools
|
||||
*
|
||||
*/
|
||||
class ReseedBundler {
|
||||
|
||||
private final RouterContext _context;
|
||||
private final static String ROUTERINFO_PREFIX = "routerInfo-";
|
||||
private final static String ROUTERINFO_SUFFIX = ".dat";
|
||||
|
||||
public ReseedBundler(RouterContext ctx) {
|
||||
_context = ctx;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a zip file with
|
||||
* a random selection of 'count' router infos from configDir/netDb
|
||||
* to 'toDir'. Skip your own router info, and old, hidden, unreachable, and
|
||||
* introduced routers, and those from bad countries.
|
||||
*
|
||||
* The file will be in the temp directory. Caller must move or delete.
|
||||
*/
|
||||
public File createZip(int count) throws IOException {
|
||||
Hash me = _context.routerHash();
|
||||
int routerCount = 0;
|
||||
int copied = 0;
|
||||
long tooOld = System.currentTimeMillis() - 7*24*60*60*1000L;
|
||||
List<RouterInfo> infos = new ArrayList<RouterInfo>(_context.netDb().getRouters());
|
||||
// IP to router hash
|
||||
Map<String, Hash> ipMap = new HashMap<String, Hash>(count);
|
||||
List<RouterInfo> toWrite = new ArrayList<RouterInfo>(count);
|
||||
Collections.shuffle(infos);
|
||||
for (RouterInfo ri : infos) {
|
||||
if (copied >= count)
|
||||
break;
|
||||
Hash key = ri.getIdentity().calculateHash();
|
||||
if (key.equals(me)) {
|
||||
continue;
|
||||
}
|
||||
if (ri.getPublished() < tooOld)
|
||||
continue;
|
||||
if (ri.getCapabilities().contains("U"))
|
||||
continue;
|
||||
if (ri.getCapabilities().contains("K"))
|
||||
continue;
|
||||
Collection<RouterAddress> addrs = ri.getAddresses();
|
||||
if (addrs.isEmpty())
|
||||
continue;
|
||||
|
||||
String name = getRouterInfoName(key);
|
||||
boolean hasIntro = false;
|
||||
boolean hasIPv4 = false;
|
||||
boolean dupIP = false;
|
||||
for (RouterAddress addr : addrs) {
|
||||
if ("SSU".equals(addr.getTransportStyle()) && addr.getOption("ihost0") != null) {
|
||||
hasIntro = true;
|
||||
break;
|
||||
}
|
||||
String host = addr.getHost();
|
||||
if (host != null && host.contains(".")) {
|
||||
hasIPv4 = true;
|
||||
Hash old = ipMap.put(host, key);
|
||||
if (old != null && !old.equals(key)) {
|
||||
dupIP = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dupIP)
|
||||
continue;
|
||||
if (hasIntro)
|
||||
continue;
|
||||
if (!hasIPv4)
|
||||
continue;
|
||||
if (_context.commSystem().isInBadCountry(ri))
|
||||
continue;
|
||||
|
||||
toWrite.add(ri);
|
||||
copied++;
|
||||
}
|
||||
|
||||
if (toWrite.isEmpty())
|
||||
throw new IOException("No router infos to include");
|
||||
|
||||
File rv = new File(_context.getTempDir(), "genreseed-" + _context.random().nextInt() + ".zip");
|
||||
ZipOutputStream zip = null;
|
||||
try {
|
||||
zip = new ZipOutputStream(new FileOutputStream(rv) );
|
||||
for (RouterInfo ri : toWrite) {
|
||||
String name = getRouterInfoName(ri.getIdentity().calculateHash());
|
||||
ZipEntry entry = new ZipEntry(name);
|
||||
entry.setTime(ri.getPublished());
|
||||
zip.putNextEntry(entry);
|
||||
ri.writeBytes(zip);
|
||||
}
|
||||
} catch (DataFormatException dfe) {
|
||||
rv.delete();
|
||||
IOException ioe = new IOException(dfe.getMessage());
|
||||
ioe.initCause(dfe);
|
||||
throw ioe;
|
||||
} catch (IOException ioe) {
|
||||
rv.delete();
|
||||
throw ioe;
|
||||
} finally {
|
||||
if ( zip != null) {
|
||||
try {
|
||||
zip.finish();
|
||||
zip.close();
|
||||
} catch (IOException ioe) {
|
||||
rv.delete();
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied/modded from PersistentDataStore
|
||||
*/
|
||||
private static String getRouterInfoName(Hash hash) {
|
||||
String b64 = hash.toBase64();
|
||||
return ROUTERINFO_PREFIX + b64 + ROUTERINFO_SUFFIX;
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Handler to create a i2preseed.zip file
|
||||
* @since 0.9.19
|
||||
*/
|
||||
public class ReseedGenerator extends HelperBase {
|
||||
|
||||
public File createZip() throws IOException {
|
||||
ReseedBundler rb = new ReseedBundler(_context);
|
||||
return rb.createZip(100);
|
||||
}
|
||||
}
|
@@ -19,8 +19,40 @@
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigReseedHandler" id="formhandler" scope="request" />
|
||||
<%@include file="formhandler.jsi" %>
|
||||
|
||||
<div class="configure"><form action="" method="POST">
|
||||
<input type="hidden" name="nonce" value="<%=pageNonce%>" >
|
||||
<h3><%=intl._("Manual Reseed from URL")%></h3>
|
||||
<p><%=intl._("Enter zip or su3 URL")%> :
|
||||
<input name="url" type="text" size="60" value="" />
|
||||
</p>
|
||||
<div class="formaction">
|
||||
<input type="submit" name="action" class="download" value="<%=intl._("Reseed from URL")%>" />
|
||||
</div></form></div>
|
||||
|
||||
<div class="configure">
|
||||
<form action="" method="POST" enctype="multipart/form-data" >
|
||||
<input type="hidden" name="nonce" value="<%=pageNonce%>" >
|
||||
<h3><%=intl._("Manual Reseed from File")%></h3>
|
||||
<p><%=intl._("Select zip or su3 file")%> :
|
||||
<input name="file" type="file" value="" />
|
||||
</p>
|
||||
<div class="formaction">
|
||||
<input type="submit" name="action" class="download" value="<%=intl._("Reseed from file")%>" />
|
||||
</div></form></div>
|
||||
|
||||
<div class="configure">
|
||||
<form action="/createreseed" method="GET">
|
||||
<h3><%=intl._("Create Reseed File")%></h3>
|
||||
<p><%=intl._("Create a new reseed zip file you may share for others to reseed manually.")%>
|
||||
</p>
|
||||
<div class="formaction">
|
||||
<input type="submit" name="action" class="go" value="<%=intl._("Create reseed file")%>" />
|
||||
</div></form></div>
|
||||
|
||||
<div class="configure">
|
||||
<form action="" method="POST">
|
||||
<input type="hidden" name="nonce" value="<%=pageNonce%>" >
|
||||
<h3><%=intl._("Reseeding Configuration")%></h3>
|
||||
<p><%=intl._("Reseeding is the bootstrapping process used to find other routers when you first install I2P, or when your router has too few router references remaining.")%>
|
||||
<%=intl._("If reseeding has failed, you should first check your network connection.")%>
|
||||
@@ -71,7 +103,7 @@
|
||||
-->
|
||||
|
||||
</table></div>
|
||||
<hr><div class="formaction">
|
||||
<div class="formaction">
|
||||
<input type="submit" class="cancel" name="foo" value="<%=intl._("Cancel")%>" />
|
||||
<input type="submit" name="action" class="download" value="<%=intl._("Save changes and reseed now")%>" />
|
||||
<input type="submit" name="action" class="accept" value="<%=intl._("Save changes")%>" />
|
||||
|
45
apps/routerconsole/jsp/createreseed.jsp
Normal file
45
apps/routerconsole/jsp/createreseed.jsp
Normal file
@@ -0,0 +1,45 @@
|
||||
<jsp:useBean class="net.i2p.router.web.ReseedGenerator" id="gen" scope="request" /><jsp:setProperty name="gen" property="contextId" value="<%=(String)session.getAttribute(\"i2p.contextId\")%>" /><%
|
||||
/*
|
||||
* USE CAUTION WHEN EDITING
|
||||
* Trailing whitespace OR NEWLINE on the last line will cause
|
||||
* IllegalStateExceptions !!!
|
||||
*
|
||||
* Do not tag this file for translation.
|
||||
*/
|
||||
try {
|
||||
java.io.InputStream in = null;
|
||||
java.io.File zip = null;
|
||||
try {
|
||||
zip = gen.createZip();
|
||||
response.setContentLength((int) zip.length());
|
||||
long lastmod = zip.lastModified();
|
||||
if (lastmod > 0)
|
||||
response.setDateHeader("Last-Modified", lastmod);
|
||||
response.setDateHeader("Expires", 0);
|
||||
response.addHeader("Cache-Control", "no-store, max-age=0, no-cache, must-revalidate");
|
||||
response.addHeader("Pragma", "no-cache");
|
||||
response.setContentType("application/zip; name=\"i2preseed.zip\"");
|
||||
response.addHeader("Content-Disposition", "attachment; filename=\"i2preseed.zip\"");
|
||||
byte buf[] = new byte[16*1024];
|
||||
in = new java.io.FileInputStream(zip);
|
||||
int read = 0;
|
||||
java.io.OutputStream cout = response.getOutputStream();
|
||||
while ( (read = in.read(buf)) != -1) {
|
||||
cout.write(buf, 0, read);
|
||||
}
|
||||
} finally {
|
||||
if (in != null)
|
||||
try { in.close(); } catch (java.io.IOException ioe) {}
|
||||
if (zip != null)
|
||||
zip.delete();
|
||||
}
|
||||
} catch (java.io.IOException ioe) {
|
||||
// prevent 'Committed' IllegalStateException from Jetty
|
||||
if (!response.isCommitted()) {
|
||||
response.sendError(403, ioe.toString());
|
||||
} else {
|
||||
// Jetty doesn't log this
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
%>
|
Reference in New Issue
Block a user