Compare commits

...

114 Commits

Author SHA1 Message Date
zzz
746bad3c30 remove jetty fixes from release target 2010-06-07 12:57:10 +00:00
zzz
5bbd61b75c 0.7.14 2010-06-07 12:18:43 +00:00
zzz
27eb7e46d0 tweak 2 2010-06-06 20:38:19 +00:00
zzz
c20bef3731 tweaks after review 2010-06-06 20:36:54 +00:00
dev
fc60768a66 prevent an NPE in case the connection is gone already(but that should not happen?) 2010-06-06 15:49:29 +00:00
zzz
2024fb1b65 * Netdb:
- Use new receivedAsReply flag in LeaseSet to mark
        those received as response to a query
      - Mark which methods in FloodfillPeerSelector may return
        our own hash
      - Redefine selectNearest() so it may return our own hash,
        so it can be used for closeness measurement
      - Redefine findNearestRouters() to return Hashes
        instead of RouterInfos
      - Fix LeaseSet response decisions for floodfills, based
        on partial keyspace and closeness measurements
      - List only count of published leasesets in netdb
2010-06-05 01:10:56 +00:00
zzz
617ca79b8f conn throttler fix when only total configured 2010-06-05 01:07:29 +00:00
zzz
7bfb5b1bf4 readme cleanup 2010-06-04 12:18:57 +00:00
8d73529fa4 po revise 2010-06-04 03:46:52 +00:00
dev
a19d04d3ba merge of '4002ce96746459cd6ab6f91f16795bdbe3165644'
and 'db4aaff4718328041f29e6166333139f845406cd'
2010-06-03 23:13:35 +00:00
dev
a9c7748a52 minor code style updates to ntcp EventPumper 2010-06-03 23:13:13 +00:00
zzz
41e4e952b7 * Update: Fix multiple updates after manually
starting update - caused by refreshing summary bar
      (thx 'backup'!)
2010-06-03 16:53:55 +00:00
zzz
c0b0b5e4c5 Add min delay after startup before fetching news 2010-06-03 16:51:37 +00:00
e424479e7e peers.jsp:
Show definitions panel if any transport is enabled (was: only for UDP). 
  Use div.wideload for the whole page (was: only for transports and broken if only one of them enabled).
2010-06-03 08:35:14 +00:00
a8804f3093 merge of 'bdef8183da2c97dd55e2c2fad915537640e0f404'
and 'f908793c77bb4bd3d5fa3dd71bed704f32404fd0'
2010-06-03 07:21:52 +00:00
6479a24bb7 merge of '0bd9edccbe59dc0c8dddee2b45cde1af0f8551f2'
and '779311c9e2df158049abc2e0f56e4e9fcb071142'
2010-06-03 06:27:12 +00:00
8b372ad306 Fixed build.sh
jbigi's build.sh had a number of failed assumptions as per where I2P and JAVA_HOME were which needed to be removed and a warning put in their place. A better solution would be to have some way to search for JAVA_HOME and I2P in common locations, but at least this solution works if you do it manually:

I2P=~i2p JAVA_HOME=/usr/lib/jvm/java-6-sun-1.6.0.20 sh build.sh dynamic

thank zzz for prodding me to do this
2010-06-03 03:35:46 +00:00
86791a2f1b Russian translation updated (descriptions for the stats that are graphed by default) 2010-06-03 03:22:35 +00:00
zzz
7cf0aad388 * UDP: Fix a bug from a blank i2np.udp.host config
causing frequent RouterInfo updates and incorrect
      addition of introducers, caused by config.jsp handling
2010-06-02 18:20:13 +00:00
zzz
c5ea51beec * graphs.jsp: Tag some more 2010-06-02 18:16:43 +00:00
zzz
7cc8e51d73 * Update: Change default update URLs to .su2 for pack200 2010-06-02 18:13:45 +00:00
zzz
75ba58d68c * Translation: Set xgettext add-comments option 2010-06-02 18:12:46 +00:00
zzz
cd35b219db * i2psnark:
- More listing fixes (more thanks to 'backup')
      - Start end game a little sooner
