* i2psnark:

- Add refresh time option
    - Add public file permissions option (ticket #501)
    - Fix configuration of tunnel parameters (ticket #524)
    - Allow changing I2CP parameters while tunnel is open
    - Remove duplicated options in I2CP options string
    - Don't open tunnel when saving config
This commit is contained in:
zzz
2011-09-14 13:06:03 +00:00
parent bd06a44706
commit 87439e19ca
6 changed files with 257 additions and 112 deletions

View File

@ -44,34 +44,34 @@ import org.klomp.snark.dht.DHT;
* (but not multiple SnarkManagers, it is still static)
*/
public class I2PSnarkUtil {
private I2PAppContext _context;
private Log _log;
private final I2PAppContext _context;
private final Log _log;
private boolean _shouldProxy;
private String _proxyHost;
private int _proxyPort;
private String _i2cpHost;
private int _i2cpPort;
private Map<String, String> _opts;
private final Map<String, String> _opts;
private I2PSocketManager _manager;
private boolean _configured;
private final Set<Hash> _shitlist;
private int _maxUploaders;
private int _maxUpBW;
private int _maxConnections;
private File _tmpDir;
private final File _tmpDir;
private int _startupDelay;
private boolean _shouldUseOT;
private boolean _areFilesPublic;
private String _openTrackerString;
private DHT _dht;
public static final int DEFAULT_STARTUP_DELAY = 3;
public static final String PROP_USE_OPENTRACKERS = "i2psnark.useOpentrackers";
public static final boolean DEFAULT_USE_OPENTRACKERS = true;
public static final String PROP_OPENTRACKERS = "i2psnark.opentrackers";
public static final String DEFAULT_OPENTRACKERS = "http://tracker.welterde.i2p/a";
public static final int DEFAULT_MAX_UP_BW = 8; //KBps
public static final int MAX_CONNECTIONS = 16; // per torrent
private static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond";
public static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond";
//private static final boolean ENABLE_DHT = true;
public I2PSnarkUtil(I2PAppContext ctx) {
@ -125,6 +125,8 @@ public class I2PSnarkUtil {
// can't remove any options this way...
if (opts != null)
_opts.putAll(opts);
// this updates the session options and tells the router
setMaxUpBW(_maxUpBW);
_configured = true;
}
@ -134,6 +136,7 @@ public class I2PSnarkUtil {
}
/**
* This updates the session options and tells the router
* @param limit KBps
*/
public void setMaxUpBW(int limit) {
@ -175,6 +178,12 @@ public class I2PSnarkUtil {
public int getMaxConnections() { return _maxConnections; }
public int getStartupDelay() { return _startupDelay; }
/** @since 0.8.9 */
public boolean getFilesPublic() { return _areFilesPublic; }
/** @since 0.8.9 */
public void setFilesPublic(boolean yes) { _areFilesPublic = yes; }
/**
* Connect to the router, if we aren't already
*/
@ -433,14 +442,13 @@ public class I2PSnarkUtil {
/** @param ot non-null */
public void setOpenTrackerString(String ot) {
_opts.put(PROP_OPENTRACKERS, ot);
_openTrackerString = ot;
}
public String getOpenTrackerString() {
String rv = (String) _opts.get(PROP_OPENTRACKERS);
if (rv == null)
if (_openTrackerString == null)
return DEFAULT_OPENTRACKERS;
return rv;
return _openTrackerString;
}
/** comma delimited list open trackers to use as backups */

View File

@ -3,6 +3,7 @@ package org.klomp.snark;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStream;
@ -71,17 +72,23 @@ public class SnarkManager implements Snark.CompleteListener {
public static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet.";
private static final String CONFIG_FILE = "i2psnark.config";
public static final String PROP_FILES_PUBLIC = "i2psnark.filesPublic";
public static final String PROP_AUTO_START = "i2snark.autoStart"; // oops
public static final String DEFAULT_AUTO_START = "false";
public static final String PROP_LINK_PREFIX = "i2psnark.linkPrefix";
public static final String DEFAULT_LINK_PREFIX = "file:///";
//public static final String PROP_LINK_PREFIX = "i2psnark.linkPrefix";
//public static final String DEFAULT_LINK_PREFIX = "file:///";
public static final String PROP_STARTUP_DELAY = "i2psnark.startupDelay";
public static final String PROP_REFRESH_DELAY = "i2psnark.refreshSeconds";
public static final String PROP_THEME = "i2psnark.theme";
public static final String DEFAULT_THEME = "ubergine";
private static final String PROP_USE_OPENTRACKERS = "i2psnark.useOpentrackers";
public static final String PROP_OPENTRACKERS = "i2psnark.opentrackers";
public static final int MIN_UP_BW = 2;
public static final int DEFAULT_MAX_UP_BW = 10;
public static final int DEFAULT_STARTUP_DELAY = 3;
public static final int DEFAULT_REFRESH_DELAY_SECS = 60;
private SnarkManager() {
_snarks = new ConcurrentHashMap();
_magnets = new ConcurrentHashSet();
@ -136,20 +143,57 @@ public class SnarkManager implements Snark.CompleteListener {
}
}
public boolean shouldAutoStart() {
return Boolean.valueOf(_config.getProperty(PROP_AUTO_START, DEFAULT_AUTO_START+"")).booleanValue();
/**
* @return default false
* @since 0.8.9
*/
public boolean areFilesPublic() {
return Boolean.valueOf(_config.getProperty(PROP_FILES_PUBLIC)).booleanValue();
}
public boolean shouldAutoStart() {
return Boolean.valueOf(_config.getProperty(PROP_AUTO_START, DEFAULT_AUTO_START)).booleanValue();
}
/****
public String linkPrefix() {
return _config.getProperty(PROP_LINK_PREFIX, DEFAULT_LINK_PREFIX + getDataDir().getAbsolutePath() + File.separatorChar);
}
private int getStartupDelayMinutes() {
return Integer.valueOf(_config.getProperty(PROP_STARTUP_DELAY)).intValue();
****/
/**
* @return -1 for never
* @since 0.8.9
*/
public int getRefreshDelaySeconds() {
try {
return Integer.parseInt(_config.getProperty(PROP_REFRESH_DELAY));
} catch (NumberFormatException nfe) {
return DEFAULT_REFRESH_DELAY_SECS;
}
}
private int getStartupDelayMinutes() {
try {
return Integer.parseInt(_config.getProperty(PROP_STARTUP_DELAY));
} catch (NumberFormatException nfe) {
return DEFAULT_STARTUP_DELAY;
}
}
public File getDataDir() {
String dir = _config.getProperty(PROP_DIR, "i2psnark");
File f = new SecureDirectory(dir);
if (!f.isAbsolute())
f = new SecureDirectory(_context.getAppDir(), dir);
File f;
if (areFilesPublic())
f = new File(dir);
else
f = new SecureDirectory(dir);
if (!f.isAbsolute()) {
if (areFilesPublic())
f = new File(_context.getAppDir(), dir);
else
f = new SecureDirectory(_context.getAppDir(), dir);
}
return f;
}
@ -187,8 +231,10 @@ public class SnarkManager implements Snark.CompleteListener {
_config.setProperty(PROP_DIR, "i2psnark");
if (!_config.containsKey(PROP_AUTO_START))
_config.setProperty(PROP_AUTO_START, DEFAULT_AUTO_START);
if (!_config.containsKey(PROP_REFRESH_DELAY))
_config.setProperty(PROP_REFRESH_DELAY, Integer.toString(DEFAULT_REFRESH_DELAY_SECS));
if (!_config.containsKey(PROP_STARTUP_DELAY))
_config.setProperty(PROP_STARTUP_DELAY, "" + DEFAULT_STARTUP_DELAY);
_config.setProperty(PROP_STARTUP_DELAY, Integer.toString(DEFAULT_STARTUP_DELAY));
if (!_config.containsKey(PROP_THEME))
_config.setProperty(PROP_THEME, DEFAULT_THEME);
updateConfig();
@ -258,10 +304,11 @@ public class SnarkManager implements Snark.CompleteListener {
_util.setMaxUploaders(getInt(PROP_UPLOADERS_TOTAL, Snark.MAX_TOTAL_UPLOADERS));
_util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW));
_util.setStartupDelay(getInt(PROP_STARTUP_DELAY, DEFAULT_STARTUP_DELAY));
String ot = _config.getProperty(I2PSnarkUtil.PROP_OPENTRACKERS);
_util.setFilesPublic(areFilesPublic());
String ot = _config.getProperty(PROP_OPENTRACKERS);
if (ot != null)
_util.setOpenTrackerString(ot);
String useOT = _config.getProperty(I2PSnarkUtil.PROP_USE_OPENTRACKERS);
String useOT = _config.getProperty(PROP_USE_OPENTRACKERS);
boolean bOT = useOT == null || Boolean.valueOf(useOT).booleanValue();
_util.setUseOpenTrackers(bOT);
getDataDir().mkdirs();
@ -278,7 +325,8 @@ public class SnarkManager implements Snark.CompleteListener {
return defaultVal;
}
public void updateConfig(String dataDir, boolean autoStart, String startDelay, String seedPct, String eepHost,
public void updateConfig(String dataDir, boolean filesPublic, boolean autoStart, String refreshDelay,
String startDelay, String seedPct, String eepHost,
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
String upLimit, String upBW, boolean useOpenTrackers, String openTrackers, String theme) {
boolean changed = false;
@ -333,19 +381,34 @@ public class SnarkManager implements Snark.CompleteListener {
changed = true;
_config.setProperty(PROP_STARTUP_DELAY, "" + minutes);
addMessage(_("Startup delay changed to {0}", DataHelper.formatDuration2(minutes * 60 * 1000)));
}
}
}
// FIXME do this even if == null
if (i2cpHost != null) {
if (refreshDelay != null) {
try {
int secs = Integer.parseInt(refreshDelay);
if (secs != getRefreshDelaySeconds()) {
changed = true;
_config.setProperty(PROP_REFRESH_DELAY, refreshDelay);
if (secs >= 0)
addMessage(_("Refresh time changed to {0}", DataHelper.formatDuration2(secs * 1000)));
else
addMessage(_("Refresh disabled"));
}
} catch (NumberFormatException nfe) {}
}
// Start of I2CP stuff.
// i2cpHost will generally be null since it is hidden from the form if in router context.
int oldI2CPPort = _util.getI2CPPort();
String oldI2CPHost = _util.getI2CPHost();
int port = oldI2CPPort;
if (i2cpPort != null) {
try { port = Integer.parseInt(i2cpPort); } catch (NumberFormatException nfe) {}
}
String host = oldI2CPHost;
Map opts = new HashMap();
Map<String, String> opts = new HashMap();
if (i2cpOpts == null) i2cpOpts = "";
StringTokenizer tok = new StringTokenizer(i2cpOpts, " \t\n");
while (tok.hasMoreTokens()) {
@ -354,7 +417,7 @@ public class SnarkManager implements Snark.CompleteListener {
if (split > 0)
opts.put(pair.substring(0, split), pair.substring(split+1));
}
Map oldOpts = new HashMap();
Map<String, String> oldOpts = new HashMap();
String oldI2CPOpts = _config.getProperty(PROP_I2CP_OPTS);
if (oldI2CPOpts == null) oldI2CPOpts = "";
tok = new StringTokenizer(oldI2CPOpts, " \t\n");
@ -365,37 +428,39 @@ public class SnarkManager implements Snark.CompleteListener {
oldOpts.put(pair.substring(0, split), pair.substring(split+1));
}
if ( (i2cpHost.trim().length() > 0) && (port > 0) &&
((!host.equals(i2cpHost) ||
(port != _util.getI2CPPort()) ||
(!oldOpts.equals(opts)))) ) {
boolean reconnect = i2cpHost != null && i2cpHost.trim().length() > 0 && port > 0 &&
(port != _util.getI2CPPort() || !oldI2CPHost.equals(i2cpHost));
if (reconnect || !oldOpts.equals(opts)) {
boolean snarksActive = false;
Set names = listTorrentFiles();
for (Iterator iter = names.iterator(); iter.hasNext(); ) {
Snark snark = getTorrent((String)iter.next());
if ( (snark != null) && (!snark.isStopped()) ) {
snarksActive = true;
break;
if (reconnect) {
for (Snark snark : _snarks.values()) {
if (!snark.isStopped()) {
snarksActive = true;
break;
}
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("i2cp host [" + i2cpHost + "] i2cp port " + port + " opts [" + opts
+ "] oldOpts [" + oldOpts + "]");
if (snarksActive) {
Properties p = new Properties();
p.putAll(opts);
_util.setI2CPConfig(i2cpHost, port, p);
_util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW));
addMessage(_("I2CP and tunnel changes will take effect after stopping all torrents"));
if (_log.shouldLog(Log.DEBUG))
_log.debug("i2cp host [" + i2cpHost + "] i2cp port " + port + " opts [" + opts
+ "] oldOpts [" + oldOpts + "]");
} else if (!reconnect) {
// The usual case, the other two are if not in router context
_config.setProperty(PROP_I2CP_OPTS, i2cpOpts.trim());
addMessage(_("I2CP options changed to {0}", i2cpOpts));
_util.setI2CPConfig(oldI2CPHost, oldI2CPPort, opts);
} else {
if (_util.connected()) {
_util.disconnect();
addMessage(_("Disconnecting old I2CP destination"));
}
Properties p = new Properties();
p.putAll(opts);
addMessage(_("I2CP settings changed to {0}", i2cpHost + ":" + port + " (" + i2cpOpts.trim() + ")"));
_util.setI2CPConfig(i2cpHost, port, p);
addMessage(_("I2CP settings changed to {0}", i2cpHost + ':' + port + ' ' + i2cpOpts));
_util.setI2CPConfig(i2cpHost, port, opts);
_util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW));
boolean ok = _util.connect();
if (!ok) {
@ -409,22 +474,29 @@ public class SnarkManager implements Snark.CompleteListener {
_config.setProperty(PROP_I2CP_HOST, i2cpHost.trim());
_config.setProperty(PROP_I2CP_PORT, "" + port);
_config.setProperty(PROP_I2CP_OPTS, i2cpOpts.trim());
changed = true;
// no PeerAcceptors/I2PServerSockets to deal with, since all snarks are inactive
for (Iterator iter = names.iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
Snark snark = getTorrent(name);
if (snark != null && snark.restartAcceptor()) {
for (Snark snark : _snarks.values()) {
if (snark.restartAcceptor()) {
addMessage(_("I2CP listener restarted for \"{0}\"", snark.getBaseName()));
}
}
}
}
changed = true;
}
} // reconnect || changed options
if (areFilesPublic() != filesPublic) {
_config.setProperty(PROP_FILES_PUBLIC, Boolean.toString(filesPublic));
_util.setFilesPublic(filesPublic);
if (filesPublic)
addMessage(_("New files will be publicly readable"));
else
addMessage(_("New files will not be publicly readable"));
changed = true;
}
if (shouldAutoStart() != autoStart) {
_config.setProperty(PROP_AUTO_START, autoStart + "");
_config.setProperty(PROP_AUTO_START, Boolean.toString(autoStart));
if (autoStart)
addMessage(_("Enabled autostart"));
else
@ -432,7 +504,7 @@ public class SnarkManager implements Snark.CompleteListener {
changed = true;
}
if (_util.shouldUseOpenTrackers() != useOpenTrackers) {
_config.setProperty(I2PSnarkUtil.PROP_USE_OPENTRACKERS, useOpenTrackers + "");
_config.setProperty(PROP_USE_OPENTRACKERS, useOpenTrackers + "");
if (useOpenTrackers)
addMessage(_("Enabled open trackers - torrent restart required to take effect."));
else
@ -442,7 +514,7 @@ public class SnarkManager implements Snark.CompleteListener {
}
if (openTrackers != null) {
if (openTrackers.trim().length() > 0 && !openTrackers.trim().equals(_util.getOpenTrackerString())) {
_config.setProperty(I2PSnarkUtil.PROP_OPENTRACKERS, openTrackers.trim());
_config.setProperty(PROP_OPENTRACKERS, openTrackers.trim());
_util.setOpenTrackerString(openTrackers);
addMessage(_("Open Tracker list changed - torrent restart required to take effect."));
changed = true;
@ -489,7 +561,7 @@ public class SnarkManager implements Snark.CompleteListener {
* Grab the torrent given the (canonical) filename of the .torrent file
* @return Snark or null
*/
public Snark getTorrent(String filename) { synchronized (_snarks) { return (Snark)_snarks.get(filename); } }
public Snark getTorrent(String filename) { synchronized (_snarks) { return _snarks.get(filename); } }
/**
* Grab the torrent given the base name of the storage
@ -723,7 +795,7 @@ public class SnarkManager implements Snark.CompleteListener {
// so addTorrent won't recheck
saveTorrentStatus(metainfo, bitfield, null); // no file priorities
try {
locked_writeMetaInfo(metainfo, filename);
locked_writeMetaInfo(metainfo, filename, areFilesPublic());
// hold the lock for a long time
addTorrent(filename, dontAutoStart);
} catch (IOException ioe) {
@ -754,7 +826,8 @@ public class SnarkManager implements Snark.CompleteListener {
_log.error("Failed to write torrent file to " + filename);
return;
}
SecureFileOutputStream.setPerms(new File(filename));
if (!areFilesPublic())
SecureFileOutputStream.setPerms(new File(filename));
// hold the lock for a long time
addTorrent(filename);
}
@ -769,13 +842,16 @@ public class SnarkManager implements Snark.CompleteListener {
* Must be a filesystem-safe name.
* @since 0.8.4
*/
private static void locked_writeMetaInfo(MetaInfo metainfo, String filename) throws IOException {
private static void locked_writeMetaInfo(MetaInfo metainfo, String filename, boolean areFilesPublic) throws IOException {
File file = new File(filename);
if (file.exists())
throw new IOException("Cannot overwrite an existing .torrent file: " + file.getPath());
OutputStream out = null;
try {
out = new SecureFileOutputStream(filename);
if (areFilesPublic)
out = new FileOutputStream(filename);
else
out = new SecureFileOutputStream(filename);
out.write(metainfo.getTorrentData());
} catch (IOException ioe) {
// remove any partial
@ -1170,7 +1246,7 @@ public class SnarkManager implements Snark.CompleteListener {
if (announce != null)
meta = meta.reannounce(announce);
synchronized (_snarks) {
locked_writeMetaInfo(meta, name);
locked_writeMetaInfo(meta, name, areFilesPublic());
// put it in the list under the new name
_snarks.remove(snark.getName());
_snarks.put(name, snark);

View File

@ -48,7 +48,7 @@ public class Storage
private int[] priorities;
private final StorageListener listener;
private I2PSnarkUtil _util;
private final I2PSnarkUtil _util;
private /* FIXME final FIXME */ BitField bitfield; // BitField to represent the pieces
private int needed; // Number of pieces needed
@ -433,7 +433,12 @@ public class Storage
/** use a saved bitfield and timestamp from a config file */
public void check(String rootDir, long savedTime, BitField savedBitField) throws IOException
{
File base = new SecureFile(rootDir, filterName(metainfo.getName()));
File base;
boolean areFilesPublic = _util.getFilesPublic();
if (areFilesPublic)
base = new File(rootDir, filterName(metainfo.getName()));
else
base = new SecureFile(rootDir, filterName(metainfo.getName()));
boolean useSavedBitField = savedTime > 0 && savedBitField != null;
List<List<String>> files = metainfo.getFiles();
@ -479,7 +484,7 @@ public class Storage
for (int i = 0; i < size; i++)
{
List<String> path = files.get(i);
File f = createFileFromNames(base, path);
File f = createFileFromNames(base, path, areFilesPublic);
// dup file name check after filtering
for (int j = 0; j < i; j++) {
if (f.equals(RAFfile[j])) {
@ -495,7 +500,7 @@ public class Storage
else
lastPath = '_' + lastPath;
path.set(last, lastPath);
f = createFileFromNames(base, path);
f = createFileFromNames(base, path, areFilesPublic);
j = 0;
}
}
@ -585,7 +590,7 @@ public class Storage
* things going in the wrong place if there are duplicates
* in intermediate path elements after filtering.
*/
private static File createFileFromNames(File base, List<String> names) throws IOException
private static File createFileFromNames(File base, List<String> names, boolean areFilesPublic) throws IOException
{
File f = null;
Iterator<String> it = names.iterator();
@ -595,7 +600,10 @@ public class Storage
if (it.hasNext())
{
// Another dir in the hierarchy.
f = new File(base, name);
if (areFilesPublic)
f = new File(base, name);
else
f = new SecureFile(base, name);
if (!f.mkdir() && !f.isDirectory())
throw new IOException("Could not create directory " + f);
base = f;
@ -603,7 +611,10 @@ public class Storage
else
{
// The final element (file) in the hierarchy.
f = new SecureFile(base, name);
if (areFilesPublic)
f = new File(base, name);
else
f = new SecureFile(base, name);
if (!f.createNewFile() && !f.exists())
throw new IOException("Could not create file " + f);
}

View File

@ -33,7 +33,6 @@ import net.i2p.data.DataHelper;
import net.i2p.util.FileUtil;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.SecureFileOutputStream;
import org.klomp.snark.I2PSnarkUtil;
import org.klomp.snark.MetaInfo;
@ -208,8 +207,11 @@ public class I2PSnarkServlet extends Default {
out.write("</title>\n");
// we want it to go to the base URI so we don't refresh with some funky action= value
if (!isConfigure)
out.write("<meta http-equiv=\"refresh\" content=\"60;/i2psnark/" + peerString + "\">\n");
if (!isConfigure) {
int delay = _manager.getRefreshDelaySeconds();
if (delay > 0)
out.write("<meta http-equiv=\"refresh\" content=\"" + delay + ";/i2psnark/" + peerString + "\">\n");
}
out.write(HEADER_A + _themePath + HEADER_B);
out.write("</head><body>");
out.write("<center>");
@ -611,6 +613,7 @@ public class I2PSnarkServlet extends Default {
}
} else if ("Save".equals(action)) {
String dataDir = req.getParameter("dataDir");
boolean filesPublic = req.getParameter("filesPublic") != null;
boolean autoStart = req.getParameter("autoStart") != null;
String seedPct = req.getParameter("seedPct");
String eepHost = req.getParameter("eepHost");
@ -620,11 +623,14 @@ public class I2PSnarkServlet extends Default {
String i2cpOpts = buildI2CPOpts(req);
String upLimit = req.getParameter("upLimit");
String upBW = req.getParameter("upBW");
String refreshDel = req.getParameter("refreshDelay");
String startupDel = req.getParameter("startupDelay");
boolean useOpenTrackers = req.getParameter("useOpenTrackers") != null;
String openTrackers = req.getParameter("openTrackers");
String theme = req.getParameter("theme");
_manager.updateConfig(dataDir, autoStart, startupDel, seedPct, eepHost, eepPort, i2cpHost, i2cpPort, i2cpOpts, upLimit, upBW, useOpenTrackers, openTrackers, theme);
_manager.updateConfig(dataDir, filesPublic, autoStart, refreshDel, startupDel,
seedPct, eepHost, eepPort, i2cpHost, i2cpPort, i2cpOpts,
upLimit, upBW, useOpenTrackers, openTrackers, theme);
} else if ("Create".equals(action)) {
String baseData = req.getParameter("baseFile");
if (baseData != null && baseData.trim().length() > 0) {
@ -1259,44 +1265,55 @@ public class I2PSnarkServlet extends Default {
out.write("&nbsp;<input type=\"text\" name=\"announceURLOther\" size=\"57\" value=\"http://\" " +
"title=\"");
out.write(_("Specify custom tracker announce URL"));
out.write("\" > ");
out.write("<input type=\"submit\" value=\"");
out.write("\" > " +
"<input type=\"submit\" value=\"");
out.write(_("Create torrent"));
out.write("\" name=\"foo\" ></table>\n");
out.write("</form></div></div>");
out.write("\" name=\"foo\" ></table>\n" +
"</form></div></div>");
}
private static final int[] times = { 30, 60, 2*60, 5*60, 10*60, 30*60, -1 };
private void writeConfigForm(PrintWriter out, HttpServletRequest req) throws IOException {
String dataDir = _manager.getDataDir().getAbsolutePath();
boolean filesPublic = _manager.areFilesPublic();
boolean autoStart = _manager.shouldAutoStart();
boolean useOpenTrackers = _manager.util().shouldUseOpenTrackers();
String openTrackers = _manager.util().getOpenTrackerString();
//int seedPct = 0;
out.write("<form action=\"/i2psnark/configure\" method=\"POST\">\n");
out.write("<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n");
out.write("<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n");
out.write("<input type=\"hidden\" name=\"action\" value=\"Save\" >\n");
out.write("<span class=\"snarkConfigTitle\">");
out.write("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> ");
out.write("<form action=\"/i2psnark/configure\" method=\"POST\">\n" +
"<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n" +
"<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n" +
"<input type=\"hidden\" name=\"action\" value=\"Save\" >\n" +
"<span class=\"snarkConfigTitle\">" +
"<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> ");
out.write(_("Configuration"));
out.write("</span><hr>\n");
out.write("<table border=\"0\"><tr><td>");
out.write("</span><hr>\n" +
"<table border=\"0\"><tr><td>");
out.write(_("Data directory"));
out.write(": <td><code>" + dataDir + "</code> <i>(");
out.write(_("Edit i2psnark.config and restart to change"));
out.write(")</i><br>\n");
out.write(")</i><br>\n" +
out.write("<tr><td>");
"<tr><td>");
out.write(_("Files readable by all"));
out.write(": <td><input type=\"checkbox\" class=\"optbox\" name=\"filesPublic\" value=\"true\" "
+ (filesPublic ? "checked " : "")
+ "title=\"");
out.write(_("If checked, other users may access the downloaded files"));
out.write("\" >" +
"<tr><td>");
out.write(_("Auto start"));
out.write(": <td><input type=\"checkbox\" class=\"optbox\" name=\"autoStart\" value=\"true\" "
+ (autoStart ? "checked " : "")
+ "title=\"");
out.write(_("If checked, automatically start torrents that are added"));
out.write("\" >");
out.write("\" >" +
out.write("<tr><td>");
"<tr><td>");
out.write(_("Theme"));
out.write(": <td><select name='theme'>");
String theme = _manager.getTheme();
@ -1307,9 +1324,28 @@ public class I2PSnarkServlet extends Default {
else
out.write("\n<OPTION value=\"" + themes[i] + "\">" + themes[i]);
}
out.write("</select>\n");
out.write("</select>\n" +
out.write("<tr><td>");
"<tr><td>");
out.write(_("Refresh time"));
out.write(": <td><select name=\"refreshDelay\">");
int delay = _manager.getRefreshDelaySeconds();
for (int i = 0; i < times.length; i++) {
out.write("<option value=\"");
out.write(Integer.toString(times[i]));
out.write("\"");
if (times[i] == delay)
out.write(" selected=\"true\"");
out.write(">");
if (times[i] > 0)
out.write(DataHelper.formatDuration2(times[i] * 1000));
else
out.write(_("Never"));
out.write("</option>\n");
}
out.write("</select><br>" +
"<tr><td>");
out.write(_("Startup delay"));
out.write(": <td><input name=\"startupDelay\" size=\"3\" class=\"r\" value=\"" + _manager.util().getStartupDelay() + "\"> ");
out.write(_("minutes"));
@ -1340,26 +1376,26 @@ public class I2PSnarkServlet extends Default {
out.write(": <td><input type=\"text\" name=\"upLimit\" class=\"r\" value=\""
+ _manager.util().getMaxUploaders() + "\" size=\"3\" maxlength=\"3\" > ");
out.write(_("peers"));
out.write("<br>\n");
out.write("<br>\n" +
out.write("<tr><td>");
"<tr><td>");
out.write(_("Up bandwidth limit"));
out.write(": <td><input type=\"text\" name=\"upBW\" class=\"r\" value=\""
+ _manager.util().getMaxUpBW() + "\" size=\"3\" maxlength=\"3\" > KBps <i>(");
out.write(_("Half available bandwidth recommended."));
out.write(" <a href=\"/config.jsp\" target=\"blank\">");
out.write(_("View or change router bandwidth"));
out.write("</a>)</i><br>\n");
out.write("</a>)</i><br>\n" +
out.write("<tr><td>");
"<tr><td>");
out.write(_("Use open trackers also"));
out.write(": <td><input type=\"checkbox\" class=\"optbox\" name=\"useOpenTrackers\" value=\"true\" "
+ (useOpenTrackers ? "checked " : "")
+ "title=\"");
out.write(_("If checked, announce torrents to open trackers as well as the tracker listed in the torrent file"));
out.write("\" > ");
out.write("\" > " +
out.write("<tr><td>");
"<tr><td>");
out.write(_("Open tracker announce URLs"));
out.write(": <td><input type=\"text\" name=\"openTrackers\" value=\""
+ openTrackers + "\" size=\"50\" ><br>\n");
@ -1388,36 +1424,38 @@ public class I2PSnarkServlet extends Default {
out.write("<tr><td>");
out.write(_("I2CP host"));
out.write(": <td><input type=\"text\" name=\"i2cpHost\" value=\""
+ _manager.util().getI2CPHost() + "\" size=\"15\" > ");
+ _manager.util().getI2CPHost() + "\" size=\"15\" > " +
out.write("<tr><td>");
"<tr><td>");
out.write(_("I2CP port"));
out.write(": <td><input type=\"text\" name=\"i2cpPort\" class=\"r\" value=\"" +
+ _manager.util().getI2CPPort() + "\" size=\"5\" maxlength=\"5\" > <br>\n");
}
options.remove(I2PSnarkUtil.PROP_MAX_BW);
// was accidentally in the I2CP options prior to 0.8.9 so it will be in old config files
options.remove(SnarkManager.PROP_OPENTRACKERS);
StringBuilder opts = new StringBuilder(64);
for (Iterator iter = options.entrySet().iterator(); iter.hasNext(); ) {
Map.Entry entry = (Map.Entry)iter.next();
String key = (String)entry.getKey();
String val = (String)entry.getValue();
for (Map.Entry<String, String> e : options.entrySet()) {
String key = e.getKey();
String val = e.getValue();
opts.append(key).append('=').append(val).append(' ');
}
out.write("<tr><td>");
out.write(_("I2CP options"));
out.write(": <td><textarea name=\"i2cpOpts\" cols=\"60\" rows=\"1\" wrap=\"off\" spellcheck=\"false\" >"
+ opts.toString() + "</textarea><br>\n");
+ opts.toString() + "</textarea><br>\n" +
out.write("<tr><td>&nbsp;<td><input type=\"submit\" value=\"");
"<tr><td>&nbsp;<td><input type=\"submit\" value=\"");
out.write(_("Save configuration"));
out.write("\" name=\"foo\" >\n");
out.write("</table></div></div></form>");
out.write("\" name=\"foo\" >\n" +
"</table></div></div></form>");
}
private void writeConfigLink(PrintWriter out) throws IOException {
out.write("<div class=\"configsection\"><span class=\"snarkConfig\">\n");
out.write("<span class=\"snarkConfigTitle\"><a href=\"configure\">");
out.write("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> ");
out.write("<div class=\"configsection\"><span class=\"snarkConfig\">\n" +
"<span class=\"snarkConfigTitle\"><a href=\"configure\">" +
"<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> ");
out.write(_("Configuration"));
out.write("</a></span></span></div>\n");
}

View File

@ -1,3 +1,15 @@
2011-09-14 zzz
* Console: Verify valid host/IP before saving on net config form
* i2psnark:
- Add refresh time option
- Add public file permissions option (ticket #501)
- Fix configuration of tunnel parameters (ticket #524)
- Allow changing I2CP parameters while tunnel is open
- Remove duplicated options in I2CP options string
- Don't open tunnel when saving config
* IRC DCC: Fix conn limit options
* Router: Set permissions on router.ping file
2011-09-13 kytv
* Update i2prouter script

View File

@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 17;
public final static long BUILD = 18;
/** for example "-test" */
public final static String EXTRA = "";