forked from I2P_Developers/i2p.i2p
* 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:
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(" <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> <td><input type=\"submit\" value=\"");
|
||||
"<tr><td> <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");
|
||||
}
|
||||
|
12
history.txt
12
history.txt
@ -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
|
||||
|
||||
|
@ -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 = "";
|
||||
|
Reference in New Issue
Block a user