2010-06-01 22:19:10 +00:00
zzz
4a863f8ce7 comment 2010-06-01 14:02:21 +00:00
zzz
24264548a6 * Installer: Disable pack200 in updater again, doesn't work
on Java 1.5
    * Remove jetty from updater - it's been in for a few
      releases, and i2psnark now has its own listHTML method
2010-06-01 14:01:21 +00:00
zzz
f9e4b1a56b snark css tweak 2010-06-01 13:57:39 +00:00
zzz
13b54b864e * i2psnark:
- More listing fixes
      - Revert choker change
      thx 'backup' !
2010-06-01 13:56:53 +00:00
05d45fe945 po update 2010-05-29 07:18:56 +00:00
2781f6035a Russian translation updated for ngettext (plural forms) strings 2010-05-27 17:59:57 +00:00
zzz
dc3378d084 * Translate: Add GNU ngettext (plurals) support 2010-05-27 00:38:32 +00:00
zzz
9132e94143 * i2psnark: Listing fixes and cleanups; icons on front page; tweak bw choker again 2010-05-26 14:28:46 +00:00
b61e2aa73c Russian translation updated 2010-05-26 04:37:28 +00:00
7fdbae3b0f Tagged "bytes remaining" 2010-05-26 04:37:03 +00:00
dev
4dc6fc3b5d merge of '20f5a25a77de641ddf49c4d47d4ede923b59bfa3'
and '7dfc6bc466e7b6ee3212af949a08c51d4e3dd3db'
2010-05-25 19:08:31 +00:00
dev
618275b1f9 merge of '13c351b9c26b147632b40df8c0e8d9ca7d2d4485'
and 'b281a23e2f1719a388abed362ec3653f63e6769b'
2010-05-25 19:07:13 +00:00
dev
7a1111d845 updated history 2010-05-25 19:06:15 +00:00
zzz
3af356840e -11 2010-05-25 18:31:09 +00:00
zzz
911a278926 snark listing icons and cleanups 2010-05-25 13:08:34 +00:00
dev
014063700f merge of '82b66240733c560b038d4874d1630bf59f5fbe1a'
and 'd6f8e674646687b5efb03d09b6cdca57c6bd8f50'
2010-05-23 19:52:49 +00:00
dev
f7c0db0454 -10 2010-05-23 19:52:06 +00:00
zzz
a534d25d82 -10 2010-05-23 19:18:50 +00:00
zzz
bcf3e4a2d3 merge of '200dbdfc1dba31eb7abc6bb3403ac77cc9072c94'
and '56425d32b819bb74fe3abb999e7e3763814533ac'
2010-05-23 19:17:50 +00:00
dev
0cdfbd9803 merge of '01deefdd2f5a2b8f21fd3e97d1a6bd0dd66fecab'
and '1a75d8e703883bde472616a9def0b27bb64b7815'
2010-05-23 18:08:26 +00:00
dev
a3e5654d86 merge of '03e8a3d066ce112bb4ddaa98c0387dfefde94a0e'
and '751ff97c62634ee13a8f8baf3d7947e373d5368a'
2010-05-23 17:05:15 +00:00
dev
2f9364db2b fixed a major bug in the datagram dissector, improved performance a little bit and added a utility method to get the already calculated hash of the payload 2010-05-23 17:04:37 +00:00
zzz
5d7c9ebf82 * i2psnark:
- Choke slower when at bandwidth limit
      - Fix completion % for small files
      - Use Random from context
2010-05-23 16:18:10 +00:00
zzz
48da98d0e4 * NewsFetcher:
- Add backup URL
      - Change to 0 retries (was 2)
