Compare commits
114 Commits
i2p-0.7.13
...
i2p-0.7.14
Author | SHA1 | Date | |
---|---|---|---|
746bad3c30 | |||
5bbd61b75c | |||
27eb7e46d0 | |||
c20bef3731 | |||
fc60768a66 | |||
2024fb1b65 | |||
617ca79b8f | |||
7bfb5b1bf4 | |||
8d73529fa4 | |||
a19d04d3ba | |||
a9c7748a52 | |||
41e4e952b7 | |||
c0b0b5e4c5 | |||
e424479e7e | |||
a8804f3093 | |||
6479a24bb7 | |||
8b372ad306 | |||
86791a2f1b | |||
7cf0aad388 | |||
c5ea51beec | |||
7cc8e51d73 | |||
75ba58d68c | |||
cd35b219db | |||
4a863f8ce7 | |||
24264548a6 | |||
f9e4b1a56b | |||
13b54b864e | |||
05d45fe945 | |||
2781f6035a | |||
dc3378d084 | |||
9132e94143 | |||
b61e2aa73c | |||
7fdbae3b0f | |||
4dc6fc3b5d | |||
618275b1f9 | |||
7a1111d845 | |||
3af356840e | |||
911a278926 | |||
014063700f | |||
f7c0db0454 | |||
a534d25d82 | |||
bcf3e4a2d3 | |||
0cdfbd9803 | |||
a3e5654d86 | |||
2f9364db2b | |||
5d7c9ebf82 | |||
48da98d0e4 | |||
55e994ac3c | |||
6d46a21f9f | |||
fdc83484fd | |||
6786817fff | |||
b77cd0db15 | |||
20bef76878 | |||
7a30490482 | |||
3bc2e469cc | |||
d770d3c6da | |||
339a001592 | |||
ace57a96a9 | |||
2c26b8d422 | |||
e1eafa2394 | |||
39cb51c9eb | |||
fa5016ab04 | |||
b134ef1a74 | |||
234dff888d | |||
a08c15a3ee | |||
cfa894e7b6 | |||
d6c8e64575 | |||
dc91580e30 | |||
7ec1dd7a98 | |||
82f3f7506c | |||
e26df1c26b | |||
aea77cf225 | |||
a1e3ef9c5c | |||
7aece71342 | |||
bdbde54f04 | |||
157e035710 | |||
97d9a3a4e5 | |||
cb7f111ade | |||
35f670706a | |||
3fac888fe5 | |||
d843646b4f | |||
c2b73d9fb5 | |||
9da95b8165 | |||
5bcd8efe14 | |||
027a1d748d | |||
6d6e012c19 | |||
a8db6b007f | |||
f3576e54c6 | |||
0325f6c4d2 | |||
8225ce063a | |||
c2c379c994 | |||
7344c2af47 | |||
f484ea8c64 | |||
ac790492eb | |||
9ac5fb4890 | |||
2baee7413c | |||
16bec08f09 | |||
afb3c76922 | |||
2f526b35e8 | |||
2dc32aa310 | |||
10e669165a | |||
b6cb90d731 | |||
949a8901fb | |||
d608f450af | |||
e0a1341901 | |||
2cfb03f17d | |||
4dd0f51da4 | |||
d65a3e54a2 | |||
c212eacf19 | |||
46f341d782 | |||
ab4ff5548d | |||
d4713e1e6c | |||
8a3a1466c9 | |||
a5af9dc973 |
@ -64,6 +64,9 @@ Public domain except as listed below:
|
||||
Copyright 2006 Gregory Rubin grrubin@gmail.com
|
||||
See licenses/LICENSE-HashCash.txt
|
||||
|
||||
GettextResource from gettext v0.18:
|
||||
Copyright (C) 2001, 2007 Free Software Foundation, Inc.
|
||||
See licenses/LICENSE-LGPLv2.1.txt
|
||||
|
||||
|
||||
Router:
|
||||
@ -139,6 +142,7 @@ Applications:
|
||||
I2PSnark:
|
||||
Copyright (C) 2003 Mark J. Wielaard
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
Silk icons: See licenses/LICENSE-SilkIcons.txt
|
||||
|
||||
I2PTunnel:
|
||||
(c) 2003 - 2004 mihi
|
||||
@ -176,6 +180,7 @@ Applications:
|
||||
Router console:
|
||||
Public domain.
|
||||
Flag icons: public domain, courtesy mjames@gmail.com http://www.famfamfam.com/
|
||||
Silk icons: See licenses/LICENSE-SilkIcons.txt
|
||||
|
||||
GeoIP Data:
|
||||
Copyright (c) 2003 Direct Information Pvt. Ltd. All Rights Reserved.
|
||||
|
@ -69,7 +69,7 @@ public class I2PAndroid extends Activity
|
||||
|
||||
// from routerconsole ContextHelper
|
||||
List contexts = RouterContext.listContexts();
|
||||
if ( (contexts == null) || (contexts.size() <= 0) )
|
||||
if ( (contexts == null) || (contexts.isEmpty()) )
|
||||
throw new IllegalStateException("No contexts. This is usually because the router is either starting up or shutting down.");
|
||||
RouterContext ctx = (RouterContext)contexts.get(0);
|
||||
|
||||
|
BIN
apps/i2psnark/_icons/application.png
Normal file
After Width: | Height: | Size: 464 B |
BIN
apps/i2psnark/_icons/book.png
Normal file
After Width: | Height: | Size: 593 B |
BIN
apps/i2psnark/_icons/bug.png
Normal file
After Width: | Height: | Size: 774 B |
BIN
apps/i2psnark/_icons/cancel.png
Normal file
After Width: | Height: | Size: 587 B |
BIN
apps/i2psnark/_icons/clock.png
Normal file
After Width: | Height: | Size: 882 B |
BIN
apps/i2psnark/_icons/compress.png
Normal file
After Width: | Height: | Size: 766 B |
BIN
apps/i2psnark/_icons/film.png
Normal file
After Width: | Height: | Size: 653 B |
BIN
apps/i2psnark/_icons/folder.png
Normal file
After Width: | Height: | Size: 537 B |
BIN
apps/i2psnark/_icons/html.png
Normal file
After Width: | Height: | Size: 578 B |
BIN
apps/i2psnark/_icons/music.png
Normal file
After Width: | Height: | Size: 385 B |
BIN
apps/i2psnark/_icons/package.png
Normal file
After Width: | Height: | Size: 853 B |
BIN
apps/i2psnark/_icons/page.png
Normal file
After Width: | Height: | Size: 635 B |
BIN
apps/i2psnark/_icons/page_white_acrobat.png
Normal file
After Width: | Height: | Size: 591 B |
BIN
apps/i2psnark/_icons/photo.png
Normal file
After Width: | Height: | Size: 589 B |
BIN
apps/i2psnark/_icons/plugin.png
Normal file
After Width: | Height: | Size: 591 B |
BIN
apps/i2psnark/_icons/tick.png
Normal file
After Width: | Height: | Size: 537 B |
@ -52,7 +52,7 @@
|
||||
<classes dir="./build/obj" includes="**/I2PSnarkServlet*.class" />
|
||||
-->
|
||||
<target name="war" depends="jar, bundle">
|
||||
<war destfile="../i2psnark.war" webxml="../web.xml">
|
||||
<war destfile="../i2psnark.war" webxml="../web.xml" basedir="../" includes="_icons/*" >
|
||||
<!-- include only the web stuff, as of 0.7.12 the router will add i2psnark.jar to the classpath for the war -->
|
||||
<classes dir="./build/obj" includes="**/web/*.class" />
|
||||
</war>
|
||||
|
@ -49,7 +49,7 @@ do
|
||||
# To start a new translation, copy the header from an old translation to the new .po file,
|
||||
# then ant distclean poupdate.
|
||||
find $JPATHS -name *.java > $TMPFILE
|
||||
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 \
|
||||
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 --add-comments\
|
||||
--keyword=_ --keyword=_x \
|
||||
-o ${i}t
|
||||
if [ $? -ne 0 ]
|
||||
|
@ -45,7 +45,7 @@ public class I2PSnarkUtil {
|
||||
private int _proxyPort;
|
||||
private String _i2cpHost;
|
||||
private int _i2cpPort;
|
||||
private Map _opts;
|
||||
private Map<String, String> _opts;
|
||||
private I2PSocketManager _manager;
|
||||
private boolean _configured;
|
||||
private final Set<Hash> _shitlist;
|
||||
@ -65,7 +65,7 @@ public class I2PSnarkUtil {
|
||||
_context = ctx;
|
||||
_log = _context.logManager().getLog(Snark.class);
|
||||
_opts = new HashMap();
|
||||
setProxy("127.0.0.1", 4444);
|
||||
//setProxy("127.0.0.1", 4444);
|
||||
setI2CPConfig("127.0.0.1", 7654, null);
|
||||
_shitlist = new ConcurrentHashSet();
|
||||
_configured = false;
|
||||
@ -85,6 +85,7 @@ public class I2PSnarkUtil {
|
||||
* host for no proxying)
|
||||
*
|
||||
*/
|
||||
/*****
|
||||
public void setProxy(String host, int port) {
|
||||
if ( (host != null) && (port > 0) ) {
|
||||
_shouldProxy = true;
|
||||
@ -97,6 +98,7 @@ public class I2PSnarkUtil {
|
||||
}
|
||||
_configured = true;
|
||||
}
|
||||
******/
|
||||
|
||||
public boolean configured() { return _configured; }
|
||||
|
||||
@ -128,7 +130,7 @@ public class I2PSnarkUtil {
|
||||
|
||||
public String getI2CPHost() { return _i2cpHost; }
|
||||
public int getI2CPPort() { return _i2cpPort; }
|
||||
public Map getI2CPOptions() { return _opts; }
|
||||
public Map<String, String> getI2CPOptions() { return _opts; }
|
||||
public String getEepProxyHost() { return _proxyHost; }
|
||||
public int getEepProxyPort() { return _proxyPort; }
|
||||
public boolean getEepProxySet() { return _shouldProxy; }
|
||||
@ -355,7 +357,7 @@ public class I2PSnarkUtil {
|
||||
while (tok.hasMoreTokens())
|
||||
rv.add(tok.nextToken());
|
||||
|
||||
if (rv.size() <= 0)
|
||||
if (rv.isEmpty())
|
||||
return null;
|
||||
return rv;
|
||||
}
|
||||
@ -428,4 +430,9 @@ public class I2PSnarkUtil {
|
||||
public String getString(String s, Object o, Object o2) {
|
||||
return Translate.getString(s, o, o2, _context, BUNDLE_NAME);
|
||||
}
|
||||
|
||||
/** ngettext @since 0.7.14 */
|
||||
public String getString(int n, String s, String p) {
|
||||
return Translate.getString(n, s, p, _context, BUNDLE_NAME);
|
||||
}
|
||||
}
|
||||
|
@ -388,6 +388,7 @@ public class Peer implements Comparable
|
||||
* Sets whether or not we are interested in pieces from this peer.
|
||||
* Defaults to false. When interest is true and this peer unchokes
|
||||
* us then we start downloading from it. Has no effect when not connected.
|
||||
* @deprecated unused
|
||||
*/
|
||||
public void setInteresting(boolean interest)
|
||||
{
|
||||
|
@ -26,6 +26,8 @@ import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* TimerTask that checks for good/bad up/downloader. Works together
|
||||
* with the PeerCoordinator to select which Peers get (un)choked.
|
||||
@ -43,7 +45,7 @@ class PeerCheckerTask extends TimerTask
|
||||
this.coordinator = coordinator;
|
||||
}
|
||||
|
||||
private Random random = new Random();
|
||||
private static final Random random = I2PAppContext.getGlobalContext().random();
|
||||
|
||||
public void run()
|
||||
{
|
||||
@ -113,7 +115,7 @@ class PeerCheckerTask extends TimerTask
|
||||
+ " C: " + peer.isChoked(),
|
||||
Snark.DEBUG);
|
||||
|
||||
// Choke half of them rather than all so it isn't so drastic...
|
||||
// Choke a percentage of them rather than all so it isn't so drastic...
|
||||
// unless this torrent is over the limit all by itself.
|
||||
boolean overBWLimitChoke = upload > 0 &&
|
||||
((overBWLimit && random.nextBoolean()) ||
|
||||
|
@ -142,7 +142,7 @@ class PeerConnectionOut implements Runnable
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
if (m == null && sendQueue.size() > 0) {
|
||||
if (m == null && !sendQueue.isEmpty()) {
|
||||
m = (Message)sendQueue.remove(0);
|
||||
SimpleTimer.getInstance().removeEvent(m.expireEvent);
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ public class PeerCoordinator implements PeerListener
|
||||
// Install a timer to check the uploaders.
|
||||
// Randomize the first start time so multiple tasks are spread out,
|
||||
// this will help the behavior with global limits
|
||||
Random r = new Random();
|
||||
Random r = I2PAppContext.getGlobalContext().random();
|
||||
timer.schedule(new PeerCheckerTask(_util, this), (CHECK_PERIOD / 2) + r.nextInt((int) CHECK_PERIOD), CHECK_PERIOD);
|
||||
}
|
||||
|
||||
@ -272,7 +272,7 @@ public class PeerCoordinator implements PeerListener
|
||||
peerCount = 0;
|
||||
}
|
||||
|
||||
while (removed.size() > 0) {
|
||||
while (!removed.isEmpty()) {
|
||||
Peer peer = (Peer)removed.remove(0);
|
||||
peer.disconnect();
|
||||
removePeerFromPieces(peer);
|
||||
@ -428,7 +428,7 @@ public class PeerCoordinator implements PeerListener
|
||||
}
|
||||
}
|
||||
|
||||
while (uploaders < maxUploaders && interested.size() > 0)
|
||||
while (uploaders < maxUploaders && !interested.isEmpty())
|
||||
{
|
||||
Peer peer = (Peer)interested.remove(0);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -489,6 +489,13 @@ public class PeerCoordinator implements PeerListener
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be somewhat less than the max conns per torrent,
|
||||
* but not too much less, so a torrent doesn't get stuck near the end.
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private static final int END_GAME_THRESHOLD = 8;
|
||||
|
||||
/**
|
||||
* Returns one of pieces in the given BitField that is still wanted or
|
||||
* -1 if none of the given pieces are wanted.
|
||||
@ -522,6 +529,11 @@ public class PeerCoordinator implements PeerListener
|
||||
|
||||
//Only request a piece we've requested before if there's no other choice.
|
||||
if (piece == null) {
|
||||
// AND if there are almost no wanted pieces left (real end game).
|
||||
// If we do end game all the time, we generate lots of extra traffic
|
||||
// when the seeder is super-slow and all the peers are "caught up"
|
||||
if (wantedPieces.size() > END_GAME_THRESHOLD)
|
||||
return -1; // nothing to request and not in end game
|
||||
// let's not all get on the same piece
|
||||
Collections.shuffle(requested);
|
||||
Iterator it2 = requested.iterator();
|
||||
|
@ -321,7 +321,7 @@ public class Snark
|
||||
// sixteen random bytes.
|
||||
byte snark = (((3 + 7 + 10) * (1000 - 8)) / 992) - 17;
|
||||
id = new byte[20];
|
||||
Random random = new Random();
|
||||
Random random = I2PAppContext.getGlobalContext().random();
|
||||
int i;
|
||||
for (i = 0; i < 9; i++)
|
||||
id[i] = 0;
|
||||
@ -618,14 +618,14 @@ public class Snark
|
||||
command_interpreter = false;
|
||||
i++;
|
||||
}
|
||||
else if (args[i].equals("--eepproxy"))
|
||||
{
|
||||
String proxyHost = args[i+1];
|
||||
String proxyPort = args[i+2];
|
||||
if (!configured)
|
||||
util.setProxy(proxyHost, Integer.parseInt(proxyPort));
|
||||
i += 3;
|
||||
}
|
||||
//else if (args[i].equals("--eepproxy"))
|
||||
// {
|
||||
// String proxyHost = args[i+1];
|
||||
// String proxyPort = args[i+2];
|
||||
// if (!configured)
|
||||
// util.setProxy(proxyHost, Integer.parseInt(proxyPort));
|
||||
// i += 3;
|
||||
// }
|
||||
else if (args[i].equals("--i2cp"))
|
||||
{
|
||||
String i2cpHost = args[i+1];
|
||||
@ -734,7 +734,7 @@ public class Snark
|
||||
//if (debug >= INFO && t != null)
|
||||
// t.printStackTrace();
|
||||
stopTorrent();
|
||||
throw new RuntimeException("die bart die");
|
||||
throw new RuntimeException(s + (t == null ? "" : ": " + t));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,8 +29,8 @@ 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;
|
||||
/** map of (canonical) filename of the .torrent file to Snark instance (unsynchronized) */
|
||||
private final Map<String, Snark> _snarks;
|
||||
private final Object _addSnarkLock;
|
||||
private /* FIXME final FIXME */ File _configFile;
|
||||
private Properties _config;
|
||||
@ -46,8 +46,8 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
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_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";
|
||||
@ -157,10 +157,10 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
_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_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))
|
||||
@ -198,10 +198,10 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
_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);
|
||||
//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));
|
||||
String ot = _config.getProperty(I2PSnarkUtil.PROP_OPENTRACKERS);
|
||||
@ -226,20 +226,20 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
|
||||
String upLimit, String upBW, boolean useOpenTrackers, String openTrackers) {
|
||||
boolean changed = false;
|
||||
if (eepHost != null) {
|
||||
// unused, we use socket eepget
|
||||
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 (eepHost != null) {
|
||||
// // unused, we use socket eepget
|
||||
// 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) {}
|
||||
@ -308,7 +308,10 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
}
|
||||
}
|
||||
if (snarksActive) {
|
||||
addMessage(_("Cannot change the I2CP settings while torrents are active"));
|
||||
Properties p = new Properties();
|
||||
p.putAll(opts);
|
||||
_util.setI2CPConfig(i2cpHost, port, p);
|
||||
addMessage(_("I2CP and tunnel changes will take effect after stopping all torrents"));
|
||||
_log.debug("i2cp host [" + i2cpHost + "] i2cp port " + port + " opts [" + opts
|
||||
+ "] oldOpts [" + oldOpts + "]");
|
||||
} else {
|
||||
@ -393,12 +396,30 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
/** 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()); } }
|
||||
/** set of canonical .torrent filenames that we are dealing with */
|
||||
public Set<String> listTorrentFiles() { synchronized (_snarks) { return new HashSet(_snarks.keySet()); } }
|
||||
|
||||
/**
|
||||
* Grab the torrent given the (canonical) filename
|
||||
* 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); } }
|
||||
|
||||
/**
|
||||
* Grab the torrent given the base name of the storage
|
||||
* @return Snark or null
|
||||
* @since 0.7.14
|
||||
*/
|
||||
public Snark getTorrentByBaseName(String filename) {
|
||||
synchronized (_snarks) {
|
||||
for (Snark s : _snarks.values()) {
|
||||
if (s.storage.getBaseName().equals(filename))
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addTorrent(String filename) { addTorrent(filename, false); }
|
||||
public void addTorrent(String filename, boolean dontAutoStart) {
|
||||
if ((!dontAutoStart) && !_util.connected()) {
|
||||
@ -592,8 +613,8 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
} else if (info.getPieces() > Storage.MAX_PIECES) {
|
||||
return _("Too many pieces in \"{0}\", limit is {1}, deleting it!", info.getName(), Storage.MAX_PIECES);
|
||||
} else if (info.getPieceLength(0) > Storage.MAX_PIECE_SIZE) {
|
||||
return _("Pieces are too large in \"{0}\" ({1}B), deleting it.", info.getName(), DataHelper.formatSize(info.getPieceLength(0))) + ' ' +
|
||||
_("Limit is {0}B", DataHelper.formatSize(Storage.MAX_PIECE_SIZE));
|
||||
return _("Pieces are too large in \"{0}\" ({1}B), deleting it.", info.getName(), DataHelper.formatSize2(info.getPieceLength(0))) + ' ' +
|
||||
_("Limit is {0}B", DataHelper.formatSize2(Storage.MAX_PIECE_SIZE));
|
||||
} else if (info.getTotalLength() > Storage.MAX_TOTAL_SIZE) {
|
||||
System.out.println("torrent info: " + info.toString());
|
||||
List lengths = info.getLengths();
|
||||
@ -686,7 +707,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
public void torrentComplete(Snark snark) {
|
||||
File f = new File(snark.torrent);
|
||||
long len = snark.meta.getTotalLength();
|
||||
addMessage(_("Download finished: \"{0}\"", f.getName()) + " (" + _("size: {0}B", DataHelper.formatSize(len)) + ')');
|
||||
addMessage(_("Download finished: \"{0}\"", f.getName()) + " (" + _("size: {0}B", DataHelper.formatSize2(len)) + ')');
|
||||
updateStatus(snark);
|
||||
}
|
||||
|
||||
|
@ -286,6 +286,50 @@ public class Storage
|
||||
return needed == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file canonical path (non-directory)
|
||||
* @return number of bytes remaining; -1 if unknown file
|
||||
* @since 0.7.14
|
||||
*/
|
||||
public long remaining(String file) {
|
||||
long bytes = 0;
|
||||
for (int i = 0; i < rafs.length; i++) {
|
||||
File f = RAFfile[i];
|
||||
// use canonical in case snark dir or sub dirs are symlinked
|
||||
String canonical = null;
|
||||
if (f != null) {
|
||||
try {
|
||||
canonical = f.getCanonicalPath();
|
||||
} catch (IOException ioe) {
|
||||
f = null;
|
||||
}
|
||||
}
|
||||
if (f != null && canonical.equals(file)) {
|
||||
if (complete())
|
||||
return 0;
|
||||
int psz = metainfo.getPieceLength(0);
|
||||
long start = bytes;
|
||||
long end = start + lengths[i];
|
||||
int pc = (int) (bytes / psz);
|
||||
long rv = 0;
|
||||
if (!bitfield.get(pc))
|
||||
rv = Math.min(psz - (start % psz), lengths[i]);
|
||||
int pieces = metainfo.getPieces();
|
||||
for (int j = pc + 1; (((long)j) * psz) < end && j < pieces; j++) {
|
||||
if (!bitfield.get(j)) {
|
||||
if (((long)(j+1))*psz < end)
|
||||
rv += psz;
|
||||
else
|
||||
rv += end - (((long)j) * psz);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
bytes += lengths[i];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The BitField that tells which pieces this storage contains.
|
||||
* Do not change this since this is the current state of the storage.
|
||||
@ -295,6 +339,18 @@ public class Storage
|
||||
return bitfield;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base file or directory name of the data,
|
||||
* as specified in the .torrent file, but filtered to remove
|
||||
* illegal characters. This is where the data actually is,
|
||||
* relative to the snark base dir.
|
||||
*
|
||||
* @since 0.7.14
|
||||
*/
|
||||
public String getBaseName() {
|
||||
return filterName(metainfo.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates (and/or checks) all files from the metainfo file list.
|
||||
*/
|
||||
|
@ -163,7 +163,7 @@ public class TrackerClient extends I2PAppThread
|
||||
}
|
||||
}
|
||||
|
||||
if (tlist.size() <= 0) {
|
||||
if (tlist.isEmpty()) {
|
||||
// FIXME really need to get this message to the gui
|
||||
stop = true;
|
||||
_log.error("No valid trackers for infoHash: " + infoHash);
|
||||
@ -183,7 +183,7 @@ public class TrackerClient extends I2PAppThread
|
||||
boolean runStarted = false;
|
||||
boolean firstTime = true;
|
||||
int consecutiveFails = 0;
|
||||
Random r = new Random();
|
||||
Random r = I2PAppContext.getGlobalContext().random();
|
||||
while(!stop)
|
||||
{
|
||||
try
|
||||
|
@ -7,6 +7,9 @@ import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -15,6 +18,7 @@ import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@ -34,20 +38,27 @@ import org.klomp.snark.SnarkManager;
|
||||
import org.klomp.snark.Storage;
|
||||
import org.klomp.snark.TrackerClient;
|
||||
|
||||
import org.mortbay.http.HttpResponse;
|
||||
import org.mortbay.jetty.servlet.Default;
|
||||
import org.mortbay.util.Resource;
|
||||
import org.mortbay.util.URI;
|
||||
|
||||
/**
|
||||
*
|
||||
* We extend Default instead of HTTPServlet so we can handle
|
||||
* i2psnark/ file requests with http:// instead of the flaky and
|
||||
* often-blocked-by-the-browser file://
|
||||
*/
|
||||
public class I2PSnarkServlet extends HttpServlet {
|
||||
public class I2PSnarkServlet extends Default {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private SnarkManager _manager;
|
||||
private static long _nonce;
|
||||
private Resource _resourceBase;
|
||||
|
||||
public static final String PROP_CONFIG_FILE = "i2psnark.configFile";
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig cfg) throws ServletException {
|
||||
super.init(cfg);
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_log = _context.logManager().getLog(I2PSnarkServlet.class);
|
||||
_nonce = _context.random().nextLong();
|
||||
@ -57,6 +68,10 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
configFile = "i2psnark.config";
|
||||
_manager.loadConfig(configFile);
|
||||
_manager.start();
|
||||
try {
|
||||
_resourceBase = Resource.newResource(_manager.getDataDir().getAbsolutePath());
|
||||
} catch (IOException ioe) {}
|
||||
super.init(cfg);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -65,8 +80,70 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* We override this instead of passing a resource base to super(), because
|
||||
* if a resource base is set, super.getResource() always uses that base,
|
||||
* and we can't get any resources (like icons) out of the .war
|
||||
*/
|
||||
@Override
|
||||
protected Resource getResource(String pathInContext) throws IOException
|
||||
{
|
||||
if (pathInContext == null || pathInContext.equals("/") || pathInContext.equals("/index.jsp") ||
|
||||
pathInContext.equals("/index.html") || pathInContext.startsWith("/_icons/"))
|
||||
return super.getResource(pathInContext);
|
||||
// files in the i2psnark/ directory
|
||||
return _resourceBase.addPath(pathInContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some parts modified from:
|
||||
* <pre>
|
||||
// ========================================================================
|
||||
// $Id: Default.java,v 1.51 2006/10/08 14:13:18 gregwilkins Exp $
|
||||
// Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========================================================================
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
// this is the part after /i2psnark
|
||||
String path = req.getServletPath();
|
||||
// index.jsp doesn't work, it is grabbed by the war handler before here
|
||||
if (!(path == null || path.equals("/") || path.equals("/index.jsp") || path.equals("/index.html"))) {
|
||||
if (path.endsWith("/")) {
|
||||
// bypass the horrid Resource.getListHTML()
|
||||
String pathInfo = req.getPathInfo();
|
||||
String pathInContext = URI.addPaths(path, pathInfo);
|
||||
resp.setCharacterEncoding("UTF-8");
|
||||
resp.setContentType("text/html; charset=UTF-8");
|
||||
Resource resource = getResource(pathInContext);
|
||||
if (resource == null || (!resource.exists()) || !resource.isDirectory()) {
|
||||
resp.sendError(HttpResponse.__404_Not_Found);
|
||||
} else {
|
||||
String base = URI.addPaths(req.getRequestURI(), "/");
|
||||
String listing = getListHTML(resource, base, true);
|
||||
if (listing != null)
|
||||
resp.getWriter().write(listing);
|
||||
else // shouldn't happen
|
||||
resp.sendError(HttpResponse.__404_Not_Found);
|
||||
}
|
||||
} else {
|
||||
super.service(req, resp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
req.setCharacterEncoding("UTF-8");
|
||||
resp.setCharacterEncoding("UTF-8");
|
||||
resp.setContentType("text/html; charset=UTF-8");
|
||||
@ -129,7 +206,7 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
String uri = req.getRequestURI();
|
||||
out.write(TABLE_HEADER);
|
||||
out.write(_("Status"));
|
||||
if (_manager.util().connected() && snarks.size() > 0) {
|
||||
if (_manager.util().connected() && !snarks.isEmpty()) {
|
||||
out.write(" (<a href=\"");
|
||||
out.write(req.getRequestURI());
|
||||
if (peerParam != null) {
|
||||
@ -163,7 +240,7 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
out.write("\">");
|
||||
out.write(_("Stop All"));
|
||||
out.write("</a>");
|
||||
} else if (snarks.size() > 0) {
|
||||
} else if (!snarks.isEmpty()) {
|
||||
out.write("<a href=\"" + uri + "?action=StartAll&nonce=" + _nonce +
|
||||
"\" title=\"");
|
||||
out.write(_("Start all torrents and the I2P tunnel"));
|
||||
@ -181,7 +258,7 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
displaySnark(out, snark, uri, i, stats, showPeers, showDebug);
|
||||
}
|
||||
|
||||
if (snarks.size() <= 0) {
|
||||
if (snarks.isEmpty()) {
|
||||
out.write("<tr class=\"snarkTorrentEven\">" +
|
||||
"<td class=\"snarkTorrentEven\" align=\"center\"" +
|
||||
" colspan=\"8\"><i>");
|
||||
@ -192,10 +269,10 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
" <th align=\"left\" colspan=\"2\">");
|
||||
out.write(_("Totals"));
|
||||
out.write(" (");
|
||||
out.write(_("{0} torrents", snarks.size()));
|
||||
out.write(ngettext("1 torrent", "{0} torrents", snarks.size()));
|
||||
out.write(", ");
|
||||
out.write(DataHelper.formatSize(stats[5]) + "B, ");
|
||||
out.write(_("{0} connected peers", stats[4]));
|
||||
out.write(DataHelper.formatSize2(stats[5]) + "B, ");
|
||||
out.write(ngettext("1 connected peer", "{0} connected peers", (int) stats[4]));
|
||||
out.write(")</th>\n" +
|
||||
" <th> </th>\n" +
|
||||
" <th align=\"right\">" + formatSize(stats[0]) + "</th>\n" +
|
||||
@ -257,7 +334,7 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
}
|
||||
} else if (newURL != null) {
|
||||
if (newURL.startsWith("http://")) {
|
||||
_manager.addMessage(_("Fetching {0}", newURL));
|
||||
_manager.addMessage(_("Fetching {0}", urlify(newURL)));
|
||||
I2PAppThread fetch = new I2PAppThread(new FetchAndAdd(_manager, newURL), "Fetch and add");
|
||||
fetch.start();
|
||||
} else {
|
||||
@ -371,7 +448,7 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
String eepPort = req.getParameter("eepPort");
|
||||
String i2cpHost = req.getParameter("i2cpHost");
|
||||
String i2cpPort = req.getParameter("i2cpPort");
|
||||
String i2cpOpts = req.getParameter("i2cpOpts");
|
||||
String i2cpOpts = buildI2CPOpts(req);
|
||||
String upLimit = req.getParameter("upLimit");
|
||||
String upBW = req.getParameter("upBW");
|
||||
boolean useOpenTrackers = req.getParameter("useOpenTrackers") != null;
|
||||
@ -438,11 +515,53 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
}
|
||||
}
|
||||
|
||||
private List getSortedSnarks(HttpServletRequest req) {
|
||||
Set files = _manager.listTorrentFiles();
|
||||
TreeSet fileNames = new TreeSet(Collator.getInstance()); // sorts it alphabetically
|
||||
private static final String iopts[] = {"inbound.length", "inbound.quantity",
|
||||
"outbound.length", "outbound.quantity" };
|
||||
|
||||
/** put the individual i2cp selections into the option string */
|
||||
private static String buildI2CPOpts(HttpServletRequest req) {
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
String p = req.getParameter("i2cpOpts");
|
||||
if (p != null)
|
||||
buf.append(p);
|
||||
for (int i = 0; i < iopts.length; i++) {
|
||||
p = req.getParameter(iopts[i]);
|
||||
if (p != null)
|
||||
buf.append(' ').append(iopts[i]).append('=').append(p);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort alphabetically in current locale, ignore case, ignore leading "the "
|
||||
* (I guess this is worth it, a lot of torrents start with "The "
|
||||
* These are full path names which makes it harder
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private class TorrentNameComparator implements Comparator<String> {
|
||||
private final Comparator collator = Collator.getInstance();
|
||||
private final String skip = _manager.getDataDir().getAbsolutePath() + File.separator;
|
||||
|
||||
public int compare(String l, String r) {
|
||||
if (l.startsWith(skip))
|
||||
l = l.substring(skip.length());
|
||||
if (r.startsWith(skip))
|
||||
r = r.substring(skip.length());
|
||||
String llc = l.toLowerCase();
|
||||
if (llc.startsWith("the ") || llc.startsWith("the."))
|
||||
l = l.substring(4);
|
||||
String rlc = r.toLowerCase();
|
||||
if (rlc.startsWith("the ") || rlc.startsWith("the."))
|
||||
r = r.substring(4);
|
||||
return collator.compare(l, r);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Snark> getSortedSnarks(HttpServletRequest req) {
|
||||
Set<String> files = _manager.listTorrentFiles();
|
||||
TreeSet<String> fileNames = new TreeSet(new TorrentNameComparator());
|
||||
fileNames.addAll(files);
|
||||
ArrayList rv = new ArrayList(fileNames.size());
|
||||
ArrayList<Snark> rv = new ArrayList(fileNames.size());
|
||||
for (Iterator iter = fileNames.iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
Snark snark = _manager.getTorrent(name);
|
||||
@ -512,10 +631,12 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
if (err != null) {
|
||||
if (isRunning && curPeers > 0 && !showPeers)
|
||||
statusString = "<a title=\"" + err + "\">" + _("TrackerErr") + "</a> (" +
|
||||
curPeers + "/" + knownPeers +
|
||||
" <a href=\"" + uri + "?p=" + Base64.encode(snark.meta.getInfoHash()) + "\">" + _("peers") + "</a>)";
|
||||
"<a href=\"" + uri + "?p=" + Base64.encode(snark.meta.getInfoHash()) + "\">" +
|
||||
curPeers + '/' +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>)";
|
||||
else if (isRunning)
|
||||
statusString = "<a title=\"" + err + "\">" + _("TrackerErr") + " (" + curPeers + '/' + knownPeers + ' ' + _("peers") + ')';
|
||||
statusString = "<a title=\"" + err + "\">" + _("TrackerErr") + " (" + curPeers + '/' +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + ')';
|
||||
else {
|
||||
if (err.length() > MAX_DISPLAYED_ERROR_LENGTH)
|
||||
err = err.substring(0, MAX_DISPLAYED_ERROR_LENGTH) + "…";
|
||||
@ -524,25 +645,31 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
} else if (remaining <= 0) {
|
||||
if (isRunning && curPeers > 0 && !showPeers)
|
||||
statusString = _("Seeding") + " (" +
|
||||
curPeers + '/' + knownPeers +
|
||||
" <a href=\"" + uri + "?p=" + Base64.encode(snark.meta.getInfoHash()) + "\">" + _("peers") + "</a>)";
|
||||
"<a href=\"" + uri + "?p=" + Base64.encode(snark.meta.getInfoHash()) + "\">" +
|
||||
curPeers + '/' +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>)";
|
||||
else if (isRunning)
|
||||
statusString = _("Seeding") + " (" + curPeers + "/" + knownPeers + ' ' + _("peers") + ')';
|
||||
statusString = _("Seeding") + " (" + curPeers + "/" +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + ')';
|
||||
else
|
||||
statusString = _("Complete");
|
||||
} else {
|
||||
if (isRunning && curPeers > 0 && downBps > 0 && !showPeers)
|
||||
statusString = _("OK") + " (" +
|
||||
curPeers + "/" + knownPeers +
|
||||
" <a href=\"" + uri + "?p=" + Base64.encode(snark.meta.getInfoHash()) + "\">" + _("peers") + "</a>)";
|
||||
"<a href=\"" + uri + "?p=" + Base64.encode(snark.meta.getInfoHash()) + "\">" +
|
||||
curPeers + "/" +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>)";
|
||||
else if (isRunning && curPeers > 0 && downBps > 0)
|
||||
statusString = _("OK") + " (" + curPeers + "/" + knownPeers + ' ' + _("peers") + ')';
|
||||
statusString = _("OK") + " (" + curPeers + "/" +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + ')';
|
||||
else if (isRunning && curPeers > 0 && !showPeers)
|
||||
statusString = _("Stalled") + " (" +
|
||||
curPeers + '/' + knownPeers +
|
||||
" <a href=\"" + uri + "?p=" + Base64.encode(snark.meta.getInfoHash()) + "\">" + _("peers") + "</a>)";
|
||||
"<a href=\"" + uri + "?p=" + Base64.encode(snark.meta.getInfoHash()) + "\">" +
|
||||
curPeers + '/' +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>)";
|
||||
else if (isRunning && curPeers > 0)
|
||||
statusString = _("Stalled") + " (" + curPeers + '/' + knownPeers + ' ' + _("peers") + ')';
|
||||
statusString = _("Stalled") + " (" + curPeers + '/' +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + ')';
|
||||
else if (isRunning)
|
||||
statusString = _("No Peers") + " (0/" + knownPeers + ')';
|
||||
else
|
||||
@ -555,17 +682,25 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
out.write(statusString + "</td>\n\t");
|
||||
out.write("<td align=\"left\" class=\"snarkTorrentName " + rowClass + "\">");
|
||||
|
||||
if (remaining == 0) {
|
||||
out.write("<a href=\"" + _manager.linkPrefix() + snark.meta.getName()
|
||||
+ "\" title=\"");
|
||||
if (remaining == 0 || snark.meta.getFiles() != null) {
|
||||
out.write("<a href=\"" + snark.storage.getBaseName());
|
||||
if (snark.meta.getFiles() != null)
|
||||
out.write("/");
|
||||
out.write("\" title=\"");
|
||||
if (snark.meta.getFiles() != null)
|
||||
out.write(_("View files"));
|
||||
else
|
||||
out.write(_("Open file"));
|
||||
out.write("\">");
|
||||
}
|
||||
String icon;
|
||||
if (snark.meta.getFiles() != null)
|
||||
icon = "folder";
|
||||
else
|
||||
icon = toIcon(snark.meta.getName());
|
||||
out.write(toImg(icon));
|
||||
out.write(filename);
|
||||
if (remaining == 0)
|
||||
if (remaining == 0 || snark.meta.getFiles() != null)
|
||||
out.write("</a>");
|
||||
// temporarily hardcoded for postman* and anonymity, requires bytemonsoon patch for lookup by info_hash
|
||||
String announce = snark.meta.getAnnounce();
|
||||
@ -599,7 +734,7 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentDownloaded " + rowClass + "\">");
|
||||
if (remaining > 0)
|
||||
out.write(formatSize(total-remaining) + "/" + formatSize(total)); // 18MB/3GB
|
||||
out.write(formatSize(total-remaining) + " / " + formatSize(total)); // 18MB/3GB
|
||||
else
|
||||
out.write(formatSize(total)); // 3GB
|
||||
out.write("</td>\n\t");
|
||||
@ -687,7 +822,7 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
client = "Transmission";
|
||||
else
|
||||
client = _("Unknown") + " (" + ch + ')';
|
||||
out.write(client + " " + peer.toString().substring(5, 9));
|
||||
out.write(client + " <tt>" + peer.toString().substring(5, 9)+ "</tt>");
|
||||
if (showDebug)
|
||||
out.write(" inactive " + (peer.getInactiveTime() / 1000) + "s");
|
||||
out.write("</td>\n\t");
|
||||
@ -876,14 +1011,14 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
*/
|
||||
out.write("<tr><td>");
|
||||
out.write(_("Total uploader limit"));
|
||||
out.write(": <td><input type=\"text\" name=\"upLimit\" value=\""
|
||||
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("<tr><td>");
|
||||
out.write(_("Up bandwidth limit"));
|
||||
out.write(": <td><input type=\"text\" name=\"upBW\" value=\""
|
||||
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\">");
|
||||
@ -909,6 +1044,20 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
//out.write("port: <input type=\"text\" name=\"eepPort\" value=\""
|
||||
// + _manager.util().getEepProxyPort() + "\" size=\"5\" maxlength=\"5\" /><br>\n");
|
||||
|
||||
Map<String, String> options = new TreeMap(_manager.util().getI2CPOptions());
|
||||
out.write("<tr><td>");
|
||||
out.write(_("Inbound Settings"));
|
||||
out.write(":<td>");
|
||||
out.write(renderOptions(1, 6, options.remove("inbound.quantity"), "inbound.quantity", TUNNEL));
|
||||
out.write(" ");
|
||||
out.write(renderOptions(0, 4, options.remove("inbound.length"), "inbound.length", HOP));
|
||||
out.write("<tr><td>");
|
||||
out.write(_("Outbound Settings"));
|
||||
out.write(":<td>");
|
||||
out.write(renderOptions(1, 6, options.remove("outbound.quantity"), "outbound.quantity", TUNNEL));
|
||||
out.write(" ");
|
||||
out.write(renderOptions(0, 4, options.remove("outbound.length"), "outbound.length", HOP));
|
||||
|
||||
out.write("<tr><td>");
|
||||
out.write(_("I2CP host"));
|
||||
out.write(": <td><input type=\"text\" name=\"i2cpHost\" value=\""
|
||||
@ -916,11 +1065,10 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
|
||||
out.write("<tr><td>");
|
||||
out.write(_("I2CP port"));
|
||||
out.write(": <td><input type=\"text\" name=\"i2cpPort\" value=\"" +
|
||||
out.write(": <td><input type=\"text\" name=\"i2cpPort\" class=\"r\" value=\"" +
|
||||
+ _manager.util().getI2CPPort() + "\" size=\"5\" maxlength=\"5\" > <br>\n");
|
||||
|
||||
StringBuilder opts = new StringBuilder(64);
|
||||
Map options = new TreeMap(_manager.util().getI2CPOptions());
|
||||
for (Iterator iter = options.entrySet().iterator(); iter.hasNext(); ) {
|
||||
Map.Entry entry = (Map.Entry)iter.next();
|
||||
String key = (String)entry.getKey();
|
||||
@ -939,6 +1087,36 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
out.write("</form></div>");
|
||||
}
|
||||
|
||||
/** copied from ConfigTunnelsHelper */
|
||||
private static final String HOP = "hop";
|
||||
private static final String TUNNEL = "tunnel";
|
||||
/** dummies for translation */
|
||||
private static final String HOPS = ngettext("1 hop", "{0} hops");
|
||||
private static final String TUNNELS = ngettext("1 tunnel", "{0} tunnels");
|
||||
/** prevents the ngettext line below from getting tagged */
|
||||
private static final String DUMMY0 = "{0} ";
|
||||
private static final String DUMMY1 = "1 ";
|
||||
|
||||
/** modded from ConfigTunnelsHelper @since 0.7.14 */
|
||||
private String renderOptions(int min, int max, String strNow, String selName, String name) {
|
||||
int now = 2;
|
||||
try {
|
||||
now = Integer.parseInt(strNow);
|
||||
} catch (Throwable t) {}
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
buf.append("<select name=\"").append(selName).append("\">\n");
|
||||
for (int i = min; i <= max; i++) {
|
||||
buf.append("<option value=\"").append(i).append("\" ");
|
||||
if (i == now)
|
||||
buf.append("selected=\"true\" ");
|
||||
// constants to prevent tagging
|
||||
buf.append(">").append(ngettext(DUMMY1 + name, DUMMY0 + name + 's', i));
|
||||
buf.append("</option>\n");
|
||||
}
|
||||
buf.append("</select>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** translate */
|
||||
private String _(String s) {
|
||||
return _manager.util().getString(s);
|
||||
@ -949,19 +1127,36 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
return _manager.util().getString(s, o);
|
||||
}
|
||||
|
||||
/** translate (ngettext) @since 0.7.14 */
|
||||
private String ngettext(String s, String p, int n) {
|
||||
return _manager.util().getString(n, s, p);
|
||||
}
|
||||
|
||||
/** dummy for tagging */
|
||||
private static String ngettext(String s, String p) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// rounding makes us look faster :)
|
||||
private String formatSize(long bytes) {
|
||||
private static String formatSize(long bytes) {
|
||||
if (bytes < 5*1024)
|
||||
return bytes + "B";
|
||||
return bytes + " B";
|
||||
else if (bytes < 5*1024*1024)
|
||||
return ((bytes + 512)/1024) + "KB";
|
||||
return ((bytes + 512)/1024) + " KB";
|
||||
else if (bytes < 10*1024*1024*1024l)
|
||||
return ((bytes + 512*1024)/(1024*1024)) + "MB";
|
||||
return ((bytes + 512*1024)/(1024*1024)) + " MB";
|
||||
else
|
||||
return ((bytes + 512*1024*1024)/(1024*1024*1024)) + "GB";
|
||||
return ((bytes + 512*1024*1024)/(1024*1024*1024)) + " GB";
|
||||
}
|
||||
|
||||
private static final String HEADER = "<link href=\"../themes/console/snark.css\" rel=\"stylesheet\" type=\"text/css\" >";
|
||||
/** @since 0.7.14 */
|
||||
private static String urlify(String s) {
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
buf.append("<a href=\"").append(s).append("\">").append(s).append("</a>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static final String HEADER = "<link href=\"/themes/console/snark.css\" rel=\"stylesheet\" type=\"text/css\" >";
|
||||
|
||||
|
||||
private static final String TABLE_HEADER = "<table border=\"0\" class=\"snarkTorrents\" width=\"100%\" cellpadding=\"0 10px\">\n" +
|
||||
@ -972,6 +1167,231 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
|
||||
private static final String FOOTER = "</div></div></div></center></body></html>";
|
||||
|
||||
/**
|
||||
* Modded heavily from the Jetty version in Resource.java,
|
||||
* pass Resource as 1st param
|
||||
* All the xxxResource constructors are package local so we can't extend them.
|
||||
*
|
||||
* <pre>
|
||||
// ========================================================================
|
||||
// $Id: Resource.java,v 1.32 2009/05/16 01:53:36 gregwilkins Exp $
|
||||
// Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========================================================================
|
||||
* </pre>
|
||||
*
|
||||
* Get the resource list as a HTML directory listing.
|
||||
* @param r The Resource
|
||||
* @param base The base URL
|
||||
* @param parent True if the parent directory should be included
|
||||
* @return String of HTML
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private String getListHTML(Resource r, String base, boolean parent)
|
||||
throws IOException
|
||||
{
|
||||
if (!r.isDirectory())
|
||||
return null;
|
||||
|
||||
String[] ls = r.list();
|
||||
if (ls==null)
|
||||
return null;
|
||||
Arrays.sort(ls, Collator.getInstance());
|
||||
|
||||
StringBuilder buf=new StringBuilder(4096);
|
||||
buf.append("<HTML><HEAD><TITLE>");
|
||||
String title = URI.decodePath(base);
|
||||
if (title.startsWith("/i2psnark/"))
|
||||
title = title.substring("/i2psnark/".length());
|
||||
|
||||
// Get the snark associated with this directory
|
||||
String torrentName;
|
||||
int slash = title.indexOf('/');
|
||||
if (slash > 0)
|
||||
torrentName = title.substring(0, slash);
|
||||
else
|
||||
torrentName = title;
|
||||
Snark snark = _manager.getTorrentByBaseName(torrentName);
|
||||
if (title.endsWith("/"))
|
||||
title = title.substring(0, title.length() - 1);
|
||||
title = _("Torrent") + ": " + title;
|
||||
buf.append(title);
|
||||
buf.append("</TITLE>").append(HEADER).append("</HEAD><BODY>\n<div class=\"snarknavbar\">");
|
||||
buf.append(title);
|
||||
|
||||
if (parent)
|
||||
{
|
||||
buf.append("\n<br><A HREF=\"");
|
||||
// corrupts utf-8
|
||||
//buf.append(URI.encodePath(URI.addPaths(base,"../")));
|
||||
buf.append(URI.addPaths(base,"../"));
|
||||
buf.append("\"><img border=\"0\" src=\"/themes/console/images/outbound.png\"> ")
|
||||
.append(_("Up to higher level directory")).append("</A>\n");
|
||||
}
|
||||
|
||||
buf.append("</div><div class=\"page\"><div class=\"mainsection\">" +
|
||||
"<TABLE BORDER=0 class=\"snarkTorrents\" cellpadding=\"5px 10px\">" +
|
||||
"<thead><tr><th>").append(_("File")).append("</th><th>").append(_("Size"))
|
||||
.append("</th><th>").append(_("Status")).append("</th></tr></thead>");
|
||||
//DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
|
||||
// DateFormat.MEDIUM);
|
||||
for (int i=0 ; i< ls.length ; i++)
|
||||
{
|
||||
String encoded=URI.encodePath(ls[i]);
|
||||
// bugfix for I2P - Backport from Jetty 6 (zero file lengths and last-modified times)
|
||||
// http://jira.codehaus.org/browse/JETTY-361?page=com.atlassian.jira.plugin.system.issuetabpanels%3Achangehistory-tabpanel#issue-tabs
|
||||
// See resource.diff attachment
|
||||
//Resource item = addPath(encoded);
|
||||
Resource item = r.addPath(ls[i]);
|
||||
|
||||
String rowClass = (i % 2 == 0 ? "snarkTorrentEven" : "snarkTorrentOdd");
|
||||
buf.append("<TR class=\"").append(rowClass).append("\"><TD class=\"snarkFileName ")
|
||||
.append(rowClass).append("\">");
|
||||
|
||||
// Get completeness and status string
|
||||
boolean complete = false;
|
||||
String status = "";
|
||||
long length = item.length();
|
||||
if (item.isDirectory()) {
|
||||
complete = true;
|
||||
status = toImg("tick") + _("Directory");
|
||||
} else {
|
||||
if (snark == null) {
|
||||
// Assume complete, perhaps he removed a completed torrent but kept a bookmark
|
||||
complete = true;
|
||||
status = toImg("cancel") + _("Torrent not found?");
|
||||
} else {
|
||||
try {
|
||||
File f = item.getFile();
|
||||
if (f != null) {
|
||||
long remaining = snark.storage.remaining(f.getCanonicalPath());
|
||||
if (remaining < 0) {
|
||||
complete = true;
|
||||
status = toImg("cancel") + _("File not found in torrent?");
|
||||
} else if (remaining == 0 || length <= 0) {
|
||||
complete = true;
|
||||
status = toImg("tick") + _("Complete");
|
||||
} else {
|
||||
status = toImg("clock") +
|
||||
(100 * (length - remaining) / length) + "% " + _("complete") +
|
||||
" (" + DataHelper.formatSize2(remaining) + _("bytes remaining") + ")";
|
||||
}
|
||||
} else {
|
||||
status = "Not a file?";
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
status = "Not a file? " + ioe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String path=URI.addPaths(base,encoded);
|
||||
if (item.isDirectory() && !path.endsWith("/"))
|
||||
path=URI.addPaths(path,"/");
|
||||
String icon = toIcon(item);
|
||||
|
||||
if (complete) {
|
||||
buf.append("<a href=\"").append(path).append("\">");
|
||||
// thumbnail ?
|
||||
String plc = item.toString().toLowerCase();
|
||||
if (plc.endsWith(".jpg") || plc.endsWith(".jpeg") || plc.endsWith(".png") ||
|
||||
plc.endsWith(".gif") || plc.endsWith(".ico")) {
|
||||
buf.append("<img alt=\"\" border=\"0\" class=\"thumb\" src=\"")
|
||||
.append(path).append("\"></a> ");
|
||||
} else {
|
||||
buf.append(toImg(icon));
|
||||
}
|
||||
buf.append("<A HREF=\"");
|
||||
buf.append(path);
|
||||
buf.append("\">");
|
||||
} else {
|
||||
buf.append(toImg(icon));
|
||||
}
|
||||
buf.append(ls[i]);
|
||||
if (complete)
|
||||
buf.append("</a>");
|
||||
buf.append("</TD><TD ALIGN=right class=\"").append(rowClass).append(" snarkFileSize\">");
|
||||
if (!item.isDirectory())
|
||||
buf.append(DataHelper.formatSize2(length)).append('B');
|
||||
buf.append("</TD><TD class=\"").append(rowClass).append(" snarkFileStatus\">");
|
||||
//buf.append(dfmt.format(new Date(item.lastModified())));
|
||||
buf.append(status);
|
||||
buf.append("</TD></TR>\n");
|
||||
}
|
||||
buf.append("</TABLE>\n");
|
||||
buf.append("</div></div></BODY></HTML>\n");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** @since 0.7.14 */
|
||||
private String toIcon(Resource item) {
|
||||
if (item.isDirectory())
|
||||
return "folder";
|
||||
return toIcon(item.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick an icon; try to catch the common types in an i2p environment
|
||||
* @return file name not including ".png"
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private String toIcon(String path) {
|
||||
String icon;
|
||||
// Should really just add to the mime.properties file in org.mortbay.jetty.jar
|
||||
// instead of this mishmash. We can't get to HttpContext.setMimeMapping()
|
||||
// from here? We could do it from a web.xml perhaps.
|
||||
// Or could we put our own org/mortbay/http/mime.properties file in the war?
|
||||
String plc = path.toLowerCase();
|
||||
String mime = getServletContext().getMimeType(path);
|
||||
if (mime == null)
|
||||
mime = "";
|
||||
if (mime.equals("text/html"))
|
||||
icon = "html";
|
||||
else if (mime.equals("text/plain") || plc.endsWith(".nfo"))
|
||||
icon = "page";
|
||||
else if (mime.equals("application/java-archive") || plc.endsWith(".war"))
|
||||
icon = "package";
|
||||
else if (plc.endsWith(".xpi2p"))
|
||||
icon = "plugin";
|
||||
else if (mime.equals("application/pdf"))
|
||||
icon = "page_white_acrobat";
|
||||
else if (mime.startsWith("image/") || plc.endsWith(".ico"))
|
||||
icon = "photo";
|
||||
else if (mime.startsWith("audio/") || mime.equals("application/ogg") ||
|
||||
plc.endsWith(".flac") || plc.endsWith(".m4a") || plc.endsWith(".wma") ||
|
||||
plc.endsWith(".ape"))
|
||||
icon = "music";
|
||||
else if (mime.startsWith("video/") || plc.endsWith(".mkv") || plc.endsWith(".m4v") ||
|
||||
plc.endsWith(".mp4") || plc.endsWith(".wmv"))
|
||||
icon = "film";
|
||||
else if (mime.equals("application/zip") || mime.equals("application/x-gtar") ||
|
||||
mime.equals("application/compress") || mime.equals("application/gzip") ||
|
||||
mime.equals("application/x-tar") ||
|
||||
plc.endsWith(".rar") || plc.endsWith(".bz2") || plc.endsWith(".7z"))
|
||||
icon = "compress";
|
||||
else if (plc.endsWith(".exe"))
|
||||
icon = "application";
|
||||
else
|
||||
icon = "bug";
|
||||
return icon;
|
||||
}
|
||||
|
||||
/** @since 0.7.14 */
|
||||
private static String toImg(String icon) {
|
||||
return "<img alt=\"\" height=\"16\" width=\"16\" src=\"/i2psnark/_icons/" + icon + ".png\"> ";
|
||||
}
|
||||
|
||||
|
||||
/** inner class, don't bother reindenting */
|
||||
private static class FetchAndAdd implements Runnable {
|
||||
private SnarkManager _manager;
|
||||
@ -986,7 +1406,7 @@ private static class FetchAndAdd implements Runnable {
|
||||
File file = _manager.util().get(_url, false, 3);
|
||||
try {
|
||||
if ( (file != null) && (file.exists()) && (file.length() > 0) ) {
|
||||
_manager.addMessage(_("Torrent fetched from {0}", _url));
|
||||
_manager.addMessage(_("Torrent fetched from {0}", urlify(_url)));
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(file);
|
||||
@ -1014,12 +1434,12 @@ private static class FetchAndAdd implements Runnable {
|
||||
_manager.addTorrent(canonical);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_manager.addMessage(_("Torrent at {0} was not valid", _url) + ": " + ioe.getMessage());
|
||||
_manager.addMessage(_("Torrent at {0} was not valid", urlify(_url)) + ": " + ioe.getMessage());
|
||||
} finally {
|
||||
try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
} else {
|
||||
_manager.addMessage(_("Torrent was not retrieved from {0}", _url));
|
||||
_manager.addMessage(_("Torrent was not retrieved from {0}", urlify(_url)));
|
||||
}
|
||||
} finally {
|
||||
if (file != null) file.delete();
|
||||
|
@ -8,661 +8,756 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P i2psnark\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2010-03-13 09:42+0000\n"
|
||||
"PO-Revision-Date: 2010-03-13 09:44+0000\n"
|
||||
"POT-Creation-Date: 2010-05-27 14:45+0000\n"
|
||||
"PO-Revision-Date: 2010-05-27 16:54+0000\n"
|
||||
"Last-Translator: 4get <forget@mail.i2p>\n"
|
||||
"Language-Team: foo <foo@bar>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Poedit-Language: Russian\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:84
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:87
|
||||
#, java-format
|
||||
msgid "Adding torrents in {0} minutes"
|
||||
msgstr "Торренты будут подгружены через {0} минут(ы)"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:242
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:251
|
||||
#, java-format
|
||||
msgid "Total uploaders limit changed to {0}"
|
||||
msgstr "Новое значение лимита количества слотов отдачи: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:244
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:253
|
||||
#, java-format
|
||||
msgid "Minimum total uploaders limit is {0}"
|
||||
msgstr "Минимально допустимое значение для количества слотов: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:256
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:265
|
||||
#, java-format
|
||||
msgid "Up BW limit changed to {0}KBps"
|
||||
msgstr "Новое значение лимита скорости отдачи: {0} KBps"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:258
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:267
|
||||
#, java-format
|
||||
msgid "Minimum up bandwidth limit is {0}KBps"
|
||||
msgstr "Минимально допустимое значение для лимита скорости отдачи: {0} KBps"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:302
|
||||
msgid "Cannot change the I2CP settings while torrents are active"
|
||||
msgstr "Невозможно изменить настройки I2CP пока есть активные торренты"
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:314
|
||||
msgid "I2CP and tunnel changes will take effect after stopping all torrents"
|
||||
msgstr "Изменения настроек I2CP и туннелей вступят в силу после остановки всех торрентов."
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:308
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:320
|
||||
msgid "Disconnecting old I2CP destination"
|
||||
msgstr "Рассоединяемся по старому адресу I2CP"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:312
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:324
|
||||
#, java-format
|
||||
msgid "I2CP settings changed to {0}"
|
||||
msgstr "Новые параметры I2CP: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:316
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:328
|
||||
msgid "Unable to connect with the new settings, reverting to the old I2CP settings"
|
||||
msgstr "Не удалось соединиться с использованием новых настроек I2CP, возвращаемся к старым настройкам"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:320
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:332
|
||||
msgid "Unable to reconnect with the old settings!"
|
||||
msgstr "Не удалось пересоединиться с использованием старых настроек I2CP!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:322
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:334
|
||||
msgid "Reconnected on the new I2CP destination"
|
||||
msgstr "Пересоединились по новому адресу I2CP"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:333
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:345
|
||||
#, java-format
|
||||
msgid "I2CP listener restarted for \"{0}\""
|
||||
msgstr "I2CP-приёмник перезапущен для \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:344
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:356
|
||||
msgid "Enabled autostart"
|
||||
msgstr "Автостарт включен"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:346
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:358
|
||||
msgid "Disabled autostart"
|
||||
msgstr "Автостарт выключен"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:352
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:364
|
||||
msgid "Enabled open trackers - torrent restart required to take effect."
|
||||
msgstr "Включено использование открытых трекеров. Требуется перезапуск торрента, чтобы изменения вступили в силу."
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:354
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:366
|
||||
msgid "Disabled open trackers - torrent restart required to take effect."
|
||||
msgstr "Отключено использование открытых трекеров. Требуется перезапуск торрента, чтобы изменения вступили в силу."
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:361
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:373
|
||||
msgid "Open Tracker list changed - torrent restart required to take effect."
|
||||
msgstr "Изменен список открытых трекеров. Требуется перезапуск торрента, чтобы изменения вступили в силу."
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:368
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:380
|
||||
msgid "Configuration unchanged."
|
||||
msgstr "Настройки не изменились."
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:378
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:390
|
||||
#, java-format
|
||||
msgid "Unable to save the config to {0}"
|
||||
msgstr "Не удалось сохранить настройки в {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:396
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:408
|
||||
msgid "Connecting to I2P"
|
||||
msgstr "Устанавливается соединение с I2P"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:399
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:411
|
||||
msgid "Error connecting to I2P - check your I2CP settings!"
|
||||
msgstr "Ошибка соединения с I2P, проверьте настройки I2CP!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:408
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:420
|
||||
#, java-format
|
||||
msgid "Error: Could not add the torrent {0}"
|
||||
msgstr "Ошибка: Не удалось добавить торрент {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:430
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:442
|
||||
#, java-format
|
||||
msgid "Cannot open \"{0}\""
|
||||
msgstr "Не удалось открыть \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:443
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:455
|
||||
#, java-format
|
||||
msgid "Warning - Ignoring non-i2p tracker in \"{0}\", will announce to i2p open trackers only"
|
||||
msgstr "Внимание: указанные в \"{0}\" не-i2p трекеры будут проигнорированы, будут использоваться только открытые i2p трекеры"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:445
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:457
|
||||
#, java-format
|
||||
msgid "Warning - Ignoring non-i2p tracker in \"{0}\", and open trackers are disabled, you must enable open trackers before starting the torrent!"
|
||||
msgstr "Внимание: указанные в \"{0}\" не-i2p трекеры будут проигнорированы, однако использование открытых i2p трекеров отключено, Вы должны включить поддержку открытых i2p трекеров перед запуском этого торрента!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:464
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:476
|
||||
#, java-format
|
||||
msgid "Torrent in \"{0}\" is invalid"
|
||||
msgstr "Торрент в \"{0}\" некорректен"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:479
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:491
|
||||
#, java-format
|
||||
msgid "Torrent added and started: \"{0}\""
|
||||
msgstr "Торрент добавлен и запущен: \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:481
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:493
|
||||
#, java-format
|
||||
msgid "Torrent added: \"{0}\""
|
||||
msgstr "Торрент добавлен: \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:578
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:590
|
||||
#, java-format
|
||||
msgid "Too many files in \"{0}\" ({1}), deleting it!"
|
||||
msgstr "Слишком много файлов в торренте \"{0}\" ({1}), удаляем его!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:580
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:592
|
||||
#, java-format
|
||||
msgid "Torrent file \"{0}\" cannot end in \".torrent\", deleting it!"
|
||||
msgstr "Торрент \"{0}\" содержит единственный файл заканчивающийся на \".torrent\", удаляем его!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:582
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:594
|
||||
#, java-format
|
||||
msgid "No pieces in \"{0}\", deleting it!"
|
||||
msgstr "В торренте \"{0}\" не оказалось ни одной части, удаляем его!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:584
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:596
|
||||
#, java-format
|
||||
msgid "Too many pieces in \"{0}\", limit is {1}, deleting it!"
|
||||
msgstr "Слишком много частей в торренте \"{0}\" (наш предел {1}), удаляем его!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:586
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:598
|
||||
#, java-format
|
||||
msgid "Pieces are too large in \"{0}\" ({1}B), deleting it."
|
||||
msgstr "Слишком крупные части в торренте \"{0}\" ({1}B), удаляем его."
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:587
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:599
|
||||
#, java-format
|
||||
msgid "Limit is {0}B"
|
||||
msgstr "Наш предел {0}B"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:595
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:607
|
||||
#, java-format
|
||||
msgid "Torrents larger than {0}B are not supported yet, deleting \"{1}\""
|
||||
msgstr "Торренты крупнее чем {0}B пока не поддерживается, удаляем \"{1}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:611
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:623
|
||||
#, java-format
|
||||
msgid "Error: Could not remove the torrent {0}"
|
||||
msgstr "Ошибка: Невозможно удалить торрент {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:632
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:644
|
||||
#, java-format
|
||||
msgid "Torrent stopped: \"{0}\""
|
||||
msgstr "Торрент остановлен: \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:647
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:659
|
||||
#, java-format
|
||||
msgid "Torrent removed: \"{0}\""
|
||||
msgstr "Торрент удален: \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:680
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:692
|
||||
#, java-format
|
||||
msgid "Download finished: \"{0}\""
|
||||
msgstr "Завершена загрузка: \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:680
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:692
|
||||
#, java-format
|
||||
msgid "size: {0}B"
|
||||
msgstr "размер: {0}B"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:708
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:720
|
||||
msgid "Unable to connect to I2P!"
|
||||
msgstr "Не удалось установить соединение с I2P!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:86
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:169
|
||||
msgid "I2PSnark - Anonymous BitTorrent Client"
|
||||
msgstr "I2PSnark — Анонимный BitTorrent Клиент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:95
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:178
|
||||
msgid "Refresh page"
|
||||
msgstr "Обновить страницу"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:97
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:669
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:180
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:808
|
||||
msgid "I2PSnark"
|
||||
msgstr "I2PSnark"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:99
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:182
|
||||
msgid "Forum"
|
||||
msgstr "Форум"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:125
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:208
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1243
|
||||
msgid "Status"
|
||||
msgstr "Статус"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:131
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:214
|
||||
msgid "Hide Peers"
|
||||
msgstr "спрятать список пиров"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:134
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:217
|
||||
msgid "Show Peers"
|
||||
msgstr "показать список пиров"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:139
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:222
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1225
|
||||
msgid "Torrent"
|
||||
msgstr "Торрент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:141
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:224
|
||||
msgid "ETA"
|
||||
msgstr "Осталось"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:143
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:226
|
||||
msgid "Downloaded"
|
||||
msgstr "Получено"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:145
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:228
|
||||
msgid "Uploaded"
|
||||
msgstr "Отдано"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:147
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:230
|
||||
msgid "Down Rate"
|
||||
msgstr "Скорость загрузки"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:149
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:232
|
||||
msgid "Up Rate"
|
||||
msgstr "Скорость отдачи"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:156
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:239
|
||||
msgid "Stop all torrents and the I2P tunnel"
|
||||
msgstr "Остановить все торренты и закрыть соединение с I2P"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:158
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:241
|
||||
msgid "Stop All"
|
||||
msgstr "Остановить все"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:163
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:246
|
||||
msgid "Start all torrents and the I2P tunnel"
|
||||
msgstr "Запустить все торренты и открыть соединение с I2P"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:165
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:248
|
||||
msgid "Start All"
|
||||
msgstr "Запустить все"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:182
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:265
|
||||
msgid "No torrents loaded."
|
||||
msgstr "Нет загруженных торрентов."
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:187
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:270
|
||||
msgid "Totals"
|
||||
msgstr "Всего"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:189
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:272
|
||||
#, java-format
|
||||
msgid "{0} torrents"
|
||||
msgstr "{0} торрентов"
|
||||
msgid "1 torrent"
|
||||
msgid_plural "{0} torrents"
|
||||
msgstr[0] "{0} торрент"
|
||||
msgstr[1] "{0} торрента"
|
||||
msgstr[2] "{0} торрентов"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:192
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:275
|
||||
#, java-format
|
||||
msgid "{0} connected peers"
|
||||
msgstr "{0} подсоединенных пиров"
|
||||
msgid "1 connected peer"
|
||||
msgid_plural "{0} connected peers"
|
||||
msgstr[0] "{0} подсоединенный пир"
|
||||
msgstr[1] "{0} подсоединенных пиров"
|
||||
msgstr[2] "{0} подсоединенных пиров"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:227
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:310
|
||||
#, java-format
|
||||
msgid "Torrent file {0} does not exist"
|
||||
msgstr "Торрент {0} не существует"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:237
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1003
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:320
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1428
|
||||
#, java-format
|
||||
msgid "Torrent already running: {0}"
|
||||
msgstr "Торрент уже запущен: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:239
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1005
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:322
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1430
|
||||
#, java-format
|
||||
msgid "Torrent already in the queue: {0}"
|
||||
msgstr "Торрент уже в очереди: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:243
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:326
|
||||
#, java-format
|
||||
msgid "Copying torrent to {0}"
|
||||
msgstr "Копируем торрент в: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:246
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:329
|
||||
#, java-format
|
||||
msgid "Unable to copy the torrent to {0}"
|
||||
msgstr "Не удалось скопировать торрент в: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:246
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:329
|
||||
#, java-format
|
||||
msgid "from {0}"
|
||||
msgstr "из: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:254
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:337
|
||||
#, java-format
|
||||
msgid "Fetching {0}"
|
||||
msgstr "Получение торрента: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:258
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:341
|
||||
msgid "Invalid URL - must start with http://"
|
||||
msgstr "Некорректный URL, должен начинаться с http://"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:288
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:371
|
||||
#, java-format
|
||||
msgid "Starting up torrent {0}"
|
||||
msgstr "Запускаем торрент: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:308
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:326
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:391
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:409
|
||||
#, java-format
|
||||
msgid "Torrent file deleted: {0}"
|
||||
msgstr "Удален торрент: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:332
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:342
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:415
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:425
|
||||
#, java-format
|
||||
msgid "Data file deleted: {0}"
|
||||
msgstr "Файл удален: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:334
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:344
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:417
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:427
|
||||
#, java-format
|
||||
msgid "Data file could not be deleted: {0}"
|
||||
msgstr "Не удалось удалить файл: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:353
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:436
|
||||
#, java-format
|
||||
msgid "Data dir deleted: {0}"
|
||||
msgstr "Директория удалена: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:384
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:467
|
||||
msgid "Error creating torrent - you must select a tracker"
|
||||
msgstr "Торрент не создан — вы должны указать трекер"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:399
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:482
|
||||
#, java-format
|
||||
msgid "Torrent created for \"{0}\""
|
||||
msgstr "Создан торрент для \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:402
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:485
|
||||
#, java-format
|
||||
msgid "Many I2P trackers require you to register new torrents before seeding - please do so before starting \"{0}\""
|
||||
msgstr "Многие I2P трекеры требуют зарегистрировать на них торрент перед началом раздачи — пожалуйста проверьте требуется ли это перед запуском \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:404
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:487
|
||||
#, java-format
|
||||
msgid "Error creating a torrent for \"{0}\""
|
||||
msgstr "Ошибка при создании торрента для: \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:407
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:490
|
||||
#, java-format
|
||||
msgid "Cannot create a torrent for the nonexistent data: {0}"
|
||||
msgstr "Невозможно создать торрент для несуществующего файла или директории: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:410
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:493
|
||||
msgid "Error creating torrent - you must enter a file or directory"
|
||||
msgstr "Торрент не создан — вы должны указать файл или директорию"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:413
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:496
|
||||
msgid "Stopping all torrents and closing the I2P tunnel."
|
||||
msgstr "Останавливаем все торренты и закрываем соединение с I2P"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:422
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:505
|
||||
msgid "I2P tunnel closed."
|
||||
msgstr "Соединение с I2P закрыто."
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:425
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:508
|
||||
msgid "Opening the I2P tunnel and starting all torrents."
|
||||
msgstr "Соединяемся с I2P и запускаем все торренты."
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:505
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:683
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:628
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:822
|
||||
msgid "Unknown"
|
||||
msgstr "Неизвестный"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:508
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:512
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:516
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:631
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:636
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:641
|
||||
msgid "TrackerErr"
|
||||
msgstr "ОшибкаТрекера"
|
||||
|
||||
# TODO should replace "uploader limit NN peers" with "global number of upload slots: NN"
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:510
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:512
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:522
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:524
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:531
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:533
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:537
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:539
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:875
|
||||
msgid "peers"
|
||||
msgstr "пир."
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:634
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:637
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:648
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:651
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:659
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:662
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:667
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:670
|
||||
#, java-format
|
||||
msgid "1 peer"
|
||||
msgid_plural "{0} peers"
|
||||
msgstr[0] "{0} пир"
|
||||
msgstr[1] "{0} пира"
|
||||
msgstr[2] "{0} пиров"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:520
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:524
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:645
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:650
|
||||
msgid "Seeding"
|
||||
msgstr "Раздается"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:526
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:653
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1281
|
||||
msgid "Complete"
|
||||
msgstr "Завершен"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:529
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:533
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:656
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:661
|
||||
msgid "OK"
|
||||
msgstr "Загружается"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:535
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:539
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:664
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:669
|
||||
msgid "Stalled"
|
||||
msgstr "Простаивает"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:541
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:672
|
||||
msgid "No Peers"
|
||||
msgstr "Нет Пиров"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:543
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:674
|
||||
msgid "Stopped"
|
||||
msgstr "Остановлен"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:556
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:689
|
||||
msgid "View files"
|
||||
msgstr "Открыть директорию"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:558
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:691
|
||||
msgid "Open file"
|
||||
msgstr "Открыть файл"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:582
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:794
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:721
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:933
|
||||
msgid "Tracker"
|
||||
msgstr "Трекер"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:583
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:722
|
||||
msgid "Details"
|
||||
msgstr "Подробнее"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:617
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:756
|
||||
msgid "Stop the torrent"
|
||||
msgstr "Остановить торрент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:619
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:758
|
||||
msgid "Stop"
|
||||
msgstr "Остановить"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:625
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:764
|
||||
msgid "Start the torrent"
|
||||
msgstr "Запустить торрент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:627
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:766
|
||||
msgid "Start"
|
||||
msgstr "Запустить"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:632
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:771
|
||||
msgid "Remove the torrent from the active list, deleting the .torrent file"
|
||||
msgstr "Удалить торрент из списка и с диска"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:637
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:776
|
||||
#, java-format
|
||||
msgid "Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?"
|
||||
msgstr "Вы действительно хотите удалить \\''{0}.torrent\\''? (загруженные файлы удаляться НЕ будут)"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:639
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:778
|
||||
msgid "Remove"
|
||||
msgstr "Удалить"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:643
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:782
|
||||
msgid "Delete the .torrent file and the associated data file(s)"
|
||||
msgstr "Удалить торрент и стереть загруженные файлы"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:648
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:787
|
||||
#, java-format
|
||||
msgid "Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?"
|
||||
msgstr "Вы действительно хотите удалить торрент \\''{0}\\'' и все загруженные файлы?"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:650
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:789
|
||||
msgid "Delete"
|
||||
msgstr "Стереть"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:693
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:832
|
||||
msgid "Seed"
|
||||
msgstr "Сид"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:711
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:850
|
||||
msgid "Uninteresting (The peer has no pieces we need)"
|
||||
msgstr "Uninteresting (У пира нет нужных нам частей торрента)"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:713
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:852
|
||||
msgid "Choked (The peer is not allowing us to request pieces)"
|
||||
msgstr "Choked (Этот пир не позволяет нам запрашивать части торрента)"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:727
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:866
|
||||
msgid "Uninterested (We have no pieces the peer needs)"
|
||||
msgstr "Uninterested (У нас нужных этому пиру частей торрента)"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:729
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:868
|
||||
msgid "Choking (We are not allowing the peer to request pieces)"
|
||||
msgstr "Choking (Мы не позволяем этому пиру запрашивать у нас части торрента)"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:756
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:895
|
||||
msgid "Add Torrent"
|
||||
msgstr "Добавить Торрент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:758
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:897
|
||||
msgid "From URL"
|
||||
msgstr "Из URL"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:763
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:902
|
||||
msgid "Add torrent"
|
||||
msgstr "Добавить торрент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:766
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:905
|
||||
#, java-format
|
||||
msgid "Alternately, you can copy .torrent files to the directory {0}."
|
||||
msgstr "Ну или вы можете скопировать .torrent-файлы в директорию {0}."
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:768
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:907
|
||||
msgid "Removing a .torrent file will cause the torrent to stop."
|
||||
msgstr "Удаление .torrent-файла приведет к остановке торрента."
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:785
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:924
|
||||
msgid "Create Torrent"
|
||||
msgstr "Создать Торрент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:788
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:927
|
||||
msgid "Data to seed"
|
||||
msgstr "Файлы для раздачи"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:792
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:931
|
||||
msgid "File or directory to seed (must be within the specified path)"
|
||||
msgstr "Файл или директория для раздачи (вводите только название файла или директории, указание абсолютных путей не поддерживается)"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:796
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:935
|
||||
msgid "Select a tracker"
|
||||
msgstr "Выбрать трекер"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:809
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:948
|
||||
msgid "or"
|
||||
msgstr "или"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:812
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:951
|
||||
msgid "Specify custom tracker announce URL"
|
||||
msgstr "Задать URL анонсера вручную"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:815
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:954
|
||||
msgid "Create torrent"
|
||||
msgstr "Создать торрент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:833
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:972
|
||||
msgid "Configuration"
|
||||
msgstr "Настройки"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:836
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:975
|
||||
msgid "Data directory"
|
||||
msgstr "Директория для файлов"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:839
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:978
|
||||
msgid "Directory to store torrents and data"
|
||||
msgstr "Директория, где будут храниться торренты и загружаемые файлы"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:841
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:980
|
||||
msgid "Edit i2psnark.config and restart to change"
|
||||
msgstr "Для изменения отредактируйте файл i2psnark.config и перезагрузите I2PSnark"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:845
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:984
|
||||
msgid "Auto start"
|
||||
msgstr "Автозапуск"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:849
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:988
|
||||
msgid "If checked, automatically start torrents that are added"
|
||||
msgstr "Автоматически запускать торренты после добавления"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:872
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1011
|
||||
msgid "Total uploader limit"
|
||||
msgstr "Ограничение количества слотов отдачи"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:879
|
||||
# TODO should replace "uploader limit NN peers" with "global number of upload slots: NN"
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1014
|
||||
msgid "peers"
|
||||
msgstr "пир."
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1018
|
||||
msgid "Up bandwidth limit"
|
||||
msgstr "Ограничение скорости отдачи"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:882
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1021
|
||||
msgid "Half available bandwidth recommended."
|
||||
msgstr "Рекомендуется использовать половину от доступной пропускной способности."
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:884
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1023
|
||||
msgid "View or change router bandwidth"
|
||||
msgstr "Посмотреть/настроить ограничения скорости в маршрутизаторе I2P"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:888
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1027
|
||||
msgid "Use open trackers also"
|
||||
msgstr "Дополнительно использовать открытые трекеры"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:892
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1031
|
||||
msgid "If checked, announce torrents to open trackers as well as the tracker listed in the torrent file"
|
||||
msgstr "Анонсировать торренты на открытых трекерах, дополнительно к тем, что указаны внутри торрента"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:896
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1035
|
||||
msgid "Open tracker announce URLs"
|
||||
msgstr "URL открытых трекеров"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:907
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1047
|
||||
msgid "Inbound Settings"
|
||||
msgstr "Входящие туннели"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1053
|
||||
msgid "Outbound Settings"
|
||||
msgstr "Исходящие туннели"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1060
|
||||
msgid "I2CP host"
|
||||
msgstr "Адрес I2CP"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:912
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1065
|
||||
msgid "I2CP port"
|
||||
msgstr "Порт I2CP"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:925
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1077
|
||||
msgid "I2CP options"
|
||||
msgstr "Параметры I2CP"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:930
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1082
|
||||
msgid "Save configuration"
|
||||
msgstr "Сохранить настройки"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:983
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1092
|
||||
#, java-format
|
||||
msgid "1 hop"
|
||||
msgid_plural "{0} hops"
|
||||
msgstr[0] "{0} хоп"
|
||||
msgstr[1] "{0} хопа"
|
||||
msgstr[2] "{0} хопов"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1093
|
||||
#, java-format
|
||||
msgid "1 tunnel"
|
||||
msgid_plural "{0} tunnels"
|
||||
msgstr[0] "{0} туннель"
|
||||
msgstr[1] "{0} туннеля"
|
||||
msgstr[2] "{0} туннелей"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1107
|
||||
#, java-format
|
||||
msgid "1 "
|
||||
msgid_plural "{0} "
|
||||
msgstr[0] "{0} "
|
||||
msgstr[1] "{0} "
|
||||
msgstr[2] "{0} "
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1237
|
||||
msgid "Up to higher level directory"
|
||||
msgstr "Перейти в директорию уровнем выше"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1242
|
||||
msgid "File"
|
||||
msgstr "Файл"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1242
|
||||
msgid "Size"
|
||||
msgstr "Размер"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1265
|
||||
msgid "Directory"
|
||||
msgstr "Директория"
|
||||
|
||||
# This debug error message intentionally left in English
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1270
|
||||
msgid "Torrent not found?"
|
||||
msgstr "Torrent not found?"
|
||||
|
||||
# This debug error message intentionally left in English
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1278
|
||||
msgid "File not found in torrent?"
|
||||
msgstr "File not found in torrent?"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1284
|
||||
msgid "complete"
|
||||
msgstr "скачано"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1285
|
||||
msgid "bytes remaining"
|
||||
msgstr "байт осталось"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1408
|
||||
#, java-format
|
||||
msgid "Torrent fetched from {0}"
|
||||
msgstr "Получен торрент из: {0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1011
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1436
|
||||
#, java-format
|
||||
msgid "Torrent at {0} was not valid"
|
||||
msgstr "Торрент полученный из {0} некорректен"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1016
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1441
|
||||
#, java-format
|
||||
msgid "Torrent was not retrieved from {0}"
|
||||
msgstr "Не удалось получить торрент из: {0}"
|
||||
|
||||
#~ msgid "{0} torrents"
|
||||
#~ msgstr "{0} торрентов"
|
||||
#~ msgid "hops"
|
||||
#~ msgstr "хопа(-ов)"
|
||||
#~ msgid "tunnels"
|
||||
#~ msgstr "туннеля(-ей)"
|
||||
#~ msgid "Bytes"
|
||||
#~ msgstr "байт"
|
||||
#~ msgid "Cannot change the I2CP settings while torrents are active"
|
||||
#~ msgstr "Невозможно изменить настройки I2CP пока есть активные торренты"
|
||||
|
||||
|
@ -8,663 +8,736 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P i2psnark\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2010-03-05 12:41+0000\n"
|
||||
"PO-Revision-Date: 2010-03-05 21:58+0800\n"
|
||||
"POT-Creation-Date: 2010-05-29 02:34+0000\n"
|
||||
"PO-Revision-Date: 2010-05-29 10:55+0800\n"
|
||||
"Last-Translator: walking <walking@mail.i2p>\n"
|
||||
"Language-Team: foo <foo@bar>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Poedit-Language: Chinese\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:84
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:87
|
||||
#, java-format
|
||||
msgid "Adding torrents in {0} minutes"
|
||||
msgstr "{0}分钟内完成添加"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:242
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:251
|
||||
#, java-format
|
||||
msgid "Total uploaders limit changed to {0}"
|
||||
msgstr ""
|
||||
msgstr "总上传种子数限制已更新为{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:244
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:253
|
||||
#, java-format
|
||||
msgid "Minimum total uploaders limit is {0}"
|
||||
msgstr ""
|
||||
msgstr "最低上传种子数限制为{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:256
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:265
|
||||
#, java-format
|
||||
msgid "Up BW limit changed to {0}KBps"
|
||||
msgstr "上传带宽限制改为 {0} KBps"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:258
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:267
|
||||
#, java-format
|
||||
msgid "Minimum up bandwidth limit is {0}KBps"
|
||||
msgstr "最小上传带宽限制为 {0} KBps"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:302
|
||||
msgid "Cannot change the I2CP settings while torrents are active"
|
||||
msgstr "正在下载/上传,无法更改I2CP设置"
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:314
|
||||
msgid "I2CP and tunnel changes will take effect after stopping all torrents"
|
||||
msgstr "I2CP与隧道设置的变化在所有种子停止后才能生效"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:308
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:320
|
||||
msgid "Disconnecting old I2CP destination"
|
||||
msgstr "正在断开旧的I2CP目标"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:312
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:324
|
||||
#, java-format
|
||||
msgid "I2CP settings changed to {0}"
|
||||
msgstr "I2CP设置改为{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:316
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:328
|
||||
msgid "Unable to connect with the new settings, reverting to the old I2CP settings"
|
||||
msgstr "无法通过新设置连接,恢复I2CP的旧设置"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:320
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:332
|
||||
msgid "Unable to reconnect with the old settings!"
|
||||
msgstr "旧设置也无法连接!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:322
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:334
|
||||
msgid "Reconnected on the new I2CP destination"
|
||||
msgstr "重新连接新I2CP目标"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:333
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:345
|
||||
#, java-format
|
||||
msgid "I2CP listener restarted for \"{0}\""
|
||||
msgstr "\"{0}\"的I2CP监听端口已启动"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:344
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:356
|
||||
msgid "Enabled autostart"
|
||||
msgstr "启用自动启动"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:346
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:358
|
||||
msgid "Disabled autostart"
|
||||
msgstr "禁用自动启动"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:352
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:364
|
||||
msgid "Enabled open trackers - torrent restart required to take effect."
|
||||
msgstr "启用OpenTracker-重新启动种子后生效"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:354
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:366
|
||||
msgid "Disabled open trackers - torrent restart required to take effect."
|
||||
msgstr "禁用OpenTracker - 重新启动种子后生效"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:361
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:373
|
||||
msgid "Open Tracker list changed - torrent restart required to take effect."
|
||||
msgstr "OpenTracker列表已改变 - 重新启动种子后生效"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:368
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:380
|
||||
msgid "Configuration unchanged."
|
||||
msgstr "设置未改变"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:378
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:390
|
||||
#, java-format
|
||||
msgid "Unable to save the config to {0}"
|
||||
msgstr "无法保存设置到{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:396
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:408
|
||||
msgid "Connecting to I2P"
|
||||
msgstr "正在连接到I2P"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:399
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:411
|
||||
msgid "Error connecting to I2P - check your I2CP settings!"
|
||||
msgstr "连接I2P时发生错误 - 请检查I2CP设置!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:408
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:420
|
||||
#, java-format
|
||||
msgid "Error: Could not add the torrent {0}"
|
||||
msgstr "错误:无法添加种子{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:430
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:442
|
||||
#, java-format
|
||||
msgid "Cannot open \"{0}\""
|
||||
msgstr "无法打开 \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:443
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:455
|
||||
#, java-format
|
||||
msgid "Warning - Ignoring non-i2p tracker in \"{0}\", will announce to i2p open trackers only"
|
||||
msgstr "警告 - 忽略\"{0}\"文件中I2P网络外的Tracker服务器,文件将仅发布至 I2P 内的 Open Tracker 服务器。"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:445
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:457
|
||||
#, java-format
|
||||
msgid "Warning - Ignoring non-i2p tracker in \"{0}\", and open trackers are disabled, you must enable open trackers before starting the torrent!"
|
||||
msgstr "警告 - 忽略\"{0}\"文件中I2P网络外的Tracker服务器,OpenTracker已禁用,启动此种子前您必须启用OpenTracker。"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:464
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:476
|
||||
#, java-format
|
||||
msgid "Torrent in \"{0}\" is invalid"
|
||||
msgstr "无效种子 \"{0}\" "
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:479
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:491
|
||||
#, java-format
|
||||
msgid "Torrent added and started: \"{0}\""
|
||||
msgstr "已添加并启动种子:\"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:481
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:493
|
||||
#, java-format
|
||||
msgid "Torrent added: \"{0}\""
|
||||
msgstr "已添加种子:\"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:578
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:590
|
||||
#, java-format
|
||||
msgid "Too many files in \"{0}\" ({1}), deleting it!"
|
||||
msgstr "\"{0}\" ({1}) 含有太多文件,删除之!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:580
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:592
|
||||
#, java-format
|
||||
msgid "Torrent file \"{0}\" cannot end in \".torrent\", deleting it!"
|
||||
msgstr "种子文件 \"{0}\" 不以 \".torrent\"结尾,正在删除!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:582
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:594
|
||||
#, java-format
|
||||
msgid "No pieces in \"{0}\", deleting it!"
|
||||
msgstr "\"{0}\" 中没有数据片,删除之!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:584
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:596
|
||||
#, java-format
|
||||
msgid "Too many pieces in \"{0}\", limit is {1}, deleting it!"
|
||||
msgstr "\"{0}\" 中文件分片太多,限额为{1},删除之!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:586
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:598
|
||||
#, java-format
|
||||
msgid "Pieces are too large in \"{0}\" ({1}B), deleting it."
|
||||
msgstr "\"{0}\" ({1}B) 中文件分片过大,删除之。"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:587
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:599
|
||||
#, java-format
|
||||
msgid "Limit is {0}B"
|
||||
msgstr "限额为 {0}B"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:595
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:607
|
||||
#, java-format
|
||||
msgid "Torrents larger than {0}B are not supported yet, deleting \"{1}\""
|
||||
msgstr "目前不支持大于{0}B 的种子,正在删除\"{1}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:611
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:623
|
||||
#, java-format
|
||||
msgid "Error: Could not remove the torrent {0}"
|
||||
msgstr "错误:无法删除种子{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:632
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:644
|
||||
#, java-format
|
||||
msgid "Torrent stopped: \"{0}\""
|
||||
msgstr "种子已停止:\"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:647
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:659
|
||||
#, java-format
|
||||
msgid "Torrent removed: \"{0}\""
|
||||
msgstr "种子已删除:\"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:680
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:692
|
||||
#, java-format
|
||||
msgid "Download finished: \"{0}\""
|
||||
msgstr "下载已完成:\"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:680
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:692
|
||||
#, java-format
|
||||
msgid "size: {0}B"
|
||||
msgstr "大小:{0}B"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:708
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:720
|
||||
msgid "Unable to connect to I2P!"
|
||||
msgstr "无法连接至I2P!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:86
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:169
|
||||
msgid "I2PSnark - Anonymous BitTorrent Client"
|
||||
msgstr "I2PSnark - 匿名BitTorrent客户端"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:95
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:178
|
||||
msgid "Refresh page"
|
||||
msgstr "刷新页面"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:97
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:669
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:180
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:808
|
||||
msgid "I2PSnark"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:99
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:182
|
||||
msgid "Forum"
|
||||
msgstr "论坛"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:125
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:208
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1243
|
||||
msgid "Status"
|
||||
msgstr "状态"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:131
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:214
|
||||
msgid "Hide Peers"
|
||||
msgstr "隐藏用户"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:134
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:217
|
||||
msgid "Show Peers"
|
||||
msgstr "显示用户"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:139
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:222
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1225
|
||||
msgid "Torrent"
|
||||
msgstr "种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:141
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:224
|
||||
msgid "ETA"
|
||||
msgstr ""
|
||||
msgstr "预计剩余时间"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:143
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:226
|
||||
msgid "Downloaded"
|
||||
msgstr "已下载"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:145
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:228
|
||||
msgid "Uploaded"
|
||||
msgstr "已上传"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:147
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:230
|
||||
msgid "Down Rate"
|
||||
msgstr "下载速度"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:149
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:232
|
||||
msgid "Up Rate"
|
||||
msgstr "上传速度"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:156
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:239
|
||||
msgid "Stop all torrents and the I2P tunnel"
|
||||
msgstr "停止全部种子及I2P隧道"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:158
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:241
|
||||
msgid "Stop All"
|
||||
msgstr "停止全部"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:163
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:246
|
||||
msgid "Start all torrents and the I2P tunnel"
|
||||
msgstr "启动全部种子及I2P隧道"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:165
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:248
|
||||
msgid "Start All"
|
||||
msgstr "启动全部"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:182
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:265
|
||||
msgid "No torrents loaded."
|
||||
msgstr "未载入任何种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:187
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:270
|
||||
msgid "Totals"
|
||||
msgstr "总计"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:189
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:272
|
||||
#, java-format
|
||||
msgid "{0} torrents"
|
||||
msgstr "{0} 个种子"
|
||||
msgid "1 torrent"
|
||||
msgid_plural "{0} torrents"
|
||||
msgstr[0] "{0}个种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:192
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:275
|
||||
#, java-format
|
||||
msgid "{0} connected peers"
|
||||
msgstr "{0} 已连接用户"
|
||||
msgid "1 connected peer"
|
||||
msgid_plural "{0} connected peers"
|
||||
msgstr[0] "{0}个已连接用户"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:227
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:310
|
||||
#, java-format
|
||||
msgid "Torrent file {0} does not exist"
|
||||
msgstr "种子文件{0}不存在"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:237
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1003
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:320
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1428
|
||||
#, java-format
|
||||
msgid "Torrent already running: {0}"
|
||||
msgstr "种子已启动:{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:239
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1005
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:322
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1430
|
||||
#, java-format
|
||||
msgid "Torrent already in the queue: {0}"
|
||||
msgstr "种子排队中:{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:243
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:326
|
||||
#, java-format
|
||||
msgid "Copying torrent to {0}"
|
||||
msgstr "正在复制种子到{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:246
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:329
|
||||
#, java-format
|
||||
msgid "Unable to copy the torrent to {0}"
|
||||
msgstr "无法复制种子文件到{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:246
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:329
|
||||
#, java-format
|
||||
msgid "from {0}"
|
||||
msgstr "来源{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:254
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:337
|
||||
#, java-format
|
||||
msgid "Fetching {0}"
|
||||
msgstr "正在获取{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:258
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:341
|
||||
msgid "Invalid URL - must start with http://"
|
||||
msgstr "无效链接 - 必须以http:// 开头"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:288
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:371
|
||||
#, java-format
|
||||
msgid "Starting up torrent {0}"
|
||||
msgstr "正在启动种子{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:308
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:326
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:391
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:409
|
||||
#, java-format
|
||||
msgid "Torrent file deleted: {0}"
|
||||
msgstr "种子文件已删除:{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:332
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:342
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:415
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:425
|
||||
#, java-format
|
||||
msgid "Data file deleted: {0}"
|
||||
msgstr "数据文件已删除:{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:334
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:344
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:417
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:427
|
||||
#, java-format
|
||||
msgid "Data file could not be deleted: {0}"
|
||||
msgstr "无法删除数据文件:{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:353
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:436
|
||||
#, java-format
|
||||
msgid "Data dir deleted: {0}"
|
||||
msgstr "数据文件夹已删除:{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:384
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:467
|
||||
msgid "Error creating torrent - you must select a tracker"
|
||||
msgstr "创建种子时发生错误 - 您必须选择一个Tracker"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:399
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:482
|
||||
#, java-format
|
||||
msgid "Torrent created for \"{0}\""
|
||||
msgstr "种子创建成功\"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:402
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:485
|
||||
#, java-format
|
||||
msgid "Many I2P trackers require you to register new torrents before seeding - please do so before starting \"{0}\""
|
||||
msgstr "多数I2PTracker需要用户在做种前注册新种子 - 请在启动 \"{0}\"前到所使用的Tracker进行注册。"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:404
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:487
|
||||
#, java-format
|
||||
msgid "Error creating a torrent for \"{0}\""
|
||||
msgstr "创建种子时发生错误 \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:407
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:490
|
||||
#, java-format
|
||||
msgid "Cannot create a torrent for the nonexistent data: {0}"
|
||||
msgstr "无法为不存在的数据文件创建种子:{0}"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:410
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:493
|
||||
msgid "Error creating torrent - you must enter a file or directory"
|
||||
msgstr "创建种子时发生错误 - 必须指定文件或文件夹"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:413
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:496
|
||||
msgid "Stopping all torrents and closing the I2P tunnel."
|
||||
msgstr "正在停用所有种子并关闭I2P隧道。"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:422
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:505
|
||||
msgid "I2P tunnel closed."
|
||||
msgstr "I2P隧道已关闭"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:425
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:508
|
||||
msgid "Opening the I2P tunnel and starting all torrents."
|
||||
msgstr "正在打开I2P隧道并启动所有种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:505
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:683
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:628
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:822
|
||||
msgid "Unknown"
|
||||
msgstr "未知"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:508
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:512
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:516
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:631
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:636
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:641
|
||||
msgid "TrackerErr"
|
||||
msgstr "Tracker错误"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:510
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:512
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:522
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:524
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:531
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:533
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:537
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:539
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:875
|
||||
msgid "peers"
|
||||
msgstr "用户"
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:634
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:637
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:648
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:651
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:659
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:662
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:667
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:670
|
||||
#, java-format
|
||||
msgid "1 peer"
|
||||
msgid_plural "{0} peers"
|
||||
msgstr[0] "{0}个用户"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:520
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:524
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:645
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:650
|
||||
msgid "Seeding"
|
||||
msgstr "正做种"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:526
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:653
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1281
|
||||
msgid "Complete"
|
||||
msgstr "完成"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:529
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:533
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:656
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:661
|
||||
msgid "OK"
|
||||
msgstr "确定"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:535
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:539
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:664
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:669
|
||||
msgid "Stalled"
|
||||
msgstr "等待"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:541
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:672
|
||||
msgid "No Peers"
|
||||
msgstr "没有用户"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:543
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:674
|
||||
msgid "Stopped"
|
||||
msgstr "已停用"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:556
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:689
|
||||
msgid "View files"
|
||||
msgstr "浏览文件"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:558
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:691
|
||||
msgid "Open file"
|
||||
msgstr "打开文件"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:582
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:794
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:721
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:933
|
||||
msgid "Tracker"
|
||||
msgstr "Tracker服务器"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:583
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:722
|
||||
msgid "Details"
|
||||
msgstr "详情"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:617
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:756
|
||||
msgid "Stop the torrent"
|
||||
msgstr "停止种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:619
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:758
|
||||
msgid "Stop"
|
||||
msgstr "停止"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:625
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:764
|
||||
msgid "Start the torrent"
|
||||
msgstr "启动种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:627
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:766
|
||||
msgid "Start"
|
||||
msgstr "启动"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:632
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:771
|
||||
msgid "Remove the torrent from the active list, deleting the .torrent file"
|
||||
msgstr "取消下载任务并删除对应种子文件。"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:637
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:776
|
||||
#, java-format
|
||||
msgid "Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?"
|
||||
msgstr "您确定要删除文件“{0}.torrent”(下载的数据文件不会被删除)?"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:639
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:778
|
||||
msgid "Remove"
|
||||
msgstr "移除"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:643
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:782
|
||||
msgid "Delete the .torrent file and the associated data file(s)"
|
||||
msgstr "删除种子及所下载的文件"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:648
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:787
|
||||
#, java-format
|
||||
msgid "Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?"
|
||||
msgstr "您确定要删除种子“{0}”(下载的数据文件会一并被删除)?"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:650
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:789
|
||||
msgid "Delete"
|
||||
msgstr "删除"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:693
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:832
|
||||
msgid "Seed"
|
||||
msgstr "种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:711
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:850
|
||||
msgid "Uninteresting (The peer has no pieces we need)"
|
||||
msgstr "无需要部分"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:713
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:852
|
||||
msgid "Choked (The peer is not allowing us to request pieces)"
|
||||
msgstr "拒绝请求"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:727
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:866
|
||||
msgid "Uninterested (We have no pieces the peer needs)"
|
||||
msgstr "无需要部分"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:729
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:868
|
||||
msgid "Choking (We are not allowing the peer to request pieces)"
|
||||
msgstr "拒绝请求"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:756
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:895
|
||||
msgid "Add Torrent"
|
||||
msgstr "添加种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:758
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:897
|
||||
msgid "From URL"
|
||||
msgstr "从URL"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:763
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:902
|
||||
msgid "Add torrent"
|
||||
msgstr "添加种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:766
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:905
|
||||
#, java-format
|
||||
msgid "Alternately, you can copy .torrent files to the directory {0}."
|
||||
msgstr "或者您可以将.torrent文件复制到以下目录{0}."
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:768
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:907
|
||||
msgid "Removing a .torrent file will cause the torrent to stop."
|
||||
msgstr "删除种子文件将导致中止该下载任务。"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:785
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:924
|
||||
msgid "Create Torrent"
|
||||
msgstr "创建种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:788
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:927
|
||||
msgid "Data to seed"
|
||||
msgstr "做种数据"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:792
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:931
|
||||
msgid "File or directory to seed (must be within the specified path)"
|
||||
msgstr "做种文件或文件夹(必须下面为Snark指定的文件夹中)"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:796
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:935
|
||||
msgid "Select a tracker"
|
||||
msgstr "选择一个Tracker"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:809
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:948
|
||||
msgid "or"
|
||||
msgstr "或"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:812
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:951
|
||||
msgid "Specify custom tracker announce URL"
|
||||
msgstr "指定Open Tracker发布链接"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:815
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:954
|
||||
msgid "Create torrent"
|
||||
msgstr "创建种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:833
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:972
|
||||
msgid "Configuration"
|
||||
msgstr "设置"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:836
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:975
|
||||
msgid "Data directory"
|
||||
msgstr "数据文件夹"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:839
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:978
|
||||
msgid "Directory to store torrents and data"
|
||||
msgstr "种子及被做种文件的保存位置。"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:841
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:980
|
||||
msgid "Edit i2psnark.config and restart to change"
|
||||
msgstr "编辑 i2psnark.config 并重启Snark后生效"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:845
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:984
|
||||
msgid "Auto start"
|
||||
msgstr "自动启动"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:849
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:988
|
||||
msgid "If checked, automatically start torrents that are added"
|
||||
msgstr "选中后Snark将自动启动已添加的所有种子。"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:872
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1011
|
||||
msgid "Total uploader limit"
|
||||
msgstr ""
|
||||
msgstr "限制总上传种子数为"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:879
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1014
|
||||
msgid "peers"
|
||||
msgstr "用户"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1018
|
||||
msgid "Up bandwidth limit"
|
||||
msgstr "上传带宽限制"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:882
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1021
|
||||
msgid "Half available bandwidth recommended."
|
||||
msgstr "推荐设置为可用带宽的一半。"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:884
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1023
|
||||
msgid "View or change router bandwidth"
|
||||
msgstr "浏览或修改路由器带宽"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:888
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1027
|
||||
msgid "Use open trackers also"
|
||||
msgstr "同时使用OpenTracker"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:892
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1031
|
||||
msgid "If checked, announce torrents to open trackers as well as the tracker listed in the torrent file"
|
||||
msgstr "选择后在OpenTracker及种子文件中的Tracker上同时发布。"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:896
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1035
|
||||
msgid "Open tracker announce URLs"
|
||||
msgstr "Open Tracker发布链接"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:907
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1047
|
||||
msgid "Inbound Settings"
|
||||
msgstr "入站设置"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1053
|
||||
msgid "Outbound Settings"
|
||||
msgstr "出站设置"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1060
|
||||
msgid "I2CP host"
|
||||
msgstr "I2CP主机"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:912
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1065
|
||||
msgid "I2CP port"
|
||||
msgstr "I2CP端口"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:925
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1077
|
||||
msgid "I2CP options"
|
||||
msgstr "I2CP选项"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:930
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1082
|
||||
msgid "Save configuration"
|
||||
msgstr "保存设置"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:983
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1092
|
||||
#, java-format
|
||||
msgid "1 hop"
|
||||
msgid_plural "{0} hops"
|
||||
msgstr[0] "{0}跳"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1093
|
||||
#, java-format
|
||||
msgid "1 tunnel"
|
||||
msgid_plural "{0} tunnels"
|
||||
msgstr[0] "{0}隧道"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1107
|
||||
#, java-format
|
||||
msgid "1 "
|
||||
msgid_plural "{0} "
|
||||
msgstr[0] ""
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1237
|
||||
msgid "Up to higher level directory"
|
||||
msgstr "上一层文件夹"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1242
|
||||
msgid "File"
|
||||
msgstr "文件"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1242
|
||||
msgid "Size"
|
||||
msgstr "大小"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1265
|
||||
msgid "Directory"
|
||||
msgstr "文件夹"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1270
|
||||
msgid "Torrent not found?"
|
||||
msgstr "种子未找到"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1278
|
||||
msgid "File not found in torrent?"
|
||||
msgstr "种子中没有发现文件?"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1284
|
||||
msgid "complete"
|
||||
msgstr "完成"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1285
|
||||
msgid "bytes remaining"
|
||||
msgstr "剩余字节数"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1408
|
||||
#, java-format
|
||||
msgid "Torrent fetched from {0}"
|
||||
msgstr "从{0}获取种子成功"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1011
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1436
|
||||
#, java-format
|
||||
msgid "Torrent at {0} was not valid"
|
||||
msgstr "{0}的种子中有错误"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1016
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1441
|
||||
#, java-format
|
||||
msgid "Torrent was not retrieved from {0}"
|
||||
msgstr "从{0}获得种子失败"
|
||||
|
||||
#~ msgid "Cannot change the I2CP settings while torrents are active"
|
||||
#~ msgstr "正在下载/上传,无法更改I2CP设置"
|
||||
#~ msgid "{0} torrents"
|
||||
#~ msgstr "{0} 个种子"
|
||||
#~ msgid "Non-i2p tracker in \"{0}\", deleting it from our list of trackers!"
|
||||
#~ msgstr ""
|
||||
#~ "【匿名性警告】\"{0}\" 中含有非I2P Tracker,程序将从Tracker列表中将其删除。"
|
||||
|
@ -51,7 +51,7 @@ do
|
||||
# To start a new translation, copy the header from an old translation to the new .po file,
|
||||
# then ant distclean updater.
|
||||
find $JPATHS -name *.java > $TMPFILE
|
||||
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 \
|
||||
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 --add-comments\
|
||||
--keyword=_ --keyword=_x --keyword=intl._ --keyword=intl.title \
|
||||
-o ${i}t
|
||||
if [ $? -ne 0 ]
|
||||
|
@ -54,7 +54,7 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
}
|
||||
}
|
||||
|
||||
if (dests.size() <= 0) {
|
||||
if (dests.isEmpty()) {
|
||||
l.log("No target destinations found");
|
||||
notifyEvent("openClientResult", "error");
|
||||
return;
|
||||
|
@ -641,7 +641,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
while (open) {
|
||||
try {
|
||||
synchronized (_waitingSockets) {
|
||||
if (_waitingSockets.size() <= 0)
|
||||
if (_waitingSockets.isEmpty())
|
||||
_waitingSockets.wait();
|
||||
else
|
||||
s = (Socket)_waitingSockets.remove(0);
|
||||
|
@ -508,7 +508,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
|
||||
line = method + " " + request.substring(pos);
|
||||
} else if (host.toLowerCase().equals("localhost") || host.equals("127.0.0.1")) {
|
||||
} else if (host.toLowerCase().equals("localhost") || host.equals("127.0.0.1") ||
|
||||
host.startsWith("192.168.")) {
|
||||
// if somebody is trying to get to 192.168.example.com, oh well
|
||||
if (out != null) {
|
||||
out.write(getErrorPage("localhost", ERR_LOCALHOST));
|
||||
writeFooter(out);
|
||||
|
@ -61,7 +61,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
if (dests.size() <= 0) {
|
||||
if (dests.isEmpty()) {
|
||||
l.log("No target destinations found");
|
||||
notifyEvent("openClientResult", "error");
|
||||
return;
|
||||
|
@ -31,6 +31,9 @@ import net.i2p.util.Log;
|
||||
*
|
||||
* There are three options for mangling the desthash. Put the option in the
|
||||
* "custom options" section of i2ptunnel.
|
||||
* - ircserver.method unset: Defaults to user.
|
||||
* - ircserver.method=user: Use method described above.
|
||||
* - ircserver.method=webirc: Use the WEBIRC protocol.
|
||||
* - ircserver.cloakKey unset: Cloak with a random value that is persistent for
|
||||
* the life of this tunnel. This is the default.
|
||||
* - ircserver.cloakKey=somepassphrase: Cloak with the hash of the passphrase. Use this to
|
||||
@ -39,6 +42,8 @@ import net.i2p.util.Log;
|
||||
* be able to track users even when they switch servers.
|
||||
* Note: don't quote or put spaces in the passphrase,
|
||||
* the i2ptunnel gui can't handle it.
|
||||
* - ircserver.webircPassword=password The password to use for the WEBIRC protocol.
|
||||
* - ircserver.webircSpoofIP=IP The IP
|
||||
* - ircserver.fakeHostname=%f.b32.i2p: Set the fake hostname sent by I2PTunnel,
|
||||
* %f is the full B32 destination hash
|
||||
* %c is the cloaked hash.
|
||||
@ -48,7 +53,12 @@ import net.i2p.util.Log;
|
||||
* @author zzz
|
||||
*/
|
||||
public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
|
||||
public static final String PROP_METHOD="ircserver.method";
|
||||
public static final String PROP_METHOD_DEFAULT="user";
|
||||
public static final String PROP_CLOAK="ircserver.cloakKey";
|
||||
public static final String PROP_WEBIRC_PASSWORD="ircserver.webircPassword";
|
||||
public static final String PROP_WEBIRC_SPOOF_IP="ircserver.webircSpoofIP";
|
||||
public static final String PROP_WEBIRC_SPOOF_IP_DEFAULT="127.0.0.1";
|
||||
public static final String PROP_HOSTNAME="ircserver.fakeHostname";
|
||||
public static final String PROP_HOSTNAME_DEFAULT="%f.b32.i2p";
|
||||
|
||||
@ -67,7 +77,20 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
|
||||
|
||||
/** generate a random 32 bytes, or the hash of the passphrase */
|
||||
private void initCloak(I2PTunnel tunnel) {
|
||||
// get the properties of this server-tunnel
|
||||
Properties opts = tunnel.getClientOptions();
|
||||
|
||||
// get method of host faking
|
||||
this.method = opts.getProperty(PROP_METHOD, PROP_METHOD_DEFAULT);
|
||||
assert this.method != null;
|
||||
|
||||
// get the password for the webirc method
|
||||
this.webircPassword = opts.getProperty(PROP_WEBIRC_PASSWORD);
|
||||
|
||||
// get the spoof IP for the webirc method
|
||||
this.webircSpoofIP = opts.getProperty(PROP_WEBIRC_SPOOF_IP, PROP_WEBIRC_SPOOF_IP_DEFAULT);
|
||||
|
||||
// get the cloaking passphrase
|
||||
String passphrase = opts.getProperty(PROP_CLOAK);
|
||||
if (passphrase == null) {
|
||||
this.cloakKey = new byte[Hash.HASH_LENGTH];
|
||||
@ -76,17 +99,30 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
|
||||
this.cloakKey = SHA256Generator.getInstance().calculateHash(passphrase.trim().getBytes()).getData();
|
||||
}
|
||||
|
||||
// get the fake hostmask to use
|
||||
this.hostname = opts.getProperty(PROP_HOSTNAME, PROP_HOSTNAME_DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blockingHandle(I2PSocket socket) {
|
||||
try {
|
||||
// give them 15 seconds to send in the request
|
||||
socket.setReadTimeout(15*1000);
|
||||
InputStream in = socket.getInputStream();
|
||||
String modifiedRegistration = filterRegistration(in, cloakDest(socket.getPeerDestination()));
|
||||
socket.setReadTimeout(readTimeout);
|
||||
String modifiedRegistration;
|
||||
if(!this.method.equals("webirc")) {
|
||||
// give them 15 seconds to send in the request
|
||||
socket.setReadTimeout(15*1000);
|
||||
InputStream in = socket.getInputStream();
|
||||
modifiedRegistration = filterRegistration(in, cloakDest(socket.getPeerDestination()));
|
||||
socket.setReadTimeout(readTimeout);
|
||||
} else {
|
||||
StringBuffer buf = new StringBuffer("WEBIRC ");
|
||||
buf.append(this.webircPassword);
|
||||
buf.append(" cgiirc ");
|
||||
buf.append(cloakDest(socket.getPeerDestination()));
|
||||
buf.append(' ');
|
||||
buf.append(this.webircSpoofIP);
|
||||
buf.append("\r\n");
|
||||
modifiedRegistration = buf.toString();
|
||||
}
|
||||
Socket s = new Socket(remoteHost, remotePort);
|
||||
new I2PTunnelRunner(s, socket, slock, null, modifiedRegistration.getBytes(), null);
|
||||
} catch (SocketException ex) {
|
||||
@ -185,4 +221,7 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
|
||||
|
||||
private byte[] cloakKey; // 32 bytes of stuff to scramble the dest with
|
||||
private String hostname;
|
||||
private String method;
|
||||
private String webircPassword;
|
||||
private String webircSpoofIP;
|
||||
}
|
||||
|
@ -329,7 +329,7 @@ public class TunnelControllerGroup {
|
||||
Set owners = (Set)_sessions.get(session);
|
||||
if (owners != null) {
|
||||
owners.remove(controller);
|
||||
if (owners.size() <= 0) {
|
||||
if (owners.isEmpty()) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("After releasing session " + session + " by " + controller + ", no more owners remain");
|
||||
shouldClose = true;
|
||||
|
@ -224,7 +224,7 @@ public class SOCKS4aServer extends SOCKSServer {
|
||||
throw new SOCKSException(err);
|
||||
} else {
|
||||
List<String> proxies = t.getProxies(connPort);
|
||||
if (proxies == null || proxies.size() <= 0) {
|
||||
if (proxies == null || proxies.isEmpty()) {
|
||||
String err = "No outproxy configured for port " + connPort + " and no default configured either - host: " + connHostName;
|
||||
_log.error(err);
|
||||
try {
|
||||
|
@ -332,7 +332,7 @@ public class SOCKS5Server extends SOCKSServer {
|
||||
throw new SOCKSException(err);
|
||||
} else {
|
||||
List<String> proxies = t.getProxies(connPort);
|
||||
if (proxies == null || proxies.size() <= 0) {
|
||||
if (proxies == null || proxies.isEmpty()) {
|
||||
String err = "No outproxy configured for port " + connPort + " and no default configured either";
|
||||
_log.error(err);
|
||||
try {
|
||||
|
@ -1301,7 +1301,7 @@ public class HttpContext extends Container
|
||||
|
||||
List scss= _constraintMap.getMatches(pathInContext);
|
||||
String pattern=null;
|
||||
if (scss != null && scss.size() > 0)
|
||||
if (scss != null && !scss.isEmpty())
|
||||
{
|
||||
Object constraints= null;
|
||||
|
||||
|
431
apps/jetty/java/src/org/mortbay/util/Resource.java
Normal file
@ -0,0 +1,431 @@
|
||||
// ========================================================================
|
||||
// $Id: Resource.java,v 1.32 2009/05/16 01:53:36 gregwilkins Exp $
|
||||
// Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========================================================================
|
||||
package org.mortbay.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.mortbay.log.LogFactory;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Abstract resource class.
|
||||
*
|
||||
* @version $Id: Resource.java,v 1.32 2009/05/16 01:53:36 gregwilkins Exp $
|
||||
* @author Nuno Preguica
|
||||
* @author Greg Wilkins (gregw)
|
||||
*/
|
||||
public abstract class Resource implements Serializable
|
||||
{
|
||||
private static Log log = LogFactory.getLog(Resource.class);
|
||||
|
||||
Object _associate;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Construct a resource from a url.
|
||||
* @param url A URL.
|
||||
* @return A Resource object.
|
||||
*/
|
||||
public static Resource newResource(URL url)
|
||||
throws IOException
|
||||
{
|
||||
if (url==null)
|
||||
return null;
|
||||
|
||||
String urls=url.toExternalForm();
|
||||
if( urls.startsWith( "file:"))
|
||||
{
|
||||
try
|
||||
{
|
||||
FileResource fileResource= new FileResource(url);
|
||||
return fileResource;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
log.debug(LogSupport.EXCEPTION,e);
|
||||
return new BadResource(url,e.toString());
|
||||
}
|
||||
}
|
||||
else if( urls.startsWith( "jar:file:"))
|
||||
{
|
||||
return new JarFileResource(url);
|
||||
}
|
||||
else if( urls.startsWith( "jar:"))
|
||||
{
|
||||
return new JarResource(url);
|
||||
}
|
||||
|
||||
return new URLResource(url,null);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Construct a resource from a string.
|
||||
* @param resource A URL or filename.
|
||||
* @return A Resource object.
|
||||
*/
|
||||
public static Resource newResource(String resource)
|
||||
throws MalformedURLException, IOException
|
||||
{
|
||||
URL url=null;
|
||||
try
|
||||
{
|
||||
// Try to format as a URL?
|
||||
url = new URL(resource);
|
||||
}
|
||||
catch(MalformedURLException e)
|
||||
{
|
||||
if(!resource.startsWith("ftp:") &&
|
||||
!resource.startsWith("file:") &&
|
||||
!resource.startsWith("jar:"))
|
||||
{
|
||||
try
|
||||
{
|
||||
// It's a file.
|
||||
if (resource.startsWith("./"))
|
||||
resource=resource.substring(2);
|
||||
|
||||
File file=new File(resource).getCanonicalFile();
|
||||
url=file.toURI().toURL();
|
||||
|
||||
URLConnection connection=url.openConnection();
|
||||
FileResource fileResource= new FileResource(url,connection,file);
|
||||
return fileResource;
|
||||
}
|
||||
catch(Exception e2)
|
||||
{
|
||||
log.debug(LogSupport.EXCEPTION,e2);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("Bad Resource: "+resource);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
String nurl=url.toString();
|
||||
if (nurl.length()>0 &&
|
||||
nurl.charAt(nurl.length()-1)!=
|
||||
resource.charAt(resource.length()-1))
|
||||
{
|
||||
if ((nurl.charAt(nurl.length()-1)!='/' ||
|
||||
nurl.charAt(nurl.length()-2)!=resource.charAt(resource.length()-1))
|
||||
&&
|
||||
(resource.charAt(resource.length()-1)!='/' ||
|
||||
resource.charAt(resource.length()-2)!=nurl.charAt(nurl.length()-1)
|
||||
))
|
||||
{
|
||||
return new BadResource(url,"Trailing special characters stripped by URL in "+resource);
|
||||
}
|
||||
}
|
||||
return newResource(url);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Construct a system resource from a string.
|
||||
* The resource is tried as classloader resource before being
|
||||
* treated as a normal resource.
|
||||
*/
|
||||
public static Resource newSystemResource(String resource)
|
||||
throws IOException
|
||||
{
|
||||
URL url=null;
|
||||
// Try to format as a URL?
|
||||
ClassLoader
|
||||
loader=Thread.currentThread().getContextClassLoader();
|
||||
if (loader!=null)
|
||||
{
|
||||
url=loader.getResource(resource);
|
||||
if (url==null && resource.startsWith("/"))
|
||||
url=loader.getResource(resource.substring(1));
|
||||
}
|
||||
if (url==null)
|
||||
{
|
||||
loader=Resource.class.getClassLoader();
|
||||
if (loader!=null)
|
||||
{
|
||||
url=loader.getResource(resource);
|
||||
if (url==null && resource.startsWith("/"))
|
||||
url=loader.getResource(resource.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
if (url==null)
|
||||
{
|
||||
url=ClassLoader.getSystemResource(resource);
|
||||
if (url==null && resource.startsWith("/"))
|
||||
url=loader.getResource(resource.substring(1));
|
||||
}
|
||||
|
||||
if (url==null)
|
||||
return null;
|
||||
|
||||
return newResource(url);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void finalize()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Release any resources held by the resource.
|
||||
*/
|
||||
public abstract void release();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns true if the respresened resource exists.
|
||||
*/
|
||||
public abstract boolean exists();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns true if the respresenetd resource is a container/directory.
|
||||
* If the resource is not a file, resources ending with "/" are
|
||||
* considered directories.
|
||||
*/
|
||||
public abstract boolean isDirectory();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns the last modified time
|
||||
*/
|
||||
public abstract long lastModified();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Return the length of the resource
|
||||
*/
|
||||
public abstract long length();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns an URL representing the given resource
|
||||
*/
|
||||
public abstract URL getURL();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns an File representing the given resource or NULL if this
|
||||
* is not possible.
|
||||
*/
|
||||
public abstract File getFile()
|
||||
throws IOException;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns the name of the resource
|
||||
*/
|
||||
public abstract String getName();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns an input stream to the resource
|
||||
*/
|
||||
public abstract InputStream getInputStream()
|
||||
throws java.io.IOException;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns an output stream to the resource
|
||||
*/
|
||||
public abstract OutputStream getOutputStream()
|
||||
throws java.io.IOException, SecurityException;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Deletes the given resource
|
||||
*/
|
||||
public abstract boolean delete()
|
||||
throws SecurityException;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Rename the given resource
|
||||
*/
|
||||
public abstract boolean renameTo( Resource dest)
|
||||
throws SecurityException;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns a list of resource names contained in the given resource
|
||||
* The resource names are not URL encoded.
|
||||
*/
|
||||
public abstract String[] list();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns the resource contained inside the current resource with the
|
||||
* given name.
|
||||
* @param path The path segment to add, which should be encoded by the
|
||||
* encode method.
|
||||
*/
|
||||
public abstract Resource addPath(String path)
|
||||
throws IOException,MalformedURLException;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Encode according to this resource type.
|
||||
* The default implementation calls URI.encodePath(uri)
|
||||
* @param uri
|
||||
* @return String encoded for this resource type.
|
||||
*/
|
||||
public String encode(String uri)
|
||||
{
|
||||
return URI.encodePath(uri);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Object getAssociate()
|
||||
{
|
||||
return _associate;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void setAssociate(Object o)
|
||||
{
|
||||
_associate=o;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return The canonical Alias of this resource or null if none.
|
||||
*/
|
||||
public URL getAlias()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public CachedResource cache()
|
||||
throws IOException
|
||||
{
|
||||
return new CachedResource(this);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the resource list as a HTML directory listing.
|
||||
* @param base The base URL
|
||||
* @param parent True if the parent directory should be included
|
||||
* @return String of HTML
|
||||
*/
|
||||
public String getListHTML(String base,
|
||||
boolean parent)
|
||||
throws IOException
|
||||
{
|
||||
if (!isDirectory())
|
||||
return null;
|
||||
|
||||
|
||||
String[] ls = list();
|
||||
if (ls==null)
|
||||
return null;
|
||||
Arrays.sort(ls);
|
||||
|
||||
String title = "Directory: "+URI.decodePath(base);
|
||||
title=StringUtil.replace(StringUtil.replace(title,"<","<"),">",">");
|
||||
StringBuffer buf=new StringBuffer(4096);
|
||||
buf.append("<HTML><HEAD><TITLE>");
|
||||
buf.append(title);
|
||||
buf.append("</TITLE></HEAD><BODY>\n<H1>");
|
||||
buf.append(title);
|
||||
buf.append("</H1><TABLE BORDER=0>");
|
||||
|
||||
if (parent)
|
||||
{
|
||||
buf.append("<TR><TD><A HREF=");
|
||||
buf.append(URI.encodePath(URI.addPaths(base,"../")));
|
||||
buf.append(">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
|
||||
}
|
||||
|
||||
DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
|
||||
DateFormat.MEDIUM);
|
||||
for (int i=0 ; i< ls.length ; i++)
|
||||
{
|
||||
String encoded=URI.encodePath(ls[i]);
|
||||
// bugfix for I2P - Backport from Jetty 6 (zero file lengths and last-modified times)
|
||||
// http://jira.codehaus.org/browse/JETTY-361?page=com.atlassian.jira.plugin.system.issuetabpanels%3Achangehistory-tabpanel#issue-tabs
|
||||
// See resource.diff attachment
|
||||
//Resource item = addPath(encoded);
|
||||
Resource item = addPath(ls[i]);
|
||||
|
||||
buf.append("<TR><TD><A HREF=\"");
|
||||
|
||||
String path=URI.addPaths(base,encoded);
|
||||
|
||||
if (item.isDirectory() && !path.endsWith("/"))
|
||||
path=URI.addPaths(path,"/");
|
||||
buf.append(path);
|
||||
buf.append("\">");
|
||||
buf.append(StringUtil.replace(StringUtil.replace(ls[i],"<","<"),">",">"));
|
||||
buf.append(" ");
|
||||
buf.append("</TD><TD ALIGN=right>");
|
||||
buf.append(item.length());
|
||||
buf.append(" bytes </TD><TD>");
|
||||
buf.append(dfmt.format(new Date(item.lastModified())));
|
||||
buf.append("</TD></TR>\n");
|
||||
}
|
||||
buf.append("</TABLE>\n");
|
||||
buf.append("</BODY></HTML>\n");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param out
|
||||
* @param start First byte to write
|
||||
* @param count Bytes to write or -1 for all of them.
|
||||
*/
|
||||
public void writeTo(OutputStream out,long start,long count)
|
||||
throws IOException
|
||||
{
|
||||
InputStream in = getInputStream();
|
||||
try
|
||||
{
|
||||
in.skip(start);
|
||||
if (count<0)
|
||||
IO.copy(in,out);
|
||||
else
|
||||
IO.copy(in,out,(int)count);
|
||||
}
|
||||
finally
|
||||
{
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -69,7 +69,7 @@ class I2PServerSocketImpl implements I2PServerSocket {
|
||||
I2PSocket ret = null;
|
||||
|
||||
while ( (ret == null) && (!closing) ){
|
||||
while (pendingSockets.size() <= 0) {
|
||||
while (pendingSockets.isEmpty()) {
|
||||
if (closing) throw new ConnectException("I2PServerSocket closed");
|
||||
try {
|
||||
synchronized(socketAddedLock) {
|
||||
@ -78,7 +78,7 @@ class I2PServerSocketImpl implements I2PServerSocket {
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
synchronized (pendingSockets) {
|
||||
if (pendingSockets.size() > 0) {
|
||||
if (!pendingSockets.isEmpty()) {
|
||||
ret = (I2PSocket)pendingSockets.remove(0);
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,10 @@ ROUTERFILES="\
|
||||
../../../router/java/src/net/i2p/router/transport/GetBidsJob.java \
|
||||
../../../router/java/src/net/i2p/router/Blocklist.java \
|
||||
../../../router/java/src/net/i2p/router/transport/ntcp/EstablishState.java \
|
||||
../../../router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java"
|
||||
../../../router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java \
|
||||
../../../router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java \
|
||||
../../../router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java \
|
||||
../../../router/java/src/net/i2p/router/transport/udp/UDPTransport.java"
|
||||
|
||||
# add ../java/ so the refs will work in the po file
|
||||
JPATHS="../java/src ../jsp/WEB-INF ../java/strings $JFILE $ROUTERFILES"
|
||||
@ -79,7 +82,7 @@ do
|
||||
# To start a new translation, copy the header from an old translation to the new .po file,
|
||||
# then ant distclean updater.
|
||||
find $JPATHS -name *.java > $TMPFILE
|
||||
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 \
|
||||
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 --add-comments\
|
||||
--keyword=_ --keyword=_x --keyword=intl._ --keyword=intl.title \
|
||||
--keyword=handler._ --keyword=formhandler._ \
|
||||
--keyword=net.i2p.router.web.Messages.getString \
|
||||
|
@ -19,6 +19,9 @@ public class CSSHelper extends HelperBase {
|
||||
if (userAgent != null && userAgent.contains("MSIE")) {
|
||||
url += FORCE + "/";
|
||||
} else {
|
||||
// This is the first thing to use _context on most pages
|
||||
if (_context == null)
|
||||
throw new IllegalStateException("No contexts. This is usually because the router is either starting up or shutting down.");
|
||||
String theme = _context.getProperty(PROP_THEME_NAME, DEFAULT_THEME);
|
||||
url += theme + "/";
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ public class ConfigClientsHelper extends HelperBase {
|
||||
String app = name.substring(PluginStarter.PREFIX.length(), name.lastIndexOf(PluginStarter.ENABLED));
|
||||
String val = props.getProperty(name);
|
||||
Properties appProps = PluginStarter.pluginProperties(_context, app);
|
||||
if (appProps.size() <= 0)
|
||||
if (appProps.isEmpty())
|
||||
continue;
|
||||
StringBuilder desc = new StringBuilder(256);
|
||||
desc.append("<table border=\"0\">")
|
||||
|
@ -145,7 +145,10 @@ public class ConfigNetHandler extends FormHandler {
|
||||
}
|
||||
_context.router().setConfigSetting(UDPTransport.PROP_SOURCES, _udpAutoIP);
|
||||
// Todo: Catch local IPs right here rather than complaining later
|
||||
_context.router().setConfigSetting(UDPTransport.PROP_EXTERNAL_HOST, uhost);
|
||||
if (uhost.length() > 0)
|
||||
_context.router().setConfigSetting(UDPTransport.PROP_EXTERNAL_HOST, uhost);
|
||||
else
|
||||
_context.router().removeConfigSetting(UDPTransport.PROP_EXTERNAL_HOST);
|
||||
if ((!oldUdp.equals(_udpAutoIP)) || (!oldUHost.equals(uhost))) {
|
||||
addFormNotice(_("Updating IP address"));
|
||||
restartRequired = true;
|
||||
|
@ -166,7 +166,8 @@ public class ConfigNetHelper extends HelperBase {
|
||||
return kbytesToBits(getShareBandwidth());
|
||||
}
|
||||
private String kbytesToBits(int kbytes) {
|
||||
return DataHelper.formatSize(kbytes * 8 * 1024) + ' ' + _("bits per second");
|
||||
return DataHelper.formatSize(kbytes * 8 * 1024) + ' ' + _("bits per second") +
|
||||
' ' + _("or {0} bytes per month maximum", DataHelper.formatSize(kbytes * 1024l * 60 * 60 * 24 * 31));
|
||||
}
|
||||
public String getInboundBurstRate() {
|
||||
return "" + _context.bandwidthLimiter().getInboundBurstKBytesPerSecond();
|
||||
|
@ -70,7 +70,7 @@ public class ConfigStatsHelper extends HelperBase {
|
||||
* @return true if a valid stat is available, otherwise false
|
||||
*/
|
||||
public boolean hasMoreStats() {
|
||||
if (_stats.size() <= 0)
|
||||
if (_stats.isEmpty())
|
||||
return false;
|
||||
_currentIsGraphed = false;
|
||||
_currentStatName = (String)_stats.remove(0);
|
||||
|
@ -8,11 +8,11 @@ import net.i2p.data.Destination;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
|
||||
public class ConfigTunnelsHelper extends HelperBase {
|
||||
static final String HOP = _x("hop");
|
||||
static final String TUNNEL = _x("tunnel");
|
||||
private static final String HOP = "hop";
|
||||
private static final String TUNNEL = "tunnel";
|
||||
/** dummies for translation */
|
||||
static final String HOPS = _x("hops");
|
||||
static final String TUNNELS = _x("tunnels");
|
||||
private static final String HOPS = ngettext("1 hop", "{0} hops");
|
||||
private static final String TUNNELS = ngettext("1 tunnel", "{0} tunnels");
|
||||
|
||||
public ConfigTunnelsHelper() {}
|
||||
|
||||
@ -163,7 +163,7 @@ public class ConfigTunnelsHelper extends HelperBase {
|
||||
// TunnelPoolOptions, so make the boxes readonly.
|
||||
// And let's not display them at all unless they have contents, which should be rare.
|
||||
Properties props = in.getUnknownOptions();
|
||||
if (props.size() > 0) {
|
||||
if (!props.isEmpty()) {
|
||||
buf.append("<tr><td align=\"right\" class=\"mediumtags\">" + _("Inbound options") + ":</td>\n" +
|
||||
"<td colspan=\"2\" align=\"center\"><input name=\"").append(index);
|
||||
buf.append(".inboundOptions\" type=\"text\" size=\"32\" disabled=\"true\" " +
|
||||
@ -176,7 +176,7 @@ public class ConfigTunnelsHelper extends HelperBase {
|
||||
buf.append("\"></td></tr>\n");
|
||||
}
|
||||
props = out.getUnknownOptions();
|
||||
if (props.size() > 0) {
|
||||
if (!props.isEmpty()) {
|
||||
buf.append("<tr><td align=\"right\" class=\"mediumtags\">" + _("Outbound options") + ":</td>\n" +
|
||||
"<td colspan=\"2\" align=\"center\"><input name=\"").append(index);
|
||||
buf.append(".outboundOptions\" type=\"text\" size=\"32\" disabled=\"true\" " +
|
||||
@ -196,14 +196,13 @@ public class ConfigTunnelsHelper extends HelperBase {
|
||||
buf.append("<option value=\"").append(i).append("\" ");
|
||||
if (i == now)
|
||||
buf.append("selected=\"true\" ");
|
||||
String pname;
|
||||
// pluralize and then translate
|
||||
if (i != 1 && i != -1)
|
||||
pname = name + 's';
|
||||
else
|
||||
pname = name;
|
||||
buf.append(">").append(prefix).append(i).append(' ').append(_(pname));
|
||||
buf.append(">").append(_(i, "1 " + name, "{0} " + name + 's'));
|
||||
buf.append("</option>\n");
|
||||
}
|
||||
}
|
||||
|
||||
/** dummy for tagging */
|
||||
private static String ngettext(String s, String p) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -42,11 +42,15 @@ public class ConfigUpdateHandler extends FormHandler {
|
||||
public static final String PROP_ZIP_URL = "router.updateUnsignedURL";
|
||||
|
||||
public static final String PROP_UPDATE_URL = "router.updateURL";
|
||||
/**
|
||||
* Changed as of release 0.7.14 from .sud to .su2
|
||||
* Update hosts must maintain both for several releases
|
||||
*/
|
||||
public static final String DEFAULT_UPDATE_URL =
|
||||
"http://echelon.i2p/i2p/i2pupdate.sud\r\n" +
|
||||
"http://stats.i2p/i2p/i2pupdate.sud\r\n" +
|
||||
"http://www.i2p2.i2p/_static/i2pupdate.sud\r\n" +
|
||||
"http://update.postman.i2p/i2pupdate.sud" ;
|
||||
"http://echelon.i2p/i2p/i2pupdate.su2\r\n" +
|
||||
"http://stats.i2p/i2p/i2pupdate.su2\r\n" +
|
||||
"http://www.i2p2.i2p/_static/i2pupdate.su2\r\n" +
|
||||
"http://update.postman.i2p/i2pupdate.su2" ;
|
||||
|
||||
public static final String PROP_TRUSTED_KEYS = "router.trustedUpdateKeys";
|
||||
|
||||
|
@ -6,9 +6,11 @@ import net.i2p.data.Hash;
|
||||
import net.i2p.router.RouterContext;
|
||||
|
||||
class ContextHelper {
|
||||
|
||||
/** @throws IllegalStateException if no context available */
|
||||
public static RouterContext getContext(String contextId) {
|
||||
List contexts = RouterContext.listContexts();
|
||||
if ( (contexts == null) || (contexts.size() <= 0) )
|
||||
if ( (contexts == null) || (contexts.isEmpty()) )
|
||||
throw new IllegalStateException("No contexts. This is usually because the router is either starting up or shutting down.");
|
||||
if ( (contextId == null) || (contextId.trim().length() <= 0) )
|
||||
return (RouterContext)contexts.get(0);
|
||||
|
@ -85,16 +85,16 @@ public class FormHandler {
|
||||
public String getAllMessages() {
|
||||
validate();
|
||||
process();
|
||||
if (_errors.size() <= 0 && _notices.size() <= 0)
|
||||
if (_errors.isEmpty() && _notices.isEmpty())
|
||||
return "";
|
||||
StringBuilder buf = new StringBuilder(512);
|
||||
buf.append("<div class=\"messages\" id=\"messages\"><p>");
|
||||
if (_errors.size() > 0) {
|
||||
if (!_errors.isEmpty()) {
|
||||
buf.append("<span class=\"error\">");
|
||||
buf.append(render(_errors));
|
||||
buf.append("</span>");
|
||||
}
|
||||
if (_notices.size() > 0) {
|
||||
if (!_notices.isEmpty()) {
|
||||
buf.append("<span class=\"notice\">");
|
||||
buf.append(render(_notices));
|
||||
buf.append("</span>");
|
||||
@ -174,8 +174,8 @@ public class FormHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private String render(List<String> source) {
|
||||
if (source.size() <= 0) {
|
||||
private static String render(List<String> source) {
|
||||
if (source.isEmpty()) {
|
||||
return "";
|
||||
} else {
|
||||
StringBuilder buf = new StringBuilder(512);
|
||||
|
@ -79,19 +79,21 @@ public class GraphHelper extends HelperBase {
|
||||
+ "&width=" + (3 * _width)
|
||||
+ "&height=" + (3 * _height)
|
||||
+ "\" / target=\"_blank\">");
|
||||
String title = _("Combined bandwidth graph");
|
||||
_out.write("<img class=\"statimage\" width=\""
|
||||
+ (_width + 83) + "\" height=\"" + (_height + 92)
|
||||
+ "\" src=\"viewstat.jsp?stat=bw.combined"
|
||||
+ "&periodCount=" + _periodCount
|
||||
+ "&width=" + _width
|
||||
+ "&height=" + (_height - 14)
|
||||
+ "\" alt=\"Combined bandwidth graph\" title=\"Combined bandwidth graph\"></a>\n");
|
||||
+ "\" alt=\"" + title + "\" title=\"" + title + "\"></a>\n");
|
||||
}
|
||||
|
||||
for (Iterator iter = ordered.iterator(); iter.hasNext(); ) {
|
||||
SummaryListener lsnr = (SummaryListener)iter.next();
|
||||
Rate r = lsnr.getRate();
|
||||
String title = r.getRateStat().getName() + " for " + DataHelper.formatDuration(_periodCount * r.getPeriod());
|
||||
// e.g. "statname for 60m"
|
||||
String title = _("{0} for {1}", r.getRateStat().getName(), DataHelper.formatDuration(_periodCount * r.getPeriod()));
|
||||
_out.write("<a href=\"viewstat.jsp?stat="
|
||||
+ r.getRateStat().getName()
|
||||
+ "&showEvents=" + _showEvents
|
||||
|
@ -51,6 +51,16 @@ public abstract class HelperBase {
|
||||
return Messages.getString(s, o, _context);
|
||||
}
|
||||
|
||||
/** two params @since 0.7.14 */
|
||||
public String _(String s, Object o, Object o2) {
|
||||
return Messages.getString(s, o, o2, _context);
|
||||
}
|
||||
|
||||
/** translate (ngettext) @since 0.7.14 */
|
||||
public String _(int n, String s, String p) {
|
||||
return Messages.getString(n, s, p, _context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a string for extraction by xgettext and translation.
|
||||
* Use this only in static initializers.
|
||||
|
@ -29,4 +29,14 @@ public class Messages extends Translate {
|
||||
public static String getString(String s, Object o, I2PAppContext ctx) {
|
||||
return Translate.getString(s, o, ctx, BUNDLE_NAME);
|
||||
}
|
||||
|
||||
/** two params @since 0.7.14 */
|
||||
public static String getString(String s, Object o, Object o2, I2PAppContext ctx) {
|
||||
return Translate.getString(s, o, o2, ctx, BUNDLE_NAME);
|
||||
}
|
||||
|
||||
/** translate (ngettext) @since 0.7.14 */
|
||||
public static String getString(int n, String s, String p, I2PAppContext ctx) {
|
||||
return Translate.getString(n, s, p, ctx, BUNDLE_NAME);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,8 @@ import net.i2p.data.DataHelper;
|
||||
public class NetDbHelper extends HelperBase {
|
||||
private String _routerPrefix;
|
||||
private int _full;
|
||||
private boolean _lease = false;
|
||||
private boolean _lease;
|
||||
private boolean _debug;
|
||||
|
||||
public NetDbHelper() {}
|
||||
|
||||
@ -23,7 +24,11 @@ public class NetDbHelper extends HelperBase {
|
||||
_full = Integer.parseInt(f);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
public void setLease(String l) { _lease = "1".equals(l); }
|
||||
|
||||
public void setLease(String l) {
|
||||
_debug = "2".equals(l);
|
||||
_lease = _debug || "1".equals(l);
|
||||
}
|
||||
|
||||
public String getNetDbSummary() {
|
||||
NetDbRenderer renderer = new NetDbRenderer(_context);
|
||||
@ -32,7 +37,7 @@ public class NetDbHelper extends HelperBase {
|
||||
if (_routerPrefix != null)
|
||||
renderer.renderRouterInfoHTML(_out, _routerPrefix);
|
||||
else if (_lease)
|
||||
renderer.renderLeaseSetHTML(_out);
|
||||
renderer.renderLeaseSetHTML(_out, _debug);
|
||||
else
|
||||
renderer.renderStatusHTML(_out, _full);
|
||||
return "";
|
||||
@ -41,7 +46,7 @@ public class NetDbHelper extends HelperBase {
|
||||
if (_routerPrefix != null)
|
||||
renderer.renderRouterInfoHTML(new OutputStreamWriter(baos), _routerPrefix);
|
||||
else if (_lease)
|
||||
renderer.renderLeaseSetHTML(new OutputStreamWriter(baos));
|
||||
renderer.renderLeaseSetHTML(new OutputStreamWriter(baos), _debug);
|
||||
else
|
||||
renderer.renderStatusHTML(new OutputStreamWriter(baos), _full);
|
||||
return new String(baos.toByteArray());
|
||||
|
@ -10,6 +10,8 @@ package net.i2p.router.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.math.BigInteger; // debug
|
||||
import java.text.DecimalFormat; // debug
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@ -27,6 +29,8 @@ import net.i2p.data.RouterAddress;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
import net.i2p.router.networkdb.kademlia.HashDistance; // debug
|
||||
import net.i2p.util.HexDump; // debug
|
||||
import net.i2p.util.ObjectCounter;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
@ -37,10 +41,10 @@ public class NetDbRenderer {
|
||||
_context = ctx;
|
||||
}
|
||||
|
||||
private class LeaseSetComparator implements Comparator {
|
||||
public int compare(Object l, Object r) {
|
||||
Destination dl = ((LeaseSet)l).getDestination();
|
||||
Destination dr = ((LeaseSet)r).getDestination();
|
||||
private class LeaseSetComparator implements Comparator<LeaseSet> {
|
||||
public int compare(LeaseSet l, LeaseSet r) {
|
||||
Destination dl = l.getDestination();
|
||||
Destination dr = r.getDestination();
|
||||
boolean locall = _context.clientManager().isLocal(dl);
|
||||
boolean localr = _context.clientManager().isLocal(dr);
|
||||
if (locall && !localr) return -1;
|
||||
@ -49,9 +53,20 @@ public class NetDbRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private static class RouterInfoComparator implements Comparator {
|
||||
public int compare(Object l, Object r) {
|
||||
return ((RouterInfo)l).getIdentity().getHash().toBase64().compareTo(((RouterInfo)r).getIdentity().getHash().toBase64());
|
||||
/** for debugging @since 0.7.14 */
|
||||
private static class LeaseSetRoutingKeyComparator implements Comparator<LeaseSet> {
|
||||
private final Hash _us;
|
||||
public LeaseSetRoutingKeyComparator(Hash us) {
|
||||
_us = us;
|
||||
}
|
||||
public int compare(LeaseSet l, LeaseSet r) {
|
||||
return HashDistance.getDistance(_us, l.getRoutingKey()).subtract(HashDistance.getDistance(_us, r.getRoutingKey())).signum();
|
||||
}
|
||||
}
|
||||
|
||||
private static class RouterInfoComparator implements Comparator<RouterInfo> {
|
||||
public int compare(RouterInfo l, RouterInfo r) {
|
||||
return l.getIdentity().getHash().toBase64().compareTo(r.getIdentity().getHash().toBase64());
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,13 +93,31 @@ public class NetDbRenderer {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public void renderLeaseSetHTML(Writer out) throws IOException {
|
||||
/**
|
||||
* @param debug @since 0.7.14 sort by distance from us, display
|
||||
* median distance, and other stuff, useful when floodfill
|
||||
*/
|
||||
public void renderLeaseSetHTML(Writer out, boolean debug) throws IOException {
|
||||
StringBuilder buf = new StringBuilder(4*1024);
|
||||
buf.append("<h2>" + _("Network Database Contents") + "</h2>\n");
|
||||
buf.append("<a href=\"netdb.jsp\">" + _("View RouterInfo") + "</a>");
|
||||
buf.append("<h3>").append(_("LeaseSets")).append("</h3>\n");
|
||||
Set leases = new TreeSet(new LeaseSetComparator());
|
||||
Hash ourRKey;
|
||||
Set<LeaseSet> leases;
|
||||
DecimalFormat fmt;
|
||||
if (debug) {
|
||||
ourRKey = _context.routerHash();
|
||||
leases = new TreeSet(new LeaseSetRoutingKeyComparator(ourRKey));
|
||||
fmt = new DecimalFormat("#0.00");
|
||||
} else {
|
||||
ourRKey = null;
|
||||
leases = new TreeSet(new LeaseSetComparator());
|
||||
fmt = null;
|
||||
}
|
||||
leases.addAll(_context.netDb().getLeases());
|
||||
int medianCount = leases.size() / 2;
|
||||
BigInteger median = null;
|
||||
int c = 0;
|
||||
long now = _context.clock().now();
|
||||
for (Iterator iter = leases.iterator(); iter.hasNext(); ) {
|
||||
LeaseSet ls = (LeaseSet)iter.next();
|
||||
@ -115,6 +148,16 @@ public class NetDbRenderer {
|
||||
buf.append(_("Expires in {0}", DataHelper.formatDuration(exp))).append("<br>\n");
|
||||
else
|
||||
buf.append(_("Expired {0} ago", DataHelper.formatDuration(0-exp))).append("<br>\n");
|
||||
if (debug) {
|
||||
buf.append("RAP? " + ls.getReceivedAsPublished() + ' ');
|
||||
buf.append("RAR? " + ls.getReceivedAsReply() + ' ');
|
||||
BigInteger dist = HashDistance.getDistance(ourRKey, ls.getRoutingKey());
|
||||
if (c++ == medianCount)
|
||||
median = dist;
|
||||
buf.append("Dist: <b>" + fmt.format(biLog2(dist)) + "</b> ");
|
||||
buf.append("RKey: " + ls.getRoutingKey().toBase64() + ' ');
|
||||
buf.append("<br>");
|
||||
}
|
||||
for (int i = 0; i < ls.getLeaseCount(); i++) {
|
||||
buf.append(_("Lease")).append(' ').append(i + 1).append(": " + _("Gateway") + ' ');
|
||||
buf.append(_context.commSystem().renderPeerHTML(ls.getLease(i).getGateway()));
|
||||
@ -124,10 +167,42 @@ public class NetDbRenderer {
|
||||
out.write(buf.toString());
|
||||
buf.setLength(0);
|
||||
}
|
||||
if (debug) {
|
||||
buf.append("<p><b>Total Leasesets: " + leases.size());
|
||||
buf.append("<p><b>Published (RAP) Leasesets: " + _context.netDb().getKnownLeaseSets());
|
||||
buf.append("<p>Mod Data: " + HexDump.dump(_context.routingKeyGenerator().getModData()) + "<p>");
|
||||
buf.append("<p>Network data (only valid if floodfill):");
|
||||
buf.append("<p>Center of Key Space (router hash): " + ourRKey.toBase64() + "<p>");
|
||||
if (median != null) {
|
||||
double log2 = biLog2(median);
|
||||
buf.append("<p>Median distance (bits): " + fmt.format(log2));
|
||||
// 3 for 8 floodfills... -1 for median
|
||||
int total = (int) Math.round(Math.pow(2, 3 + 256 - 1 - log2));
|
||||
buf.append("<p>Estimated total floodfills: " + total);
|
||||
buf.append("<p>Estimated network total leasesets: " + (total * leases.size() / 8));
|
||||
}
|
||||
}
|
||||
out.write(buf.toString());
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* For debugging
|
||||
* http://forums.sun.com/thread.jspa?threadID=597652
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private static double biLog2(BigInteger a) {
|
||||
int b = a.bitLength() - 1;
|
||||
double c = 0;
|
||||
double d = 0.5;
|
||||
for (int i = b; i >= 0; --i) {
|
||||
if (a.testBit(i))
|
||||
c += d;
|
||||
d /= 2;
|
||||
}
|
||||
return b + c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mode 0: our info and charts only; 1: full routerinfos and charts; 2: abbreviated routerinfos and charts
|
||||
*/
|
||||
@ -160,7 +235,7 @@ public class NetDbRenderer {
|
||||
ObjectCounter<String> countries = new ObjectCounter();
|
||||
int[] transportCount = new int[8];
|
||||
|
||||
Set routers = new TreeSet(new RouterInfoComparator());
|
||||
Set<RouterInfo> routers = new TreeSet(new RouterInfoComparator());
|
||||
routers.addAll(_context.netDb().getRouters());
|
||||
for (Iterator iter = routers.iterator(); iter.hasNext(); ) {
|
||||
RouterInfo ri = (RouterInfo)iter.next();
|
||||
@ -185,7 +260,7 @@ public class NetDbRenderer {
|
||||
buf.append("<table border=\"0\" cellspacing=\"30\"><tr><th colspan=\"3\">").append(_("Network Database Router Statistics")).append("</th><tr><td>");
|
||||
// versions table
|
||||
List<String> versionList = new ArrayList(versions.objects());
|
||||
if (versionList.size() > 0) {
|
||||
if (!versionList.isEmpty()) {
|
||||
Collections.sort(versionList, Collections.reverseOrder(new VersionComparator()));
|
||||
buf.append("<table>\n");
|
||||
buf.append("<tr><th>" + _("Version") + "</th><th>" + _("Count") + "</th></tr>\n");
|
||||
@ -217,7 +292,7 @@ public class NetDbRenderer {
|
||||
|
||||
// country table
|
||||
List<String> countryList = new ArrayList(countries.objects());
|
||||
if (countryList.size() > 0) {
|
||||
if (!countryList.isEmpty()) {
|
||||
Collections.sort(countryList, new CountryComparator());
|
||||
buf.append("<table>\n");
|
||||
buf.append("<tr><th align=\"left\">" + _("Country") + "</th><th>" + _("Count") + "</th></tr>\n");
|
||||
|
@ -47,6 +47,8 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
||||
|
||||
private static final String NEWS_FILE = "docs/news.xml";
|
||||
private static final String TEMP_NEWS_FILE = "news.xml.temp";
|
||||
/** @since 0.7.14 not configurable */
|
||||
private static final String BACKUP_NEWS_URL = "http://www.i2p2.i2p/_static/news/news.xml";
|
||||
|
||||
private NewsFetcher(I2PAppContext ctx) {
|
||||
_context = ctx;
|
||||
@ -90,8 +92,11 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static final long INITIAL_DELAY = 5*60*1000;
|
||||
private static final long RUN_DELAY = 10*60*1000;
|
||||
|
||||
public void run() {
|
||||
try { Thread.sleep(_context.random().nextLong(5*60*1000)); } catch (InterruptedException ie) {}
|
||||
try { Thread.sleep(INITIAL_DELAY + _context.random().nextLong(INITIAL_DELAY)); } catch (InterruptedException ie) {}
|
||||
while (true) {
|
||||
if (!_updateAvailable) checkForUpdates();
|
||||
if (shouldFetchNews()) {
|
||||
@ -99,7 +104,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
||||
if (shouldFetchUnsigned())
|
||||
fetchUnsignedHead();
|
||||
}
|
||||
try { Thread.sleep(10*60*1000); } catch (InterruptedException ie) {}
|
||||
try { Thread.sleep(RUN_DELAY); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,12 +156,20 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
||||
try {
|
||||
EepGet get = null;
|
||||
if (shouldProxy)
|
||||
get = new EepGet(_context, true, proxyHost, proxyPort, 2, _tempFile.getAbsolutePath(), newsURL, true, null, _lastModified);
|
||||
get = new EepGet(_context, true, proxyHost, proxyPort, 0, _tempFile.getAbsolutePath(), newsURL, true, null, _lastModified);
|
||||
else
|
||||
get = new EepGet(_context, false, null, 0, 0, _tempFile.getAbsolutePath(), newsURL, true, null, _lastModified);
|
||||
get.addStatusListener(this);
|
||||
if (get.fetch())
|
||||
if (get.fetch()) {
|
||||
_lastModified = get.getLastModified();
|
||||
} else {
|
||||
// backup news location - always proxied
|
||||
_tempFile.delete();
|
||||
get = new EepGet(_context, true, proxyHost, proxyPort, 0, _tempFile.getAbsolutePath(), BACKUP_NEWS_URL, true, null, _lastModified);
|
||||
get.addStatusListener(this);
|
||||
if (get.fetch())
|
||||
_lastModified = get.getLastModified();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
_log.error("Error fetching the news", t);
|
||||
}
|
||||
@ -322,7 +335,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
||||
handler = new UpdateHandler((RouterContext)_context);
|
||||
} else {
|
||||
List contexts = RouterContext.listContexts();
|
||||
if (contexts.size() > 0)
|
||||
if (!contexts.isEmpty())
|
||||
handler = new UpdateHandler((RouterContext)contexts.get(0));
|
||||
else
|
||||
_log.log(Log.CRIT, "No router context to update with?");
|
||||
|
@ -2,6 +2,7 @@ package net.i2p.router.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.ClassLoader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
@ -48,6 +49,7 @@ public class PluginStarter implements Runnable {
|
||||
"midnight" };
|
||||
private static Map<String, ThreadGroup> pluginThreadGroups = new ConcurrentHashMap<String, ThreadGroup>(); // one thread group per plugin (map key=plugin name)
|
||||
private static Map<String, Collection<Job>> pluginJobs = new ConcurrentHashMap<String, Collection<Job>>();
|
||||
private static Map<String, ClassLoader> _clCache = new ConcurrentHashMap();
|
||||
|
||||
public PluginStarter(RouterContext ctx) {
|
||||
_context = ctx;
|
||||
@ -87,7 +89,7 @@ public class PluginStarter implements Runnable {
|
||||
*/
|
||||
static boolean startPlugin(RouterContext ctx, String appName) throws Exception {
|
||||
Log log = ctx.logManager().getLog(PluginStarter.class);
|
||||
File pluginDir = new File(ctx.getAppDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName);
|
||||
File pluginDir = new File(ctx.getConfigDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName);
|
||||
if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) {
|
||||
log.error("Cannot start nonexistent plugin: " + appName);
|
||||
return false;
|
||||
@ -195,7 +197,7 @@ public class PluginStarter implements Runnable {
|
||||
*/
|
||||
static boolean stopPlugin(RouterContext ctx, String appName) throws Exception {
|
||||
Log log = ctx.logManager().getLog(PluginStarter.class);
|
||||
File pluginDir = new File(ctx.getAppDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName);
|
||||
File pluginDir = new File(ctx.getConfigDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName);
|
||||
if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) {
|
||||
log.error("Cannot stop nonexistent plugin: " + appName);
|
||||
return false;
|
||||
@ -244,7 +246,7 @@ public class PluginStarter implements Runnable {
|
||||
/** @return true on success - caller should call stopPlugin() first */
|
||||
static boolean deletePlugin(RouterContext ctx, String appName) throws Exception {
|
||||
Log log = ctx.logManager().getLog(PluginStarter.class);
|
||||
File pluginDir = new File(ctx.getAppDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName);
|
||||
File pluginDir = new File(ctx.getConfigDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName);
|
||||
if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) {
|
||||
log.error("Cannot delete nonexistent plugin: " + appName);
|
||||
return false;
|
||||
@ -287,7 +289,7 @@ public class PluginStarter implements Runnable {
|
||||
|
||||
/** plugin.config */
|
||||
public static Properties pluginProperties(I2PAppContext ctx, String appName) {
|
||||
File cfgFile = new File(ctx.getAppDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName + '/' + "plugin.config");
|
||||
File cfgFile = new File(ctx.getConfigDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName + '/' + "plugin.config");
|
||||
Properties rv = new Properties();
|
||||
try {
|
||||
DataHelper.loadProps(rv, cfgFile);
|
||||
@ -322,7 +324,7 @@ public class PluginStarter implements Runnable {
|
||||
*/
|
||||
public static List<String> getPlugins() {
|
||||
List<String> rv = new ArrayList();
|
||||
File pluginDir = new File(I2PAppContext.getGlobalContext().getAppDir(), PluginUpdateHandler.PLUGIN_DIR);
|
||||
File pluginDir = new File(I2PAppContext.getGlobalContext().getConfigDir(), PluginUpdateHandler.PLUGIN_DIR);
|
||||
File[] files = pluginDir.listFiles();
|
||||
if (files == null)
|
||||
return rv;
|
||||
@ -405,6 +407,8 @@ public class PluginStarter implements Runnable {
|
||||
argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
ClassLoader cl = null;
|
||||
if (app.classpath != null) {
|
||||
String cp = new String(app.classpath);
|
||||
if (cp.indexOf("$") >= 0) {
|
||||
@ -412,24 +416,63 @@ public class PluginStarter implements Runnable {
|
||||
cp = cp.replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
|
||||
cp = cp.replace("$PLUGIN", pluginDir.getAbsolutePath());
|
||||
}
|
||||
addToClasspath(cp, app.clientName, log);
|
||||
|
||||
// Old way - add for the whole JVM
|
||||
//addToClasspath(cp, app.clientName, log);
|
||||
|
||||
// New way - add only for this client
|
||||
// We cache the ClassLoader we start the client with, so
|
||||
// we can reuse it for stopping and uninstalling.
|
||||
// If we don't, the client won't be able to find its
|
||||
// static members.
|
||||
String clCacheKey = pluginName + app.className + app.args;
|
||||
if (!action.equals("start"))
|
||||
cl = _clCache.get(clCacheKey);
|
||||
if (cl == null) {
|
||||
URL[] urls = classpathToURLArray(cp, app.clientName, log);
|
||||
if (urls != null) {
|
||||
cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());
|
||||
if (action.equals("start"))
|
||||
_clCache.put(clCacheKey, cl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (app.delay < 0 && action.equals("start")) {
|
||||
// this will throw exceptions
|
||||
LoadClientAppsJob.runClientInline(app.className, app.clientName, argVal, log);
|
||||
LoadClientAppsJob.runClientInline(app.className, app.clientName, argVal, log, cl);
|
||||
} else if (app.delay == 0 || !action.equals("start")) {
|
||||
// quick check, will throw ClassNotFoundException on error
|
||||
LoadClientAppsJob.testClient(app.className);
|
||||
LoadClientAppsJob.testClient(app.className, cl);
|
||||
// run this guy now
|
||||
LoadClientAppsJob.runClient(app.className, app.clientName, argVal, log, pluginThreadGroup);
|
||||
LoadClientAppsJob.runClient(app.className, app.clientName, argVal, log, pluginThreadGroup, cl);
|
||||
} else {
|
||||
// quick check, will throw ClassNotFoundException on error
|
||||
LoadClientAppsJob.testClient(app.className);
|
||||
// wait before firing it up
|
||||
Job job = new LoadClientAppsJob.DelayedRunClient(ctx, app.className, app.clientName, argVal, app.delay, pluginThreadGroup);
|
||||
ctx.jobQueue().addJob(job);
|
||||
pluginJobs.get(pluginName).add(job);
|
||||
// If there is some delay, there may be a really good reason for it.
|
||||
// Loading a class would be one of them!
|
||||
// So we do a quick check first, If it bombs out, we delay and try again.
|
||||
// If it bombs after that, then we throw the ClassNotFoundException.
|
||||
try {
|
||||
// quick check
|
||||
LoadClientAppsJob.testClient(app.className, cl);
|
||||
} catch(ClassNotFoundException ex) {
|
||||
// Try again 1 or 2 seconds later.
|
||||
// This should be enough time. Although it is a lousy hack
|
||||
// it should work for most cases.
|
||||
// Perhaps it may be even better to delay a percentage
|
||||
// if > 1, and reduce the delay time.
|
||||
// Under normal circumstances there will be no delay at all.
|
||||
if(app.delay > 1) {
|
||||
Thread.sleep(2000);
|
||||
} else {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
}
|
||||
// quick check, will throw ClassNotFoundException on error
|
||||
LoadClientAppsJob.testClient(app.className, cl);
|
||||
// wait before firing it up
|
||||
Job job = new LoadClientAppsJob.DelayedRunClient(ctx, app.className, app.clientName, argVal, app.delay, pluginThreadGroup, cl);
|
||||
ctx.jobQueue().addJob(job);
|
||||
pluginJobs.get(pluginName).add(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -470,6 +513,7 @@ public class PluginStarter implements Runnable {
|
||||
* but I don't see how to make it magically get used for everything.
|
||||
* So add this to the whole JVM's classpath.
|
||||
*/
|
||||
/******
|
||||
private static void addToClasspath(String classpath, String clientName, Log log) {
|
||||
StringTokenizer tok = new StringTokenizer(classpath, ",");
|
||||
while (tok.hasMoreTokens()) {
|
||||
@ -488,6 +532,33 @@ public class PluginStarter implements Runnable {
|
||||
}
|
||||
}
|
||||
}
|
||||
*****/
|
||||
|
||||
/**
|
||||
* @return null if no valid elements
|
||||
*/
|
||||
private static URL[] classpathToURLArray(String classpath, String clientName, Log log) {
|
||||
StringTokenizer tok = new StringTokenizer(classpath, ",");
|
||||
List<URL> urls = new ArrayList();
|
||||
while (tok.hasMoreTokens()) {
|
||||
String elem = tok.nextToken().trim();
|
||||
File f = new File(elem);
|
||||
if (!f.isAbsolute()) {
|
||||
log.error("Plugin client " + clientName + " classpath element is not absolute: " + f);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
urls.add(f.toURI().toURL());
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("INFO: Adding plugin to classpath: " + f);
|
||||
} catch (Exception e) {
|
||||
log.error("Plugin client " + clientName + " bad classpath element: " + f, e);
|
||||
}
|
||||
}
|
||||
if (urls.isEmpty())
|
||||
return null;
|
||||
return urls.toArray(new URL[urls.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* http://jimlife.wordpress.com/2007/12/19/java-adding-new-classpath-at-runtime/
|
||||
|
@ -131,6 +131,7 @@ public class PluginUpdateChecker extends UpdateHandler {
|
||||
boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
|
||||
String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST);
|
||||
int proxyPort = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT_INT);
|
||||
_baos.reset();
|
||||
try {
|
||||
_get = new PartialEepGet(_context, proxyHost, proxyPort, _baos, _xpi2pURL, TrustedUpdate.HEADER_BYTES);
|
||||
_get.addStatusListener(PluginUpdateCheckerRunner.this);
|
||||
|
@ -150,7 +150,7 @@ public class PluginUpdateHandler extends UpdateHandler {
|
||||
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
|
||||
updateStatus("<b>" + _("Plugin downloaded") + "</b>");
|
||||
File f = new File(_updateFile);
|
||||
File appDir = new File(_context.getAppDir(), PLUGIN_DIR);
|
||||
File appDir = new File(_context.getConfigDir(), PLUGIN_DIR);
|
||||
if ((!appDir.exists()) && (!appDir.mkdir())) {
|
||||
f.delete();
|
||||
statusDone("<b>" + _("Cannot create plugin directory {0}", appDir.getAbsolutePath()) + "</b>");
|
||||
|
@ -75,7 +75,7 @@ public class RouterConsoleRunner {
|
||||
_server = new Server();
|
||||
boolean rewrite = false;
|
||||
Properties props = webAppProperties();
|
||||
if (props.size() <= 0) {
|
||||
if (props.isEmpty()) {
|
||||
props.setProperty(PREFIX + ROUTERCONSOLE + ENABLED, "true");
|
||||
rewrite = true;
|
||||
}
|
||||
@ -161,6 +161,7 @@ public class RouterConsoleRunner {
|
||||
"If so, you may ignore this error, or remove the\n" +
|
||||
"\"::1,\" in the \"clientApp.0.args\" line of the clients.config file.\n" +
|
||||
"Exception: " + me);
|
||||
me.printStackTrace();
|
||||
}
|
||||
|
||||
if (baseHandler != null) {
|
||||
|
@ -58,7 +58,7 @@ public class ShitlistRenderer {
|
||||
else
|
||||
buf.append(_("Banned until restart or in {0}", expireString));
|
||||
Set transports = entry.transports;
|
||||
if ( (transports != null) && (transports.size() > 0) )
|
||||
if ( (transports != null) && (!transports.isEmpty()) )
|
||||
buf.append(" on the following transport: ").append(transports);
|
||||
if (entry.cause != null) {
|
||||
buf.append("<br>\n");
|
||||
|
@ -149,6 +149,10 @@ public class StatSummarizer implements Runnable {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This does the two-data bandwidth graph only.
|
||||
* For all other graphs see SummaryRenderer
|
||||
*/
|
||||
public boolean renderRatePng(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, boolean showCredit) throws IOException {
|
||||
long end = _context.clock().now() - 60*1000;
|
||||
if (width > GraphHelper.MAX_X)
|
||||
@ -166,22 +170,23 @@ public class StatSummarizer implements Runnable {
|
||||
def.setTimePeriod(start/1000, 0);
|
||||
def.setLowerLimit(0d);
|
||||
def.setBaseValue(1024);
|
||||
String title = "Bandwidth usage";
|
||||
// Note to translators: all runtime zh translation disabled in this file, no font available in RRD
|
||||
String title = _("Bandwidth usage");
|
||||
if (!hideTitle)
|
||||
def.setTitle(title);
|
||||
String sendName = SummaryListener.createName(_context, "bw.sendRate.60000");
|
||||
String recvName = SummaryListener.createName(_context, "bw.recvRate.60000");
|
||||
def.datasource(sendName, sendName, sendName, "AVERAGE", "MEMORY");
|
||||
def.datasource(recvName, recvName, recvName, "AVERAGE", "MEMORY");
|
||||
def.area(sendName, Color.BLUE, "Outbound bytes/sec");
|
||||
def.area(sendName, Color.BLUE, _("Outbound bytes/sec"));
|
||||
//def.line(sendName, Color.BLUE, "Outbound bytes/sec", 3);
|
||||
def.line(recvName, Color.RED, "Inbound bytes/sec@r", 3);
|
||||
def.line(recvName, Color.RED, _("Inbound bytes/sec") + "@r", 3);
|
||||
//def.area(recvName, Color.RED, "Inbound bytes/sec@r");
|
||||
if (!hideLegend) {
|
||||
def.gprint(sendName, "AVERAGE", "out average: @2@sbytes/sec");
|
||||
def.gprint(sendName, "MAX", " max: @2@sbytes/sec@r");
|
||||
def.gprint(recvName, "AVERAGE", "in average: @2@sbytes/sec");
|
||||
def.gprint(recvName, "MAX", " max: @2@sbytes/sec@r");
|
||||
def.gprint(sendName, "AVERAGE", _("out average") + ": @2@s" + _("bytes/sec"));
|
||||
def.gprint(sendName, "MAX", ' ' + _("max") + ": @2@s" + _("bytes/sec") + "@r");
|
||||
def.gprint(recvName, "AVERAGE", _("in average") + ": @2@s" + _("bytes/sec"));
|
||||
def.gprint(recvName, "MAX", ' ' + _("max") + ": @2@s" + _("bytes/sec") + "@r");
|
||||
}
|
||||
if (!showCredit)
|
||||
def.setShowSignature(false);
|
||||
@ -248,4 +253,12 @@ public class StatSummarizer implements Runnable {
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/** translate a string */
|
||||
private String _(String s) {
|
||||
// the RRD font doesn't have zh chars, at least on my system
|
||||
if ("zh".equals(Messages.getLanguage(_context)))
|
||||
return s;
|
||||
return Messages.getString(s, _context);
|
||||
}
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ public class SummaryBarRenderer {
|
||||
.append(_("Active"))
|
||||
.append(":</b></td><td align=\"right\">")
|
||||
.append(_helper.getActivePeers())
|
||||
.append('/')
|
||||
.append(" / ")
|
||||
.append(_helper.getActiveProfiles())
|
||||
.append("</td></tr>\n" +
|
||||
|
||||
@ -328,54 +328,48 @@ public class SummaryBarRenderer {
|
||||
"<table>\n" +
|
||||
|
||||
"<tr><td align=\"left\"><b>1s:</b></td><td align=\"right\">")
|
||||
.append(_helper.getInboundSecondKBps())
|
||||
.append('/')
|
||||
.append(_helper.getOutboundSecondKBps())
|
||||
.append("K/s</td></tr>\n" +
|
||||
.append(_helper.getSecondKBps())
|
||||
.append("Bps</td></tr>\n");
|
||||
|
||||
"<tr><td align=\"left\"><b>5m:</b></td><td align=\"right\">")
|
||||
.append(_helper.getInboundFiveMinuteKBps())
|
||||
.append('/')
|
||||
.append(_helper.getOutboundFiveMinuteKBps())
|
||||
.append("K/s</td></tr>\n" +
|
||||
if (_context.router().getUptime() > 6*60*1000) {
|
||||
buf.append("<tr><td align=\"left\"><b>5m:</b></td><td align=\"right\">")
|
||||
.append(_helper.getFiveMinuteKBps())
|
||||
.append("Bps</td></tr>\n");
|
||||
}
|
||||
|
||||
"<tr><td align=\"left\"><b>")
|
||||
if (_context.router().getUptime() > 2*60*1000) {
|
||||
buf.append("<tr><td align=\"left\"><b>")
|
||||
.append(_("Total"))
|
||||
.append(":</b></td><td align=\"right\">")
|
||||
.append(_helper.getInboundLifetimeKBps())
|
||||
.append('/')
|
||||
.append(_helper.getOutboundLifetimeKBps())
|
||||
.append("K/s</td></tr>\n" +
|
||||
.append(_helper.getLifetimeKBps())
|
||||
.append("Bps</td></tr>\n");
|
||||
}
|
||||
|
||||
"<tr><td align=\"left\"><b>")
|
||||
buf.append("<tr><td align=\"left\"><b>")
|
||||
.append(_("Used"))
|
||||
.append(":</b></td><td align=\"right\">")
|
||||
.append(_helper.getInboundTransferred())
|
||||
.append('/')
|
||||
.append(" / ")
|
||||
.append(_helper.getOutboundTransferred())
|
||||
.append("</td></tr></table>\n" +
|
||||
|
||||
"<hr><h3><a href=\"/tunnels.jsp\" target=\"_top\" title=\"")
|
||||
.append(_("View existing tunnels and tunnel build status"))
|
||||
.append("\">")
|
||||
.append(_("Tunnels in/out"))
|
||||
.append(_("Tunnels"))
|
||||
.append("</a></h3><hr>" +
|
||||
"<table>\n" +
|
||||
|
||||
"<tr><td align=\"left\"><b>")
|
||||
.append(_("Exploratory"))
|
||||
.append(":</b></td><td align=\"right\">")
|
||||
.append(_helper.getInboundTunnels())
|
||||
.append('/')
|
||||
.append(_helper.getOutboundTunnels())
|
||||
.append(_helper.getInboundTunnels() + _helper.getOutboundTunnels())
|
||||
.append("</td></tr>\n" +
|
||||
|
||||
"<tr><td align=\"left\"><b>")
|
||||
.append(_("Client"))
|
||||
.append(":</b></td><td align=\"right\">")
|
||||
.append(_helper.getInboundClientTunnels())
|
||||
.append('/')
|
||||
.append(_helper.getOutboundClientTunnels())
|
||||
.append(_helper.getInboundClientTunnels() + _helper.getOutboundClientTunnels())
|
||||
.append("</td></tr>\n" +
|
||||
|
||||
"<tr><td align=\"left\"><b>")
|
||||
|
@ -245,96 +245,86 @@ public class SummaryHelper extends HelperBase {
|
||||
********/
|
||||
|
||||
/**
|
||||
* How fast we have been receiving data over the last second (pretty printed
|
||||
* string with 2 decimal places representing the KBps)
|
||||
*
|
||||
* @return "x.xx / y.yy {K|M}"
|
||||
*/
|
||||
public String getInboundSecondKBps() {
|
||||
public String getSecondKBps() {
|
||||
if (_context == null)
|
||||
return "0";
|
||||
double kbps = _context.bandwidthLimiter().getReceiveBps()/1024d;
|
||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||
return fmt.format(kbps);
|
||||
}
|
||||
/**
|
||||
* How fast we have been sending data over the last second (pretty printed
|
||||
* string with 2 decimal places representing the KBps)
|
||||
*
|
||||
*/
|
||||
public String getOutboundSecondKBps() {
|
||||
if (_context == null)
|
||||
return "0";
|
||||
double kbps = _context.bandwidthLimiter().getSendBps()/1024d;
|
||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||
return fmt.format(kbps);
|
||||
return "0 / 0";
|
||||
return formatPair(_context.bandwidthLimiter().getReceiveBps(),
|
||||
_context.bandwidthLimiter().getSendBps());
|
||||
}
|
||||
|
||||
/**
|
||||
* How fast we have been receiving data over the last 5 minutes (pretty printed
|
||||
* string with 2 decimal places representing the KBps)
|
||||
*
|
||||
* @return "x.xx / y.yy {K|M}"
|
||||
*/
|
||||
public String getInboundFiveMinuteKBps() {
|
||||
public String getFiveMinuteKBps() {
|
||||
if (_context == null)
|
||||
return "0";
|
||||
return "0 / 0";
|
||||
|
||||
RateStat receiveRate = _context.statManager().getRate("bw.recvRate");
|
||||
if (receiveRate == null) return "0";
|
||||
Rate rate = receiveRate.getRate(5*60*1000);
|
||||
double kbps = rate.getAverageValue()/1024;
|
||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||
return fmt.format(kbps);
|
||||
}
|
||||
|
||||
/**
|
||||
* How fast we have been sending data over the last 5 minutes (pretty printed
|
||||
* string with 2 decimal places representing the KBps)
|
||||
*
|
||||
*/
|
||||
public String getOutboundFiveMinuteKBps() {
|
||||
if (_context == null)
|
||||
return "0";
|
||||
|
||||
RateStat receiveRate = _context.statManager().getRate("bw.sendRate");
|
||||
if (receiveRate == null) return "0";
|
||||
Rate rate = receiveRate.getRate(5*60*1000);
|
||||
double kbps = rate.getAverageValue()/1024;
|
||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||
return fmt.format(kbps);
|
||||
}
|
||||
|
||||
/**
|
||||
* How fast we have been receiving data since the router started (pretty printed
|
||||
* string with 2 decimal places representing the KBps)
|
||||
*
|
||||
*/
|
||||
public String getInboundLifetimeKBps() {
|
||||
if (_context == null)
|
||||
return "0";
|
||||
|
||||
RateStat receiveRate = _context.statManager().getRate("bw.recvRate");
|
||||
if (receiveRate == null) return "0";
|
||||
double kbps = receiveRate.getLifetimeAverageValue()/1024;
|
||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||
return fmt.format(kbps);
|
||||
}
|
||||
|
||||
/**
|
||||
* How fast we have been sending data since the router started (pretty printed
|
||||
* string with 2 decimal places representing the KBps)
|
||||
*
|
||||
*/
|
||||
public String getOutboundLifetimeKBps() {
|
||||
if (_context == null)
|
||||
return "0";
|
||||
|
||||
double in = 0;
|
||||
if (receiveRate != null) {
|
||||
Rate r = receiveRate.getRate(5*60*1000);
|
||||
if (r != null)
|
||||
in = r.getAverageValue();
|
||||
}
|
||||
RateStat sendRate = _context.statManager().getRate("bw.sendRate");
|
||||
if (sendRate == null) return "0";
|
||||
double kbps = sendRate.getLifetimeAverageValue()/1024;
|
||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||
return fmt.format(kbps);
|
||||
double out = 0;
|
||||
if (sendRate != null) {
|
||||
Rate r = sendRate.getRate(5*60*1000);
|
||||
if (r != null)
|
||||
out = r.getAverageValue();
|
||||
}
|
||||
return formatPair(in, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return "x.xx / y.yy {K|M}"
|
||||
*/
|
||||
public String getLifetimeKBps() {
|
||||
if (_context == null)
|
||||
return "0 / 0";
|
||||
|
||||
RateStat receiveRate = _context.statManager().getRate("bw.recvRate");
|
||||
double in;
|
||||
if (receiveRate == null)
|
||||
in = 0;
|
||||
else
|
||||
in = receiveRate.getLifetimeAverageValue();
|
||||
RateStat sendRate = _context.statManager().getRate("bw.sendRate");
|
||||
double out;
|
||||
if (sendRate == null)
|
||||
out = 0;
|
||||
else
|
||||
out = sendRate.getLifetimeAverageValue();
|
||||
return formatPair(in, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return "x.xx / y.yy {K|M}"
|
||||
*/
|
||||
private static String formatPair(double in, double out) {
|
||||
boolean mega = in >= 1024*1024 || out >= 1024*1024;
|
||||
// scale both the same
|
||||
if (mega) {
|
||||
in /= 1024*1024;
|
||||
out /= 1024*1024;
|
||||
} else {
|
||||
in /= 1024;
|
||||
out /= 1024;
|
||||
}
|
||||
// control total width
|
||||
DecimalFormat fmt;
|
||||
if (in >= 1000 || out >= 1000)
|
||||
fmt = new DecimalFormat("#0");
|
||||
else if (in >= 100 || out >= 100)
|
||||
fmt = new DecimalFormat("#0.0");
|
||||
else
|
||||
fmt = new DecimalFormat("#0.00");
|
||||
return fmt.format(in) + " / " + fmt.format(out) +
|
||||
(mega ? 'M' : 'K');
|
||||
}
|
||||
|
||||
/**
|
||||
* How much data have we received since the router started (pretty printed
|
||||
* string with 2 decimal places and the appropriate units - GB/MB/KB/bytes)
|
||||
@ -373,7 +363,7 @@ public class SummaryHelper extends HelperBase {
|
||||
|
||||
StringBuilder buf = new StringBuilder(512);
|
||||
buf.append("<h3><a href=\"/i2ptunnel/index.jsp\" target=\"_blank\" title=\"").append(_("Add/remove/edit & control your client and server tunnels")).append("\">").append(_("Local Destinations")).append("</a></h3><hr><div class=\"tunnels\">");
|
||||
if (clients.size() > 0) {
|
||||
if (!clients.isEmpty()) {
|
||||
Collections.sort(clients, new AlphaComparator());
|
||||
buf.append("<table>");
|
||||
|
||||
|
@ -142,127 +142,3 @@ class SummaryListener implements RateSummaryListener {
|
||||
@Override
|
||||
public int hashCode() { return _rate.hashCode(); }
|
||||
}
|
||||
|
||||
class SummaryRenderer {
|
||||
private Log _log;
|
||||
private SummaryListener _listener;
|
||||
public SummaryRenderer(I2PAppContext ctx, SummaryListener lsnr) {
|
||||
_log = ctx.logManager().getLog(SummaryRenderer.class);
|
||||
_listener = lsnr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the stats as determined by the specified JRobin xml config,
|
||||
* but note that this doesn't work on stock jvms, as it requires
|
||||
* DOM level 3 load and store support. Perhaps we can bundle that, or
|
||||
* specify who can get it from where, etc.
|
||||
*
|
||||
*/
|
||||
public static synchronized void render(I2PAppContext ctx, OutputStream out, String filename) throws IOException {
|
||||
long end = ctx.clock().now() - 60*1000;
|
||||
long start = end - 60*1000*SummaryListener.PERIODS;
|
||||
try {
|
||||
RrdGraphDefTemplate template = new RrdGraphDefTemplate(filename);
|
||||
RrdGraphDef def = template.getRrdGraphDef();
|
||||
def.setTimePeriod(start/1000, end/1000); // ignore the periods in the template
|
||||
RrdGraph graph = new RrdGraph(def);
|
||||
byte img[] = graph.getPNGBytes();
|
||||
out.write(img);
|
||||
} catch (RrdException re) {
|
||||
//_log.error("Error rendering " + filename, re);
|
||||
throw new IOException("Error plotting: " + re.getMessage());
|
||||
} catch (IOException ioe) {
|
||||
//_log.error("Error rendering " + filename, ioe);
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
public void render(OutputStream out) throws IOException { render(out, -1, -1, false, false, false, false, -1, false); }
|
||||
public void render(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, boolean showCredit) throws IOException {
|
||||
long end = _listener.now() - 60*1000;
|
||||
if (periodCount <= 0) periodCount = SummaryListener.PERIODS;
|
||||
if (periodCount > SummaryListener.PERIODS)
|
||||
periodCount = SummaryListener.PERIODS;
|
||||
long start = end - _listener.getRate().getPeriod()*periodCount;
|
||||
//long begin = System.currentTimeMillis();
|
||||
try {
|
||||
RrdGraphDef def = new RrdGraphDef();
|
||||
def.setTimePeriod(start/1000, 0);
|
||||
def.setLowerLimit(0d);
|
||||
String name = _listener.getRate().getRateStat().getName();
|
||||
// heuristic to set K=1024
|
||||
if ((name.startsWith("bw.") || name.indexOf("Size") >= 0 || name.indexOf("Bps") >= 0 || name.indexOf("memory") >= 0)
|
||||
&& !showEvents)
|
||||
def.setBaseValue(1024);
|
||||
String title = name;
|
||||
if (showEvents)
|
||||
title = title + " events in ";
|
||||
else
|
||||
title = title + " averaged for ";
|
||||
title = title + DataHelper.formatDuration(_listener.getRate().getPeriod());
|
||||
if (!hideTitle)
|
||||
def.setTitle(title);
|
||||
String path = _listener.getData().getPath();
|
||||
String dsNames[] = _listener.getData().getDsNames();
|
||||
String plotName = null;
|
||||
String descr = null;
|
||||
if (showEvents) {
|
||||
// include the average event count on the plot
|
||||
plotName = dsNames[1];
|
||||
descr = "Events per period";
|
||||
} else {
|
||||
// include the average value
|
||||
plotName = dsNames[0];
|
||||
descr = _listener.getRate().getRateStat().getDescription();
|
||||
}
|
||||
def.datasource(plotName, path, plotName, "AVERAGE", "MEMORY");
|
||||
def.area(plotName, Color.BLUE, descr + "@r");
|
||||
if (!hideLegend) {
|
||||
def.gprint(plotName, "AVERAGE", "avg: @2@s");
|
||||
def.gprint(plotName, "MAX", " max: @2@s");
|
||||
def.gprint(plotName, "LAST", " now: @2@s@r");
|
||||
}
|
||||
if (!showCredit)
|
||||
def.setShowSignature(false);
|
||||
/*
|
||||
// these four lines set up a graph plotting both values and events on the same chart
|
||||
// (but with the same coordinates, so the values may look pretty skewed)
|
||||
def.datasource(dsNames[0], path, dsNames[0], "AVERAGE", "MEMORY");
|
||||
def.datasource(dsNames[1], path, dsNames[1], "AVERAGE", "MEMORY");
|
||||
def.area(dsNames[0], Color.BLUE, _listener.getRate().getRateStat().getDescription());
|
||||
def.line(dsNames[1], Color.RED, "Events per period");
|
||||
*/
|
||||
if (hideLegend)
|
||||
def.setShowLegend(false);
|
||||
if (hideGrid) {
|
||||
def.setGridX(false);
|
||||
def.setGridY(false);
|
||||
}
|
||||
//System.out.println("rendering: path=" + path + " dsNames[0]=" + dsNames[0] + " dsNames[1]=" + dsNames[1] + " lsnr.getName=" + _listener.getName());
|
||||
def.setAntiAliasing(false);
|
||||
//System.out.println("Rendering: \n" + def.exportXmlTemplate());
|
||||
//System.out.println("*****************\nData: \n" + _listener.getData().dump());
|
||||
RrdGraph graph = new RrdGraph(def);
|
||||
//System.out.println("Graph created");
|
||||
byte data[] = null;
|
||||
if ( (width <= 0) || (height <= 0) )
|
||||
data = graph.getPNGBytes();
|
||||
else
|
||||
data = graph.getPNGBytes(width, height);
|
||||
//long timeToPlot = System.currentTimeMillis() - begin;
|
||||
out.write(data);
|
||||
//File t = File.createTempFile("jrobinData", ".xml");
|
||||
//_listener.getData().dumpXml(new FileOutputStream(t));
|
||||
//System.out.println("plotted: " + (data != null ? data.length : 0) + " bytes in " + timeToPlot
|
||||
// ); // + ", data written to " + t.getAbsolutePath());
|
||||
} catch (RrdException re) {
|
||||
_log.error("Error rendering", re);
|
||||
throw new IOException("Error plotting: " + re.getMessage());
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error rendering", ioe);
|
||||
throw ioe;
|
||||
} catch (OutOfMemoryError oom) {
|
||||
_log.error("Error rendering", oom);
|
||||
throw new IOException("Error plotting: " + oom.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,175 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.stat.Rate;
|
||||
import net.i2p.stat.RateStat;
|
||||
import net.i2p.stat.RateSummaryListener;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import org.jrobin.core.RrdBackendFactory;
|
||||
import org.jrobin.core.RrdDb;
|
||||
import org.jrobin.core.RrdDef;
|
||||
import org.jrobin.core.RrdException;
|
||||
import org.jrobin.core.RrdMemoryBackendFactory;
|
||||
import org.jrobin.core.Sample;
|
||||
import org.jrobin.graph.RrdGraph;
|
||||
import org.jrobin.graph.RrdGraphDef;
|
||||
import org.jrobin.graph.RrdGraphDefTemplate;
|
||||
|
||||
class SummaryRenderer {
|
||||
private Log _log;
|
||||
private SummaryListener _listener;
|
||||
private I2PAppContext _context;
|
||||
|
||||
public SummaryRenderer(I2PAppContext ctx, SummaryListener lsnr) {
|
||||
_log = ctx.logManager().getLog(SummaryRenderer.class);
|
||||
_listener = lsnr;
|
||||
_context = ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the stats as determined by the specified JRobin xml config,
|
||||
* but note that this doesn't work on stock jvms, as it requires
|
||||
* DOM level 3 load and store support. Perhaps we can bundle that, or
|
||||
* specify who can get it from where, etc.
|
||||
*
|
||||
*/
|
||||
public static synchronized void render(I2PAppContext ctx, OutputStream out, String filename) throws IOException {
|
||||
long end = ctx.clock().now() - 60*1000;
|
||||
long start = end - 60*1000*SummaryListener.PERIODS;
|
||||
try {
|
||||
RrdGraphDefTemplate template = new RrdGraphDefTemplate(filename);
|
||||
RrdGraphDef def = template.getRrdGraphDef();
|
||||
def.setTimePeriod(start/1000, end/1000); // ignore the periods in the template
|
||||
RrdGraph graph = new RrdGraph(def);
|
||||
byte img[] = graph.getPNGBytes();
|
||||
out.write(img);
|
||||
} catch (RrdException re) {
|
||||
//_log.error("Error rendering " + filename, re);
|
||||
throw new IOException("Error plotting: " + re.getMessage());
|
||||
} catch (IOException ioe) {
|
||||
//_log.error("Error rendering " + filename, ioe);
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
|
||||
public void render(OutputStream out) throws IOException { render(out, -1, -1, false, false, false, false, -1, false); }
|
||||
|
||||
public void render(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, boolean showCredit) throws IOException {
|
||||
long end = _listener.now() - 60*1000;
|
||||
if (periodCount <= 0) periodCount = SummaryListener.PERIODS;
|
||||
if (periodCount > SummaryListener.PERIODS)
|
||||
periodCount = SummaryListener.PERIODS;
|
||||
long start = end - _listener.getRate().getPeriod()*periodCount;
|
||||
//long begin = System.currentTimeMillis();
|
||||
try {
|
||||
RrdGraphDef def = new RrdGraphDef();
|
||||
def.setTimePeriod(start/1000, 0);
|
||||
def.setLowerLimit(0d);
|
||||
String name = _listener.getRate().getRateStat().getName();
|
||||
// heuristic to set K=1024
|
||||
if ((name.startsWith("bw.") || name.indexOf("Size") >= 0 || name.indexOf("Bps") >= 0 || name.indexOf("memory") >= 0)
|
||||
&& !showEvents)
|
||||
def.setBaseValue(1024);
|
||||
if (!hideTitle) {
|
||||
String title;
|
||||
String p = DataHelper.formatDuration(_listener.getRate().getPeriod());
|
||||
if (showEvents)
|
||||
// Note to translators: all runtime zh translation disabled in this file, no font available in RRD
|
||||
title = name + ' ' + _("events in {0}", p);
|
||||
else
|
||||
title = name + ' ' + _("averaged for {0}", p);
|
||||
def.setTitle(title);
|
||||
}
|
||||
String path = _listener.getData().getPath();
|
||||
String dsNames[] = _listener.getData().getDsNames();
|
||||
String plotName = null;
|
||||
String descr = null;
|
||||
if (showEvents) {
|
||||
// include the average event count on the plot
|
||||
plotName = dsNames[1];
|
||||
descr = _("Events per period");
|
||||
} else {
|
||||
// include the average value
|
||||
plotName = dsNames[0];
|
||||
// The descriptions are not tagged in the createRateStat calls
|
||||
// (there are over 500 of them)
|
||||
// but the descriptions for the default graphs are tagged in
|
||||
// Strings.java
|
||||
descr = _(_listener.getRate().getRateStat().getDescription());
|
||||
}
|
||||
def.datasource(plotName, path, plotName, "AVERAGE", "MEMORY");
|
||||
def.area(plotName, Color.BLUE, descr + "@r");
|
||||
if (!hideLegend) {
|
||||
def.gprint(plotName, "AVERAGE", _("avg") + ": @2@s");
|
||||
def.gprint(plotName, "MAX", ' ' + _("max") + ": @2@s");
|
||||
def.gprint(plotName, "LAST", ' ' + _("now") + ": @2@s@r");
|
||||
}
|
||||
if (!showCredit)
|
||||
def.setShowSignature(false);
|
||||
/*
|
||||
// these four lines set up a graph plotting both values and events on the same chart
|
||||
// (but with the same coordinates, so the values may look pretty skewed)
|
||||
def.datasource(dsNames[0], path, dsNames[0], "AVERAGE", "MEMORY");
|
||||
def.datasource(dsNames[1], path, dsNames[1], "AVERAGE", "MEMORY");
|
||||
def.area(dsNames[0], Color.BLUE, _listener.getRate().getRateStat().getDescription());
|
||||
def.line(dsNames[1], Color.RED, "Events per period");
|
||||
*/
|
||||
if (hideLegend)
|
||||
def.setShowLegend(false);
|
||||
if (hideGrid) {
|
||||
def.setGridX(false);
|
||||
def.setGridY(false);
|
||||
}
|
||||
//System.out.println("rendering: path=" + path + " dsNames[0]=" + dsNames[0] + " dsNames[1]=" + dsNames[1] + " lsnr.getName=" + _listener.getName());
|
||||
def.setAntiAliasing(false);
|
||||
//System.out.println("Rendering: \n" + def.exportXmlTemplate());
|
||||
//System.out.println("*****************\nData: \n" + _listener.getData().dump());
|
||||
RrdGraph graph = new RrdGraph(def);
|
||||
//System.out.println("Graph created");
|
||||
byte data[] = null;
|
||||
if ( (width <= 0) || (height <= 0) )
|
||||
data = graph.getPNGBytes();
|
||||
else
|
||||
data = graph.getPNGBytes(width, height);
|
||||
//long timeToPlot = System.currentTimeMillis() - begin;
|
||||
out.write(data);
|
||||
//File t = File.createTempFile("jrobinData", ".xml");
|
||||
//_listener.getData().dumpXml(new FileOutputStream(t));
|
||||
//System.out.println("plotted: " + (data != null ? data.length : 0) + " bytes in " + timeToPlot
|
||||
// ); // + ", data written to " + t.getAbsolutePath());
|
||||
} catch (RrdException re) {
|
||||
_log.error("Error rendering", re);
|
||||
throw new IOException("Error plotting: " + re.getMessage());
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error rendering", ioe);
|
||||
throw ioe;
|
||||
} catch (OutOfMemoryError oom) {
|
||||
_log.error("Error rendering", oom);
|
||||
throw new IOException("Error plotting: " + oom.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** translate a string */
|
||||
private String _(String s) {
|
||||
// the RRD font doesn't have zh chars, at least on my system
|
||||
if ("zh".equals(Messages.getLanguage(_context)))
|
||||
return s;
|
||||
return Messages.getString(s, _context);
|
||||
}
|
||||
|
||||
/**
|
||||
* translate a string with a parameter
|
||||
*/
|
||||
private String _(String s, String o) {
|
||||
// the RRD font doesn't have zh chars, at least on my system
|
||||
if ("zh".equals(Messages.getLanguage(_context)))
|
||||
return s.replace("{0}", o);
|
||||
return Messages.getString(s, o, _context);
|
||||
}
|
||||
}
|
@ -201,15 +201,17 @@ public class TunnelRenderer {
|
||||
out.write("</table>\n");
|
||||
if (in != null) {
|
||||
List pending = in.listPending();
|
||||
if (pending.size() > 0)
|
||||
if (!pending.isEmpty()) {
|
||||
out.write("<div class=\"statusnotes\"><center><b>" + _("Build in progress") + ": " + pending.size() + " " + _("inbound") + "</b></center></div>\n");
|
||||
live += pending.size();
|
||||
live += pending.size();
|
||||
}
|
||||
}
|
||||
if (outPool != null) {
|
||||
List pending = outPool.listPending();
|
||||
if (pending.size() > 0)
|
||||
if (!pending.isEmpty()) {
|
||||
out.write("<div class=\"statusnotes\"><center><b>" + _("Build in progress") + ": " + pending.size() + " " + _("outbound") + "</b></center></div>\n");
|
||||
live += pending.size();
|
||||
live += pending.size();
|
||||
}
|
||||
}
|
||||
if (live <= 0)
|
||||
out.write("<div class=\"statusnotes\"><center><b>" + _("No tunnels; waiting for the grace period to end.") + "</center></b></div>\n");
|
||||
|
@ -54,7 +54,7 @@ public class WebAppConfiguration implements WebApplicationContext.Configuration
|
||||
I2PAppContext i2pContext = I2PAppContext.getGlobalContext();
|
||||
File libDir = new File(i2pContext.getBaseDir(), "lib");
|
||||
// FIXME this only works if war is the same name as the plugin
|
||||
File pluginDir = new File(i2pContext.getAppDir(),
|
||||
File pluginDir = new File(i2pContext.getConfigDir(),
|
||||
PluginUpdateHandler.PLUGIN_DIR + ctxPath);
|
||||
|
||||
File dir = libDir;
|
||||
|
@ -3,10 +3,13 @@ package net.i2p.router.web;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.FileUtil;
|
||||
|
||||
import org.mortbay.http.HttpContext;
|
||||
import org.mortbay.http.HttpListener;
|
||||
@ -31,6 +34,8 @@ import org.mortbay.jetty.servlet.WebApplicationContext;
|
||||
*/
|
||||
public class WebAppStarter {
|
||||
|
||||
static final Map<String, Long> warModTimes = new ConcurrentHashMap();
|
||||
|
||||
/**
|
||||
* adds and starts
|
||||
* @throws just about anything, caller would be wise to catch Throwable
|
||||
@ -54,6 +59,28 @@ public class WebAppStarter {
|
||||
stopWebApp(server, appName);
|
||||
} catch (Throwable t) {}
|
||||
|
||||
// To avoid ZipErrors from JarURLConnetion caching,
|
||||
// (used by Jetty JarResource and JarFileResource)
|
||||
// copy the war to a new directory if it is newer than the one we loaded originally.
|
||||
// Yes, URLConnection has a setDefaultUseCaches() method, but it's hard to get to
|
||||
// because it's non-static and the class is abstract, and we don't really want to
|
||||
// set the default to false for everything.
|
||||
long newmod = (new File(warPath)).lastModified();
|
||||
if (newmod <= 0)
|
||||
throw new IOException("Web app " + warPath + " does not exist");
|
||||
Long oldmod = warModTimes.get(warPath);
|
||||
if (oldmod == null) {
|
||||
warModTimes.put(warPath, new Long(newmod));
|
||||
} else if (oldmod.longValue() < newmod) {
|
||||
// copy war to temporary directory
|
||||
File warTmpDir = new File(ctx.getTempDir(), "war-copy-" + appName + ctx.random().nextInt());
|
||||
warTmpDir.mkdir();
|
||||
String tmpPath = (new File(warTmpDir, appName + ".war")).getAbsolutePath();
|
||||
if (!FileUtil.copy(warPath, tmpPath, true))
|
||||
throw new IOException("Web app failed copy from " + warPath + " to " + tmpPath);
|
||||
warPath = tmpPath;
|
||||
}
|
||||
|
||||
WebApplicationContext wac = server.addWebApplication("/"+ appName, warPath);
|
||||
tmpdir.mkdir();
|
||||
wac.setTempDirectory(tmpdir);
|
||||
|
@ -96,5 +96,12 @@ class Dummy {
|
||||
_("itag1");
|
||||
_("itag2");
|
||||
|
||||
// Descriptions for the stats that are graphed by default
|
||||
// There are over 500 stats currently defined, we aren't going to tag them all
|
||||
_("Low-level bandwidth receive rate"); // bw.recvRate
|
||||
_("Low-level bandwidth send rate"); // bw.sendRate
|
||||
_("How many peers we are actively talking with"); // router.activePeers
|
||||
// router.memoryUsed currently has the max size in the description so it can't be tagged
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,11 @@
|
||||
</head><body>
|
||||
<%@include file="summary.jsi" %>
|
||||
<h1><%=intl._("I2P Network Peers")%></h1>
|
||||
<div class="main" id="main">
|
||||
<div class="main" id="main"><div class="wideload">
|
||||
<jsp:useBean class="net.i2p.router.web.PeerHelper" id="peerHelper" scope="request" />
|
||||
<jsp:setProperty name="peerHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
<jsp:setProperty name="peerHelper" property="writer" value="<%=out%>" />
|
||||
<jsp:setProperty name="peerHelper" property="urlBase" value="peers.jsp" />
|
||||
<jsp:setProperty name="peerHelper" property="sort" value="<%=request.getParameter("sort") != null ? request.getParameter("sort") : ""%>" />
|
||||
<jsp:getProperty name="peerHelper" property="peerSummary" />
|
||||
</div></body></html>
|
||||
</div></div></body></html>
|
||||
|
@ -41,7 +41,7 @@
|
||||
long delay = 60;
|
||||
try { delay = Long.parseLong(d); } catch (NumberFormatException nfe) {}
|
||||
if (delay*1000 < timeleft + 5000)
|
||||
out.print("<meta http-equiv=\"refresh\" content=\"" + d + "\" >\n");
|
||||
out.print("<meta http-equiv=\"refresh\" content=\"" + d + ";url=/summaryframe.jsp\" >\n");
|
||||
else
|
||||
shutdownSoon = true;
|
||||
}
|
||||
|
@ -739,7 +739,7 @@ public class SAMStreamSession {
|
||||
data = null;
|
||||
try {
|
||||
synchronized (_data) {
|
||||
if (_data.size() > 0) {
|
||||
if (!_data.isEmpty()) {
|
||||
data = (ByteArray)_data.remove(0);
|
||||
} else if (_shuttingDownGracefully) {
|
||||
/* No data left and shutting down gracefully?
|
||||
|
@ -308,7 +308,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
protected boolean execDestMessage(String opcode, Properties props) {
|
||||
|
||||
if (opcode.equals("GENERATE")) {
|
||||
if (props.size() > 0) {
|
||||
if (!props.isEmpty()) {
|
||||
_log.debug("Properties specified in DEST GENERATE message");
|
||||
return false;
|
||||
}
|
||||
|
@ -371,7 +371,7 @@ public class SAMv2StreamSession extends SAMStreamSession
|
||||
{
|
||||
synchronized ( _data )
|
||||
{
|
||||
if ( _data.size() > 0 )
|
||||
if ( !_data.isEmpty() )
|
||||
{
|
||||
int formerSize = _dataSize ;
|
||||
data = ( ByteArray ) _data.remove ( 0 );
|
||||
|
@ -0,0 +1,58 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.ObjectCounter;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
|
||||
/**
|
||||
* Count how often we have received an incoming connection
|
||||
* This offers basic DOS protection but is not a complete solution.
|
||||
*
|
||||
* @since 0.7.14
|
||||
*/
|
||||
class ConnThrottler {
|
||||
private final ObjectCounter<Hash> counter;
|
||||
private final int _max;
|
||||
private final int _totalMax;
|
||||
private final AtomicInteger _currentTotal;
|
||||
|
||||
/*
|
||||
* @param max per-peer, 0 for unlimited
|
||||
* @param totalMax for all peers, 0 for unlimited
|
||||
* @param period ms
|
||||
*/
|
||||
ConnThrottler(int max, int totalMax, long period) {
|
||||
_max = max;
|
||||
_totalMax = totalMax;
|
||||
if (max > 0)
|
||||
this.counter = new ObjectCounter();
|
||||
else
|
||||
this.counter = null;
|
||||
if (totalMax > 0)
|
||||
_currentTotal = new AtomicInteger();
|
||||
else
|
||||
_currentTotal = null;
|
||||
SimpleScheduler.getInstance().addPeriodicEvent(new Cleaner(), period);
|
||||
}
|
||||
|
||||
/** increments before checking */
|
||||
boolean shouldThrottle(Hash h) {
|
||||
if (_totalMax > 0 && _currentTotal.incrementAndGet() > _totalMax)
|
||||
return true;
|
||||
if (_max > 0)
|
||||
return this.counter.increment(h) > _max;
|
||||
return false;
|
||||
}
|
||||
|
||||
private class Cleaner implements SimpleTimer.TimedEvent {
|
||||
public void timeReached() {
|
||||
if (_totalMax > 0)
|
||||
_currentTotal.set(0);
|
||||
if (_max > 0)
|
||||
ConnThrottler.this.counter.clear();
|
||||
}
|
||||
}
|
||||
}
|
@ -449,7 +449,7 @@ public class Connection {
|
||||
}
|
||||
_outboundPackets.notifyAll();
|
||||
}
|
||||
if ((acked != null) && (acked.size() > 0) )
|
||||
if ((acked != null) && (!acked.isEmpty()) )
|
||||
_ackSinceCongestion = true;
|
||||
return acked;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
@ -35,10 +36,14 @@ public class ConnectionManager {
|
||||
/** Ping ID (Long) to PingRequest */
|
||||
private final Map<Long, PingRequest> _pendingPings;
|
||||
private boolean _allowIncoming;
|
||||
private boolean _throttlersInitialized;
|
||||
private int _maxConcurrentStreams;
|
||||
private ConnectionOptions _defaultOptions;
|
||||
private volatile int _numWaiting;
|
||||
private long SoTimeout;
|
||||
private ConnThrottler _minuteThrottler;
|
||||
private ConnThrottler _hourThrottler;
|
||||
private ConnThrottler _dayThrottler;
|
||||
|
||||
public ConnectionManager(I2PAppContext context, I2PSession session, int maxConcurrent, ConnectionOptions defaultOptions) {
|
||||
_context = context;
|
||||
@ -106,7 +111,23 @@ public class ConnectionManager {
|
||||
|
||||
public void setAllowIncomingConnections(boolean allow) {
|
||||
_connectionHandler.setActive(allow);
|
||||
if (allow && !_throttlersInitialized) {
|
||||
_throttlersInitialized = true;
|
||||
if (_defaultOptions.getMaxConnsPerMinute() > 0 || _defaultOptions.getMaxTotalConnsPerMinute() > 0) {
|
||||
_context.statManager().createRateStat("stream.con.throttledMinute", "Dropped for conn limit", "Stream", new long[] { 5*60*1000 });
|
||||
_minuteThrottler = new ConnThrottler(_defaultOptions.getMaxConnsPerMinute(), _defaultOptions.getMaxTotalConnsPerMinute(), 60*1000);
|
||||
}
|
||||
if (_defaultOptions.getMaxConnsPerHour() > 0 || _defaultOptions.getMaxTotalConnsPerHour() > 0) {
|
||||
_context.statManager().createRateStat("stream.con.throttledHour", "Dropped for conn limit", "Stream", new long[] { 5*60*1000 });
|
||||
_hourThrottler = new ConnThrottler(_defaultOptions.getMaxConnsPerHour(), _defaultOptions.getMaxTotalConnsPerHour(), 60*60*1000);
|
||||
}
|
||||
if (_defaultOptions.getMaxConnsPerDay() > 0 || _defaultOptions.getMaxTotalConnsPerDay() > 0) {
|
||||
_context.statManager().createRateStat("stream.con.throttledDay", "Dropped for conn limit", "Stream", new long[] { 5*60*1000 });
|
||||
_dayThrottler = new ConnThrottler(_defaultOptions.getMaxConnsPerDay(), _defaultOptions.getMaxTotalConnsPerDay(), 24*60*60*1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return if we should accept connections */
|
||||
public boolean getAllowIncomingConnections() {
|
||||
return _connectionHandler.getActive();
|
||||
@ -140,8 +161,15 @@ public class ConnectionManager {
|
||||
+ _maxConcurrentStreams + " connections");
|
||||
reject = true;
|
||||
} else if (shouldRejectConnection(synPacket)) {
|
||||
_log.error("Refusing connection since peer is " +
|
||||
(_defaultOptions.isAccessListEnabled() ? "not whitelisted: " : "blacklisted: ") +
|
||||
// this may not be right if more than one is enabled
|
||||
String why;
|
||||
if (_defaultOptions.isAccessListEnabled())
|
||||
why = "not whitelisted: ";
|
||||
else if (_defaultOptions.isBlacklistEnabled())
|
||||
why = "blacklisted: ";
|
||||
else
|
||||
why = "throttled: ";
|
||||
_log.error("Refusing connection since peer is " + why +
|
||||
(synPacket.getOptionalFrom() == null ? "null from" : synPacket.getOptionalFrom().calculateHash().toBase64()));
|
||||
reject = true;
|
||||
} else {
|
||||
@ -281,11 +309,28 @@ public class ConnectionManager {
|
||||
Destination from = syn.getOptionalFrom();
|
||||
if (from == null)
|
||||
return true;
|
||||
Hash h = from.calculateHash();
|
||||
boolean throttled = false;
|
||||
// always call all 3 to increment all counters
|
||||
if (_minuteThrottler != null && _minuteThrottler.shouldThrottle(h)) {
|
||||
_context.statManager().addRateData("stream.con.throttledMinute", 1, 0);
|
||||
throttled = true;
|
||||
}
|
||||
if (_hourThrottler != null && _hourThrottler.shouldThrottle(h)) {
|
||||
_context.statManager().addRateData("stream.con.throttledHour", 1, 0);
|
||||
throttled = true;
|
||||
}
|
||||
if (_dayThrottler != null && _dayThrottler.shouldThrottle(h)) {
|
||||
_context.statManager().addRateData("stream.con.throttledDay", 1, 0);
|
||||
throttled = true;
|
||||
}
|
||||
if (throttled)
|
||||
return true;
|
||||
// if the sig is absent or bad it will be caught later (in CPH)
|
||||
if (_defaultOptions.isAccessListEnabled())
|
||||
return !_defaultOptions.getAccessList().contains(from.calculateHash());
|
||||
return !_defaultOptions.getAccessList().contains(h);
|
||||
if (_defaultOptions.isBlacklistEnabled())
|
||||
return _defaultOptions.getBlacklist().contains(from.calculateHash());
|
||||
return _defaultOptions.getBlacklist().contains(h);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,12 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
private boolean _blackListEnabled;
|
||||
private Set<Hash> _accessList;
|
||||
private Set<Hash> _blackList;
|
||||
private int _maxConnsPerMinute;
|
||||
private int _maxConnsPerHour;
|
||||
private int _maxConnsPerDay;
|
||||
private int _maxTotalConnsPerMinute;
|
||||
private int _maxTotalConnsPerHour;
|
||||
private int _maxTotalConnsPerDay;
|
||||
|
||||
public static final int PROFILE_BULK = 1;
|
||||
public static final int PROFILE_INTERACTIVE = 2;
|
||||
@ -67,9 +73,17 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
public static final String PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR = "i2p.streaming.congestionAvoidanceGrowthRateFactor";
|
||||
public static final String PROP_SLOW_START_GROWTH_RATE_FACTOR = "i2p.streaming.slowStartGrowthRateFactor";
|
||||
public static final String PROP_ANSWER_PINGS = "i2p.streaming.answerPings";
|
||||
/** all of these are @since 0.7.13 */
|
||||
public static final String PROP_ENABLE_ACCESS_LIST = "i2cp.enableAccessList";
|
||||
public static final String PROP_ENABLE_BLACKLIST = "i2cp.enableBlackList";
|
||||
public static final String PROP_ACCESS_LIST = "i2cp.accessList";
|
||||
/** all of these are @since 0.7.14 */
|
||||
public static final String PROP_MAX_CONNS_MIN = "i2p.streaming.maxConnsPerMinute";
|
||||
public static final String PROP_MAX_CONNS_HOUR = "i2p.streaming.maxConnsPerHour";
|
||||
public static final String PROP_MAX_CONNS_DAY = "i2p.streaming.maxConnsPerDay";
|
||||
public static final String PROP_MAX_TOTAL_CONNS_MIN = "i2p.streaming.maxTotalConnsPerMinute";
|
||||
public static final String PROP_MAX_TOTAL_CONNS_HOUR = "i2p.streaming.maxTotalConnsPerHour";
|
||||
public static final String PROP_MAX_TOTAL_CONNS_DAY = "i2p.streaming.maxTotalConnsPerDay";
|
||||
|
||||
private static final int TREND_COUNT = 3;
|
||||
static final int INITIAL_WINDOW_SIZE = 6;
|
||||
@ -222,6 +236,12 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
setReadTimeout(opts.getReadTimeout());
|
||||
setAnswerPings(opts.getAnswerPings());
|
||||
initLists(opts);
|
||||
_maxConnsPerMinute = opts.getMaxConnsPerMinute();
|
||||
_maxConnsPerHour = opts.getMaxConnsPerHour();
|
||||
_maxConnsPerDay = opts.getMaxConnsPerDay();
|
||||
_maxTotalConnsPerMinute = opts.getMaxTotalConnsPerMinute();
|
||||
_maxTotalConnsPerHour = opts.getMaxTotalConnsPerHour();
|
||||
_maxTotalConnsPerDay = opts.getMaxTotalConnsPerDay();
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,6 +268,12 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
|
||||
setAnswerPings(getBool(opts, PROP_ANSWER_PINGS, DEFAULT_ANSWER_PINGS));
|
||||
initLists(opts);
|
||||
_maxConnsPerMinute = getInt(opts, PROP_MAX_CONNS_MIN, 0);
|
||||
_maxConnsPerHour = getInt(opts, PROP_MAX_CONNS_HOUR, 0);
|
||||
_maxConnsPerDay = getInt(opts, PROP_MAX_CONNS_DAY, 0);
|
||||
_maxTotalConnsPerMinute = getInt(opts, PROP_MAX_TOTAL_CONNS_MIN, 0);
|
||||
_maxTotalConnsPerHour = getInt(opts, PROP_MAX_TOTAL_CONNS_HOUR, 0);
|
||||
_maxTotalConnsPerDay = getInt(opts, PROP_MAX_TOTAL_CONNS_DAY, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -291,6 +317,18 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
if (opts.containsKey(PROP_ANSWER_PINGS))
|
||||
setAnswerPings(getBool(opts, PROP_ANSWER_PINGS, DEFAULT_ANSWER_PINGS));
|
||||
initLists(opts);
|
||||
if (opts.containsKey(PROP_MAX_CONNS_MIN))
|
||||
_maxConnsPerMinute = getInt(opts, PROP_MAX_CONNS_MIN, 0);
|
||||
if (opts.containsKey(PROP_MAX_CONNS_HOUR))
|
||||
_maxConnsPerHour = getInt(opts, PROP_MAX_CONNS_HOUR, 0);
|
||||
if (opts.containsKey(PROP_MAX_CONNS_DAY))
|
||||
_maxConnsPerDay = getInt(opts, PROP_MAX_CONNS_DAY, 0);
|
||||
if (opts.containsKey(PROP_MAX_TOTAL_CONNS_MIN))
|
||||
_maxTotalConnsPerMinute = getInt(opts, PROP_MAX_TOTAL_CONNS_MIN, 0);
|
||||
if (opts.containsKey(PROP_MAX_TOTAL_CONNS_HOUR))
|
||||
_maxTotalConnsPerHour = getInt(opts, PROP_MAX_TOTAL_CONNS_HOUR, 0);
|
||||
if (opts.containsKey(PROP_MAX_TOTAL_CONNS_DAY))
|
||||
_maxTotalConnsPerDay = getInt(opts, PROP_MAX_TOTAL_CONNS_DAY, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -523,6 +561,14 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
public int getSlowStartGrowthRateFactor() { return _slowStartGrowthRateFactor; }
|
||||
public void setSlowStartGrowthRateFactor(int factor) { _slowStartGrowthRateFactor = factor; }
|
||||
|
||||
/** all of these are @since 0.7.14; no public setters */
|
||||
public int getMaxConnsPerMinute() { return _maxConnsPerMinute; }
|
||||
public int getMaxConnsPerHour() { return _maxConnsPerHour; }
|
||||
public int getMaxConnsPerDay() { return _maxConnsPerDay; }
|
||||
public int getMaxTotalConnsPerMinute() { return _maxTotalConnsPerMinute; }
|
||||
public int getMaxTotalConnsPerHour() { return _maxTotalConnsPerHour; }
|
||||
public int getMaxTotalConnsPerDay() { return _maxTotalConnsPerDay; }
|
||||
|
||||
public boolean isAccessListEnabled() { return _accessListEnabled; }
|
||||
public boolean isBlacklistEnabled() { return _blackListEnabled; }
|
||||
public Set<Hash> getAccessList() { return _accessList; }
|
||||
|
@ -253,7 +253,7 @@ public class ConnectionPacketHandler {
|
||||
else
|
||||
return false;
|
||||
|
||||
if ( (acked != null) && (acked.size() > 0) ) {
|
||||
if ( (acked != null) && (!acked.isEmpty()) ) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(acked.size() + " of our packets acked with " + packet);
|
||||
// use the highest RTT, since these would likely be bunched together,
|
||||
|
@ -283,15 +283,15 @@ public class MessageInputStream extends InputStream {
|
||||
expiration = _readTimeout + System.currentTimeMillis();
|
||||
synchronized (_dataLock) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
if ( (_readyDataBlocks.size() <= 0) && (i == 0) ) {
|
||||
if ( (_readyDataBlocks.isEmpty()) && (i == 0) ) {
|
||||
// ok, we havent found anything, so lets block until we get
|
||||
// at least one byte
|
||||
|
||||
while (_readyDataBlocks.size() <= 0) {
|
||||
while (_readyDataBlocks.isEmpty()) {
|
||||
if (_locallyClosed)
|
||||
throw new IOException("Already closed, you wanker");
|
||||
|
||||
if ( (_notYetReadyBlocks.size() <= 0) && (_closeReceived) ) {
|
||||
if ( (_notYetReadyBlocks.isEmpty()) && (_closeReceived) ) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("read(...," + offset + ", " + length + ")[" + i
|
||||
+ "] got EOF after " + _readTotal + " " + toString());
|
||||
@ -322,7 +322,7 @@ public class MessageInputStream extends InputStream {
|
||||
+ ") with nonblocking setup: " + toString());
|
||||
return i;
|
||||
}
|
||||
if (_readyDataBlocks.size() <= 0) {
|
||||
if (_readyDataBlocks.isEmpty()) {
|
||||
if ( (_readTimeout > 0) && (expiration < System.currentTimeMillis()) ) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("read(...," + offset+", " + length+ ")[" + i
|
||||
@ -334,7 +334,7 @@ public class MessageInputStream extends InputStream {
|
||||
}
|
||||
// we looped a few times then got data, so this pass doesnt count
|
||||
i--;
|
||||
} else if (_readyDataBlocks.size() <= 0) {
|
||||
} else if (_readyDataBlocks.isEmpty()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read(...," + offset+", " + length+ ")[" + i
|
||||
+ "] no more ready blocks, returning");
|
||||
|
@ -70,9 +70,9 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
|
||||
* @deprecated I2PSession throws out the tags
|
||||
*/
|
||||
public void setTagsSent(Set tags) {
|
||||
if (tags != null && tags.size() > 0)
|
||||
if (tags != null && !tags.isEmpty())
|
||||
_log.error("Who is sending tags thru the streaming lib? " + tags.size());
|
||||
if ( (_tagsSent != null) && (_tagsSent.size() > 0) && (tags.size() > 0) ) {
|
||||
if ( (_tagsSent != null) && (!_tagsSent.isEmpty()) && (!tags.isEmpty()) ) {
|
||||
//int old = _tagsSent.size();
|
||||
//_tagsSent.addAll(tags);
|
||||
if (!_tagsSent.equals(tags))
|
||||
@ -160,7 +160,7 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
|
||||
if (con != null)
|
||||
buf.append(" rtt ").append(con.getOptions().getRTT());
|
||||
|
||||
if ( (_tagsSent != null) && (_tagsSent.size() > 0) )
|
||||
if ( (_tagsSent != null) && (!_tagsSent.isEmpty()) )
|
||||
buf.append(" with tags");
|
||||
|
||||
if (_ackOn > 0)
|
||||
|
@ -8,8 +8,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P susidns\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-12-19 11:30+0000\n"
|
||||
"PO-Revision-Date: 2009-12-20 07:03+0000\n"
|
||||
"POT-Creation-Date: 2010-05-25 21:15+0000\n"
|
||||
"PO-Revision-Date: 2010-05-25 21:43+0000\n"
|
||||
"Last-Translator: 4get <forget@mail.i2p>\n"
|
||||
"Language-Team: foo <foo@bar>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -17,79 +17,109 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Poedit-Language: Russian\n"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:197
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:188
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:192
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:199
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:193
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:197
|
||||
msgid "Search"
|
||||
msgstr "Поиск"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:201
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:203
|
||||
msgid "Search within filtered list"
|
||||
msgstr "Поиск в отфильтрованном списке"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:203
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:205
|
||||
msgid "Filtered list"
|
||||
msgstr "Отфильтрованный список"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:207
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:209
|
||||
msgid "no matches"
|
||||
msgstr "ничего не найдено"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:210
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:212
|
||||
msgid "Addressbook"
|
||||
msgstr "Адресная книга"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:212
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:214
|
||||
msgid "contains no entries"
|
||||
msgstr "не содержит записей"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:214
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:216
|
||||
msgid "contains 1 entry"
|
||||
msgstr "содержит одну запись"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:216
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:218
|
||||
#, java-format
|
||||
msgid "contains {0} entries"
|
||||
msgstr "содержит {0} записей"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:226
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:228
|
||||
#, java-format
|
||||
msgid "Showing {0} of {1}"
|
||||
msgstr "Показаны {0} из {1}"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:257
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:359
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:259
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:368
|
||||
msgid "Add"
|
||||
msgstr "Добавить"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:261
|
||||
msgid "Destination added."
|
||||
msgstr "Адрес добавлен."
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:259
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:264
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:366
|
||||
msgid "Replace"
|
||||
msgstr "Заменить"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:263
|
||||
#, java-format
|
||||
msgid "Host name {0} is already in addressbook, unchanged."
|
||||
msgstr "Для узла {0} уже существует запись с совпадающим адресом назначения."
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:265
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:307
|
||||
msgid "Delete"
|
||||
msgstr "Удалить"
|
||||
#, java-format
|
||||
msgid "Host name {0} is already in addressbook with a different destination. Click \"Replace\" to overwrite."
|
||||
msgstr "Для узла {0} уже существует запись с другим адресом назначения. Нажмите \"Заменить\" для перезаписи."
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:275
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:277
|
||||
#, java-format
|
||||
msgid "Destination added for {0}."
|
||||
msgstr "Добавлен адрес для {0}."
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:279
|
||||
#, java-format
|
||||
msgid "Destination changed for {0}."
|
||||
msgstr "Заменён адрес для {0}."
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:284
|
||||
msgid "Invalid Base 64 destination."
|
||||
msgstr "Некорректный Base-64 адрес."
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:288
|
||||
msgid "Please enter a host name and destination"
|
||||
msgstr "Пожалуйста, введите имя узла и адрес назначения"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:292
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:314
|
||||
msgid "Delete Selected"
|
||||
msgstr "Удалить выделенное"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:302
|
||||
#, java-format
|
||||
msgid "Destination {0} deleted."
|
||||
msgstr "Адрес {0} удален."
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:277
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:304
|
||||
#, java-format
|
||||
msgid "{0} destinations deleted."
|
||||
msgstr "{0} адресов удалено."
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:283
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:310
|
||||
msgid "Addressbook saved."
|
||||
msgstr "Адресная книга сохранена."
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:286
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:313
|
||||
msgid "ERROR: Could not write addressbook file."
|
||||
msgstr "ОШИБКА: Не удалось сохранить файл адресной книги."
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:291
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:318
|
||||
#: ../src/java/src/i2p/susi/dns/ConfigBean.java:148
|
||||
#: ../src/java/src/i2p/susi/dns/SubscriptionsBean.java:150
|
||||
msgid "Invalid form submission, probably because you used the \"back\" or \"reload\" button on your browser. Please resubmit."
|
||||
@ -97,8 +127,8 @@ msgstr "Форма не принята, скорее всего это прои
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/ConfigBean.java:139
|
||||
#: ../src/java/src/i2p/susi/dns/SubscriptionsBean.java:129
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:123
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:123
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:130
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:130
|
||||
msgid "Save"
|
||||
msgstr "Сохранить"
|
||||
|
||||
@ -108,8 +138,8 @@ msgstr "Настройки сохранены."
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/ConfigBean.java:142
|
||||
#: ../src/java/src/i2p/susi/dns/SubscriptionsBean.java:144
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:125
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:125
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:128
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:128
|
||||
msgid "Reload"
|
||||
msgstr "Перезагрузить"
|
||||
|
||||
@ -129,267 +159,274 @@ msgstr "Подписки сохранены."
|
||||
msgid "Subscriptions reloaded."
|
||||
msgstr "Подписки перезагружены."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:120
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:140
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:125
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:145
|
||||
msgid "addressbook"
|
||||
msgstr "адресная книга"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:122
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:99
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:88
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:99
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:127
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:104
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:93
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:104
|
||||
msgid "addressbooks"
|
||||
msgstr "адресные книги"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:124
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:101
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:90
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:101
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:129
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:106
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:95
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:106
|
||||
msgid "private"
|
||||
msgstr "приватная"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:126
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:103
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:92
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:103
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:131
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:108
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:97
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:108
|
||||
msgid "master"
|
||||
msgstr "основная"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:128
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:105
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:94
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:105
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:133
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:110
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:99
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:110
|
||||
msgid "router"
|
||||
msgstr "маршрутизатор"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:130
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:107
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:96
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:107
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:135
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:112
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:101
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:112
|
||||
msgid "published"
|
||||
msgstr "публикуемая"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:132
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:109
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:98
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:97
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:109
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:137
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:114
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:103
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:102
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:114
|
||||
msgid "subscriptions"
|
||||
msgstr "подписки"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:134
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:97
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:111
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:100
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:111
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:139
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:102
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:116
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:105
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:116
|
||||
msgid "configuration"
|
||||
msgstr "настройки"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:136
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:113
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:102
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:113
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:141
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:118
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:107
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:118
|
||||
msgid "overview"
|
||||
msgstr "введение"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:158
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:163
|
||||
msgid "Filter"
|
||||
msgstr "Фильтр"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:161
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:166
|
||||
msgid "all"
|
||||
msgstr "[без фильтра]"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:172
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:177
|
||||
msgid "Current filter"
|
||||
msgstr "Текущий фильтр"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:177
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:182
|
||||
msgid "clear filter"
|
||||
msgstr "сбросить фильтр"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:219
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:224
|
||||
msgid "Name"
|
||||
msgstr "Имя"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:221
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:355
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:226
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:362
|
||||
msgid "Destination"
|
||||
msgstr "Адрес назначения"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:252
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:257
|
||||
msgid "Mark for deletion"
|
||||
msgstr "Пометить для удаления"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:274
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:279
|
||||
msgid "address helper link"
|
||||
msgstr "addresshelper-ссылка"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:338
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:312
|
||||
msgid "Cancel"
|
||||
msgstr "Отмена"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:345
|
||||
msgid "This addressbook is empty."
|
||||
msgstr "Эта адресная книга пуста."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:349
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:356
|
||||
msgid "Add new destination"
|
||||
msgstr "Добавить новый адрес"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:351
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:358
|
||||
msgid "Hostname"
|
||||
msgstr "Имя узла"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:127
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:132
|
||||
msgid "Hints"
|
||||
msgstr "Примечания"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:129
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:134
|
||||
msgid "File and directory paths here are relative to the addressbook's working directory, which is normally ~/.i2p/addressbook/ (Linux) or %APPDATA%\\I2P\\addressbook\\ (Windows)."
|
||||
msgstr "Пути указываются относительно домашней директории адресной книги, которая обычно расположена в ~/.i2p/addressbook/ (под Linux) или в %APPDATA%\\I2P\\addressbook\\ (под Windows)."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:131
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:136
|
||||
msgid "If you want to manually add lines to an addressbook, add them to the private or master addressbooks."
|
||||
msgstr "Если Вы хотите вручную добавлять записи в адресную книгу, то добавляйте их в «приватную» или «основную»."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:133
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:138
|
||||
msgid "The router addressbook and the published addressbook are updated by the addressbook application."
|
||||
msgstr "Адресные книги «маршрутизатор» и «публикуемая» создаются/перезаписываются автоматически."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:135
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:140
|
||||
msgid "When you publish your addressbook, ALL destinations from the master and router addressbooks appear there."
|
||||
msgstr "Когда Вы публикуете свою адресную книгу, то публикуются ВСЕ записи из адресных книг «основная» и «маршрутизатор»."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:137
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:142
|
||||
msgid "Use the private addressbook for private destinations, these are not published."
|
||||
msgstr "Пользуйтесь «приватной» адресной книгой для адресов, которые Вы не хотите публиковать."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:139
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:144
|
||||
msgid "Options"
|
||||
msgstr "Параметры"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:141
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:146
|
||||
msgid "File containing the list of subscriptions URLs (no need to change)"
|
||||
msgstr "Файл для хранения списка URL подписок (перенастраивать нет необходимости)"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:143
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:148
|
||||
msgid "Update interval in hours"
|
||||
msgstr "Интервал обновления (часы)"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:145
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:150
|
||||
msgid "Your public hosts.txt file (choose a path within your webserver document root)"
|
||||
msgstr "hosts.txt для публикации (по умолчанию сохраняется в корневой директории встроенного в I2P маршутизатор вебсервера)"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:147
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:152
|
||||
msgid "Your hosts.txt (don't change)"
|
||||
msgstr "hosts.txt используемый маршрутизатором (перенастраивать не надо)"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:149
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:154
|
||||
msgid "Your personal addressbook, these hosts will be published"
|
||||
msgstr "Ваша публичная адресная книга, эти записи будут публиковаться"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:151
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:156
|
||||
msgid "Your private addressbook, it is never published"
|
||||
msgstr "Ваша приватная адресная книга, она никогда не публикуется"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:153
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:158
|
||||
msgid "Port for your eepProxy (no need to change)"
|
||||
msgstr "Порт eepProxy (перенастраивать нет необходимости)"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:155
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:160
|
||||
msgid "Hostname for your eepProxy (no need to change)"
|
||||
msgstr "Адрес eepProxy (перенастраивать нет необходимости)"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:157
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:162
|
||||
msgid "Whether to update the published addressbook"
|
||||
msgstr "Обновлять ли публикуемую адресную книгу (true/false)"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:159
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:164
|
||||
msgid "File containing the etags header from the fetched subscription URLs (no need to change)"
|
||||
msgstr "Файл для хранения etags-заголовков от загруженных адресов подписок (перенастраивать нет необходимости)"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:161
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:166
|
||||
msgid "File containing the modification timestamp for each fetched subscription URL (no need to change)"
|
||||
msgstr "Файл для хранения даты/времени модификации каждого загруженного адреса подписки (перенастраивать нет необходимости)"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:163
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:168
|
||||
msgid "File to log activity to (change to /dev/null if you like)"
|
||||
msgstr "Файл для записи журнала действий"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:86
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:91
|
||||
msgid "Introduction"
|
||||
msgstr "Введение - SusiDNS"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:104
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:109
|
||||
msgid "What is the addressbook?"
|
||||
msgstr "Что такое адресная книга?"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:106
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:111
|
||||
msgid "The addressbook application is part of your i2p installation."
|
||||
msgstr "Адресная книга — это приложение в составе Вашего I2P маршрутизатора."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:108
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:113
|
||||
msgid "It regularly updates your hosts.txt file from distributed sources or \"subscriptions\"."
|
||||
msgstr "Его задача регулярно пополнять Ваш hosts.txt адресами из настраиваемых источников («подписок»)."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:110
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:115
|
||||
msgid "In the default configuration, the addressbook is only subscribed to www.i2p2.i2p."
|
||||
msgstr "По умолчанию в адресной книге настроена лишь одна подписка — на www.i2p2.i2p."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:112
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:117
|
||||
msgid "Subscribing to additional sites is easy, just add them to your <a href=\"subscriptions.jsp\">subscriptions</a> file."
|
||||
msgstr "Добавить другие подписки просто, достаточно вписать их URL в <a href=\"subscriptions.jsp\">файл подписок</a>."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:114
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:119
|
||||
msgid "For more information on naming in i2p, see <a href=\"http://www.i2p2.i2p/naming.html\">the overview on www.i2p2.i2p</a>."
|
||||
msgstr "Подробнее о механизме доменных имен в I2P читайте на странице <a href=\"http://www.i2p2.i2p/naming.html\">Naming in I2P</a>."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:116
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:121
|
||||
msgid "How does the addressbook work?"
|
||||
msgstr "Как работает адресная книга?"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:118
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:123
|
||||
msgid "The addressbook application regularly polls your subscriptions and merges their content into your \"router\" addressbook, stored in the hosts.txt file."
|
||||
msgstr "Адресная книга периодически опрашивает Ваши подписки и добавляет их содержимое в Вашу «маршрутизаторную» адресную книгу, которая хранится в файле hosts.txt."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:120
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:125
|
||||
msgid "Then it merges your \"master\" addressbook (userhosts.txt) into the router addressbook as well."
|
||||
msgstr "После этого туда добавляется содержимое Вашей «основной» адресной книги (userhosts.txt)."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:122
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:127
|
||||
msgid "If configured, the router addressbook is now written to the \"published\" addressbook, which will be publicly available if you are running an eepsite."
|
||||
msgstr "Если разрешена публикация, то «маршрутизаторная» адресная книга копируется в «публикуемую» адресную книгу. «Публикуемая» адресная книга доступна публично, если у Вас запущен Ваш I2P-сайт."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:124
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:129
|
||||
msgid "The router also uses a private addressbook (privatehosts.txt, not shown in the picture), which is not merged or published."
|
||||
msgstr "Маршрутизатор также использует приватную адресную книгу (privatehosts.txt, на иллюстрации не показано), которая никуда не копируется и не публикуется."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:126
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:131
|
||||
msgid "Hosts in the private addressbook can be accessed by you but their addresses are never distributed to others."
|
||||
msgstr "Таким образом Вы можете пользоваться адресами из этой адресной книги, не раскрывая другим её содержимое."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:128
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:133
|
||||
msgid "The private addressbook can also be used for aliases of hosts in your other addressbooks."
|
||||
msgstr "Приватную адресную книгу также удобно иcпользовать для хранения альтернативных/коротких адресов."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:127
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:132
|
||||
msgid "The subscription file contains a list of i2p URLs."
|
||||
msgstr "Файл подписок содержит список i2p URL."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:129
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:134
|
||||
msgid "The addressbook application regularly checks this list for new eepsites."
|
||||
msgstr "Адресная книга периодически проверяет этот список на наличие новых адресов I2P-сайтов."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:131
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:136
|
||||
msgid "Those URLs refer to published hosts.txt files."
|
||||
msgstr "Каждый URL указывает на опубликованный hosts.txt файл."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:133
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:138
|
||||
msgid "The default subscription is the hosts.txt from www.i2p2.i2p, which is updated infrequently."
|
||||
msgstr "По умолчанию в списке задана только ссылка на hosts.txt с www.i2p2.i2p, который обновляется очень редко."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:135
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:140
|
||||
msgid "So it is a good idea to add additional subscriptions to sites that have the latest addresses."
|
||||
msgstr "Поэтому не помешает дополнительно подписаться на hosts.txt с более часто обновляемых сайтов."
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:137
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:142
|
||||
msgid "See the FAQ for a list of subscription URLs."
|
||||
msgstr "В соответствующем разделе FAQ можно найти несколько таких адресов."
|
||||
|
||||
#~ msgid "Delete"
|
||||
#~ msgstr "Удалить"
|
||||
|
||||
|
@ -8,8 +8,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P susidns\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2010-03-05 12:44+0000\n"
|
||||
"PO-Revision-Date: 2010-03-05 21:37+0800\n"
|
||||
"POT-Creation-Date: 2010-05-29 02:35+0000\n"
|
||||
"PO-Revision-Date: 2010-05-29 12:51+0800\n"
|
||||
"Last-Translator: walking <walking@mail.i2p>\n"
|
||||
"Language-Team: foo <foo@bar>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -17,79 +17,109 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Poedit-Language: Chinese\n"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:197
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:199
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:193
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:197
|
||||
msgid "Search"
|
||||
msgstr "搜索"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:201
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:203
|
||||
msgid "Search within filtered list"
|
||||
msgstr "在过滤结果中搜索"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:203
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:205
|
||||
msgid "Filtered list"
|
||||
msgstr "过滤结果列表"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:207
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:209
|
||||
msgid "no matches"
|
||||
msgstr "无匹配项目"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:210
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:212
|
||||
msgid "Addressbook"
|
||||
msgstr "地址簿"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:212
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:214
|
||||
msgid "contains no entries"
|
||||
msgstr "包含 0 个项目"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:214
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:216
|
||||
msgid "contains 1 entry"
|
||||
msgstr "包含 1 个项目"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:216
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:218
|
||||
#, java-format
|
||||
msgid "contains {0} entries"
|
||||
msgstr "包含 {0} 个项目"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:226
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:228
|
||||
#, java-format
|
||||
msgid "Showing {0} of {1}"
|
||||
msgstr "显示 {0} 个项目共 {1}"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:257
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:364
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:259
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:368
|
||||
msgid "Add"
|
||||
msgstr "添加"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:261
|
||||
msgid "Destination added."
|
||||
msgstr "目标已添加"
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:259
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:264
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:366
|
||||
msgid "Replace"
|
||||
msgstr "替换"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:263
|
||||
#, java-format
|
||||
msgid "Host name {0} is already in addressbook, unchanged."
|
||||
msgstr "主机名称{0}已存在于地址簿中,本次操作未更新。"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:265
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:312
|
||||
msgid "Delete"
|
||||
msgstr "删除"
|
||||
#, java-format
|
||||
msgid "Host name {0} is already in addressbook with a different destination. Click \"Replace\" to overwrite."
|
||||
msgstr "域名{0}已以不同目标密钥的存在于地址簿中,点击“替换”进行覆盖"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:275
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:277
|
||||
#, java-format
|
||||
msgid "Destination added for {0}."
|
||||
msgstr "目标密钥已添加为{0}。"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:279
|
||||
#, java-format
|
||||
msgid "Destination changed for {0}."
|
||||
msgstr "目标已更新为{0}。"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:284
|
||||
msgid "Invalid Base 64 destination."
|
||||
msgstr "无效的Base64目标密钥"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:288
|
||||
msgid "Please enter a host name and destination"
|
||||
msgstr "请输入主机名称与目标"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:292
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:314
|
||||
msgid "Delete Selected"
|
||||
msgstr "删除选中项"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:302
|
||||
#, java-format
|
||||
msgid "Destination {0} deleted."
|
||||
msgstr "目标 {0} 已删除"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:277
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:304
|
||||
#, java-format
|
||||
msgid "{0} destinations deleted."
|
||||
msgstr "{0} 个目标已删除"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:283
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:310
|
||||
msgid "Addressbook saved."
|
||||
msgstr "地址簿已保存"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:286
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:313
|
||||
msgid "ERROR: Could not write addressbook file."
|
||||
msgstr "错误:无法写入地址簿文件"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:291
|
||||
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:318
|
||||
#: ../src/java/src/i2p/susi/dns/ConfigBean.java:148
|
||||
#: ../src/java/src/i2p/susi/dns/SubscriptionsBean.java:150
|
||||
msgid "Invalid form submission, probably because you used the \"back\" or \"reload\" button on your browser. Please resubmit."
|
||||
@ -97,8 +127,8 @@ msgstr "提交数据无效,可能的原因是您使用了浏览器中的“前
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/ConfigBean.java:139
|
||||
#: ../src/java/src/i2p/susi/dns/SubscriptionsBean.java:129
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:128
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:128
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:130
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:130
|
||||
msgid "Save"
|
||||
msgstr "保存"
|
||||
|
||||
@ -108,8 +138,8 @@ msgstr "配置已保存"
|
||||
|
||||
#: ../src/java/src/i2p/susi/dns/ConfigBean.java:142
|
||||
#: ../src/java/src/i2p/susi/dns/SubscriptionsBean.java:144
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:130
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:130
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:128
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:128
|
||||
msgid "Reload"
|
||||
msgstr "刷新"
|
||||
|
||||
@ -213,7 +243,7 @@ msgid "Name"
|
||||
msgstr "名称"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:226
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:360
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:362
|
||||
msgid "Destination"
|
||||
msgstr "目标"
|
||||
|
||||
@ -225,15 +255,19 @@ msgstr "标记为删除"
|
||||
msgid "address helper link"
|
||||
msgstr "地址簿助手链接"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:343
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:312
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:345
|
||||
msgid "This addressbook is empty."
|
||||
msgstr "此地址簿为空"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:354
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:356
|
||||
msgid "Add new destination"
|
||||
msgstr "添加新目标"
|
||||
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:356
|
||||
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:358
|
||||
msgid "Hostname"
|
||||
msgstr "主机名称"
|
||||
|
||||
@ -393,3 +427,6 @@ msgstr "所以订阅一些网站的最新地址簿是个不错的主意。"
|
||||
msgid "See the FAQ for a list of subscription URLs."
|
||||
msgstr "其他来源的订阅链接参见I2P站点的 <a href=\"http://www.i2p2.i2p/faq_zh.html\">FAQ</a>"
|
||||
|
||||
#~ msgid "Delete"
|
||||
#~ msgstr "删除"
|
||||
|
||||
|
@ -51,7 +51,7 @@ do
|
||||
# To start a new translation, copy the header from an old translation to the new .po file,
|
||||
# then ant distclean updater.
|
||||
find $JPATHS -name *.java > $TMPFILE
|
||||
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 \
|
||||
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 --add-comments\
|
||||
--keyword=_ --keyword=_x --keyword=intl._ --keyword=intl.title \
|
||||
-o ${i}t
|
||||
if [ $? -ne 0 ]
|
||||
|
@ -150,3 +150,27 @@ input[type=submit]:hover {
|
||||
|
||||
}
|
||||
|
||||
input[type=reset] {
|
||||
border: 1px outset #999;
|
||||
background: #ddf;
|
||||
color: #001;
|
||||
margin: 5px;
|
||||
font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
|
||||
padding: 1px 2px;
|
||||
text-decoration: none;
|
||||
min-width: 110px;
|
||||
border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
-khtml-border-radius: 4px;
|
||||
-moz-box-shadow: inset 0px 2px 8px 0px #fff;
|
||||
color: #006;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
input[type=reset]:hover {
|
||||
background: #22a;
|
||||
color: #fff;
|
||||
border: 1px solid #f60;
|
||||
opacity: 1.0;
|
||||
-moz-box-shadow: inset 0px 0px 0px 1px #fff;
|
||||
}
|
||||
|
@ -35,7 +35,9 @@ import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
|
||||
public class AddressbookBean
|
||||
{
|
||||
@ -73,7 +75,7 @@ public class AddressbookBean
|
||||
}
|
||||
public boolean isNotEmpty()
|
||||
{
|
||||
return addressbook != null && addressbook.size() > 0;
|
||||
return addressbook != null && !addressbook.isEmpty();
|
||||
}
|
||||
public AddressbookBean()
|
||||
{
|
||||
@ -89,7 +91,7 @@ public class AddressbookBean
|
||||
{
|
||||
long currentTime = System.currentTimeMillis();
|
||||
|
||||
if( properties.size() > 0 && currentTime - configLastLoaded < 10000 )
|
||||
if( !properties.isEmpty() && currentTime - configLastLoaded < 10000 )
|
||||
return;
|
||||
|
||||
FileInputStream fis = null;
|
||||
@ -254,15 +256,40 @@ public class AddressbookBean
|
||||
boolean changed = false;
|
||||
int deleted = 0;
|
||||
String name = null;
|
||||
if (action.equals(_("Add"))) {
|
||||
if (action.equals(_("Add")) || action.equals(_("Replace"))) {
|
||||
if( addressbook != null && hostname != null && destination != null ) {
|
||||
addressbook.put( hostname, destination );
|
||||
changed = true;
|
||||
message = _("Destination added.");
|
||||
// clear search when adding
|
||||
search = null;
|
||||
String oldDest = (String) addressbook.get(hostname);
|
||||
if (destination.equals(oldDest)) {
|
||||
message = _("Host name {0} is already in addressbook, unchanged.", hostname);
|
||||
} else if (oldDest != null && !action.equals(_("Replace"))) {
|
||||
message = _("Host name {0} is already in addressbook with a different destination. Click \"Replace\" to overwrite.", hostname);
|
||||
} else {
|
||||
boolean valid = true;
|
||||
try {
|
||||
Destination dest = new Destination(destination);
|
||||
} catch (DataFormatException dfe) {
|
||||
valid = false;
|
||||
}
|
||||
if (valid) {
|
||||
addressbook.put( hostname, destination );
|
||||
changed = true;
|
||||
if (oldDest == null)
|
||||
message = _("Destination added for {0}.", hostname);
|
||||
else
|
||||
message = _("Destination changed for {0}.", hostname);
|
||||
// clear form
|
||||
hostname = null;
|
||||
destination = null;
|
||||
} else {
|
||||
message = _("Invalid Base 64 destination.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message = _("Please enter a host name and destination");
|
||||
}
|
||||
} else if (action.equals(_("Delete"))) {
|
||||
// clear search when adding
|
||||
search = null;
|
||||
} else if (action.equals(_("Delete Selected"))) {
|
||||
Iterator it = deletionMarks.iterator();
|
||||
while( it.hasNext() ) {
|
||||
name = (String)it.next();
|
||||
@ -340,7 +367,7 @@ public class AddressbookBean
|
||||
return destination;
|
||||
}
|
||||
public void setDestination(String destination) {
|
||||
this.destination = DataHelper.stripHTML(destination); // XSS
|
||||
this.destination = DataHelper.stripHTML(destination).trim(); // XSS
|
||||
}
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
@ -352,7 +379,7 @@ public class AddressbookBean
|
||||
deletionMarks.addLast( name );
|
||||
}
|
||||
public void setHostname(String hostname) {
|
||||
this.hostname = DataHelper.stripHTML(hostname); // XSS
|
||||
this.hostname = DataHelper.stripHTML(hostname).trim(); // XSS
|
||||
}
|
||||
private int getBeginInt() {
|
||||
return Math.max(0, Math.min(entries.length - 1, beginIndex));
|
||||
|
@ -49,7 +49,7 @@ public class SubscriptionsBean
|
||||
{
|
||||
long currentTime = System.currentTimeMillis();
|
||||
|
||||
if( properties.size() > 0 && currentTime - configLastLoaded < 10000 )
|
||||
if( !properties.isEmpty() && currentTime - configLastLoaded < 10000 )
|
||||
return;
|
||||
|
||||
FileInputStream fis = null;
|
||||
|
@ -160,7 +160,9 @@
|
||||
|
||||
<c:if test="${book.master || book.router || book.published || book.private}">
|
||||
<div id="buttons">
|
||||
<p class="buttons"><input type="submit" name="action" value="<%=intl._("Delete")%>" >
|
||||
<p class="buttons">
|
||||
<input type="reset" value="<%=intl._("Cancel")%>" >
|
||||
<input type="submit" name="action" value="<%=intl._("Delete Selected")%>" >
|
||||
</p>
|
||||
</div>
|
||||
</c:if>
|
||||
@ -179,6 +181,7 @@
|
||||
<b><%=intl._("Hostname")%>:</b> <input type="text" name="hostname" value="${book.hostname}" size="20">
|
||||
<b><%=intl._("Destination")%>:</b> <textarea name="destination" rows="1" style="height: 3em;" cols="40" wrap="off" >${book.destination}</textarea><br/>
|
||||
</p><p>
|
||||
<input type="submit" name="action" value="<%=intl._("Replace")%>" >
|
||||
<input type="submit" name="action" value="<%=intl._("Add")%>" >
|
||||
</p>
|
||||
</div>
|
||||
|
@ -69,8 +69,8 @@
|
||||
<textarea name="config" rows="10" cols="80">${cfg.config}</textarea>
|
||||
</div>
|
||||
<div id="buttons">
|
||||
<input type="submit" name="action" value="<%=intl._("Save")%>" >
|
||||
<input type="submit" name="action" value="<%=intl._("Reload")%>" >
|
||||
<input type="submit" name="action" value="<%=intl._("Save")%>" >
|
||||
</div>
|
||||
</form>
|
||||
<div id="help">
|
||||
|
@ -69,8 +69,8 @@
|
||||
<textarea name="content" rows="10" cols="80">${subs.content}</textarea>
|
||||
</div>
|
||||
<div id="buttons">
|
||||
<input type="submit" name="action" value="<%=intl._("Save")%>" >
|
||||
<input type="submit" name="action" value="<%=intl._("Reload")%>" >
|
||||
<input type="submit" name="action" value="<%=intl._("Save")%>" >
|
||||
</div>
|
||||
</form>
|
||||
<div id="help">
|
||||
|
@ -1411,7 +1411,7 @@ public class WebMail extends HttpServlet
|
||||
if( bccToSelf != null && bccToSelf.compareTo( "1" ) == 0 )
|
||||
recipients.add( sender );
|
||||
|
||||
if( recipients.size() == 0 ) {
|
||||
if( recipients.isEmpty() ) {
|
||||
ok = false;
|
||||
sessionObject.error += "No recipients found.<br>";
|
||||
}
|
||||
@ -1442,7 +1442,7 @@ public class WebMail extends HttpServlet
|
||||
}
|
||||
String boundary = "_="+(int)(Math.random()*Integer.MAX_VALUE)+""+(int)(Math.random()*Integer.MAX_VALUE);
|
||||
boolean multipart = false;
|
||||
if( sessionObject.attachments != null && sessionObject.attachments.size() > 0 ) {
|
||||
if( sessionObject.attachments != null && !sessionObject.attachments.isEmpty() ) {
|
||||
multipart = true;
|
||||
body.append( "\r\nMIME-Version: 1.0\r\nContent-type: multipart/mixed; boundary=\"" + boundary + "\"\r\n\r\n" );
|
||||
}
|
||||
@ -1515,7 +1515,7 @@ public class WebMail extends HttpServlet
|
||||
{
|
||||
out.println( button( SEND, "Send" ) +
|
||||
button( CANCEL, "Cancel" ) + spacer +
|
||||
(sessionObject.attachments != null && sessionObject.attachments.size() > 0 ? button( DELETE_ATTACHMENT, "Delete Attachment" ) : button2( DELETE_ATTACHMENT, "Delete Attachment" ) ) + spacer +
|
||||
(sessionObject.attachments != null && (!sessionObject.attachments.isEmpty()) ? button( DELETE_ATTACHMENT, "Delete Attachment" ) : button2( DELETE_ATTACHMENT, "Delete Attachment" ) ) + spacer +
|
||||
button( RELOAD, "Reload Config" ) + spacer +
|
||||
button( LOGOUT, "Logout" ) );
|
||||
|
||||
@ -1550,7 +1550,7 @@ public class WebMail extends HttpServlet
|
||||
"<tr><td colspan=\"2\" align=\"center\"><hr></td></tr>\n" +
|
||||
"<tr><td align=\"right\">New Attachment:</td><td align=\"left\"><input type=\"file\" size=\"50%\" name=\"" + NEW_FILENAME + "\" value=\"\"><input type=\"submit\" name=\"" + NEW_UPLOAD + "\" value=\"Upload File\"></td></tr>" );
|
||||
|
||||
if( sessionObject.attachments != null && sessionObject.attachments.size() > 0 ) {
|
||||
if( sessionObject.attachments != null && !sessionObject.attachments.isEmpty() ) {
|
||||
boolean wroteHeader = false;
|
||||
for( Iterator it = sessionObject.attachments.iterator(); it.hasNext(); ) {
|
||||
if( !wroteHeader ) {
|
||||
|
200
build.xml
@ -255,12 +255,11 @@
|
||||
<defaultexcludes default="true"/>
|
||||
</target>
|
||||
|
||||
<!-- A few reeleases only, then back to updater. First release was 0.7.11 -->
|
||||
<target name="pkg" depends="distclean, updaterWithJettyFixes, preppkg, installer" />
|
||||
<target name="pkg" depends="distclean, updater, preppkg, installer" />
|
||||
|
||||
<target name="pkgclean" depends="deletepkg-temp">
|
||||
<delete>
|
||||
<fileset dir="." includes="i2p.tar.bz2 install.jar i2pupdate.zip" />
|
||||
<fileset dir="." includes="i2p.tar.bz2 install.jar i2pupdate.zip i2pupdate200.zip" />
|
||||
</delete>
|
||||
</target>
|
||||
|
||||
@ -277,16 +276,6 @@
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<target name="preppkg-windows-only" depends="preppkg-windows">
|
||||
<!-- rip the non-windows stuff out of jbigi.jar -->
|
||||
<mkdir dir="tmpextract" />
|
||||
<unjar src="build/jbigi.jar" dest="tmpextract/" />
|
||||
<jar destfile="pkg-temp/lib/jbigi.jar" >
|
||||
<fileset dir="tmpextract/" includes="*windows*" />
|
||||
</jar>
|
||||
<delete dir="tmpextract/" />
|
||||
</target>
|
||||
|
||||
<target name="preppkg-windows" depends="preppkg-base, buildexe">
|
||||
<copy file="i2p.exe" todir="pkg-temp/" failonerror="false" />
|
||||
<copy file="apps/systray/java/lib/systray4j.dll" todir="pkg-temp/lib" />
|
||||
@ -330,6 +319,10 @@
|
||||
</target>
|
||||
|
||||
<target name="preppkg-base" depends="build, preplicenses, prepconsoleDocs">
|
||||
<!-- if updater200 was run previously, it left *.pack files in pkg-temp -->
|
||||
<delete>
|
||||
<fileset dir="pkg-temp" includes="**/*.jar.pack **/*.war.pack" />
|
||||
</delete>
|
||||
<copy file="build/i2p.jar" todir="pkg-temp/lib/" />
|
||||
<copy file="build/i2ptunnel.jar" todir="pkg-temp/lib/" />
|
||||
<copy file="build/jasper-compiler.jar" todir="pkg-temp/lib/" />
|
||||
@ -468,7 +461,8 @@
|
||||
<zip destfile="docs.zip" basedir="pkg-temp" whenempty="fail" />
|
||||
</target>
|
||||
|
||||
<target name="updater200" depends="prepupdate, preplicenses, pack200, zipit" />
|
||||
<target name="updater200" depends="prepupdate, preplicenses, pack200, zipit200" />
|
||||
<target name="updater200WithJettyFixes" depends="prepjupdatefixes, preplicenses, pack200, zipit200" />
|
||||
<target name="updater" depends="prepupdate, preplicenses, zipit" />
|
||||
<target name="updaterWithGeoIP" depends="prepupdate, prepgeoupdate, preplicenses, zipit" />
|
||||
<target name="updaterWithJetty" depends="prepjupdate, preplicenses, zipit" />
|
||||
@ -483,12 +477,30 @@
|
||||
<tar destfile="i2pupdate.tbz" basedir="pkg-temp" compression="bzip2" />
|
||||
-->
|
||||
</target>
|
||||
<target name="zipit200">
|
||||
<zip destfile="i2pupdate200.zip" basedir="pkg-temp" whenempty="fail" />
|
||||
</target>
|
||||
|
||||
<target name="pack200">
|
||||
<exec executable="sh" failifexecutionfails="true">
|
||||
<!-- *nix here -->
|
||||
<exec executable="sh" osfamily="unix" failonerror="true">
|
||||
<arg value="-c" />
|
||||
<arg value="for i in pkg-temp/lib/*.jar pkg-temp/webapps/*war; do echo pack200 $i; mv $i $i.jar; pack200 -g $i.pack $i.jar; rm -f $i.jar; done" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="mac" failonerror="true">
|
||||
<arg value="-c" />
|
||||
<!-- pack200 will only pack to a .pack file, and only from a .jar file, so we put another .jar on the end -->
|
||||
<arg value="for i in pkg-temp/lib/*.jar pkg-temp/webapps/*war; do echo pack200 $i; mv $i $i.jar; pack200 -g $i.pack $i.jar; rm -f $i.jar; done" />
|
||||
</exec>
|
||||
<!-- windoz here : i admit, i hate escaped symbols in xml, indeed = =! -->
|
||||
<exec executable="cmd" osfamily="windows" failonerror="true">
|
||||
<arg value="/c" />
|
||||
<arg value="for %i in (pkg-temp\webapps\*.war) do move %i %i.jar && pack200 -g pkg-temp\webapps\%~ni.war.pack %i.jar && del %i.jar" />
|
||||
</exec>
|
||||
<exec executable="cmd" osfamily="windows" failonerror="true">
|
||||
<arg value="/c" />
|
||||
<arg value="for %i in (pkg-temp\lib\*.jar) do move %i %i.jar && pack200 -g pkg-temp\lib\%~ni.jar.pack %i.jar && del %i.jar" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="updateTest" depends="prepupdate">
|
||||
@ -533,7 +545,7 @@
|
||||
<copy file="build/routerconsole.war" todir="pkg-temp/webapps/" />
|
||||
<copy file="build/addressbook.war" todir="pkg-temp/webapps/" />
|
||||
<!-- decapitalized the file in 0.7.8 -->
|
||||
<copy file="installer/resources/countries.txt" todir="pkg-temp/geoip/" />
|
||||
<copy file="installer/resources/countries.txt" todir="pkg-temp/geoip/" />
|
||||
</target>
|
||||
<target name="prepupdateRouter" depends="buildrouter, deletepkg-temp">
|
||||
<copy file="build/i2p.jar" todir="pkg-temp/lib/" />
|
||||
@ -569,6 +581,15 @@
|
||||
<jar destfile="./pkg-temp/installer/exec.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Exec.class">
|
||||
<manifest><attribute name="Main-Class" value="net.i2p.util.Exec" /></manifest>
|
||||
</jar>
|
||||
<!--
|
||||
Force 1.5 pack200 output
|
||||
Doesnt work!
|
||||
http://jira.codehaus.org/browse/IZPACK-404
|
||||
http://forums.sun.com/thread.jspa?threadID=773439
|
||||
http://bfo.co.uk/blog/2010/05/13/combining_ant_jar_signatures_and_pack200.html
|
||||
<property name="com.sun.java.util.jar.pack.package.majver" value="150" />
|
||||
<property name="com.sun.java.util.jar.pack.package.minver" value="7" />
|
||||
-->
|
||||
<izpack input="${basedir}/installer/install.xml" output="${basedir}/install.jar" installerType="standard" basedir="${basedir}" />
|
||||
|
||||
<ant target="installerexe" />
|
||||
@ -698,7 +719,10 @@
|
||||
<target name="distcleanWithDesktopgui" depends="distclean">
|
||||
<ant dir="apps/desktopgui" target="build_clean" />
|
||||
</target>
|
||||
<target name="release" depends="pkg">
|
||||
|
||||
<!-- this is the same dependency as pkg, but with updater200 in the middle,
|
||||
since preppkg puts too much stuff in pkg-temp -->
|
||||
<target name="release" depends="distclean, updater, updater200, preppkg, installer" >
|
||||
<echo message="================================================================" />
|
||||
<echo message="Did you update these files?" />
|
||||
<exec executable="ls" failonerror="true">
|
||||
@ -728,6 +752,7 @@
|
||||
<copy file="i2pupdate.zip" tofile="i2pupdate_${release.number}.zip" />
|
||||
<copy file="i2pinstall.exe" tofile="i2pinstall_${release.number}.exe" />
|
||||
<delete file="i2pupdate.sud" failonerror="false" />
|
||||
<delete file="i2pupdate.su2" failonerror="false" />
|
||||
<input message="Enter private signing key file:" addproperty="release.privkey" />
|
||||
<fail message="You must enter a path." >
|
||||
<condition>
|
||||
@ -745,11 +770,6 @@
|
||||
<arg value="${release.privkey}" />
|
||||
<arg value="${release.number}" />
|
||||
</java>
|
||||
<fail message="i2pupdate.sud generation failed!" >
|
||||
<condition>
|
||||
<length file="i2pupdate.sud" when="lt" length="1000000" />
|
||||
</condition>
|
||||
</fail>
|
||||
<echo message="Verify version and VALID signature:" />
|
||||
<java classname="net.i2p.crypto.TrustedUpdate" fork="true" failonerror="true">
|
||||
<classpath>
|
||||
@ -765,6 +785,32 @@
|
||||
<arg value="showversion" />
|
||||
<arg value="i2pupdate.sud" />
|
||||
</java>
|
||||
<!-- now build and verify the packed sud from the packed zip -->
|
||||
<java classname="net.i2p.crypto.TrustedUpdate" fork="true" failonerror="true">
|
||||
<classpath>
|
||||
<pathelement location="build/i2p.jar" />
|
||||
</classpath>
|
||||
<arg value="sign" />
|
||||
<arg value="i2pupdate200.zip" />
|
||||
<arg value="i2pupdate.su2" />
|
||||
<arg value="${release.privkey}" />
|
||||
<arg value="${release.number}" />
|
||||
</java>
|
||||
<echo message="Verify version and VALID signature:" />
|
||||
<java classname="net.i2p.crypto.TrustedUpdate" fork="true" failonerror="true">
|
||||
<classpath>
|
||||
<pathelement location="build/i2p.jar" />
|
||||
</classpath>
|
||||
<arg value="verifysig" />
|
||||
<arg value="i2pupdate.su2" />
|
||||
</java>
|
||||
<java classname="net.i2p.crypto.TrustedUpdate" fork="true" failonerror="true">
|
||||
<classpath>
|
||||
<pathelement location="build/i2p.jar" />
|
||||
</classpath>
|
||||
<arg value="showversion" />
|
||||
<arg value="i2pupdate.su2" />
|
||||
</java>
|
||||
<!-- will this use the monotonerc file in the current workspace? -->
|
||||
<echo message="Checking out fresh copy into ../i2p-${release.number} for tarballing:" />
|
||||
<delete dir="../i2p-${release.number}" />
|
||||
@ -808,6 +854,7 @@
|
||||
<arg value="i2pinstall_${release.number}.exe" />
|
||||
<arg value="i2psource_${release.number}.tar.bz2" />
|
||||
<arg value="i2pupdate_${release.number}.zip" />
|
||||
<arg value="i2pupdate.su2" />
|
||||
<arg value="i2pupdate.sud" />
|
||||
<arg value="i2pinstall_${release.number}.exe.sig" />
|
||||
<arg value="i2psource_${release.number}.tar.bz2.sig" />
|
||||
@ -818,11 +865,13 @@
|
||||
<arg value="i2pinstall_${release.number}.exe" />
|
||||
<arg value="i2psource_${release.number}.tar.bz2" />
|
||||
<arg value="i2pupdate_${release.number}.zip" />
|
||||
<arg value="i2pupdate.su2" />
|
||||
<arg value="i2pupdate.sud" />
|
||||
</exec>
|
||||
<echo message="Don't forget to mtn tag w: i2p-${release.number}" />
|
||||
<echo message="... and mtn cert t:i2p-${release.number} branch i2p.i2p.release" />
|
||||
</target>
|
||||
|
||||
<target name="debian">
|
||||
<!-- binary only -->
|
||||
<echo message="Did you update the version in these files?" />
|
||||
@ -849,4 +898,111 @@
|
||||
<arg value="-I_MTN" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- the following are appened to help build barebone portable version,
|
||||
none of the above is modified for this purpose -->
|
||||
<target name = "pkg-portable-clean">
|
||||
<delete dir="build/" />
|
||||
<delete dir="pkg-temp/" />
|
||||
<delete>
|
||||
<fileset dir="." includes="portable-**.zip**" />
|
||||
</delete>
|
||||
</target>
|
||||
<!-- build a portable archive -->
|
||||
|
||||
<!-- *0* Since we simply pack all files in folder "build/" into our archieve,
|
||||
we need to make sure its small, with NO redundent jars or wars.
|
||||
thus cleaning is required before each build-->
|
||||
<target name="buildSmallOnly" depends="pkg-portable-clean,buildSmall"/>
|
||||
<!-- *1* preparing the jars by OS dependent de-bloating -->
|
||||
<target name="preppkg-portable-win32-jbigi" depends="buildSmallOnly">
|
||||
<!-- rip the non-windows stuff out of jbigi.jar -->
|
||||
<mkdir dir="tmpextract" />
|
||||
<unjar src="build/jbigi.jar" dest="tmpextract/" />
|
||||
<jar destfile="build/jbigi.jar" >
|
||||
<fileset dir="tmpextract/" includes="*windows*" />
|
||||
</jar>
|
||||
<delete dir="tmpextract/" />
|
||||
</target>
|
||||
<target name="preppkg-portable-linux-jbigi" depends="buildSmallOnly">
|
||||
<!-- rip the non-linux stuff out of jbigi.jar -->
|
||||
<mkdir dir="tmpextract" />
|
||||
<unjar src="build/jbigi.jar" dest="tmpextract/" />
|
||||
<jar destfile="build/jbigi.jar" >
|
||||
<fileset dir="tmpextract/" includes="*linux*" />
|
||||
</jar>
|
||||
<delete dir="tmpextract/" />
|
||||
</target>
|
||||
|
||||
<!-- *2* os independent procedure -->
|
||||
<target name="preppkg-portable-basic" >
|
||||
<mkdir dir="pkg-temp" />
|
||||
<!-- non OS dependent configurations only, dont add *nux/win stuff here -->
|
||||
<copy todir="pkg-temp">
|
||||
<fileset dir="installer/resources/portable/configs/" />
|
||||
</copy>
|
||||
<copy file="installer/resources/blocklist.txt" todir="pkg-temp/" />
|
||||
<copy file="installer/resources/hosts.txt" todir="pkg-temp/" />
|
||||
<copy file="installer/resources/readme.license.txt" todir="pkg-temp/" />
|
||||
<mkdir dir="pkg-temp/addressbook" />
|
||||
<copy file="apps/addressbook/subscriptions.txt" todir="pkg-temp/addressbook/" />
|
||||
<copy file="apps/addressbook/myhosts.txt" todir="pkg-temp/addressbook/" />
|
||||
<!-- config.txt is in installer/resources/portable -->
|
||||
<mkdir dir="pkg-temp/docs" />
|
||||
<copy file="installer/resources/initialNews.xml" tofile="pkg-temp/docs/news.xml" overwrite="true" />
|
||||
<copy file="installer/resources/readme/readme.html" tofile="pkg-temp/docs/readme.html" />
|
||||
<copy file="installer/resources/startconsole.html" todir="pkg-temp/docs/" />
|
||||
<copy file="installer/resources/start.ico" todir="pkg-temp/docs/" />
|
||||
<copy file="installer/resources/console.ico" todir="pkg-temp/docs/" />
|
||||
<!-- HTTP Header files, english only,
|
||||
if you need a different lang do it in a sepreate target -->
|
||||
<copy todir="pkg-temp/docs/" >
|
||||
<fileset dir="installer/resources/proxy/" includes="**-header.ht" />
|
||||
</copy>
|
||||
<!-- Theme light only -->
|
||||
<copy todir="pkg-temp/docs/themes/console/light/" overwrite="true" >
|
||||
<fileset dir="installer/resources/themes/console/light/" includes="**.css" />
|
||||
</copy>
|
||||
<!-- @dr.zed where is your CJK fix for the default theme ?? put it here <copy file="installer/resources/themes/console/classic/console_big.css" todir="portable/docs/themes/console/light/" / -->
|
||||
<copy todir="pkg-temp/docs/themes/console/images/" >
|
||||
<fileset dir="installer/resources/themes/console/images/" />
|
||||
</copy>
|
||||
<!-- FLAGs for language icon (not for ip)-->
|
||||
<copy todir="pkg-temp/docs/icons/flags" >
|
||||
<fileset dir="installer/resources/icons/flags/" includes="cn.png,de.png,fr.png,nl.png,ru.png,se.png,us.png" />
|
||||
</copy>
|
||||
<mkdir dir="pkg-temp/webapps" />
|
||||
<copy todir="pkg-temp/webapps/">
|
||||
<fileset dir="build/" includes="**.war" />
|
||||
</copy>
|
||||
<mkdir dir="pkg-temp/lib" />
|
||||
<copy todir ="pkg-temp/lib/" >
|
||||
<fileset dir="build/" includes="**.jar" />
|
||||
</copy>
|
||||
<!-- 3rd party jars from apps/ -->
|
||||
<!-- jrobin - without jobin , you lost graph and get a lot error entry in logs -->
|
||||
<copy file="apps/jrobin/jrobin-1.4.0.jar" tofile="pkg-temp/lib/jrobin.jar" />
|
||||
</target>
|
||||
<!-- *3* os dependent procedure/commands -->
|
||||
<target name = "preppkg-portable-win32" depends="preppkg-portable-win32-jbigi,preppkg-portable-basic">
|
||||
<!-- systray4.j - why do we need trayicons for portable version ? dependency hardcoded in console -->
|
||||
<copy file="apps/systray/java/lib/systray4j.dll" todir="pkg-temp/lib" />
|
||||
<copy file="apps/systray/java/lib/systray4j.jar" todir="pkg-temp/lib" />
|
||||
<!--wrapper - dont even think about it. i2p cosumes appreantly more mem without it on win32-->
|
||||
<copy file="installer/lib/wrapper/win32/wrapper.dll" todir="pkg-temp/lib" />
|
||||
<copy file="installer/lib/wrapper/win32/wrapper.jar" todir="pkg-temp/lib" />
|
||||
<copy file="installer/lib/wrapper/win32/I2Psvc.exe" tofile="pkg-temp/i2psvc.ex_" />
|
||||
<!-- copy the unpack/start batchfiles -->
|
||||
<copy todir="pkg-temp">
|
||||
<fileset dir="installer/resources/portable/win32/" />
|
||||
</copy>
|
||||
</target>
|
||||
<!-- *4* command for windows package -->
|
||||
<target name = "pkg-portable-win32" depends="preppkg-portable-win32, pack200" >
|
||||
<!-- i need the portable\ folder in .zip so basedir is set to . -->
|
||||
<move file="pkg-temp" tofile="portable"/>
|
||||
<zip destfile="portable-win32.zip" basedir="." level="9" includes="portable\**" />
|
||||
<checksum file="portable-win32.zip" forceOverwrite="yes"/>
|
||||
<move file="portable" tofile="pkg-temp"/>
|
||||
</target>
|
||||
</project>
|
||||
|