* 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) * (but not multiple SnarkManagers, it is still static)
*/ */
public class I2PSnarkUtil { public class I2PSnarkUtil {
private I2PAppContext _context; private final I2PAppContext _context;
private Log _log; private final Log _log;
private boolean _shouldProxy; private boolean _shouldProxy;
private String _proxyHost; private String _proxyHost;
private int _proxyPort; private int _proxyPort;
private String _i2cpHost; private String _i2cpHost;
private int _i2cpPort; private int _i2cpPort;
private Map<String, String> _opts; private final Map<String, String> _opts;
private I2PSocketManager _manager; private I2PSocketManager _manager;
private boolean _configured; private boolean _configured;
private final Set<Hash> _shitlist; private final Set<Hash> _shitlist;
private int _maxUploaders; private int _maxUploaders;
private int _maxUpBW; private int _maxUpBW;
private int _maxConnections; private int _maxConnections;
private File _tmpDir; private final File _tmpDir;
private int _startupDelay; private int _startupDelay;
private boolean _shouldUseOT; private boolean _shouldUseOT;
private boolean _areFilesPublic;
private String _openTrackerString;
private DHT _dht; private DHT _dht;
public static final int DEFAULT_STARTUP_DELAY = 3; 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 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 String DEFAULT_OPENTRACKERS = "http://tracker.welterde.i2p/a";
public static final int DEFAULT_MAX_UP_BW = 8; //KBps public static final int DEFAULT_MAX_UP_BW = 8; //KBps
public static final int MAX_CONNECTIONS = 16; // per torrent 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; //private static final boolean ENABLE_DHT = true;
public I2PSnarkUtil(I2PAppContext ctx) { public I2PSnarkUtil(I2PAppContext ctx) {
@@ -125,6 +125,8 @@ public class I2PSnarkUtil {
// can't remove any options this way... // can't remove any options this way...
if (opts != null) if (opts != null)
_opts.putAll(opts); _opts.putAll(opts);
// this updates the session options and tells the router
setMaxUpBW(_maxUpBW);
_configured = true; _configured = true;
} }
@@ -134,6 +136,7 @@ public class I2PSnarkUtil {
} }
/** /**
* This updates the session options and tells the router
* @param limit KBps * @param limit KBps
*/ */
public void setMaxUpBW(int limit) { public void setMaxUpBW(int limit) {
@@ -175,6 +178,12 @@ public class I2PSnarkUtil {
public int getMaxConnections() { return _maxConnections; } public int getMaxConnections() { return _maxConnections; }
public int getStartupDelay() { return _startupDelay; } 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 * Connect to the router, if we aren't already
*/ */
@@ -433,14 +442,13 @@ public class I2PSnarkUtil {
/** @param ot non-null */ /** @param ot non-null */
public void setOpenTrackerString(String ot) { public void setOpenTrackerString(String ot) {
_opts.put(PROP_OPENTRACKERS, ot); _openTrackerString = ot;
} }
public String getOpenTrackerString() { public String getOpenTrackerString() {
String rv = (String) _opts.get(PROP_OPENTRACKERS); if (_openTrackerString == null)
if (rv == null)
return DEFAULT_OPENTRACKERS; return DEFAULT_OPENTRACKERS;
return rv; return _openTrackerString;
} }
/** comma delimited list open trackers to use as backups */ /** 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.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@@ -71,17 +72,23 @@ public class SnarkManager implements Snark.CompleteListener {
public static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet."; public static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet.";
private static final String CONFIG_FILE = "i2psnark.config"; 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 PROP_AUTO_START = "i2snark.autoStart"; // oops
public static final String DEFAULT_AUTO_START = "false"; public static final String DEFAULT_AUTO_START = "false";
public static final String PROP_LINK_PREFIX = "i2psnark.linkPrefix"; //public static final String PROP_LINK_PREFIX = "i2psnark.linkPrefix";
public static final String DEFAULT_LINK_PREFIX = "file:///"; //public static final String DEFAULT_LINK_PREFIX = "file:///";
public static final String PROP_STARTUP_DELAY = "i2psnark.startupDelay"; 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 PROP_THEME = "i2psnark.theme";
public static final String DEFAULT_THEME = "ubergine"; 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 MIN_UP_BW = 2;
public static final int DEFAULT_MAX_UP_BW = 10; public static final int DEFAULT_MAX_UP_BW = 10;
public static final int DEFAULT_STARTUP_DELAY = 3; public static final int DEFAULT_STARTUP_DELAY = 3;
public static final int DEFAULT_REFRESH_DELAY_SECS = 60;
private SnarkManager() { private SnarkManager() {
_snarks = new ConcurrentHashMap(); _snarks = new ConcurrentHashMap();
_magnets = new ConcurrentHashSet(); _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() { public String linkPrefix() {
return _config.getProperty(PROP_LINK_PREFIX, DEFAULT_LINK_PREFIX + getDataDir().getAbsolutePath() + File.separatorChar); 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() { public File getDataDir() {
String dir = _config.getProperty(PROP_DIR, "i2psnark"); String dir = _config.getProperty(PROP_DIR, "i2psnark");
File f = new SecureDirectory(dir); File f;
if (!f.isAbsolute()) if (areFilesPublic())
f = new SecureDirectory(_context.getAppDir(), dir); 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; return f;
} }
@@ -187,8 +231,10 @@ public class SnarkManager implements Snark.CompleteListener {
_config.setProperty(PROP_DIR, "i2psnark"); _config.setProperty(PROP_DIR, "i2psnark");
if (!_config.containsKey(PROP_AUTO_START)) if (!_config.containsKey(PROP_AUTO_START))
_config.setProperty(PROP_AUTO_START, DEFAULT_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)) 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)) if (!_config.containsKey(PROP_THEME))
_config.setProperty(PROP_THEME, DEFAULT_THEME); _config.setProperty(PROP_THEME, DEFAULT_THEME);
updateConfig(); updateConfig();
@@ -258,10 +304,11 @@ public class SnarkManager implements Snark.CompleteListener {
_util.setMaxUploaders(getInt(PROP_UPLOADERS_TOTAL, Snark.MAX_TOTAL_UPLOADERS)); _util.setMaxUploaders(getInt(PROP_UPLOADERS_TOTAL, Snark.MAX_TOTAL_UPLOADERS));
_util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW)); _util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW));
_util.setStartupDelay(getInt(PROP_STARTUP_DELAY, DEFAULT_STARTUP_DELAY)); _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) if (ot != null)
_util.setOpenTrackerString(ot); _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(); boolean bOT = useOT == null || Boolean.valueOf(useOT).booleanValue();
_util.setUseOpenTrackers(bOT); _util.setUseOpenTrackers(bOT);
getDataDir().mkdirs(); getDataDir().mkdirs();
@@ -278,7 +325,8 @@ public class SnarkManager implements Snark.CompleteListener {
return defaultVal; 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 eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
String upLimit, String upBW, boolean useOpenTrackers, String openTrackers, String theme) { String upLimit, String upBW, boolean useOpenTrackers, String openTrackers, String theme) {
boolean changed = false; boolean changed = false;
@@ -333,19 +381,34 @@ public class SnarkManager implements Snark.CompleteListener {
changed = true; changed = true;
_config.setProperty(PROP_STARTUP_DELAY, "" + minutes); _config.setProperty(PROP_STARTUP_DELAY, "" + minutes);
addMessage(_("Startup delay changed to {0}", DataHelper.formatDuration2(minutes * 60 * 1000))); 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(); int oldI2CPPort = _util.getI2CPPort();
String oldI2CPHost = _util.getI2CPHost(); String oldI2CPHost = _util.getI2CPHost();
int port = oldI2CPPort; int port = oldI2CPPort;
if (i2cpPort != null) { if (i2cpPort != null) {
try { port = Integer.parseInt(i2cpPort); } catch (NumberFormatException nfe) {} 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 = ""; if (i2cpOpts == null) i2cpOpts = "";
StringTokenizer tok = new StringTokenizer(i2cpOpts, " \t\n"); StringTokenizer tok = new StringTokenizer(i2cpOpts, " \t\n");
while (tok.hasMoreTokens()) { while (tok.hasMoreTokens()) {
@@ -354,7 +417,7 @@ public class SnarkManager implements Snark.CompleteListener {
if (split > 0) if (split > 0)
opts.put(pair.substring(0, split), pair.substring(split+1)); 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); String oldI2CPOpts = _config.getProperty(PROP_I2CP_OPTS);
if (oldI2CPOpts == null) oldI2CPOpts = ""; if (oldI2CPOpts == null) oldI2CPOpts = "";
tok = new StringTokenizer(oldI2CPOpts, " \t\n"); 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)); oldOpts.put(pair.substring(0, split), pair.substring(split+1));
} }
if ( (i2cpHost.trim().length() > 0) && (port > 0) && boolean reconnect = i2cpHost != null && i2cpHost.trim().length() > 0 && port > 0 &&
((!host.equals(i2cpHost) || (port != _util.getI2CPPort() || !oldI2CPHost.equals(i2cpHost));
(port != _util.getI2CPPort()) || if (reconnect || !oldOpts.equals(opts)) {
(!oldOpts.equals(opts)))) ) {
boolean snarksActive = false; boolean snarksActive = false;
Set names = listTorrentFiles(); if (reconnect) {
for (Iterator iter = names.iterator(); iter.hasNext(); ) { for (Snark snark : _snarks.values()) {
Snark snark = getTorrent((String)iter.next()); if (!snark.isStopped()) {
if ( (snark != null) && (!snark.isStopped()) ) { snarksActive = true;
snarksActive = true; break;
break; }
} }
} }
if (_log.shouldLog(Log.DEBUG))
_log.debug("i2cp host [" + i2cpHost + "] i2cp port " + port + " opts [" + opts
+ "] oldOpts [" + oldOpts + "]");
if (snarksActive) { if (snarksActive) {
Properties p = new Properties(); Properties p = new Properties();
p.putAll(opts); p.putAll(opts);
_util.setI2CPConfig(i2cpHost, port, p); _util.setI2CPConfig(i2cpHost, port, p);
_util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW)); _util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW));
addMessage(_("I2CP and tunnel changes will take effect after stopping all torrents")); addMessage(_("I2CP and tunnel changes will take effect after stopping all torrents"));
if (_log.shouldLog(Log.DEBUG)) } else if (!reconnect) {
_log.debug("i2cp host [" + i2cpHost + "] i2cp port " + port + " opts [" + opts // The usual case, the other two are if not in router context
+ "] oldOpts [" + oldOpts + "]"); _config.setProperty(PROP_I2CP_OPTS, i2cpOpts.trim());
addMessage(_("I2CP options changed to {0}", i2cpOpts));
_util.setI2CPConfig(oldI2CPHost, oldI2CPPort, opts);
} else { } else {
if (_util.connected()) { if (_util.connected()) {
_util.disconnect(); _util.disconnect();
addMessage(_("Disconnecting old I2CP destination")); addMessage(_("Disconnecting old I2CP destination"));
} }
Properties p = new Properties(); addMessage(_("I2CP settings changed to {0}", i2cpHost + ':' + port + ' ' + i2cpOpts));
p.putAll(opts); _util.setI2CPConfig(i2cpHost, port, opts);
addMessage(_("I2CP settings changed to {0}", i2cpHost + ":" + port + " (" + i2cpOpts.trim() + ")"));
_util.setI2CPConfig(i2cpHost, port, p);
_util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW)); _util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW));
boolean ok = _util.connect(); boolean ok = _util.connect();
if (!ok) { if (!ok) {
@@ -409,22 +474,29 @@ public class SnarkManager implements Snark.CompleteListener {
_config.setProperty(PROP_I2CP_HOST, i2cpHost.trim()); _config.setProperty(PROP_I2CP_HOST, i2cpHost.trim());
_config.setProperty(PROP_I2CP_PORT, "" + port); _config.setProperty(PROP_I2CP_PORT, "" + port);
_config.setProperty(PROP_I2CP_OPTS, i2cpOpts.trim()); _config.setProperty(PROP_I2CP_OPTS, i2cpOpts.trim());
changed = true;
// no PeerAcceptors/I2PServerSockets to deal with, since all snarks are inactive // no PeerAcceptors/I2PServerSockets to deal with, since all snarks are inactive
for (Iterator iter = names.iterator(); iter.hasNext(); ) { for (Snark snark : _snarks.values()) {
String name = (String)iter.next(); if (snark.restartAcceptor()) {
Snark snark = getTorrent(name);
if (snark != null && snark.restartAcceptor()) {
addMessage(_("I2CP listener restarted for \"{0}\"", snark.getBaseName())); addMessage(_("I2CP listener restarted for \"{0}\"", snark.getBaseName()));
} }
} }
} }
} }
changed = true; 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) { if (shouldAutoStart() != autoStart) {
_config.setProperty(PROP_AUTO_START, autoStart + ""); _config.setProperty(PROP_AUTO_START, Boolean.toString(autoStart));
if (autoStart) if (autoStart)
addMessage(_("Enabled autostart")); addMessage(_("Enabled autostart"));
else else
@@ -432,7 +504,7 @@ public class SnarkManager implements Snark.CompleteListener {
changed = true; changed = true;
} }
if (_util.shouldUseOpenTrackers() != useOpenTrackers) { if (_util.shouldUseOpenTrackers() != useOpenTrackers) {
_config.setProperty(I2PSnarkUtil.PROP_USE_OPENTRACKERS, useOpenTrackers + ""); _config.setProperty(PROP_USE_OPENTRACKERS, useOpenTrackers + "");
if (useOpenTrackers) if (useOpenTrackers)
addMessage(_("Enabled open trackers - torrent restart required to take effect.")); addMessage(_("Enabled open trackers - torrent restart required to take effect."));
else else
@@ -442,7 +514,7 @@ public class SnarkManager implements Snark.CompleteListener {
} }
if (openTrackers != null) { if (openTrackers != null) {
if (openTrackers.trim().length() > 0 && !openTrackers.trim().equals(_util.getOpenTrackerString())) { 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); _util.setOpenTrackerString(openTrackers);
addMessage(_("Open Tracker list changed - torrent restart required to take effect.")); addMessage(_("Open Tracker list changed - torrent restart required to take effect."));
changed = true; changed = true;
@@ -489,7 +561,7 @@ public class SnarkManager implements Snark.CompleteListener {
* Grab the torrent given the (canonical) filename of the .torrent file * Grab the torrent given the (canonical) filename of the .torrent file
* @return Snark or null * @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 * 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 // so addTorrent won't recheck
saveTorrentStatus(metainfo, bitfield, null); // no file priorities saveTorrentStatus(metainfo, bitfield, null); // no file priorities
try { try {
locked_writeMetaInfo(metainfo, filename); locked_writeMetaInfo(metainfo, filename, areFilesPublic());
// hold the lock for a long time // hold the lock for a long time
addTorrent(filename, dontAutoStart); addTorrent(filename, dontAutoStart);
} catch (IOException ioe) { } catch (IOException ioe) {
@@ -754,7 +826,8 @@ public class SnarkManager implements Snark.CompleteListener {
_log.error("Failed to write torrent file to " + filename); _log.error("Failed to write torrent file to " + filename);
return; return;
} }
SecureFileOutputStream.setPerms(new File(filename)); if (!areFilesPublic())
SecureFileOutputStream.setPerms(new File(filename));
// hold the lock for a long time // hold the lock for a long time
addTorrent(filename); addTorrent(filename);
} }
@@ -769,13 +842,16 @@ public class SnarkManager implements Snark.CompleteListener {
* Must be a filesystem-safe name. * Must be a filesystem-safe name.
* @since 0.8.4 * @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); File file = new File(filename);
if (file.exists()) if (file.exists())
throw new IOException("Cannot overwrite an existing .torrent file: " + file.getPath()); throw new IOException("Cannot overwrite an existing .torrent file: " + file.getPath());
OutputStream out = null; OutputStream out = null;
try { try {
out = new SecureFileOutputStream(filename); if (areFilesPublic)
out = new FileOutputStream(filename);
else
out = new SecureFileOutputStream(filename);
out.write(metainfo.getTorrentData()); out.write(metainfo.getTorrentData());
} catch (IOException ioe) { } catch (IOException ioe) {
// remove any partial // remove any partial
@@ -1170,7 +1246,7 @@ public class SnarkManager implements Snark.CompleteListener {
if (announce != null) if (announce != null)
meta = meta.reannounce(announce); meta = meta.reannounce(announce);
synchronized (_snarks) { synchronized (_snarks) {
locked_writeMetaInfo(meta, name); locked_writeMetaInfo(meta, name, areFilesPublic());
// put it in the list under the new name // put it in the list under the new name
_snarks.remove(snark.getName()); _snarks.remove(snark.getName());
_snarks.put(name, snark); _snarks.put(name, snark);

View File

@@ -48,7 +48,7 @@ public class Storage
private int[] priorities; private int[] priorities;
private final StorageListener listener; private final StorageListener listener;
private I2PSnarkUtil _util; private final I2PSnarkUtil _util;
private /* FIXME final FIXME */ BitField bitfield; // BitField to represent the pieces private /* FIXME final FIXME */ BitField bitfield; // BitField to represent the pieces
private int needed; // Number of pieces needed private int needed; // Number of pieces needed
@@ -433,7 +433,12 @@ public class Storage
/** use a saved bitfield and timestamp from a config file */ /** use a saved bitfield and timestamp from a config file */
public void check(String rootDir, long savedTime, BitField savedBitField) throws IOException 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; boolean useSavedBitField = savedTime > 0 && savedBitField != null;
List<List<String>> files = metainfo.getFiles(); List<List<String>> files = metainfo.getFiles();
@@ -479,7 +484,7 @@ public class Storage
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
List<String> path = files.get(i); List<String> path = files.get(i);
File f = createFileFromNames(base, path); File f = createFileFromNames(base, path, areFilesPublic);
// dup file name check after filtering // dup file name check after filtering
for (int j = 0; j < i; j++) { for (int j = 0; j < i; j++) {
if (f.equals(RAFfile[j])) { if (f.equals(RAFfile[j])) {
@@ -495,7 +500,7 @@ public class Storage
else else
lastPath = '_' + lastPath; lastPath = '_' + lastPath;
path.set(last, lastPath); path.set(last, lastPath);
f = createFileFromNames(base, path); f = createFileFromNames(base, path, areFilesPublic);
j = 0; j = 0;
} }
} }
@@ -585,7 +590,7 @@ public class Storage
* things going in the wrong place if there are duplicates * things going in the wrong place if there are duplicates
* in intermediate path elements after filtering. * 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; File f = null;
Iterator<String> it = names.iterator(); Iterator<String> it = names.iterator();
@@ -595,7 +600,10 @@ public class Storage
if (it.hasNext()) if (it.hasNext())
{ {
// Another dir in the hierarchy. // 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()) if (!f.mkdir() && !f.isDirectory())
throw new IOException("Could not create directory " + f); throw new IOException("Could not create directory " + f);
base = f; base = f;
@@ -603,7 +611,10 @@ public class Storage
else else
{ {
// The final element (file) in the hierarchy. // 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()) if (!f.createNewFile() && !f.exists())
throw new IOException("Could not create file " + f); 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.FileUtil;
import net.i2p.util.I2PAppThread; import net.i2p.util.I2PAppThread;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.util.SecureFileOutputStream;
import org.klomp.snark.I2PSnarkUtil; import org.klomp.snark.I2PSnarkUtil;
import org.klomp.snark.MetaInfo; import org.klomp.snark.MetaInfo;
@@ -208,8 +207,11 @@ public class I2PSnarkServlet extends Default {
out.write("</title>\n"); out.write("</title>\n");
// we want it to go to the base URI so we don't refresh with some funky action= value // we want it to go to the base URI so we don't refresh with some funky action= value
if (!isConfigure) if (!isConfigure) {
out.write("<meta http-equiv=\"refresh\" content=\"60;/i2psnark/" + peerString + "\">\n"); 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(HEADER_A + _themePath + HEADER_B);
out.write("</head><body>"); out.write("</head><body>");
out.write("<center>"); out.write("<center>");
@@ -611,6 +613,7 @@ public class I2PSnarkServlet extends Default {
} }
} else if ("Save".equals(action)) { } else if ("Save".equals(action)) {
String dataDir = req.getParameter("dataDir"); String dataDir = req.getParameter("dataDir");
boolean filesPublic = req.getParameter("filesPublic") != null;
boolean autoStart = req.getParameter("autoStart") != null; boolean autoStart = req.getParameter("autoStart") != null;
String seedPct = req.getParameter("seedPct"); String seedPct = req.getParameter("seedPct");
String eepHost = req.getParameter("eepHost"); String eepHost = req.getParameter("eepHost");
@@ -620,11 +623,14 @@ public class I2PSnarkServlet extends Default {
String i2cpOpts = buildI2CPOpts(req); String i2cpOpts = buildI2CPOpts(req);
String upLimit = req.getParameter("upLimit"); String upLimit = req.getParameter("upLimit");
String upBW = req.getParameter("upBW"); String upBW = req.getParameter("upBW");
String refreshDel = req.getParameter("refreshDelay");
String startupDel = req.getParameter("startupDelay"); String startupDel = req.getParameter("startupDelay");
boolean useOpenTrackers = req.getParameter("useOpenTrackers") != null; boolean useOpenTrackers = req.getParameter("useOpenTrackers") != null;
String openTrackers = req.getParameter("openTrackers"); String openTrackers = req.getParameter("openTrackers");
String theme = req.getParameter("theme"); 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)) { } else if ("Create".equals(action)) {
String baseData = req.getParameter("baseFile"); String baseData = req.getParameter("baseFile");
if (baseData != null && baseData.trim().length() > 0) { 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://\" " + out.write("&nbsp;<input type=\"text\" name=\"announceURLOther\" size=\"57\" value=\"http://\" " +
"title=\""); "title=\"");
out.write(_("Specify custom tracker announce URL")); out.write(_("Specify custom tracker announce URL"));
out.write("\" > "); out.write("\" > " +
out.write("<input type=\"submit\" value=\""); "<input type=\"submit\" value=\"");
out.write(_("Create torrent")); out.write(_("Create torrent"));
out.write("\" name=\"foo\" ></table>\n"); out.write("\" name=\"foo\" ></table>\n" +
out.write("</form></div></div>"); "</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 { private void writeConfigForm(PrintWriter out, HttpServletRequest req) throws IOException {
String dataDir = _manager.getDataDir().getAbsolutePath(); String dataDir = _manager.getDataDir().getAbsolutePath();
boolean filesPublic = _manager.areFilesPublic();
boolean autoStart = _manager.shouldAutoStart(); boolean autoStart = _manager.shouldAutoStart();
boolean useOpenTrackers = _manager.util().shouldUseOpenTrackers(); boolean useOpenTrackers = _manager.util().shouldUseOpenTrackers();
String openTrackers = _manager.util().getOpenTrackerString(); String openTrackers = _manager.util().getOpenTrackerString();
//int seedPct = 0; //int seedPct = 0;
out.write("<form action=\"/i2psnark/configure\" method=\"POST\">\n"); out.write("<form action=\"/i2psnark/configure\" method=\"POST\">\n" +
out.write("<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n"); "<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n" +
out.write("<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n"); "<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n" +
out.write("<input type=\"hidden\" name=\"action\" value=\"Save\" >\n"); "<input type=\"hidden\" name=\"action\" value=\"Save\" >\n" +
out.write("<span class=\"snarkConfigTitle\">"); "<span class=\"snarkConfigTitle\">" +
out.write("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> "); "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> ");
out.write(_("Configuration")); out.write(_("Configuration"));
out.write("</span><hr>\n"); out.write("</span><hr>\n" +
out.write("<table border=\"0\"><tr><td>"); "<table border=\"0\"><tr><td>");
out.write(_("Data directory")); out.write(_("Data directory"));
out.write(": <td><code>" + dataDir + "</code> <i>("); out.write(": <td><code>" + dataDir + "</code> <i>(");
out.write(_("Edit i2psnark.config and restart to change")); 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(_("Auto start"));
out.write(": <td><input type=\"checkbox\" class=\"optbox\" name=\"autoStart\" value=\"true\" " out.write(": <td><input type=\"checkbox\" class=\"optbox\" name=\"autoStart\" value=\"true\" "
+ (autoStart ? "checked " : "") + (autoStart ? "checked " : "")
+ "title=\""); + "title=\"");
out.write(_("If checked, automatically start torrents that are added")); 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(_("Theme"));
out.write(": <td><select name='theme'>"); out.write(": <td><select name='theme'>");
String theme = _manager.getTheme(); String theme = _manager.getTheme();
@@ -1307,9 +1324,28 @@ public class I2PSnarkServlet extends Default {
else else
out.write("\n<OPTION value=\"" + themes[i] + "\">" + themes[i]); 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(_("Startup delay"));
out.write(": <td><input name=\"startupDelay\" size=\"3\" class=\"r\" value=\"" + _manager.util().getStartupDelay() + "\"> "); out.write(": <td><input name=\"startupDelay\" size=\"3\" class=\"r\" value=\"" + _manager.util().getStartupDelay() + "\"> ");
out.write(_("minutes")); out.write(_("minutes"));
@@ -1340,26 +1376,26 @@ public class I2PSnarkServlet extends Default {
out.write(": <td><input type=\"text\" name=\"upLimit\" class=\"r\" value=\"" out.write(": <td><input type=\"text\" name=\"upLimit\" class=\"r\" value=\""
+ _manager.util().getMaxUploaders() + "\" size=\"3\" maxlength=\"3\" > "); + _manager.util().getMaxUploaders() + "\" size=\"3\" maxlength=\"3\" > ");
out.write(_("peers")); out.write(_("peers"));
out.write("<br>\n"); out.write("<br>\n" +
out.write("<tr><td>"); "<tr><td>");
out.write(_("Up bandwidth limit")); out.write(_("Up bandwidth limit"));
out.write(": <td><input type=\"text\" name=\"upBW\" class=\"r\" value=\"" out.write(": <td><input type=\"text\" name=\"upBW\" class=\"r\" value=\""
+ _manager.util().getMaxUpBW() + "\" size=\"3\" maxlength=\"3\" > KBps <i>("); + _manager.util().getMaxUpBW() + "\" size=\"3\" maxlength=\"3\" > KBps <i>(");
out.write(_("Half available bandwidth recommended.")); out.write(_("Half available bandwidth recommended."));
out.write(" <a href=\"/config.jsp\" target=\"blank\">"); out.write(" <a href=\"/config.jsp\" target=\"blank\">");
out.write(_("View or change router bandwidth")); 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(_("Use open trackers also"));
out.write(": <td><input type=\"checkbox\" class=\"optbox\" name=\"useOpenTrackers\" value=\"true\" " out.write(": <td><input type=\"checkbox\" class=\"optbox\" name=\"useOpenTrackers\" value=\"true\" "
+ (useOpenTrackers ? "checked " : "") + (useOpenTrackers ? "checked " : "")
+ "title=\""); + "title=\"");
out.write(_("If checked, announce torrents to open trackers as well as the tracker listed in the torrent file")); 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(_("Open tracker announce URLs"));
out.write(": <td><input type=\"text\" name=\"openTrackers\" value=\"" out.write(": <td><input type=\"text\" name=\"openTrackers\" value=\""
+ openTrackers + "\" size=\"50\" ><br>\n"); + openTrackers + "\" size=\"50\" ><br>\n");
@@ -1388,36 +1424,38 @@ public class I2PSnarkServlet extends Default {
out.write("<tr><td>"); out.write("<tr><td>");
out.write(_("I2CP host")); out.write(_("I2CP host"));
out.write(": <td><input type=\"text\" name=\"i2cpHost\" value=\"" 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(_("I2CP port"));
out.write(": <td><input type=\"text\" name=\"i2cpPort\" class=\"r\" value=\"" + out.write(": <td><input type=\"text\" name=\"i2cpPort\" class=\"r\" value=\"" +
+ _manager.util().getI2CPPort() + "\" size=\"5\" maxlength=\"5\" > <br>\n"); + _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); StringBuilder opts = new StringBuilder(64);
for (Iterator iter = options.entrySet().iterator(); iter.hasNext(); ) { for (Map.Entry<String, String> e : options.entrySet()) {
Map.Entry entry = (Map.Entry)iter.next(); String key = e.getKey();
String key = (String)entry.getKey(); String val = e.getValue();
String val = (String)entry.getValue();
opts.append(key).append('=').append(val).append(' '); opts.append(key).append('=').append(val).append(' ');
} }
out.write("<tr><td>"); out.write("<tr><td>");
out.write(_("I2CP options")); out.write(_("I2CP options"));
out.write(": <td><textarea name=\"i2cpOpts\" cols=\"60\" rows=\"1\" wrap=\"off\" spellcheck=\"false\" >" 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(_("Save configuration"));
out.write("\" name=\"foo\" >\n"); out.write("\" name=\"foo\" >\n" +
out.write("</table></div></div></form>"); "</table></div></div></form>");
} }
private void writeConfigLink(PrintWriter out) throws IOException { private void writeConfigLink(PrintWriter out) throws IOException {
out.write("<div class=\"configsection\"><span class=\"snarkConfig\">\n"); out.write("<div class=\"configsection\"><span class=\"snarkConfig\">\n" +
out.write("<span class=\"snarkConfigTitle\"><a href=\"configure\">"); "<span class=\"snarkConfigTitle\"><a href=\"configure\">" +
out.write("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> "); "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> ");
out.write(_("Configuration")); out.write(_("Configuration"));
out.write("</a></span></span></div>\n"); 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 2011-09-13 kytv
* Update i2prouter script * Update i2prouter script

View File

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