2010-05-23 16:15:37 +00:00
dev
55e994ac3c merge of '751ff97c62634ee13a8f8baf3d7947e373d5368a'
and 'ddc06f282f1b88e164c208509d818e3ed701143e'
2010-05-23 16:06:20 +00:00
dev
6d46a21f9f implemented WEBIRC support in the I2PTunnel IRC server 2010-05-23 16:04:22 +00:00
fdc83484fd NTCP bind interface
Adding support for binding to a specific IP in the NTCP configuration. Uses new config option i2np.ntcp.bindAddress.
2010-05-22 16:50:39 +00:00
zzz
6786817fff -9 2010-05-21 17:34:30 +00:00
zzz
b77cd0db15 show completion status in listing 2010-05-21 15:07:34 +00:00
zzz
20bef76878 * i2psnark:
- Spiff up dir listings
      - Urlify some messages
      - Only go into end game at the end
      - Bye Bart Bye
2010-05-21 04:38:49 +00:00
zzz
7a30490482 more validation 2010-05-19 18:55:53 +00:00
zzz
3bc2e469cc remove unnecessaary initializers from constructors 2010-05-16 18:08:24 +00:00
zzz
d770d3c6da border-radius thx dr. 2010-05-16 17:11:40 +00:00
zzz
339a001592 never used 2010-05-16 13:16:05 +00:00
zzz
ace57a96a9 translate log priorities 2010-05-15 15:42:20 +00:00
zzz
2c26b8d422 * Hash: Move caching XOR methods only used by KBucket into netdb 2010-05-15 14:21:31 +00:00
zzz
e1eafa2394 * Eepsite: Set no-cache in redirecting page 2010-05-15 14:19:41 +00:00
zzz
39cb51c9eb snark css tweaks 2010-05-15 14:18:54 +00:00
zzz
fa5016ab04 javadoc fix 2010-05-15 14:17:54 +00:00
zzz
b134ef1a74 * Console:
- Tag text in graphs
      - Move SummaryRenderer to its own file
2010-05-15 14:17:17 +00:00
zzz
234dff888d Try to prevent ZipErrors after plugin update 2010-05-13 17:04:16 +00:00
zzz
a08c15a3ee leaseset debug tweak 2010-05-13 17:02:32 +00:00
zzz
cfa894e7b6 peer id tweak 2010-05-13 17:01:30 +00:00
zzz
d6c8e64575 throttle fix 2010-05-10 16:21:20 +00:00
zzz
dc91580e30 fixes from DataHelper.eq() deprecation 2010-05-10 15:58:53 +00:00
zzz
7ec1dd7a98 netdb.jsp leaseset debug 2010-05-10 15:22:10 +00:00
zzz
82f3f7506c * NetDB:
- Handle old and duplicate stores more efficiently
      - Have DataStore put() return success
2010-05-10 15:00:13 +00:00
zzz
e26df1c26b * LeaseSet: Add receivedAsReply() methods in preparation for
some netdb changes
2010-05-10 14:52:53 +00:00
zzz
aea77cf225 * NetDB: Move getDistance() to its own class 2010-05-10 14:50:55 +00:00
zzz
a1e3ef9c5c cleanup fail output on peers.jsp 2010-05-10 14:26:19 +00:00
zzz
7aece71342 cleanup 2010-05-10 14:24:47 +00:00
zzz
bdbde54f04 * Router: Add router.forceBandwidthClass advanced config for testing 2010-05-10 14:23:25 +00:00
zzz
157e035710 summary bar tweaks 2010-05-10 14:22:37 +00:00
zzz
97d9a3a4e5 show monthly bw estimate 2010-05-10 14:21:48 +00:00
zzz
cb7f111ade * UDP: To help limit connections, don't offer to introduce
when floodfill
2010-05-10 14:20:27 +00:00
zzz
35f670706a * TunnelPoolManager: Concurrent 2010-05-10 14:18:15 +00:00
zzz
3fac888fe5 * DataHelper: Deprecate inefficient eq() methods 2010-05-10 14:17:05 +00:00
zzz
d843646b4f * Streaming: Add support for connection throttling 2010-05-10 14:15:31 +00:00
zzz
c2b73d9fb5 * i2psnark:
- Add tunnel config dropdowns
      - Comment out old proxy stuff
