diff --git a/apps/BOB/nbproject/private/private.xml b/apps/BOB/nbproject/private/private.xml index c1f155a78..685ecc5a1 100644 --- a/apps/BOB/nbproject/private/private.xml +++ b/apps/BOB/nbproject/private/private.xml @@ -1,4 +1,7 @@ + + file:/usblv/NetBeansProjects/wi2p.i2p/apps/BOB/src/net/i2p/BOB/BOB.java + diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 950f2e827..bf7c146cb 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -1,760 +1,760 @@ -package org.klomp.snark; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.TreeMap; - -import net.i2p.I2PAppContext; -import net.i2p.data.Base64; -import net.i2p.data.DataHelper; -import net.i2p.util.I2PAppThread; -import net.i2p.util.Log; - -/** - * Manage multiple snarks - */ -public class SnarkManager implements Snark.CompleteListener { - private static SnarkManager _instance = new SnarkManager(); - public static SnarkManager instance() { return _instance; } - - /** map of (canonical) filename to Snark instance (unsynchronized) */ - private final Map _snarks; - private final Object _addSnarkLock; - private /* FIXME final FIXME */ File _configFile; - private Properties _config; - private I2PAppContext _context; - private Log _log; - private final List _messages; - private I2PSnarkUtil _util; - private PeerCoordinatorSet _peerCoordinatorSet; - private ConnectionAcceptor _connectionAcceptor; - - public static final String PROP_I2CP_HOST = "i2psnark.i2cpHost"; - public static final String PROP_I2CP_PORT = "i2psnark.i2cpPort"; - public static final String PROP_I2CP_OPTS = "i2psnark.i2cpOptions"; - public static final String PROP_EEP_HOST = "i2psnark.eepHost"; - public static final String PROP_EEP_PORT = "i2psnark.eepPort"; - public static final String PROP_UPLOADERS_TOTAL = "i2psnark.uploaders.total"; - public static final String PROP_UPBW_MAX = "i2psnark.upbw.max"; - public static final String PROP_DIR = "i2psnark.dir"; - public static final String PROP_META_PREFIX = "i2psnark.zmeta."; - public static final String PROP_META_BITFIELD_SUFFIX = ".bitfield"; - - private static final String CONFIG_FILE = "i2psnark.config"; - 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 int MIN_UP_BW = 2; - public static final int DEFAULT_MAX_UP_BW = 10; - - private SnarkManager() { - _snarks = new HashMap(); - _addSnarkLock = new Object(); - _context = I2PAppContext.getGlobalContext(); - _log = _context.logManager().getLog(SnarkManager.class); - _messages = new ArrayList(16); - _util = new I2PSnarkUtil(_context); - _configFile = new File(CONFIG_FILE); - if (!_configFile.isAbsolute()) - _configFile = new File(_context.getConfigDir(), CONFIG_FILE); - loadConfig(null); - } - - /** Caller _must_ call loadConfig(file) before this if setting new values - * for i2cp host/port or i2psnark.dir - */ - public void start() { - _peerCoordinatorSet = new PeerCoordinatorSet(); - _connectionAcceptor = new ConnectionAcceptor(_util); - int minutes = getStartupDelayMinutes(); - _messages.add("Adding torrents in " + minutes + (minutes == 1 ? " minute" : " minutes")); - I2PAppThread monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor"); - monitor.setDaemon(true); - monitor.start(); - _context.addShutdownTask(new SnarkManagerShutdown()); - } - - /** hook to I2PSnarkUtil for the servlet */ - public I2PSnarkUtil util() { return _util; } - - private static final int MAX_MESSAGES = 5; - public void addMessage(String message) { - synchronized (_messages) { - _messages.add(message); - while (_messages.size() > MAX_MESSAGES) - _messages.remove(0); - } - if (_log.shouldLog(Log.INFO)) - _log.info("MSG: " + message); - } - - /** newest last */ - public List getMessages() { - synchronized (_messages) { - return new ArrayList(_messages); - } - } - - 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 3; } - public File getDataDir() { - String dir = _config.getProperty(PROP_DIR, "i2psnark"); - File f = new File(dir); - if (!f.isAbsolute()) - f = new File(_context.getAppDir(), dir); - return f; - } - - /** null to set initial defaults */ - public void loadConfig(String filename) { - if (_config == null) - _config = new Properties(); - if (filename != null) { - File cfg = new File(filename); - if (!cfg.isAbsolute()) - cfg = new File(_context.getConfigDir(), filename); - _configFile = cfg; - if (cfg.exists()) { - try { - DataHelper.loadProps(_config, cfg); - } catch (IOException ioe) { - _log.error("Error loading I2PSnark config '" + filename + "'", ioe); - } - } - } - // now add sane defaults - if (!_config.containsKey(PROP_I2CP_HOST)) - _config.setProperty(PROP_I2CP_HOST, "127.0.0.1"); - if (!_config.containsKey(PROP_I2CP_PORT)) - _config.setProperty(PROP_I2CP_PORT, "7654"); - if (!_config.containsKey(PROP_I2CP_OPTS)) - _config.setProperty(PROP_I2CP_OPTS, "inbound.length=2 inbound.lengthVariance=0 outbound.length=2 outbound.lengthVariance=0 inbound.quantity=3 outbound.quantity=3"); - if (!_config.containsKey(PROP_EEP_HOST)) - _config.setProperty(PROP_EEP_HOST, "127.0.0.1"); - if (!_config.containsKey(PROP_EEP_PORT)) - _config.setProperty(PROP_EEP_PORT, "4444"); - if (!_config.containsKey(PROP_UPLOADERS_TOTAL)) - _config.setProperty(PROP_UPLOADERS_TOTAL, "" + Snark.MAX_TOTAL_UPLOADERS); - if (!_config.containsKey(PROP_DIR)) - _config.setProperty(PROP_DIR, "i2psnark"); - if (!_config.containsKey(PROP_AUTO_START)) - _config.setProperty(PROP_AUTO_START, DEFAULT_AUTO_START); - updateConfig(); - } - - /** call from DirMonitor since loadConfig() is called before router I2CP is up */ - private void getBWLimit() { - if (!_config.containsKey(PROP_UPBW_MAX)) { - int[] limits = BWLimits.getBWLimits(_util.getI2CPHost(), _util.getI2CPPort()); - if (limits != null && limits[1] > 0) - _util.setMaxUpBW(limits[1]); - } - } - - private void updateConfig() { - String i2cpHost = _config.getProperty(PROP_I2CP_HOST); - int i2cpPort = getInt(PROP_I2CP_PORT, 7654); - String opts = _config.getProperty(PROP_I2CP_OPTS); - Map i2cpOpts = new HashMap(); - if (opts != null) { - StringTokenizer tok = new StringTokenizer(opts, " "); - while (tok.hasMoreTokens()) { - String pair = tok.nextToken(); - int split = pair.indexOf('='); - if (split > 0) - i2cpOpts.put(pair.substring(0, split), pair.substring(split+1)); - } - } - if (i2cpHost != null) { - _util.setI2CPConfig(i2cpHost, i2cpPort, i2cpOpts); - _log.debug("Configuring with I2CP options " + i2cpOpts); - } - //I2PSnarkUtil.instance().setI2CPConfig("66.111.51.110", 7654, new Properties()); - String eepHost = _config.getProperty(PROP_EEP_HOST); - int eepPort = getInt(PROP_EEP_PORT, 4444); - if (eepHost != null) - _util.setProxy(eepHost, eepPort); - _util.setMaxUploaders(getInt(PROP_UPLOADERS_TOTAL, Snark.MAX_TOTAL_UPLOADERS)); - _util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW)); - getDataDir().mkdirs(); - } - - private int getInt(String prop, int defaultVal) { - String p = _config.getProperty(prop); - try { - if ( (p != null) && (p.trim().length() > 0) ) - return Integer.parseInt(p.trim()); - } catch (NumberFormatException nfe) { - // ignore - } - return defaultVal; - } - - public void updateConfig(String dataDir, boolean autoStart, String seedPct, String eepHost, - String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts, - String upLimit, String upBW, boolean useOpenTrackers, String openTrackers) { - boolean changed = false; - if (eepHost != null) { - int port = _util.getEepProxyPort(); - try { port = Integer.parseInt(eepPort); } catch (NumberFormatException nfe) {} - String host = _util.getEepProxyHost(); - if ( (eepHost.trim().length() > 0) && (port > 0) && - ((!host.equals(eepHost) || (port != _util.getEepProxyPort()) )) ) { - _util.setProxy(eepHost, port); - changed = true; - _config.setProperty(PROP_EEP_HOST, eepHost); - _config.setProperty(PROP_EEP_PORT, eepPort+""); - addMessage("EepProxy location changed to " + eepHost + ":" + port); - } - } - if (upLimit != null) { - int limit = _util.getMaxUploaders(); - try { limit = Integer.parseInt(upLimit); } catch (NumberFormatException nfe) {} - if ( limit != _util.getMaxUploaders()) { - if ( limit >= Snark.MIN_TOTAL_UPLOADERS ) { - _util.setMaxUploaders(limit); - changed = true; - _config.setProperty(PROP_UPLOADERS_TOTAL, "" + limit); - addMessage("Total uploaders limit changed to " + limit); - } else { - addMessage("Minimum total uploaders limit is " + Snark.MIN_TOTAL_UPLOADERS); - } - } - } - if (upBW != null) { - int limit = _util.getMaxUpBW(); - try { limit = Integer.parseInt(upBW); } catch (NumberFormatException nfe) {} - if ( limit != _util.getMaxUpBW()) { - if ( limit >= MIN_UP_BW ) { - _util.setMaxUpBW(limit); - changed = true; - _config.setProperty(PROP_UPBW_MAX, "" + limit); - addMessage("Up BW limit changed to " + limit + "KBps"); - } else { - addMessage("Minimum Up BW limit is " + MIN_UP_BW + "KBps"); - } - } - } - if (i2cpHost != null) { - int oldI2CPPort = _util.getI2CPPort(); - String oldI2CPHost = _util.getI2CPHost(); - int port = oldI2CPPort; - try { port = Integer.parseInt(i2cpPort); } catch (NumberFormatException nfe) {} - String host = oldI2CPHost; - Map opts = new HashMap(); - if (i2cpOpts == null) i2cpOpts = ""; - StringTokenizer tok = new StringTokenizer(i2cpOpts, " \t\n"); - while (tok.hasMoreTokens()) { - String pair = tok.nextToken(); - int split = pair.indexOf('='); - if (split > 0) - opts.put(pair.substring(0, split), pair.substring(split+1)); - } - Map oldOpts = new HashMap(); - String oldI2CPOpts = _config.getProperty(PROP_I2CP_OPTS); - if (oldI2CPOpts == null) oldI2CPOpts = ""; - tok = new StringTokenizer(oldI2CPOpts, " \t\n"); - while (tok.hasMoreTokens()) { - String pair = tok.nextToken(); - int split = pair.indexOf('='); - if (split > 0) - 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 snarksActive = false; - Set names = listTorrentFiles(); - for (Iterator iter = names.iterator(); iter.hasNext(); ) { - Snark snark = getTorrent((String)iter.next()); - if ( (snark != null) && (!snark.stopped) ) { - snarksActive = true; - break; - } - } - if (snarksActive) { - addMessage("Cannot change the I2CP settings while torrents are active"); - _log.debug("i2cp host [" + i2cpHost + "] i2cp port " + port + " opts [" + opts - + "] oldOpts [" + oldOpts + "]"); - } else { - if (_util.connected()) { - _util.disconnect(); - addMessage("Disconnecting old I2CP destination"); - } - Properties p = new Properties(); - p.putAll(opts); - addMessage("I2CP settings changed to " + i2cpHost + ":" + port + " (" + i2cpOpts.trim() + ")"); - _util.setI2CPConfig(i2cpHost, port, p); - boolean ok = _util.connect(); - if (!ok) { - addMessage("Unable to connect with the new settings, reverting to the old I2CP settings"); - _util.setI2CPConfig(oldI2CPHost, oldI2CPPort, oldOpts); - ok = _util.connect(); - if (!ok) - addMessage("Unable to reconnect with the old settings!"); - } else { - addMessage("Reconnected on the new I2CP destination"); - _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.acceptor != null) ) { - snark.acceptor.restart(); - addMessage("I2CP listener restarted for " + snark.meta.getName()); - } - } - } - } - changed = true; - } - } - if (shouldAutoStart() != autoStart) { - _config.setProperty(PROP_AUTO_START, autoStart + ""); - addMessage("Adjusted autostart to " + autoStart); - changed = true; - } - if (_util.shouldUseOpenTrackers() != useOpenTrackers) { - _config.setProperty(I2PSnarkUtil.PROP_USE_OPENTRACKERS, useOpenTrackers + ""); - addMessage((useOpenTrackers ? "En" : "Dis") + "abled open trackers - torrent restart required to take effect."); - changed = true; - } - if (openTrackers != null) { - if (openTrackers.trim().length() > 0 && !openTrackers.trim().equals(_util.getOpenTrackerString())) { - _config.setProperty(I2PSnarkUtil.PROP_OPENTRACKERS, openTrackers.trim()); - addMessage("Open Tracker list changed - torrent restart required to take effect."); - changed = true; - } - } - if (changed) { - saveConfig(); - } else { - addMessage("Configuration unchanged."); - } - } - - public void saveConfig() { - try { - synchronized (_configFile) { - DataHelper.storeProps(_config, _configFile); - } - } catch (IOException ioe) { - addMessage("Unable to save the config to '" + _configFile.getAbsolutePath() + "'."); - } - } - - public Properties getConfig() { return _config; } - - /** hardcoded for sanity. perhaps this should be customizable, for people who increase their ulimit, etc. */ - private static final int MAX_FILES_PER_TORRENT = 512; - - /** set of filenames that we are dealing with */ - public Set listTorrentFiles() { synchronized (_snarks) { return new HashSet(_snarks.keySet()); } } - /** - * Grab the torrent given the (canonical) filename - */ - public Snark getTorrent(String filename) { synchronized (_snarks) { return (Snark)_snarks.get(filename); } } - public void addTorrent(String filename) { addTorrent(filename, false); } - public void addTorrent(String filename, boolean dontAutoStart) { - if ((!dontAutoStart) && !_util.connected()) { - addMessage("Connecting to I2P"); - boolean ok = _util.connect(); - if (!ok) { - addMessage("Error connecting to I2P - check your I2CP settings!"); - return; - } - } - File sfile = new File(filename); - try { - filename = sfile.getCanonicalPath(); - } catch (IOException ioe) { - _log.error("Unable to add the torrent " + filename, ioe); - addMessage("ERR: Could not add the torrent '" + filename + "': " + ioe.getMessage()); - return; - } - File dataDir = getDataDir(); - Snark torrent = null; - synchronized (_snarks) { - torrent = (Snark)_snarks.get(filename); - } - // don't hold the _snarks lock while verifying the torrent - if (torrent == null) { - synchronized (_addSnarkLock) { - // double-check - synchronized (_snarks) { - if(_snarks.get(filename) != null) - return; - } - - FileInputStream fis = null; - try { - fis = new FileInputStream(sfile); - MetaInfo info = new MetaInfo(fis); - fis.close(); - fis = null; - - String rejectMessage = locked_validateTorrent(info); - if (rejectMessage != null) { - sfile.delete(); - addMessage(rejectMessage); - return; - } else { - torrent = new Snark(_util, filename, null, -1, null, null, this, - _peerCoordinatorSet, _connectionAcceptor, - false, dataDir.getPath()); - torrent.completeListener = this; - synchronized (_snarks) { - _snarks.put(filename, torrent); - } - } - } catch (IOException ioe) { - addMessage("Torrent in " + sfile.getName() + " is invalid: " + ioe.getMessage()); - if (sfile.exists()) - sfile.delete(); - return; - } finally { - if (fis != null) try { fis.close(); } catch (IOException ioe) {} - } - } - } else { - return; - } - // ok, snark created, now lets start it up or configure it further - File f = new File(filename); - if (!dontAutoStart && shouldAutoStart()) { - torrent.startTorrent(); - addMessage("Torrent added and started: '" + f.getName() + "'."); - } else { - addMessage("Torrent added: '" + f.getName() + "'."); - } - } - - /** - * Get the timestamp for a torrent from the config file - */ - public long getSavedTorrentTime(Snark snark) { - MetaInfo metainfo = snark.meta; - byte[] ih = metainfo.getInfoHash(); - String infohash = Base64.encode(ih); - infohash = infohash.replace('=', '$'); - String time = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX); - if (time == null) - return 0; - int comma = time.indexOf(','); - if (comma <= 0) - return 0; - time = time.substring(0, comma); - try { return Long.parseLong(time); } catch (NumberFormatException nfe) {} - return 0; - } - - /** - * Get the saved bitfield for a torrent from the config file. - * Convert "." to a full bitfield. - */ - public BitField getSavedTorrentBitField(Snark snark) { - MetaInfo metainfo = snark.meta; - byte[] ih = metainfo.getInfoHash(); - String infohash = Base64.encode(ih); - infohash = infohash.replace('=', '$'); - String bf = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX); - if (bf == null) - return null; - int comma = bf.indexOf(','); - if (comma <= 0) - return null; - bf = bf.substring(comma + 1).trim(); - int len = metainfo.getPieces(); - if (bf.equals(".")) { - BitField bitfield = new BitField(len); - for (int i = 0; i < len; i++) - bitfield.set(i); - return bitfield; - } - byte[] bitfield = Base64.decode(bf); - if (bitfield == null) - return null; - if (bitfield.length * 8 < len) - return null; - return new BitField(bitfield, len); - } - - /** - * Save the completion status of a torrent and the current time in the config file - * in the form "i2psnark.zmeta.$base64infohash=$time,$base64bitfield". - * The config file property key is appended with the Base64 of the infohash, - * with the '=' changed to '$' since a key can't contain '='. - * The time is a standard long converted to string. - * The status is either a bitfield converted to Base64 or "." for a completed - * torrent to save space in the config file and in memory. - */ - public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield) { - byte[] ih = metainfo.getInfoHash(); - String infohash = Base64.encode(ih); - infohash = infohash.replace('=', '$'); - String now = "" + System.currentTimeMillis(); - String bfs; - if (bitfield.complete()) { - bfs = "."; - } else { - byte[] bf = bitfield.getFieldBytes(); - bfs = Base64.encode(bf); - } - _config.setProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX, now + "," + bfs); - saveConfig(); - } - - /** - * Remove the status of a torrent from the config file. - * This may help the config file from growing too big. - */ - public void removeTorrentStatus(MetaInfo metainfo) { - byte[] ih = metainfo.getInfoHash(); - String infohash = Base64.encode(ih); - infohash = infohash.replace('=', '$'); - _config.remove(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX); - saveConfig(); - } - - private String locked_validateTorrent(MetaInfo info) throws IOException { - String announce = info.getAnnounce(); - // basic validation of url - if ((!announce.startsWith("http://")) || - (announce.indexOf(".i2p/") < 0)) // need to do better than this - return "Non-i2p tracker in " + info.getName() + ", deleting it from our list of trackers!"; - List files = info.getFiles(); - if ( (files != null) && (files.size() > MAX_FILES_PER_TORRENT) ) { - return "Too many files in " + info.getName() + " (" + files.size() + "), deleting it!"; - } else if (info.getPieces() <= 0) { - return "No pieces in " + info.getName() + "? deleting it!"; - } else if (info.getPieceLength(0) > Storage.MAX_PIECE_SIZE) { - return "Pieces are too large in " + info.getName() + " (" + DataHelper.formatSize(info.getPieceLength(0)) + - "B), deleting it."; - } else if (info.getTotalLength() > Storage.MAX_TOTAL_SIZE) { - System.out.println("torrent info: " + info.toString()); - List lengths = info.getLengths(); - if (lengths != null) - for (int i = 0; i < lengths.size(); i++) - System.out.println("File " + i + " is " + lengths.get(i) + " long."); - - return "Torrents larger than " + DataHelper.formatSize(Storage.MAX_TOTAL_SIZE) + - "B are not supported yet (because we're paranoid): " + info.getName() + ", deleting it!"; - } else { - // ok - return null; - } - } - - /** - * Stop the torrent, leaving it on the list of torrents unless told to remove it - */ - public Snark stopTorrent(String filename, boolean shouldRemove) { - File sfile = new File(filename); - try { - filename = sfile.getCanonicalPath(); - } catch (IOException ioe) { - _log.error("Unable to remove the torrent " + filename, ioe); - addMessage("ERR: Could not remove the torrent '" + filename + "': " + ioe.getMessage()); - return null; - } - int remaining = 0; - Snark torrent = null; - synchronized (_snarks) { - if (shouldRemove) - torrent = (Snark)_snarks.remove(filename); - else - torrent = (Snark)_snarks.get(filename); - remaining = _snarks.size(); - } - if (torrent != null) { - boolean wasStopped = torrent.stopped; - torrent.stopTorrent(); - if (remaining == 0) { - // should we disconnect/reconnect here (taking care to deal with the other thread's - // I2PServerSocket.accept() call properly?) - ////_util. - } - if (!wasStopped) - addMessage("Torrent stopped: '" + sfile.getName() + "'."); - } - return torrent; - } - /** - * Stop the torrent and delete the torrent file itself, but leaving the data - * behind. - */ - public void removeTorrent(String filename) { - Snark torrent = stopTorrent(filename, true); - if (torrent != null) { - File torrentFile = new File(filename); - torrentFile.delete(); - if (torrent.storage != null) - removeTorrentStatus(torrent.storage.getMetaInfo()); - addMessage("Torrent removed: '" + torrentFile.getName() + "'."); - } - } - - private class DirMonitor implements Runnable { - public void run() { - try { Thread.sleep(60*1000*getStartupDelayMinutes()); } catch (InterruptedException ie) {} - // the first message was a "We are starting up in 1m" - synchronized (_messages) { - if (_messages.size() == 1) - _messages.remove(0); - } - - // here because we need to delay until I2CP is up - // although the user will see the default until then - getBWLimit(); - while (true) { - File dir = getDataDir(); - _log.debug("Directory Monitor loop over " + dir.getAbsolutePath()); - try { - monitorTorrents(dir); - } catch (Exception e) { - _log.error("Error in the DirectoryMonitor", e); - } - try { Thread.sleep(60*1000); } catch (InterruptedException ie) {} - } - } - } - - /** two listeners */ - public void torrentComplete(Snark snark) { - File f = new File(snark.torrent); - long len = snark.meta.getTotalLength(); - addMessage("Download finished: " + f.getName() + " (size: " + DataHelper.formatSize(len) + "B)"); - updateStatus(snark); - } - - public void updateStatus(Snark snark) { - saveTorrentStatus(snark.meta, snark.storage.getBitField()); - } - - private void monitorTorrents(File dir) { - String fileNames[] = dir.list(TorrentFilenameFilter.instance()); - List foundNames = new ArrayList(0); - if (fileNames != null) { - for (int i = 0; i < fileNames.length; i++) { - try { - foundNames.add(new File(dir, fileNames[i]).getCanonicalPath()); - } catch (IOException ioe) { - _log.error("Error resolving '" + fileNames[i] + "' in '" + dir, ioe); - } - } - } - - Set existingNames = listTorrentFiles(); - // lets find new ones first... - for (int i = 0; i < foundNames.size(); i++) { - if (existingNames.contains(foundNames.get(i))) { - // already known. noop - } else { - if (shouldAutoStart() && !_util.connect()) - addMessage("Unable to connect to I2P!"); - addTorrent((String)foundNames.get(i), !shouldAutoStart()); - } - } - // now lets see which ones have been removed... - for (Iterator iter = existingNames.iterator(); iter.hasNext(); ) { - String name = (String)iter.next(); - if (foundNames.contains(name)) { - // known and still there. noop - } else { - // known, but removed. drop it - stopTorrent(name, true); - } - } - } - - private static final String DEFAULT_TRACKERS[] = { -// "Postman", "http://YRgrgTLGnbTq2aZOZDJQ~o6Uk5k6TK-OZtx0St9pb0G-5EGYURZioxqYG8AQt~LgyyI~NCj6aYWpPO-150RcEvsfgXLR~CxkkZcVpgt6pns8SRc3Bi-QSAkXpJtloapRGcQfzTtwllokbdC-aMGpeDOjYLd8b5V9Im8wdCHYy7LRFxhEtGb~RL55DA8aYOgEXcTpr6RPPywbV~Qf3q5UK55el6Kex-6VCxreUnPEe4hmTAbqZNR7Fm0hpCiHKGoToRcygafpFqDw5frLXToYiqs9d4liyVB-BcOb0ihORbo0nS3CLmAwZGvdAP8BZ7cIYE3Z9IU9D1G8JCMxWarfKX1pix~6pIA-sp1gKlL1HhYhPMxwyxvuSqx34o3BqU7vdTYwWiLpGM~zU1~j9rHL7x60pVuYaXcFQDR4-QVy26b6Pt6BlAZoFmHhPcAuWfu-SFhjyZYsqzmEmHeYdAwa~HojSbofg0TMUgESRXMw6YThK1KXWeeJVeztGTz25sL8AAAA.i2p/announce.php=http://tracker.postman.i2p/" -// , "eBook", "http://E71FRom6PZNEqTN2Lr8P-sr23b7HJVC32KoGnVQjaX6zJiXwhJy2HsXob36Qmj81TYFZdewFZa9mSJ533UZgGyQkXo2ahctg82JKYZfDe5uDxAn1E9YPjxZCWJaFJh0S~UwSs~9AZ7UcauSJIoNtpxrtbmRNVFLqnkEDdLZi26TeucfOmiFmIWnVblLniWv3tG1boE9Abd-6j3FmYVrRucYuepAILYt6katmVNOk6sXmno1Eynrp~~MBuFq0Ko6~jsc2E2CRVYXDhGHEMdt-j6JUz5D7S2RIVzDRqQyAZLKJ7OdQDmI31przzmne1vOqqqLC~1xUumZVIvF~yOeJUGNjJ1Vx0J8i2BQIusn1pQJ6UCB~ZtZZLQtEb8EPVCfpeRi2ri1M5CyOuxN0V5ekmPHrYIBNevuTCRC26NP7ZS5VDgx1~NaC3A-CzJAE6f1QXi0wMI9aywNG5KGzOPifcsih8eyGyytvgLtrZtV7ykzYpPCS-rDfITncpn5hliPUAAAA.i2p/pub/bt/announce.php=http://de-ebook-archiv.i2p/pub/bt/" -// , "Gaytorrents", "http://uxPWHbK1OIj9HxquaXuhMiIvi21iK0~ZiG9d8G0840ZXIg0r6CbiV71xlsqmdnU6wm0T2LySriM0doW2gUigo-5BNkUquHwOjLROiETnB3ZR0Ml4IGa6QBPn1aAq2d9~g1r1nVjLE~pcFnXB~cNNS7kIhX1d6nLgYVZf0C2cZopEow2iWVUggGGnAA9mHjE86zLEnTvAyhbAMTqDQJhEuLa0ZYSORqzJDMkQt90MV4YMjX1ICY6RfUSFmxEqu0yWTrkHsTtRw48l~dz9wpIgc0a0T9C~eeWvmBFTqlJPtQZwntpNeH~jF7nlYzB58olgV2HHFYpVYD87DYNzTnmNWxCJ5AfDorm6AIUCV2qaE7tZtI1h6fbmGpGlPyW~Kw5GXrRfJwNvr6ajwAVi~bPVnrBwDZezHkfW4slOO8FACPR28EQvaTu9nwhAbqESxV2hCTq6vQSGjuxHeOuzBOEvRWkLKOHWTC09t2DbJ94FSqETmZopTB1ukEmaxRWbKSIaAAAA.i2p/announce.php=http://gaytorrents.i2p/" -// , "NickyB", "http://9On6d3cZ27JjwYCtyJJbowe054d5tFnfMjv4PHsYs-EQn4Y4mk2zRixatvuAyXz2MmRfXG-NAUfhKr0KCxRNZbvHmlckYfT-WBzwwpiMAl0wDFY~Pl8cqXuhfikSG5WrqdPfDNNIBuuznS0dqaczf~OyVaoEOpvuP3qV6wKqbSSLpjOwwAaQPHjlRtNIW8-EtUZp-I0LT45HSoowp~6b7zYmpIyoATvIP~sT0g0MTrczWhbVTUZnEkZeLhOR0Duw1-IRXI2KHPbA24wLO9LdpKKUXed05RTz0QklW5ROgR6TYv7aXFufX8kC0-DaKvQ5JKG~h8lcoHvm1RCzNqVE-2aiZnO2xH08H-iCWoLNJE-Td2kT-Tsc~3QdQcnEUcL5BF-VT~QYRld2--9r0gfGl-yDrJZrlrihHGr5J7ImahelNn9PpkVp6eIyABRmJHf2iicrk3CtjeG1j9OgTSwaNmEpUpn4aN7Kx0zNLdH7z6uTgCGD9Kmh1MFYrsoNlTp4AAAA.i2p/bittorrent/announce.php=http://nickyb.i2p/bittorrent/" -// , "Orion", "http://gKik1lMlRmuroXVGTZ~7v4Vez3L3ZSpddrGZBrxVriosCQf7iHu6CIk8t15BKsj~P0JJpxrofeuxtm7SCUAJEr0AIYSYw8XOmp35UfcRPQWyb1LsxUkMT4WqxAT3s1ClIICWlBu5An~q-Mm0VFlrYLIPBWlUFnfPR7jZ9uP5ZMSzTKSMYUWao3ejiykr~mtEmyls6g-ZbgKZawa9II4zjOy-hdxHgP-eXMDseFsrym4Gpxvy~3Fv9TuiSqhpgm~UeTo5YBfxn6~TahKtE~~sdCiSydqmKBhxAQ7uT9lda7xt96SS09OYMsIWxLeQUWhns-C~FjJPp1D~IuTrUpAFcVEGVL-BRMmdWbfOJEcWPZ~CBCQSO~VkuN1ebvIOr9JBerFMZSxZtFl8JwcrjCIBxeKPBmfh~xYh16BJm1BBBmN1fp2DKmZ2jBNkAmnUbjQOqWvUcehrykWk5lZbE7bjJMDFH48v3SXwRuDBiHZmSbsTY6zhGY~GkMQHNGxPMMSIAAAA.i2p/bt/announce.php=http://orion.i2p/bt/" -// , "anonymity", "http://8EoJZIKrWgGuDrxA3nRJs1jsPfiGwmFWL91hBrf0HA7oKhEvAna4Ocx47VLUR9retVEYBAyWFK-eZTPcvhnz9XffBEiJQQ~kFSCqb1fV6IfPiV3HySqi9U5Caf6~hC46fRd~vYnxmaBLICT3N160cxBETqH3v2rdxdJpvYt8q4nMk9LUeVXq7zqCTFLLG5ig1uKgNzBGe58iNcsvTEYlnbYcE930ABmrzj8G1qQSgSwJ6wx3tUQNl1z~4wSOUMan~raZQD60lRK70GISjoX0-D0Po9WmPveN3ES3g72TIET3zc3WPdK2~lgmKGIs8GgNLES1cXTolvbPhdZK1gxddRMbJl6Y6IPFyQ9o4-6Rt3Lp-RMRWZ2TG7j2OMcNSiOmATUhKEFBDfv-~SODDyopGBmfeLw16F4NnYednvn4qP10dyMHcUASU6Zag4mfc2-WivrOqeWhD16fVAh8MoDpIIT~0r9XmwdaVFyLcjbXObabJczxCAW3fodQUnvuSkwzAAAA.i2p/anonymityTracker/announce.php=http://anonymityweb.i2p/anonymityTracker/" -// , "The freak's tracker", "http://mHKva9x24E5Ygfey2llR1KyQHv5f8hhMpDMwJDg1U-hABpJ2NrQJd6azirdfaR0OKt4jDlmP2o4Qx0H598~AteyD~RJU~xcWYdcOE0dmJ2e9Y8-HY51ie0B1yD9FtIV72ZI-V3TzFDcs6nkdX9b81DwrAwwFzx0EfNvK1GLVWl59Ow85muoRTBA1q8SsZImxdyZ-TApTVlMYIQbdI4iQRwU9OmmtefrCe~ZOf4UBS9-KvNIqUL0XeBSqm0OU1jq-D10Ykg6KfqvuPnBYT1BYHFDQJXW5DdPKwcaQE4MtAdSGmj1epDoaEBUa9btQlFsM2l9Cyn1hzxqNWXELmx8dRlomQLlV4b586dRzW~fLlOPIGC13ntPXogvYvHVyEyptXkv890jC7DZNHyxZd5cyrKC36r9huKvhQAmNABT2Y~pOGwVrb~RpPwT0tBuPZ3lHYhBFYmD8y~AOhhNHKMLzea1rfwTvovBMByDdFps54gMN1mX4MbCGT4w70vIopS9yAAAA.i2p/bytemonsoon/announce.php" -// , "mastertracker", "http://VzXD~stRKbL3MOmeTn1iaCQ0CFyTmuFHiKYyo0Rd~dFPZFCYH-22rT8JD7i-C2xzYFa4jT5U2aqHzHI-Jre4HL3Ri5hFtZrLk2ax3ji7Qfb6qPnuYkuiF2E2UDmKUOppI8d9Ye7tjdhQVCy0izn55tBaB-U7UWdcvSK2i85sauyw3G0Gfads1Rvy5-CAe2paqyYATcDmGjpUNLoxbfv9KH1KmwRTNH6k1v4PyWYYnhbT39WfKMbBjSxVQRdi19cyJrULSWhjxaQfJHeWx5Z8Ev4bSPByBeQBFl2~4vqy0S5RypINsRSa3MZdbiAAyn5tr5slWR6QdoqY3qBQgBJFZppy-3iWkFqqKgSxCPundF8gdDLC5ddizl~KYcYKl42y9SGFHIukH-TZs8~em0~iahzsqWVRks3zRG~tlBcX2U3M2~OJs~C33-NKhyfZT7-XFBREvb8Szmd~p66jDxrwOnKaku-G6DyoQipJqIz4VHmY9-y5T8RrUcJcM-5lVoMpAAAA.i2p/announce.php=http://tracker.mastertracker.i2p/" -// , "Galen", "http://5jpwQMI5FT303YwKa5Rd38PYSX04pbIKgTaKQsWbqoWjIfoancFdWCShXHLI5G5ofOb0Xu11vl2VEMyPsg1jUFYSVnu4-VfMe3y4TKTR6DTpetWrnmEK6m2UXh91J5DZJAKlgmO7UdsFlBkQfR2rY853-DfbJtQIFl91tbsmjcA5CGQi4VxMFyIkBzv-pCsuLQiZqOwWasTlnzey8GcDAPG1LDcvfflGV~6F5no9mnuisZPteZKlrv~~TDoXTj74QjByWc4EOYlwqK8sbU9aOvz~s31XzErbPTfwiawiaZ0RUI-IDrKgyvmj0neuFTWgjRGVTH8bz7cBZIc3viy6ioD-eMQOrXaQL0TCWZUelRwHRvgdPiQrxdYQs7ixkajeHzxi-Pq0EMm5Vbh3j3Q9kfUFW3JjFDA-MLB4g6XnjCbM5J1rC0oOBDCIEfhQkszru5cyLjHiZ5yeA0VThgu~c7xKHybv~OMXION7V8pBKOgET7ZgAkw1xgYe3Kkyq5syAAAA.i2p/tr/announce.php=http://galen.i2p/tr/" - "POSTMAN", "http://tracker2.postman.i2p/announce.php=http://tracker2.postman.i2p/" - ,"WELTERDE", "http://BGKmlDOoH3RzFbPRfRpZV2FjpVj8~3moFftw5-dZfDf2070TOe8Tf2~DAVeaM6ZRLdmFEt~9wyFL8YMLMoLoiwGEH6IGW6rc45tstN68KsBDWZqkTohV1q9XFgK9JnCwE~Oi89xLBHsLMTHOabowWM6dkC8nI6QqJC2JODqLPIRfOVrDdkjLwtCrsckzLybNdFmgfoqF05UITDyczPsFVaHtpF1sRggOVmdvCM66otyonlzNcJbn59PA-R808vUrCPMGU~O9Wys0i-NoqtIbtWfOKnjCRFMNw5ex4n9m5Sxm9e20UkpKG6qzEuvKZWi8vTLe1NW~CBrj~vG7I3Ok4wybUFflBFOaBabxYJLlx4xTE1zJIVxlsekmAjckB4v-cQwulFeikR4LxPQ6mCQknW2HZ4JQIq6hL9AMabxjOlYnzh7kjOfRGkck8YgeozcyTvcDUcUsOuSTk06L4kdrv8h2Cozjbloi5zl6KTbj5ZTciKCxi73Pn9grICn-HQqEAAAA.i2p/a=http://tracker.welterde.i2p/stats?mode=top5" - , "CRSTRACK", "http://b4G9sCdtfvccMAXh~SaZrPqVQNyGQbhbYMbw6supq2XGzbjU4NcOmjFI0vxQ8w1L05twmkOvg5QERcX6Mi8NQrWnR0stLExu2LucUXg1aYjnggxIR8TIOGygZVIMV3STKH4UQXD--wz0BUrqaLxPhrm2Eh9Hwc8TdB6Na4ShQUq5Xm8D4elzNUVdpM~RtChEyJWuQvoGAHY3ppX-EJJLkiSr1t77neS4Lc-KofMVmgI9a2tSSpNAagBiNI6Ak9L1T0F9uxeDfEG9bBSQPNMOSUbAoEcNxtt7xOW~cNOAyMyGydwPMnrQ5kIYPY8Pd3XudEko970vE0D6gO19yoBMJpKx6Dh50DGgybLQ9CpRaynh2zPULTHxm8rneOGRcQo8D3mE7FQ92m54~SvfjXjD2TwAVGI~ae~n9HDxt8uxOecAAvjjJ3TD4XM63Q9TmB38RmGNzNLDBQMEmJFpqQU8YeuhnS54IVdUoVQFqui5SfDeLXlSkh4vYoMU66pvBfWbAAAA.i2p/tracker/announce.php=http://crstrack.i2p/tracker/" - - }; - - /** comma delimited list of name=announceURL=baseURL for the trackers to be displayed */ - public static final String PROP_TRACKERS = "i2psnark.trackers"; - private static Map trackerMap = null; - /** sorted map of name to announceURL=baseURL */ - public Map getTrackers() { - if (trackerMap != null) // only do this once, can't be updated while running - return trackerMap; - Map rv = new TreeMap(); - String trackers = _config.getProperty(PROP_TRACKERS); - if ( (trackers == null) || (trackers.trim().length() <= 0) ) - trackers = _context.getProperty(PROP_TRACKERS); - if ( (trackers == null) || (trackers.trim().length() <= 0) ) { - for (int i = 0; i < DEFAULT_TRACKERS.length; i += 2) - rv.put(DEFAULT_TRACKERS[i], DEFAULT_TRACKERS[i+1]); - } else { - StringTokenizer tok = new StringTokenizer(trackers, ","); - while (tok.hasMoreTokens()) { - String pair = tok.nextToken(); - int split = pair.indexOf('='); - if (split <= 0) - continue; - String name = pair.substring(0, split).trim(); - String url = pair.substring(split+1).trim(); - if ( (name.length() > 0) && (url.length() > 0) ) - rv.put(name, url); - } - } - - trackerMap = rv; - return trackerMap; - } - - private static class TorrentFilenameFilter implements FilenameFilter { - private static final TorrentFilenameFilter _filter = new TorrentFilenameFilter(); - public static TorrentFilenameFilter instance() { return _filter; } - public boolean accept(File dir, String name) { - return (name != null) && (name.endsWith(".torrent")); - } - } - - public class SnarkManagerShutdown extends I2PAppThread { - @Override - public void run() { - Set names = listTorrentFiles(); - for (Iterator iter = names.iterator(); iter.hasNext(); ) { - Snark snark = getTorrent((String)iter.next()); - if ( (snark != null) && (!snark.stopped) ) - snark.stopTorrent(); - } - } - } -} +package org.klomp.snark; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TreeMap; + +import net.i2p.I2PAppContext; +import net.i2p.data.Base64; +import net.i2p.data.DataHelper; +import net.i2p.util.I2PAppThread; +import net.i2p.util.Log; + +/** + * Manage multiple snarks + */ +public class SnarkManager implements Snark.CompleteListener { + private static SnarkManager _instance = new SnarkManager(); + public static SnarkManager instance() { return _instance; } + + /** map of (canonical) filename to Snark instance (unsynchronized) */ + private final Map _snarks; + private final Object _addSnarkLock; + private /* FIXME final FIXME */ File _configFile; + private Properties _config; + private I2PAppContext _context; + private Log _log; + private final List _messages; + private I2PSnarkUtil _util; + private PeerCoordinatorSet _peerCoordinatorSet; + private ConnectionAcceptor _connectionAcceptor; + + public static final String PROP_I2CP_HOST = "i2psnark.i2cpHost"; + public static final String PROP_I2CP_PORT = "i2psnark.i2cpPort"; + public static final String PROP_I2CP_OPTS = "i2psnark.i2cpOptions"; + public static final String PROP_EEP_HOST = "i2psnark.eepHost"; + public static final String PROP_EEP_PORT = "i2psnark.eepPort"; + public static final String PROP_UPLOADERS_TOTAL = "i2psnark.uploaders.total"; + public static final String PROP_UPBW_MAX = "i2psnark.upbw.max"; + public static final String PROP_DIR = "i2psnark.dir"; + public static final String PROP_META_PREFIX = "i2psnark.zmeta."; + public static final String PROP_META_BITFIELD_SUFFIX = ".bitfield"; + + private static final String CONFIG_FILE = "i2psnark.config"; + 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 int MIN_UP_BW = 2; + public static final int DEFAULT_MAX_UP_BW = 10; + + private SnarkManager() { + _snarks = new HashMap(); + _addSnarkLock = new Object(); + _context = I2PAppContext.getGlobalContext(); + _log = _context.logManager().getLog(SnarkManager.class); + _messages = new ArrayList(16); + _util = new I2PSnarkUtil(_context); + _configFile = new File(CONFIG_FILE); + if (!_configFile.isAbsolute()) + _configFile = new File(_context.getConfigDir(), CONFIG_FILE); + loadConfig(null); + } + + /** Caller _must_ call loadConfig(file) before this if setting new values + * for i2cp host/port or i2psnark.dir + */ + public void start() { + _peerCoordinatorSet = new PeerCoordinatorSet(); + _connectionAcceptor = new ConnectionAcceptor(_util); + int minutes = getStartupDelayMinutes(); + _messages.add("Adding torrents in " + minutes + (minutes == 1 ? " minute" : " minutes")); + I2PAppThread monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor"); + monitor.setDaemon(true); + monitor.start(); + _context.addShutdownTask(new SnarkManagerShutdown()); + } + + /** hook to I2PSnarkUtil for the servlet */ + public I2PSnarkUtil util() { return _util; } + + private static final int MAX_MESSAGES = 5; + public void addMessage(String message) { + synchronized (_messages) { + _messages.add(message); + while (_messages.size() > MAX_MESSAGES) + _messages.remove(0); + } + if (_log.shouldLog(Log.INFO)) + _log.info("MSG: " + message); + } + + /** newest last */ + public List getMessages() { + synchronized (_messages) { + return new ArrayList(_messages); + } + } + + 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 3; } + public File getDataDir() { + String dir = _config.getProperty(PROP_DIR, "i2psnark"); + File f = new File(dir); + if (!f.isAbsolute()) + f = new File(_context.getAppDir(), dir); + return f; + } + + /** null to set initial defaults */ + public void loadConfig(String filename) { + if (_config == null) + _config = new Properties(); + if (filename != null) { + File cfg = new File(filename); + if (!cfg.isAbsolute()) + cfg = new File(_context.getConfigDir(), filename); + _configFile = cfg; + if (cfg.exists()) { + try { + DataHelper.loadProps(_config, cfg); + } catch (IOException ioe) { + _log.error("Error loading I2PSnark config '" + filename + "'", ioe); + } + } + } + // now add sane defaults + if (!_config.containsKey(PROP_I2CP_HOST)) + _config.setProperty(PROP_I2CP_HOST, "127.0.0.1"); + if (!_config.containsKey(PROP_I2CP_PORT)) + _config.setProperty(PROP_I2CP_PORT, "7654"); + if (!_config.containsKey(PROP_I2CP_OPTS)) + _config.setProperty(PROP_I2CP_OPTS, "inbound.length=2 inbound.lengthVariance=0 outbound.length=2 outbound.lengthVariance=0 inbound.quantity=3 outbound.quantity=3"); + if (!_config.containsKey(PROP_EEP_HOST)) + _config.setProperty(PROP_EEP_HOST, "127.0.0.1"); + if (!_config.containsKey(PROP_EEP_PORT)) + _config.setProperty(PROP_EEP_PORT, "4444"); + if (!_config.containsKey(PROP_UPLOADERS_TOTAL)) + _config.setProperty(PROP_UPLOADERS_TOTAL, "" + Snark.MAX_TOTAL_UPLOADERS); + if (!_config.containsKey(PROP_DIR)) + _config.setProperty(PROP_DIR, "i2psnark"); + if (!_config.containsKey(PROP_AUTO_START)) + _config.setProperty(PROP_AUTO_START, DEFAULT_AUTO_START); + updateConfig(); + } + + /** call from DirMonitor since loadConfig() is called before router I2CP is up */ + private void getBWLimit() { + if (!_config.containsKey(PROP_UPBW_MAX)) { + int[] limits = BWLimits.getBWLimits(_util.getI2CPHost(), _util.getI2CPPort()); + if (limits != null && limits[1] > 0) + _util.setMaxUpBW(limits[1]); + } + } + + private void updateConfig() { + String i2cpHost = _config.getProperty(PROP_I2CP_HOST); + int i2cpPort = getInt(PROP_I2CP_PORT, 7654); + String opts = _config.getProperty(PROP_I2CP_OPTS); + Map i2cpOpts = new HashMap(); + if (opts != null) { + StringTokenizer tok = new StringTokenizer(opts, " "); + while (tok.hasMoreTokens()) { + String pair = tok.nextToken(); + int split = pair.indexOf('='); + if (split > 0) + i2cpOpts.put(pair.substring(0, split), pair.substring(split+1)); + } + } + if (i2cpHost != null) { + _util.setI2CPConfig(i2cpHost, i2cpPort, i2cpOpts); + _log.debug("Configuring with I2CP options " + i2cpOpts); + } + //I2PSnarkUtil.instance().setI2CPConfig("66.111.51.110", 7654, new Properties()); + String eepHost = _config.getProperty(PROP_EEP_HOST); + int eepPort = getInt(PROP_EEP_PORT, 4444); + if (eepHost != null) + _util.setProxy(eepHost, eepPort); + _util.setMaxUploaders(getInt(PROP_UPLOADERS_TOTAL, Snark.MAX_TOTAL_UPLOADERS)); + _util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW)); + getDataDir().mkdirs(); + } + + private int getInt(String prop, int defaultVal) { + String p = _config.getProperty(prop); + try { + if ( (p != null) && (p.trim().length() > 0) ) + return Integer.parseInt(p.trim()); + } catch (NumberFormatException nfe) { + // ignore + } + return defaultVal; + } + + public void updateConfig(String dataDir, boolean autoStart, String seedPct, String eepHost, + String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts, + String upLimit, String upBW, boolean useOpenTrackers, String openTrackers) { + boolean changed = false; + if (eepHost != null) { + int port = _util.getEepProxyPort(); + try { port = Integer.parseInt(eepPort); } catch (NumberFormatException nfe) {} + String host = _util.getEepProxyHost(); + if ( (eepHost.trim().length() > 0) && (port > 0) && + ((!host.equals(eepHost) || (port != _util.getEepProxyPort()) )) ) { + _util.setProxy(eepHost, port); + changed = true; + _config.setProperty(PROP_EEP_HOST, eepHost); + _config.setProperty(PROP_EEP_PORT, eepPort+""); + addMessage("EepProxy location changed to " + eepHost + ":" + port); + } + } + if (upLimit != null) { + int limit = _util.getMaxUploaders(); + try { limit = Integer.parseInt(upLimit); } catch (NumberFormatException nfe) {} + if ( limit != _util.getMaxUploaders()) { + if ( limit >= Snark.MIN_TOTAL_UPLOADERS ) { + _util.setMaxUploaders(limit); + changed = true; + _config.setProperty(PROP_UPLOADERS_TOTAL, "" + limit); + addMessage("Total uploaders limit changed to " + limit); + } else { + addMessage("Minimum total uploaders limit is " + Snark.MIN_TOTAL_UPLOADERS); + } + } + } + if (upBW != null) { + int limit = _util.getMaxUpBW(); + try { limit = Integer.parseInt(upBW); } catch (NumberFormatException nfe) {} + if ( limit != _util.getMaxUpBW()) { + if ( limit >= MIN_UP_BW ) { + _util.setMaxUpBW(limit); + changed = true; + _config.setProperty(PROP_UPBW_MAX, "" + limit); + addMessage("Up BW limit changed to " + limit + "KBps"); + } else { + addMessage("Minimum Up BW limit is " + MIN_UP_BW + "KBps"); + } + } + } + if (i2cpHost != null) { + int oldI2CPPort = _util.getI2CPPort(); + String oldI2CPHost = _util.getI2CPHost(); + int port = oldI2CPPort; + try { port = Integer.parseInt(i2cpPort); } catch (NumberFormatException nfe) {} + String host = oldI2CPHost; + Map opts = new HashMap(); + if (i2cpOpts == null) i2cpOpts = ""; + StringTokenizer tok = new StringTokenizer(i2cpOpts, " \t\n"); + while (tok.hasMoreTokens()) { + String pair = tok.nextToken(); + int split = pair.indexOf('='); + if (split > 0) + opts.put(pair.substring(0, split), pair.substring(split+1)); + } + Map oldOpts = new HashMap(); + String oldI2CPOpts = _config.getProperty(PROP_I2CP_OPTS); + if (oldI2CPOpts == null) oldI2CPOpts = ""; + tok = new StringTokenizer(oldI2CPOpts, " \t\n"); + while (tok.hasMoreTokens()) { + String pair = tok.nextToken(); + int split = pair.indexOf('='); + if (split > 0) + 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 snarksActive = false; + Set names = listTorrentFiles(); + for (Iterator iter = names.iterator(); iter.hasNext(); ) { + Snark snark = getTorrent((String)iter.next()); + if ( (snark != null) && (!snark.stopped) ) { + snarksActive = true; + break; + } + } + if (snarksActive) { + addMessage("Cannot change the I2CP settings while torrents are active"); + _log.debug("i2cp host [" + i2cpHost + "] i2cp port " + port + " opts [" + opts + + "] oldOpts [" + oldOpts + "]"); + } else { + if (_util.connected()) { + _util.disconnect(); + addMessage("Disconnecting old I2CP destination"); + } + Properties p = new Properties(); + p.putAll(opts); + addMessage("I2CP settings changed to " + i2cpHost + ":" + port + " (" + i2cpOpts.trim() + ")"); + _util.setI2CPConfig(i2cpHost, port, p); + boolean ok = _util.connect(); + if (!ok) { + addMessage("Unable to connect with the new settings, reverting to the old I2CP settings"); + _util.setI2CPConfig(oldI2CPHost, oldI2CPPort, oldOpts); + ok = _util.connect(); + if (!ok) + addMessage("Unable to reconnect with the old settings!"); + } else { + addMessage("Reconnected on the new I2CP destination"); + _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.acceptor != null) ) { + snark.acceptor.restart(); + addMessage("I2CP listener restarted for " + snark.meta.getName()); + } + } + } + } + changed = true; + } + } + if (shouldAutoStart() != autoStart) { + _config.setProperty(PROP_AUTO_START, autoStart + ""); + addMessage("Adjusted autostart to " + autoStart); + changed = true; + } + if (_util.shouldUseOpenTrackers() != useOpenTrackers) { + _config.setProperty(I2PSnarkUtil.PROP_USE_OPENTRACKERS, useOpenTrackers + ""); + addMessage((useOpenTrackers ? "En" : "Dis") + "abled open trackers - torrent restart required to take effect."); + changed = true; + } + if (openTrackers != null) { + if (openTrackers.trim().length() > 0 && !openTrackers.trim().equals(_util.getOpenTrackerString())) { + _config.setProperty(I2PSnarkUtil.PROP_OPENTRACKERS, openTrackers.trim()); + addMessage("Open Tracker list changed - torrent restart required to take effect."); + changed = true; + } + } + if (changed) { + saveConfig(); + } else { + addMessage("Configuration unchanged."); + } + } + + public void saveConfig() { + try { + synchronized (_configFile) { + DataHelper.storeProps(_config, _configFile); + } + } catch (IOException ioe) { + addMessage("Unable to save the config to '" + _configFile.getAbsolutePath() + "'."); + } + } + + public Properties getConfig() { return _config; } + + /** hardcoded for sanity. perhaps this should be customizable, for people who increase their ulimit, etc. */ + private static final int MAX_FILES_PER_TORRENT = 512; + + /** set of filenames that we are dealing with */ + public Set listTorrentFiles() { synchronized (_snarks) { return new HashSet(_snarks.keySet()); } } + /** + * Grab the torrent given the (canonical) filename + */ + public Snark getTorrent(String filename) { synchronized (_snarks) { return (Snark)_snarks.get(filename); } } + public void addTorrent(String filename) { addTorrent(filename, false); } + public void addTorrent(String filename, boolean dontAutoStart) { + if ((!dontAutoStart) && !_util.connected()) { + addMessage("Connecting to I2P"); + boolean ok = _util.connect(); + if (!ok) { + addMessage("Error connecting to I2P - check your I2CP settings!"); + return; + } + } + File sfile = new File(filename); + try { + filename = sfile.getCanonicalPath(); + } catch (IOException ioe) { + _log.error("Unable to add the torrent " + filename, ioe); + addMessage("ERR: Could not add the torrent '" + filename + "': " + ioe.getMessage()); + return; + } + File dataDir = getDataDir(); + Snark torrent = null; + synchronized (_snarks) { + torrent = (Snark)_snarks.get(filename); + } + // don't hold the _snarks lock while verifying the torrent + if (torrent == null) { + synchronized (_addSnarkLock) { + // double-check + synchronized (_snarks) { + if(_snarks.get(filename) != null) + return; + } + + FileInputStream fis = null; + try { + fis = new FileInputStream(sfile); + MetaInfo info = new MetaInfo(fis); + fis.close(); + fis = null; + + String rejectMessage = locked_validateTorrent(info); + if (rejectMessage != null) { + sfile.delete(); + addMessage(rejectMessage); + return; + } else { + torrent = new Snark(_util, filename, null, -1, null, null, this, + _peerCoordinatorSet, _connectionAcceptor, + false, dataDir.getPath()); + torrent.completeListener = this; + synchronized (_snarks) { + _snarks.put(filename, torrent); + } + } + } catch (IOException ioe) { + addMessage("Torrent in " + sfile.getName() + " is invalid: " + ioe.getMessage()); + if (sfile.exists()) + sfile.delete(); + return; + } finally { + if (fis != null) try { fis.close(); } catch (IOException ioe) {} + } + } + } else { + return; + } + // ok, snark created, now lets start it up or configure it further + File f = new File(filename); + if (!dontAutoStart && shouldAutoStart()) { + torrent.startTorrent(); + addMessage("Torrent added and started: '" + f.getName() + "'."); + } else { + addMessage("Torrent added: '" + f.getName() + "'."); + } + } + + /** + * Get the timestamp for a torrent from the config file + */ + public long getSavedTorrentTime(Snark snark) { + MetaInfo metainfo = snark.meta; + byte[] ih = metainfo.getInfoHash(); + String infohash = Base64.encode(ih); + infohash = infohash.replace('=', '$'); + String time = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX); + if (time == null) + return 0; + int comma = time.indexOf(','); + if (comma <= 0) + return 0; + time = time.substring(0, comma); + try { return Long.parseLong(time); } catch (NumberFormatException nfe) {} + return 0; + } + + /** + * Get the saved bitfield for a torrent from the config file. + * Convert "." to a full bitfield. + */ + public BitField getSavedTorrentBitField(Snark snark) { + MetaInfo metainfo = snark.meta; + byte[] ih = metainfo.getInfoHash(); + String infohash = Base64.encode(ih); + infohash = infohash.replace('=', '$'); + String bf = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX); + if (bf == null) + return null; + int comma = bf.indexOf(','); + if (comma <= 0) + return null; + bf = bf.substring(comma + 1).trim(); + int len = metainfo.getPieces(); + if (bf.equals(".")) { + BitField bitfield = new BitField(len); + for (int i = 0; i < len; i++) + bitfield.set(i); + return bitfield; + } + byte[] bitfield = Base64.decode(bf); + if (bitfield == null) + return null; + if (bitfield.length * 8 < len) + return null; + return new BitField(bitfield, len); + } + + /** + * Save the completion status of a torrent and the current time in the config file + * in the form "i2psnark.zmeta.$base64infohash=$time,$base64bitfield". + * The config file property key is appended with the Base64 of the infohash, + * with the '=' changed to '$' since a key can't contain '='. + * The time is a standard long converted to string. + * The status is either a bitfield converted to Base64 or "." for a completed + * torrent to save space in the config file and in memory. + */ + public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield) { + byte[] ih = metainfo.getInfoHash(); + String infohash = Base64.encode(ih); + infohash = infohash.replace('=', '$'); + String now = "" + System.currentTimeMillis(); + String bfs; + if (bitfield.complete()) { + bfs = "."; + } else { + byte[] bf = bitfield.getFieldBytes(); + bfs = Base64.encode(bf); + } + _config.setProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX, now + "," + bfs); + saveConfig(); + } + + /** + * Remove the status of a torrent from the config file. + * This may help the config file from growing too big. + */ + public void removeTorrentStatus(MetaInfo metainfo) { + byte[] ih = metainfo.getInfoHash(); + String infohash = Base64.encode(ih); + infohash = infohash.replace('=', '$'); + _config.remove(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX); + saveConfig(); + } + + private String locked_validateTorrent(MetaInfo info) throws IOException { + String announce = info.getAnnounce(); + // basic validation of url + if ((!announce.startsWith("http://")) || + (announce.indexOf(".i2p/") < 0)) // need to do better than this + return "Non-i2p tracker in " + info.getName() + ", deleting it from our list of trackers!"; + List files = info.getFiles(); + if ( (files != null) && (files.size() > MAX_FILES_PER_TORRENT) ) { + return "Too many files in " + info.getName() + " (" + files.size() + "), deleting it!"; + } else if (info.getPieces() <= 0) { + return "No pieces in " + info.getName() + "? deleting it!"; + } else if (info.getPieceLength(0) > Storage.MAX_PIECE_SIZE) { + return "Pieces are too large in " + info.getName() + " (" + DataHelper.formatSize(info.getPieceLength(0)) + + "B), deleting it."; + } else if (info.getTotalLength() > Storage.MAX_TOTAL_SIZE) { + System.out.println("torrent info: " + info.toString()); + List lengths = info.getLengths(); + if (lengths != null) + for (int i = 0; i < lengths.size(); i++) + System.out.println("File " + i + " is " + lengths.get(i) + " long."); + + return "Torrents larger than " + DataHelper.formatSize(Storage.MAX_TOTAL_SIZE) + + "B are not supported yet (because we're paranoid): " + info.getName() + ", deleting it!"; + } else { + // ok + return null; + } + } + + /** + * Stop the torrent, leaving it on the list of torrents unless told to remove it + */ + public Snark stopTorrent(String filename, boolean shouldRemove) { + File sfile = new File(filename); + try { + filename = sfile.getCanonicalPath(); + } catch (IOException ioe) { + _log.error("Unable to remove the torrent " + filename, ioe); + addMessage("ERR: Could not remove the torrent '" + filename + "': " + ioe.getMessage()); + return null; + } + int remaining = 0; + Snark torrent = null; + synchronized (_snarks) { + if (shouldRemove) + torrent = (Snark)_snarks.remove(filename); + else + torrent = (Snark)_snarks.get(filename); + remaining = _snarks.size(); + } + if (torrent != null) { + boolean wasStopped = torrent.stopped; + torrent.stopTorrent(); + if (remaining == 0) { + // should we disconnect/reconnect here (taking care to deal with the other thread's + // I2PServerSocket.accept() call properly?) + ////_util. + } + if (!wasStopped) + addMessage("Torrent stopped: '" + sfile.getName() + "'."); + } + return torrent; + } + /** + * Stop the torrent and delete the torrent file itself, but leaving the data + * behind. + */ + public void removeTorrent(String filename) { + Snark torrent = stopTorrent(filename, true); + if (torrent != null) { + File torrentFile = new File(filename); + torrentFile.delete(); + if (torrent.storage != null) + removeTorrentStatus(torrent.storage.getMetaInfo()); + addMessage("Torrent removed: '" + torrentFile.getName() + "'."); + } + } + + private class DirMonitor implements Runnable { + public void run() { + try { Thread.sleep(60*1000*getStartupDelayMinutes()); } catch (InterruptedException ie) {} + // the first message was a "We are starting up in 1m" + synchronized (_messages) { + if (_messages.size() == 1) + _messages.remove(0); + } + + // here because we need to delay until I2CP is up + // although the user will see the default until then + getBWLimit(); + while (true) { + File dir = getDataDir(); + _log.debug("Directory Monitor loop over " + dir.getAbsolutePath()); + try { + monitorTorrents(dir); + } catch (Exception e) { + _log.error("Error in the DirectoryMonitor", e); + } + try { Thread.sleep(60*1000); } catch (InterruptedException ie) {} + } + } + } + + /** two listeners */ + public void torrentComplete(Snark snark) { + File f = new File(snark.torrent); + long len = snark.meta.getTotalLength(); + addMessage("Download finished: " + f.getName() + " (size: " + DataHelper.formatSize(len) + "B)"); + updateStatus(snark); + } + + public void updateStatus(Snark snark) { + saveTorrentStatus(snark.meta, snark.storage.getBitField()); + } + + private void monitorTorrents(File dir) { + String fileNames[] = dir.list(TorrentFilenameFilter.instance()); + List foundNames = new ArrayList(0); + if (fileNames != null) { + for (int i = 0; i < fileNames.length; i++) { + try { + foundNames.add(new File(dir, fileNames[i]).getCanonicalPath()); + } catch (IOException ioe) { + _log.error("Error resolving '" + fileNames[i] + "' in '" + dir, ioe); + } + } + } + + Set existingNames = listTorrentFiles(); + // lets find new ones first... + for (int i = 0; i < foundNames.size(); i++) { + if (existingNames.contains(foundNames.get(i))) { + // already known. noop + } else { + if (shouldAutoStart() && !_util.connect()) + addMessage("Unable to connect to I2P!"); + addTorrent((String)foundNames.get(i), !shouldAutoStart()); + } + } + // now lets see which ones have been removed... + for (Iterator iter = existingNames.iterator(); iter.hasNext(); ) { + String name = (String)iter.next(); + if (foundNames.contains(name)) { + // known and still there. noop + } else { + // known, but removed. drop it + stopTorrent(name, true); + } + } + } + + private static final String DEFAULT_TRACKERS[] = { +// "Postman", "http://YRgrgTLGnbTq2aZOZDJQ~o6Uk5k6TK-OZtx0St9pb0G-5EGYURZioxqYG8AQt~LgyyI~NCj6aYWpPO-150RcEvsfgXLR~CxkkZcVpgt6pns8SRc3Bi-QSAkXpJtloapRGcQfzTtwllokbdC-aMGpeDOjYLd8b5V9Im8wdCHYy7LRFxhEtGb~RL55DA8aYOgEXcTpr6RPPywbV~Qf3q5UK55el6Kex-6VCxreUnPEe4hmTAbqZNR7Fm0hpCiHKGoToRcygafpFqDw5frLXToYiqs9d4liyVB-BcOb0ihORbo0nS3CLmAwZGvdAP8BZ7cIYE3Z9IU9D1G8JCMxWarfKX1pix~6pIA-sp1gKlL1HhYhPMxwyxvuSqx34o3BqU7vdTYwWiLpGM~zU1~j9rHL7x60pVuYaXcFQDR4-QVy26b6Pt6BlAZoFmHhPcAuWfu-SFhjyZYsqzmEmHeYdAwa~HojSbofg0TMUgESRXMw6YThK1KXWeeJVeztGTz25sL8AAAA.i2p/announce.php=http://tracker.postman.i2p/" +// , "eBook", "http://E71FRom6PZNEqTN2Lr8P-sr23b7HJVC32KoGnVQjaX6zJiXwhJy2HsXob36Qmj81TYFZdewFZa9mSJ533UZgGyQkXo2ahctg82JKYZfDe5uDxAn1E9YPjxZCWJaFJh0S~UwSs~9AZ7UcauSJIoNtpxrtbmRNVFLqnkEDdLZi26TeucfOmiFmIWnVblLniWv3tG1boE9Abd-6j3FmYVrRucYuepAILYt6katmVNOk6sXmno1Eynrp~~MBuFq0Ko6~jsc2E2CRVYXDhGHEMdt-j6JUz5D7S2RIVzDRqQyAZLKJ7OdQDmI31przzmne1vOqqqLC~1xUumZVIvF~yOeJUGNjJ1Vx0J8i2BQIusn1pQJ6UCB~ZtZZLQtEb8EPVCfpeRi2ri1M5CyOuxN0V5ekmPHrYIBNevuTCRC26NP7ZS5VDgx1~NaC3A-CzJAE6f1QXi0wMI9aywNG5KGzOPifcsih8eyGyytvgLtrZtV7ykzYpPCS-rDfITncpn5hliPUAAAA.i2p/pub/bt/announce.php=http://de-ebook-archiv.i2p/pub/bt/" +// , "Gaytorrents", "http://uxPWHbK1OIj9HxquaXuhMiIvi21iK0~ZiG9d8G0840ZXIg0r6CbiV71xlsqmdnU6wm0T2LySriM0doW2gUigo-5BNkUquHwOjLROiETnB3ZR0Ml4IGa6QBPn1aAq2d9~g1r1nVjLE~pcFnXB~cNNS7kIhX1d6nLgYVZf0C2cZopEow2iWVUggGGnAA9mHjE86zLEnTvAyhbAMTqDQJhEuLa0ZYSORqzJDMkQt90MV4YMjX1ICY6RfUSFmxEqu0yWTrkHsTtRw48l~dz9wpIgc0a0T9C~eeWvmBFTqlJPtQZwntpNeH~jF7nlYzB58olgV2HHFYpVYD87DYNzTnmNWxCJ5AfDorm6AIUCV2qaE7tZtI1h6fbmGpGlPyW~Kw5GXrRfJwNvr6ajwAVi~bPVnrBwDZezHkfW4slOO8FACPR28EQvaTu9nwhAbqESxV2hCTq6vQSGjuxHeOuzBOEvRWkLKOHWTC09t2DbJ94FSqETmZopTB1ukEmaxRWbKSIaAAAA.i2p/announce.php=http://gaytorrents.i2p/" +// , "NickyB", "http://9On6d3cZ27JjwYCtyJJbowe054d5tFnfMjv4PHsYs-EQn4Y4mk2zRixatvuAyXz2MmRfXG-NAUfhKr0KCxRNZbvHmlckYfT-WBzwwpiMAl0wDFY~Pl8cqXuhfikSG5WrqdPfDNNIBuuznS0dqaczf~OyVaoEOpvuP3qV6wKqbSSLpjOwwAaQPHjlRtNIW8-EtUZp-I0LT45HSoowp~6b7zYmpIyoATvIP~sT0g0MTrczWhbVTUZnEkZeLhOR0Duw1-IRXI2KHPbA24wLO9LdpKKUXed05RTz0QklW5ROgR6TYv7aXFufX8kC0-DaKvQ5JKG~h8lcoHvm1RCzNqVE-2aiZnO2xH08H-iCWoLNJE-Td2kT-Tsc~3QdQcnEUcL5BF-VT~QYRld2--9r0gfGl-yDrJZrlrihHGr5J7ImahelNn9PpkVp6eIyABRmJHf2iicrk3CtjeG1j9OgTSwaNmEpUpn4aN7Kx0zNLdH7z6uTgCGD9Kmh1MFYrsoNlTp4AAAA.i2p/bittorrent/announce.php=http://nickyb.i2p/bittorrent/" +// , "Orion", "http://gKik1lMlRmuroXVGTZ~7v4Vez3L3ZSpddrGZBrxVriosCQf7iHu6CIk8t15BKsj~P0JJpxrofeuxtm7SCUAJEr0AIYSYw8XOmp35UfcRPQWyb1LsxUkMT4WqxAT3s1ClIICWlBu5An~q-Mm0VFlrYLIPBWlUFnfPR7jZ9uP5ZMSzTKSMYUWao3ejiykr~mtEmyls6g-ZbgKZawa9II4zjOy-hdxHgP-eXMDseFsrym4Gpxvy~3Fv9TuiSqhpgm~UeTo5YBfxn6~TahKtE~~sdCiSydqmKBhxAQ7uT9lda7xt96SS09OYMsIWxLeQUWhns-C~FjJPp1D~IuTrUpAFcVEGVL-BRMmdWbfOJEcWPZ~CBCQSO~VkuN1ebvIOr9JBerFMZSxZtFl8JwcrjCIBxeKPBmfh~xYh16BJm1BBBmN1fp2DKmZ2jBNkAmnUbjQOqWvUcehrykWk5lZbE7bjJMDFH48v3SXwRuDBiHZmSbsTY6zhGY~GkMQHNGxPMMSIAAAA.i2p/bt/announce.php=http://orion.i2p/bt/" +// , "anonymity", "http://8EoJZIKrWgGuDrxA3nRJs1jsPfiGwmFWL91hBrf0HA7oKhEvAna4Ocx47VLUR9retVEYBAyWFK-eZTPcvhnz9XffBEiJQQ~kFSCqb1fV6IfPiV3HySqi9U5Caf6~hC46fRd~vYnxmaBLICT3N160cxBETqH3v2rdxdJpvYt8q4nMk9LUeVXq7zqCTFLLG5ig1uKgNzBGe58iNcsvTEYlnbYcE930ABmrzj8G1qQSgSwJ6wx3tUQNl1z~4wSOUMan~raZQD60lRK70GISjoX0-D0Po9WmPveN3ES3g72TIET3zc3WPdK2~lgmKGIs8GgNLES1cXTolvbPhdZK1gxddRMbJl6Y6IPFyQ9o4-6Rt3Lp-RMRWZ2TG7j2OMcNSiOmATUhKEFBDfv-~SODDyopGBmfeLw16F4NnYednvn4qP10dyMHcUASU6Zag4mfc2-WivrOqeWhD16fVAh8MoDpIIT~0r9XmwdaVFyLcjbXObabJczxCAW3fodQUnvuSkwzAAAA.i2p/anonymityTracker/announce.php=http://anonymityweb.i2p/anonymityTracker/" +// , "The freak's tracker", "http://mHKva9x24E5Ygfey2llR1KyQHv5f8hhMpDMwJDg1U-hABpJ2NrQJd6azirdfaR0OKt4jDlmP2o4Qx0H598~AteyD~RJU~xcWYdcOE0dmJ2e9Y8-HY51ie0B1yD9FtIV72ZI-V3TzFDcs6nkdX9b81DwrAwwFzx0EfNvK1GLVWl59Ow85muoRTBA1q8SsZImxdyZ-TApTVlMYIQbdI4iQRwU9OmmtefrCe~ZOf4UBS9-KvNIqUL0XeBSqm0OU1jq-D10Ykg6KfqvuPnBYT1BYHFDQJXW5DdPKwcaQE4MtAdSGmj1epDoaEBUa9btQlFsM2l9Cyn1hzxqNWXELmx8dRlomQLlV4b586dRzW~fLlOPIGC13ntPXogvYvHVyEyptXkv890jC7DZNHyxZd5cyrKC36r9huKvhQAmNABT2Y~pOGwVrb~RpPwT0tBuPZ3lHYhBFYmD8y~AOhhNHKMLzea1rfwTvovBMByDdFps54gMN1mX4MbCGT4w70vIopS9yAAAA.i2p/bytemonsoon/announce.php" +// , "mastertracker", "http://VzXD~stRKbL3MOmeTn1iaCQ0CFyTmuFHiKYyo0Rd~dFPZFCYH-22rT8JD7i-C2xzYFa4jT5U2aqHzHI-Jre4HL3Ri5hFtZrLk2ax3ji7Qfb6qPnuYkuiF2E2UDmKUOppI8d9Ye7tjdhQVCy0izn55tBaB-U7UWdcvSK2i85sauyw3G0Gfads1Rvy5-CAe2paqyYATcDmGjpUNLoxbfv9KH1KmwRTNH6k1v4PyWYYnhbT39WfKMbBjSxVQRdi19cyJrULSWhjxaQfJHeWx5Z8Ev4bSPByBeQBFl2~4vqy0S5RypINsRSa3MZdbiAAyn5tr5slWR6QdoqY3qBQgBJFZppy-3iWkFqqKgSxCPundF8gdDLC5ddizl~KYcYKl42y9SGFHIukH-TZs8~em0~iahzsqWVRks3zRG~tlBcX2U3M2~OJs~C33-NKhyfZT7-XFBREvb8Szmd~p66jDxrwOnKaku-G6DyoQipJqIz4VHmY9-y5T8RrUcJcM-5lVoMpAAAA.i2p/announce.php=http://tracker.mastertracker.i2p/" +// , "Galen", "http://5jpwQMI5FT303YwKa5Rd38PYSX04pbIKgTaKQsWbqoWjIfoancFdWCShXHLI5G5ofOb0Xu11vl2VEMyPsg1jUFYSVnu4-VfMe3y4TKTR6DTpetWrnmEK6m2UXh91J5DZJAKlgmO7UdsFlBkQfR2rY853-DfbJtQIFl91tbsmjcA5CGQi4VxMFyIkBzv-pCsuLQiZqOwWasTlnzey8GcDAPG1LDcvfflGV~6F5no9mnuisZPteZKlrv~~TDoXTj74QjByWc4EOYlwqK8sbU9aOvz~s31XzErbPTfwiawiaZ0RUI-IDrKgyvmj0neuFTWgjRGVTH8bz7cBZIc3viy6ioD-eMQOrXaQL0TCWZUelRwHRvgdPiQrxdYQs7ixkajeHzxi-Pq0EMm5Vbh3j3Q9kfUFW3JjFDA-MLB4g6XnjCbM5J1rC0oOBDCIEfhQkszru5cyLjHiZ5yeA0VThgu~c7xKHybv~OMXION7V8pBKOgET7ZgAkw1xgYe3Kkyq5syAAAA.i2p/tr/announce.php=http://galen.i2p/tr/" + "POSTMAN", "http://tracker2.postman.i2p/announce.php=http://tracker2.postman.i2p/" + ,"WELTERDE", "http://BGKmlDOoH3RzFbPRfRpZV2FjpVj8~3moFftw5-dZfDf2070TOe8Tf2~DAVeaM6ZRLdmFEt~9wyFL8YMLMoLoiwGEH6IGW6rc45tstN68KsBDWZqkTohV1q9XFgK9JnCwE~Oi89xLBHsLMTHOabowWM6dkC8nI6QqJC2JODqLPIRfOVrDdkjLwtCrsckzLybNdFmgfoqF05UITDyczPsFVaHtpF1sRggOVmdvCM66otyonlzNcJbn59PA-R808vUrCPMGU~O9Wys0i-NoqtIbtWfOKnjCRFMNw5ex4n9m5Sxm9e20UkpKG6qzEuvKZWi8vTLe1NW~CBrj~vG7I3Ok4wybUFflBFOaBabxYJLlx4xTE1zJIVxlsekmAjckB4v-cQwulFeikR4LxPQ6mCQknW2HZ4JQIq6hL9AMabxjOlYnzh7kjOfRGkck8YgeozcyTvcDUcUsOuSTk06L4kdrv8h2Cozjbloi5zl6KTbj5ZTciKCxi73Pn9grICn-HQqEAAAA.i2p/a=http://tracker.welterde.i2p/stats?mode=top5" + , "CRSTRACK", "http://b4G9sCdtfvccMAXh~SaZrPqVQNyGQbhbYMbw6supq2XGzbjU4NcOmjFI0vxQ8w1L05twmkOvg5QERcX6Mi8NQrWnR0stLExu2LucUXg1aYjnggxIR8TIOGygZVIMV3STKH4UQXD--wz0BUrqaLxPhrm2Eh9Hwc8TdB6Na4ShQUq5Xm8D4elzNUVdpM~RtChEyJWuQvoGAHY3ppX-EJJLkiSr1t77neS4Lc-KofMVmgI9a2tSSpNAagBiNI6Ak9L1T0F9uxeDfEG9bBSQPNMOSUbAoEcNxtt7xOW~cNOAyMyGydwPMnrQ5kIYPY8Pd3XudEko970vE0D6gO19yoBMJpKx6Dh50DGgybLQ9CpRaynh2zPULTHxm8rneOGRcQo8D3mE7FQ92m54~SvfjXjD2TwAVGI~ae~n9HDxt8uxOecAAvjjJ3TD4XM63Q9TmB38RmGNzNLDBQMEmJFpqQU8YeuhnS54IVdUoVQFqui5SfDeLXlSkh4vYoMU66pvBfWbAAAA.i2p/tracker/announce.php=http://crstrack.i2p/tracker/" + + }; + + /** comma delimited list of name=announceURL=baseURL for the trackers to be displayed */ + public static final String PROP_TRACKERS = "i2psnark.trackers"; + private static Map trackerMap = null; + /** sorted map of name to announceURL=baseURL */ + public Map getTrackers() { + if (trackerMap != null) // only do this once, can't be updated while running + return trackerMap; + Map rv = new TreeMap(); + String trackers = _config.getProperty(PROP_TRACKERS); + if ( (trackers == null) || (trackers.trim().length() <= 0) ) + trackers = _context.getProperty(PROP_TRACKERS); + if ( (trackers == null) || (trackers.trim().length() <= 0) ) { + for (int i = 0; i < DEFAULT_TRACKERS.length; i += 2) + rv.put(DEFAULT_TRACKERS[i], DEFAULT_TRACKERS[i+1]); + } else { + StringTokenizer tok = new StringTokenizer(trackers, ","); + while (tok.hasMoreTokens()) { + String pair = tok.nextToken(); + int split = pair.indexOf('='); + if (split <= 0) + continue; + String name = pair.substring(0, split).trim(); + String url = pair.substring(split+1).trim(); + if ( (name.length() > 0) && (url.length() > 0) ) + rv.put(name, url); + } + } + + trackerMap = rv; + return trackerMap; + } + + private static class TorrentFilenameFilter implements FilenameFilter { + private static final TorrentFilenameFilter _filter = new TorrentFilenameFilter(); + public static TorrentFilenameFilter instance() { return _filter; } + public boolean accept(File dir, String name) { + return (name != null) && (name.endsWith(".torrent")); + } + } + + public class SnarkManagerShutdown extends I2PAppThread { + @Override + public void run() { + Set names = listTorrentFiles(); + for (Iterator iter = names.iterator(); iter.hasNext(); ) { + Snark snark = getTorrent((String)iter.next()); + if ( (snark != null) && (!snark.stopped) ) + snark.stopTorrent(); + } + } + } +} diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java index 04d534775..696cdbe44 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -85,8 +85,8 @@ public class I2PSnarkServlet extends HttpServlet { out.write("\n"); out.write(HEADER); out.write(""); - out.write("
\"I2PSnark
"); - out.write("
Forum\n"); + out.write("
"); + out.write("
I2PSnark Forum\n"); Map trackers = _manager.getTrackers(); for (Iterator iter = trackers.entrySet().iterator(); iter.hasNext(); ) { Map.Entry entry = (Map.Entry)iter.next(); @@ -112,18 +112,18 @@ public class I2PSnarkServlet extends HttpServlet { out.write(TABLE_HEADER); if (_manager.util().connected() && snarks.size() > 0) { if (peerParam != null) - out.write("(Hide Peers)
\n"); + out.write("(Hide Peers)
\n"); else - out.write("(Show Peers)
\n"); + out.write("(Show Peers)
\n"); } out.write(TABLE_HEADER2); - out.write(""); + out.write(""); if (_manager.util().connected()) out.write("Stop All"); + "\" title=\"Stop all torrents and the I2P tunnel\">Stop All"); else if (snarks.size() > 0) out.write("Start All"); + "\" title=\"Start all torrents and the I2P tunnel\">Start All"); else out.write(" "); out.write("\n"); @@ -460,7 +460,7 @@ public class I2PSnarkServlet extends HttpServlet { else { if (err.length() > MAX_DISPLAYED_ERROR_LENGTH) err = err.substring(0, MAX_DISPLAYED_ERROR_LENGTH) + "…"; - statusString = "TrackerErr
(" + err + ")"; + statusString = "TrackerErr
(" + err + ")"; } } else if (remaining <= 0) { if (isRunning && curPeers > 0 && !showPeers) @@ -492,7 +492,7 @@ public class I2PSnarkServlet extends HttpServlet { String rowClass = (row % 2 == 0 ? "snarkTorrentEven" : "snarkTorrentOdd"); out.write(""); - out.write(""); + out.write(""); out.write(statusString + "\n\t"); out.write(""); @@ -526,7 +526,7 @@ public class I2PSnarkServlet extends HttpServlet { } out.write("\n\t"); - out.write(""); + out.write(""); if(isRunning && remainingSeconds > 0) out.write(DataHelper.formatDuration(remainingSeconds*1000)); // (eta 6h) out.write("\n\t"); @@ -546,7 +546,7 @@ public class I2PSnarkServlet extends HttpServlet { if(isRunning) out.write(formatSize(upBps) + "ps"); out.write("\n\t"); - out.write(""); + out.write(""); String parameters = "&nonce=" + _nonce + "&torrent=" + Base64.encode(snark.meta.getInfoHash()); if (showPeers) parameters = parameters + "&p=1"; @@ -558,7 +558,7 @@ public class I2PSnarkServlet extends HttpServlet { out.write("Start "); out.write("Remove
"); + + "\" title=\"Remove the torrent from the active list, deleting the .torrent file\">Remove
"); out.write("Delete "); } @@ -571,9 +571,9 @@ public class I2PSnarkServlet extends HttpServlet { if (!peer.isConnected()) continue; out.write(""); - out.write(""); + out.write(""); out.write("\n\t"); - out.write(""); + out.write(""); String ch = peer.toString().substring(0, 4); String client; if ("AwMD".equals(ch)) @@ -592,7 +592,7 @@ public class I2PSnarkServlet extends HttpServlet { client = "Robert"; else client = "Unknown (" + ch + ')'; - out.write("" + client + "  " + peer.toString().substring(5, 9) + ""); + out.write("" + client + "  " + peer.toString().substring(5, 9) + ""); if (showDebug) out.write(" inactive " + (peer.getInactiveTime() / 1000) + "s"); out.write("\n\t"); @@ -660,13 +660,13 @@ public class I2PSnarkServlet extends HttpServlet { // *not* enctype="multipart/form-data", so that the input type=file sends the filename, not the file out.write("
\n"); out.write("\n"); - out.write("
Add Torrent:
\n"); + out.write("
Add Torrent:
\n"); out.write("From URL : \n"); // not supporting from file at the moment, since the file name passed isn't always absolute (so it may not resolve) - //out.write("From file:
\n"); - out.write("
\n"); - out.write("Alternately, you can copy .torrent files to " + _manager.getDataDir().getAbsolutePath() + "
\n"); - out.write("Removing that .torrent file will cause the torrent to stop.
\n"); + //out.write("From file:
\n"); + out.write("
\n"); + out.write("Alternately, you can copy .torrent files to " + _manager.getDataDir().getAbsolutePath() + "
\n"); + out.write("Removing that .torrent file will cause the torrent to stop.
\n"); out.write("\n
"); } @@ -680,11 +680,11 @@ public class I2PSnarkServlet extends HttpServlet { // *not* enctype="multipart/form-data", so that the input type=file sends the filename, not the file out.write("
\n"); out.write("\n"); - out.write("Create Torrent:
\n"); - //out.write("From file:
\n"); + out.write("Create Torrent:
\n"); + //out.write("From file:
\n"); out.write("Data to seed: " + _manager.getDataDir().getAbsolutePath() + File.separatorChar + "
\n"); + + "\" title=\"File to seed (must be within the specified path)\" />
\n"); out.write("Tracker: \n"); - out.write("Configuration:
\n"); + out.write("Configuration:
\n"); out.write("Data directory: (Edit i2psnark.config and restart to change)
\n"); + out.write("title=\"Directory to store torrents and data\" disabled=\"true\" /> (Edit i2psnark.config and restart to change)
\n"); out.write("Auto start: "); //Auto add: //Auto stop: - //out.write("
\n"); + //out.write("
\n"); /* out.write("Seed percentage:
\n"); + out.write("
\n"); */ out.write("Total uploader limit: peers
\n"); + + _manager.util().getMaxUploaders() + "\" size=\"3\" maxlength=\"3\" /> peers
\n"); out.write("Up bandwidth limit: KBps (Half available bandwidth recommended.)
\n"); + + _manager.util().getMaxUpBW() + "\" size=\"3\" maxlength=\"3\" /> KBps (Half available bandwidth recommended.)
\n"); out.write("Use open trackers also: "); out.write("Announce URLs:
\n"); + + openTrackers + "\" size=\"50\" />
\n"); //out.write("\n"); out.write("EepProxy host: "); out.write("port:
\n"); + + _manager.util().getEepProxyPort() + "\" size=\"5\" maxlength=\"5\" />
\n"); out.write("I2CP host: "); out.write("port:
\n"); + + _manager.util().getI2CPPort() + "\" size=\"5\" maxlength=\"5\" />
\n"); StringBuilder opts = new StringBuilder(64); Map options = new TreeMap(_manager.util().getI2CPOptions()); for (Iterator iter = options.entrySet().iterator(); iter.hasNext(); ) { @@ -768,7 +768,7 @@ public class I2PSnarkServlet extends HttpServlet { opts.append(key).append('=').append(val).append(' '); } out.write("I2CP opts:
\n"); + + opts.toString() + "\" />
\n"); out.write("\n"); out.write("\n"); out.write("
"); @@ -795,11 +795,11 @@ public class I2PSnarkServlet extends HttpServlet { private static final String TABLE_HEADER = "\n" + "\n" + - "\n" + " \n" + - " \n" + + " \n" + " \n" + " \n" + " \n" + diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java index 28484a57a..7a80c181f 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java @@ -345,7 +345,7 @@ public class I2PTunnelConnectClient extends I2PTunnelClientBase implements Runna if (targetRequest != null) { out.write(targetRequest.getBytes()); if (usingWWWProxy) - out.write(("
WWW proxy: " + wwwProxy).getBytes()); + out.write(("
WWW proxy: " + wwwProxy).getBytes()); } out.write("".getBytes()); out.write("\n\n".getBytes()); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index 59169a18c..075483529 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -388,7 +388,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable String conflictURL = protocol + alias + ".i2p/?" + initialFragments; out.write(header); out.write(("To visit the destination in your host database, click here. To visit the conflicting addresshelper link by temporarily giving it a random alias, click here.

").getBytes()); - out.write("

I2P HTTP Proxy Server
Generated on: ".getBytes()); + out.write("

I2P HTTP Proxy Server
Generated on: ".getBytes()); out.write(new Date().toString().getBytes()); out.write("

\n".getBytes()); out.flush(); @@ -705,9 +705,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable out.write("\">http://".getBytes()); out.write(uri.getBytes()); out.write("".getBytes()); - if (usingWWWProxy) out.write(("
WWW proxy: " + wwwProxy).getBytes()); + if (usingWWWProxy) out.write(("
WWW proxy: " + wwwProxy).getBytes()); if (showAddrHelper) { - out.write("

Click a link below to look for an address helper by using a \"jump\" service:
".getBytes()); + out.write("

Click a link below to look for an address helper by using a \"jump\" service:
".getBytes()); for (int i = 0; i < jumpServers.length; i++) { // Skip jump servers we don't know String jumphost = jumpServers[i].substring(7); // "http://" @@ -719,7 +719,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable continue; } - out.write("
".getBytes()); @@ -729,7 +729,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable } } } - out.write("

I2P HTTP Proxy Server
Generated on: ".getBytes()); + out.write("

I2P HTTP Proxy Server
Generated on: ".getBytes()); out.write(new Date().toString().getBytes()); out.write("

\n".getBytes()); out.flush(); diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp index 1b3c1eb81..4957e3a57 100644 --- a/apps/i2ptunnel/jsp/editClient.jsp +++ b/apps/i2ptunnel/jsp/editClient.jsp @@ -192,7 +192,7 @@
-

Advanced networking options

+

Advanced networking options


(NOTE: when this client proxy is configured to share tunnels, then these options are for all the shared proxy clients!)
@@ -335,10 +335,10 @@
Status \n"; + "
Status \n"; private static final String TABLE_HEADER2 = "TorrentETAETADownloadedUploadedDown Rate
class="tickbox" /> - Enable + Enable class="tickbox" /> - Disable + Disable
@@ -411,7 +411,8 @@
-
- - +
diff --git a/apps/routerconsole/jsp/configclients.jsp b/apps/routerconsole/jsp/configclients.jsp index 3cd7f4c76..81efcd76b 100644 --- a/apps/routerconsole/jsp/configclients.jsp +++ b/apps/routerconsole/jsp/configclients.jsp @@ -2,15 +2,13 @@ <%@page pageEncoding="UTF-8"%> - -I2P Router Console - config clients +I2P Router Console - config clients <%@include file="css.jsp" %> - + <%@include file="summary.jsp" %> @@ -19,33 +17,28 @@ button span.hide{

I2P Client Configuration

<%@include file="confignav.jsp" %> - + " /> " /> " /> -
-
+
<% String prev = System.getProperty("net.i2p.router.web.ConfigClientsHandler.nonce"); if (prev != null) System.setProperty("net.i2p.router.web.ConfigClientsHandler.noncePrev", prev); System.setProperty("net.i2p.router.web.ConfigClientsHandler.nonce", new java.util.Random().nextLong()+""); %> " /> -

Client Configuration

-

+

Client Configuration

The Java clients listed below are started by the router and run in the same JVM. -

- -

- To change other client options, edit the file +

+

+

To change other client options, edit the file <%=net.i2p.router.startup.ClientAppConfig.configFile(net.i2p.I2PAppContext.getGlobalContext()).getAbsolutePath()%>. All changes require restart to take effect. -


+


-
-

WebApp Configuration

-

+

WebApp Configuration

The Java web applications listed below are started by the webConsole client and run in the same JVM as the router. They are usually web applications accessible through the router console. They may be complete applications (e.g. i2psnark), @@ -55,18 +48,10 @@ All changes require restart to take effect. A web app may also be disabled by removing the .war file from the webapps directory; however the .war file and web app will reappear when you update your router to a newer version, so disabling the web app here is the preferred method. -

+

-

-

+

All changes require restart to take effect. -

-
+


-
- -
-
-
- - +
diff --git a/apps/routerconsole/jsp/configkeyring.jsp b/apps/routerconsole/jsp/configkeyring.jsp index 7e58f4a15..88957858d 100644 --- a/apps/routerconsole/jsp/configkeyring.jsp +++ b/apps/routerconsole/jsp/configkeyring.jsp @@ -2,8 +2,7 @@ <%@page pageEncoding="UTF-8"%> - -I2P Router Console - config keyring +I2P Router Console - config keyring <%@include file="css.jsp" %> @@ -16,21 +15,16 @@ " /> - - - " /> -
-

-

Keyring

+

Keyring

The router keyring is used to decrypt encrypted leaseSets. The keyring may contain keys for local or remote encrypted destinations. -

+

-
+
<% String prev = System.getProperty("net.i2p.router.web.ConfigKeyringHandler.nonce"); @@ -41,23 +35,14 @@ Enter keys for encrypted remote destinations here. Keys for local destinations must be entered on the I2PTunnel page.

-

- - - - - - - - - -
Dest. name, hash, or full key:
Session Key:
- -
- - -
-
-
- - +
+ + + + + + + + + +
Dest. name, hash, or full key:
Session Key:
diff --git a/apps/routerconsole/jsp/configlogging.jsp b/apps/routerconsole/jsp/configlogging.jsp index 432acdfb8..b4a08ee19 100644 --- a/apps/routerconsole/jsp/configlogging.jsp +++ b/apps/routerconsole/jsp/configlogging.jsp @@ -18,7 +18,7 @@ " /> -
+
<% String prev = System.getProperty("net.i2p.router.web.ConfigLoggingHandler.nonce"); if (prev != null) System.setProperty("net.i2p.router.web.ConfigLoggingHandler.noncePrev", prev); @@ -28,43 +28,26 @@

Configure I2P Logging Options

- - - - - - - - -
Logging filename: - " /> -
(the symbol '@' will be replaced during log rotation) -
Log record format: - " /> -
(use 'd' = date, 'c' = class, 't' = thread, 'p' = priority, - 'm' = message) -
Log date format: - " /> -
('MM' = month, 'dd' = day, 'HH' = hour, 'mm' = minute, 'ss' - = second, 'SSS' = millisecond) -
Max log file size: - " /> -
-
Default log level: -
(DEBUG and INFO are not recommended defaults, - as they will drastically slow down your router) -
Log level overrides: - -

-
-
- - -
-
- -
-
-
- - + Logging filename: + " /> +
(the symbol '@' will be replaced during log rotation) + Log record format: + " /> +
(use 'd' = date, 'c' = class, 't' = thread, 'p' = priority, + 'm' = message) + Log date format: + " /> +
('MM' = month, 'dd' = day, 'HH' = hour, 'mm' = minute, 'ss' + = second, 'SSS' = millisecond) + Max log file size: + " />
+ Default log level: +
(DEBUG and INFO are not recommended defaults, + as they will drastically slow down your router) + Log level overrides: + +
+
+ + +
diff --git a/apps/routerconsole/jsp/confignav.jsp b/apps/routerconsole/jsp/confignav.jsp index 37b6adbd0..bba31052e 100644 --- a/apps/routerconsole/jsp/confignav.jsp +++ b/apps/routerconsole/jsp/confignav.jsp @@ -1,6 +1,6 @@
-<% if (request.getRequestURI().indexOf("config.jsp") != -1) { +<% if (request.getRequestURI().indexOf("config.jsp") != -1) { %>Network | <% } else { %>Network | <% } String userAgent = request.getHeader("User-Agent"); if (userAgent == null || !userAgent.contains("MSIE")) { @@ -25,5 +25,4 @@ %>Stats | <% } else { %>Stats | <% } if (request.getRequestURI().indexOf("configadvanced.jsp") != -1) { %>Advanced<% } else { %>Advanced<% } %> -
-
+
diff --git a/apps/routerconsole/jsp/configpeer.jsp b/apps/routerconsole/jsp/configpeer.jsp index 6f891a329..007452229 100644 --- a/apps/routerconsole/jsp/configpeer.jsp +++ b/apps/routerconsole/jsp/configpeer.jsp @@ -2,8 +2,7 @@ <%@page pageEncoding="UTF-8"%> - -I2P Router Console - config peers +I2P Router Console - config peers <%@include file="css.jsp" %> @@ -11,19 +10,19 @@

I2P Peer Configuration

<%@include file="confignav.jsp" %> - + " /> - + " /> <% String peer = ""; - if (request.getParameter("peer") != null) + if (request.getParameter("peer") != null) peer = request.getParameter("peer"); %>
@@ -40,9 +39,9 @@
Router Hash:

Manually Ban / Unban a Peer

- Banning will prevent the participation of this peer in tunnels you create. -
-
+ Banning will prevent the participation of this peer in tunnels you create. +
+
<% if (! "".equals(peer)) { %> @@ -51,17 +50,17 @@

Adjust Profile Bonuses

- Bonuses may be positive or negative, and affect the peer's inclusion in Fast - and High Capacity tiers. Fast peers are used for client tunnels, and High - Capacity peers are used for some exploratory tunnels. Current bonuses are - displayed on the profiles page. + Bonuses may be positive or negative, and affect the peer's inclusion in Fast + and High Capacity tiers. Fast peers are used for client tunnels, and High + Capacity peers are used for some exploratory tunnels. Current bonuses are + displayed on the profiles page.

<% long speed = 0; long capacity = 0; if (! "".equals(peer)) { // get existing bonus values? } - %> -


+ %> +
Speed: Capacity: @@ -73,12 +72,8 @@ " /> -
+
-
-
-
- - +
diff --git a/apps/routerconsole/jsp/configservice.jsp b/apps/routerconsole/jsp/configservice.jsp index 942aedf0d..45b5e435f 100644 --- a/apps/routerconsole/jsp/configservice.jsp +++ b/apps/routerconsole/jsp/configservice.jsp @@ -2,8 +2,7 @@ <%@page pageEncoding="UTF-8"%> - -I2P Router Console - config service +I2P Router Console - config service <%@include file="css.jsp" %> @@ -11,7 +10,7 @@

I2P Service Configuration

<%@include file="confignav.jsp" %> - + " /> @@ -23,7 +22,7 @@ System.setProperty("net.i2p.router.web.ConfigServiceHandler.nonce", new java.util.Random().nextLong()+""); %> " />

Shutdown the router

-

Graceful shutdown lets the router satisfy the agreements it has already made +

Graceful shutdown lets the router satisfy the agreements it has already made before shutting down, but may take a few minutes. If you need to kill the router immediately, that option is available as well.


@@ -32,7 +31,7 @@
<% if (System.getProperty("wrapper.version") != null) { %> -

If you want the router to restart itself after shutting down, you can choose one of +

If you want the router to restart itself after shutting down, you can choose one of the following. This is useful in some situations - for example, if you changed some settings that client applications only read at startup, such as the routerconsole password or the interface it listens on. A graceful restart will take a few minutes (but your peers @@ -45,46 +44,41 @@ <% if ( (System.getProperty("os.name") != null) && (System.getProperty("os.name").startsWith("Win")) ) { %>

Systray integration

-

On the windows platform, there is a small application to sit in the system +

On the windows platform, there is a small application to sit in the system tray, allowing you to view the router's status (later on, I2P client applications will be able to integrate their own functionality into the system tray as well). If you are on windows, you can either enable or disable that icon here.


-
-

Run on startup

-

You can control whether I2P is run on startup or not by selecting one of the +

Run on startup

+

You can control whether I2P is run on startup or not by selecting one of the following options - I2P will install (or remove) a service accordingly. You can - also run the install_i2p_service_winnt.bat (or + also run the install_i2p_service_winnt.bat (or uninstall_i2p_service_winnt.bat) from the command line, if you prefer.


-

Note: If you are running I2P as service right now, removing it will shut +

Note: If you are running I2P as service right now, removing it will shut down your router immediately. You may want to consider shutting down gracefully, as above, then running uninstall_i2p_service_winnt.bat.

<% } %> <% if (System.getProperty("wrapper.version") != null) { %>

Debugging

-

At times, it may be helpful to debug I2P by getting a thread dump. To do so, - please select the following option and review the thread dumped to +

At times, it may be helpful to debug I2P by getting a thread dump. To do so, + please select the following option and review the thread dumped to wrapper.log.


<% } %>
- +

Launch browser on router startup?

I2P's main configuration interface is this web console, so for your convenience - I2P can launch a web browser pointing at + I2P can launch a web browser pointing at http://127.0.0.1:7657/index.jsp whenever the router starts up.


-
- - - - + diff --git a/apps/routerconsole/jsp/configstats.jsp b/apps/routerconsole/jsp/configstats.jsp index 9a2435ec4..122d035c3 100644 --- a/apps/routerconsole/jsp/configstats.jsp +++ b/apps/routerconsole/jsp/configstats.jsp @@ -2,8 +2,7 @@ <%@page pageEncoding="UTF-8"%> - -I2P Router Console - config stats +I2P Router Console - config stats <%@include file="css.jsp" %>