2010-05-10 14:13:55 +00:00
9da95b8165 PluginStarter: 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.
2010-05-10 07:27:34 +00:00
zzz
5bcd8efe14 2 transport test classes out 2010-05-06 13:21:30 +00:00
027a1d748d merge of '19b2cad8459bddf9473031504b0f30aa3aad97e3'
and '5fc11615066ab7c27262a8670b7713405d25424c'
2010-05-06 04:15:34 +00:00
6d6e012c19 adapt to the change in build.xml 2010-05-06 03:59:23 +00:00
zzz
a8db6b007f * Plugins:
- Set classpath for specific client only, not for the whole JVM
      - Use ConfigDir() not AppDir()
2010-05-05 19:34:03 +00:00
zzz
f3576e54c6 throw IllegalStateException rather than NPE if no context 2010-05-05 18:44:12 +00:00
zzz
0325f6c4d2 more isEmpty and a static 2010-05-05 18:43:33 +00:00
zzz
8225ce063a * Console: Print stack trace if exception on startup 2010-05-05 17:50:28 +00:00
zzz
c2c379c994 * i2psnark: Skip 'the' when sorting snarks 2010-05-05 17:45:52 +00:00
zzz
7344c2af47 * I2PTunnelHTTPClient: Reject 192.168.* 2010-05-05 17:34:24 +00:00
zzz
f484ea8c64 * EepGet: Limit max times to fail completely even if numRetries is higher 2010-05-05 17:27:20 +00:00
zzz
ac790492eb * build.xml: Create packed sud in release 2010-05-05 16:55:00 +00:00
zzz
9ac5fb4890 * RouterInfo: Clean up use of sortStructures() 2010-05-05 16:54:28 +00:00
zzz
2baee7413c * Replace size() <= 0 with isEmpty() everywhere, ditto > 0 -> !isEmpty() 2010-05-05 16:51:54 +00:00
16bec08f09 merge of '03068a89c26b0986a8bf2b6f36cb478f565664eb'
and 'c3c31953c884c3aafb142e05c2dbef2809516d9c'
2010-05-03 16:44:06 +00:00
afb3c76922 - rewrite portable targets
pkg-portable-clean
	preppkg-portable-win32-jbigi
	preppkg-portable-linux-jbigi
	preppkg-portable-basic
	preppkg-portable-win32
	pkg-portable-win32
- add windoz support to target pack200
2010-05-03 16:42:45 +00:00
z3d
2f526b35e8 merge of '77299d7d613df0c3d1308d1056facc243ef693bb'
and 'a088711b406a5c062940ebbdd1709aa891283d74'
2010-05-02 16:43:46 +00:00
2dc32aa310 fix name "preppkg-linux-only" 2010-05-02 13:04:40 +00:00
zzz
10e669165a Fix plugin version check bug 2010-05-02 12:19:17 +00:00
zzz
b6cb90d731 * ByteCache:
- Add a per-cache stat
      - Limit each cache based on max memory
      - Disable in UDP MessageReceiver
      - Add clearAll() method to be called when under
        severe memory pressure; call from Router
2010-05-02 12:14:14 +00:00
zzz
949a8901fb comment out mains 2010-05-02 12:11:20 +00:00
d608f450af return what is taken a way ;) 2010-05-02 11:30:31 +00:00
z3d
e0a1341901 Adjust dimensions of installer splash graphic: was 171x275, now 171x270. 2010-04-30 10:26:54 +00:00
z3d
2cfb03f17d New installer splash graphic. 2010-04-30 09:30:01 +00:00
z3d
4dd0f51da4 merge of '6b54027d89ac66a5b395118365de13f5ab61bcaf'
and 'b915692e91863a7122937dbd0bad366bf38a7dfc'
2010-04-30 09:26:43 +00:00
dev
d65a3e54a2 update checklist 2010-04-28 17:53:18 +00:00
c212eacf19 - add new target: pkg-portable-win32 (must run buildSmall first)
- add configs/win batchfiles to installer/resources/portable

* currently only pkg-portable-win32 on win32 available
need linuxers to write target preppkg-portable-nix/pkg-portable-linux
and enable pkg-portable-win32 on linux (i doubt anyone need it ?)
shell scripts should goto installer/resources/portable/configs/linux/
2010-04-27 15:01:03 +00:00
zzz
46f341d782 peers.jsp: cleanup and tag 2010-04-27 12:55:37 +00:00
zzz
ab4ff5548d fix reseed tips links 2010-04-27 12:54:07 +00:00
zzz
d4713e1e6c every body needs some <body> 2010-04-27 12:53:16 +00:00
zzz
8a3a1466c9 * i2psnark: Serve downloaded files from the servlet rather
than with a file: link
2010-04-27 12:52:17 +00:00
zzz
a5af9dc973 * Jetty: Backport directory listing bugfix from jetty 6 2010-04-27 12:51:14 +00:00
278 changed files with 11623 additions and 7693 deletions

View File

@ -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.

View File

@ -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);

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

View File

@ -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>

View File

@ -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 ]

View File

@ -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);
}
}

View File

@ -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)
{

View File

@ -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()) ||

View File

@ -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);
}

View File

@ -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();

View File

@ -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));
}
/**

View File

@ -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);
}

View File

@ -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.
*/

View File

@ -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

View File

@ -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>&nbsp;</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) + "&hellip;";
@ -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 + "&nbsp;&nbsp;" + peer.toString().substring(5, 9));
out.write(client + "&nbsp;&nbsp;<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("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
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("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
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();

View File

@ -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 пока есть активные торренты"

View File

@ -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列表中将其删除。"

View File

@ -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 ]

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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 {

View File

@ -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 {

File diff suppressed because it is too large Load Diff

View File

@ -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;

View 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,"<","&lt;"),">","&gt;");
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],"<","&lt;"),">","&gt;"));
buf.append("&nbsp;");
buf.append("</TD><TD ALIGN=right>");
buf.append(item.length());
buf.append(" bytes&nbsp;</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();
}
}
}

View File

@ -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);
}
}

View File

@ -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 \

View File

@ -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 + "/";
}

View File

@ -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\">")

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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";

View File

@ -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);

View File

@ -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);

View File

@ -79,19 +79,21 @@ public class GraphHelper extends HelperBase {
+ "&amp;width=" + (3 * _width)
+ "&amp;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"
+ "&amp;periodCount=" + _periodCount
+ "&amp;width=" + _width
+ "&amp;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()
+ "&amp;showEvents=" + _showEvents

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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());

View File

@ -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");

View File

@ -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?");

View File

@ -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/

View File

@ -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);

View File

@ -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>");

View File

@ -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) {

View File

@ -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");

View File

@ -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);
}
}

View File

@ -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>")

View File

@ -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 &amp; 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>");

View File

@ -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());
}
}
}

View File

@ -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);
}
}

View File

@ -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");

View File

@ -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;

View File

@ -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);

View File

@ -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
}
}

View File

@ -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>

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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?

View File

@ -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;
}

View File

@ -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 );

View File

@ -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();
}
}
}

View File

@ -449,7 +449,7 @@ public class Connection {
}
_outboundPackets.notifyAll();
}
if ((acked != null) && (acked.size() > 0) )
if ((acked != null) && (!acked.isEmpty()) )
_ackSinceCongestion = true;
return acked;
}

View File

@ -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;
}

View File

@ -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; }

View File

@ -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,

View File

@ -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");

View File

@ -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)

View File

@ -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 "Удалить"

View File

@ -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 "删除"

View File

@ -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 ]

View File

@ -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;
}

View File

@ -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));

View File

@ -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;

View File

@ -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>

View File

@ -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">

View File

@ -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">

View File

@ -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
View File

@ -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 &amp;&amp; pack200 -g pkg-temp\webapps\%~ni.war.pack %i.jar &amp;&amp; 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 &amp;&amp; pack200 -g pkg-temp\lib\%~ni.jar.pack %i.jar &amp;&amp; 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>

Some files were not shown because too many files have changed in this diff Show More