Compare commits
424 Commits
i2p-0.7.9
...
i2p-0.7.14
| Author | SHA1 | Date | |
|---|---|---|---|
| 746bad3c30 | |||
| 5bbd61b75c | |||
| 27eb7e46d0 | |||
| c20bef3731 | |||
|
|
fc60768a66 | ||
| 2024fb1b65 | |||
| 617ca79b8f | |||
| 7bfb5b1bf4 | |||
|
|
8d73529fa4 | ||
|
|
a19d04d3ba | ||
|
|
a9c7748a52 | ||
| 41e4e952b7 | |||
| c0b0b5e4c5 | |||
|
|
e424479e7e | ||
|
|
a8804f3093 | ||
|
|
6479a24bb7 | ||
|
|
8b372ad306 | ||
|
|
86791a2f1b | ||
| 7cf0aad388 | |||
| c5ea51beec | |||
| 7cc8e51d73 | |||
| 75ba58d68c | |||
| cd35b219db | |||
| 4a863f8ce7 | |||
| 24264548a6 | |||
| f9e4b1a56b | |||
| 13b54b864e | |||
|
|
05d45fe945 | ||
|
|
2781f6035a | ||
| dc3378d084 | |||
| 9132e94143 | |||
|
|
b61e2aa73c | ||
|
|
7fdbae3b0f | ||
|
|
4dc6fc3b5d | ||
|
|
618275b1f9 | ||
|
|
7a1111d845 | ||
| 3af356840e | |||
| 911a278926 | |||
|
|
014063700f | ||
|
|
f7c0db0454 | ||
| a534d25d82 | |||
| bcf3e4a2d3 | |||
|
|
0cdfbd9803 | ||
|
|
a3e5654d86 | ||
|
|
2f9364db2b | ||
| 5d7c9ebf82 | |||
| 48da98d0e4 | |||
|
|
55e994ac3c | ||
|
|
6d46a21f9f | ||
|
|
fdc83484fd | ||
| 6786817fff | |||
| b77cd0db15 | |||
| 20bef76878 | |||
| 7a30490482 | |||
| 3bc2e469cc | |||
| d770d3c6da | |||
| 339a001592 | |||
| ace57a96a9 | |||
| 2c26b8d422 | |||
| e1eafa2394 | |||
| 39cb51c9eb | |||
| fa5016ab04 | |||
| b134ef1a74 | |||
| 234dff888d | |||
| a08c15a3ee | |||
| cfa894e7b6 | |||
| d6c8e64575 | |||
| dc91580e30 | |||
| 7ec1dd7a98 | |||
| 82f3f7506c | |||
| e26df1c26b | |||
| aea77cf225 | |||
| a1e3ef9c5c | |||
| 7aece71342 | |||
| bdbde54f04 | |||
| 157e035710 | |||
| 97d9a3a4e5 | |||
| cb7f111ade | |||
| 35f670706a | |||
| 3fac888fe5 | |||
| d843646b4f | |||
| c2b73d9fb5 | |||
|
|
9da95b8165 | ||
| 5bcd8efe14 | |||
|
|
027a1d748d | ||
|
|
6d6e012c19 | ||
| a8db6b007f | |||
| f3576e54c6 | |||
| 0325f6c4d2 | |||
| 8225ce063a | |||
| c2c379c994 | |||
| 7344c2af47 | |||
| f484ea8c64 | |||
| ac790492eb | |||
| 9ac5fb4890 | |||
| 2baee7413c | |||
|
|
16bec08f09 | ||
|
|
afb3c76922 | ||
|
|
2f526b35e8 | ||
|
|
2dc32aa310 | ||
| 10e669165a | |||
| b6cb90d731 | |||
| 949a8901fb | |||
|
|
d608f450af | ||
|
|
e0a1341901 | ||
|
|
2cfb03f17d | ||
|
|
4dd0f51da4 | ||
|
|
d65a3e54a2 | ||
|
|
c212eacf19 | ||
| 46f341d782 | |||
| ab4ff5548d | |||
| d4713e1e6c | |||
| 8a3a1466c9 | |||
| a5af9dc973 | |||
| 049a083e42 | |||
| 9683a110d6 | |||
| c44698f61a | |||
| 106bccda0e | |||
| b1aafa5aaf | |||
| e2e43cd534 | |||
| 43b4fe8300 | |||
| 7c3e4fd947 | |||
| 9916ef4d3d | |||
| ad4da54bc4 | |||
|
|
2415c5a38b | ||
|
|
ecbc0a2a2d | ||
| 10d37a9be5 | |||
| 590d2e4639 | |||
| 806a07acc5 | |||
| 8258cdd6cf | |||
|
|
2fcee6e87a | ||
| 27587e83c8 | |||
| a0d6741ff5 | |||
| 63562ddd48 | |||
| aac96b15b0 | |||
| 0f502b4229 | |||
| a916f970b1 | |||
| 7f2d0acc3b | |||
| 8b6751f419 | |||
| 70e9cf5838 | |||
| 24020302fd | |||
| d7e2f39d25 | |||
| 89d0d7b266 | |||
| e3c222b5c1 | |||
|
|
a199015bc9 | ||
| 23617f7b30 | |||
| f5f02236df | |||
| ad76bc378c | |||
| 570d8d15af | |||
| e254c5f31a | |||
| 2a92be5946 | |||
| caab860351 | |||
|
|
32861b7ce9 | ||
|
|
a08802c4b6 | ||
|
|
6b51be6fae | ||
| 5b5c975884 | |||
| 605dfec5e7 | |||
| 71aa0cfba7 | |||
| 55e45c4274 | |||
| 8c880b2518 | |||
| c43b16cfbb | |||
| 394903a8f0 | |||
| e31c0636ab | |||
| e9fe80f8e5 | |||
| 7671550a9f | |||
| 83d24fa90d | |||
| 3e2956da3f | |||
| cf3fd01012 | |||
| 319071c73b | |||
| ab8d9bb79b | |||
| 25eaf8cad7 | |||
| c8f97d9c73 | |||
| d3f1fe1c30 | |||
| 5fb01a01af | |||
| 617d1cd648 | |||
| f672193fcf | |||
| d3c490e9d7 | |||
| 2e8fd23f2b | |||
| 3eef403b04 | |||
| f3b78fc82f | |||
| 80654b2732 | |||
| 05597ae914 | |||
| 0f1eb464e8 | |||
| 8745ffd42f | |||
| db99e98658 | |||
| 9f1a663f63 | |||
| db0b3da446 | |||
| 5d22d41201 | |||
| b397de1d54 | |||
|
|
4bda79b263 | ||
|
|
697a9dbd06 | ||
|
|
accaabcfde | ||
| 5026cbdc8d | |||
| 16a14d4ebd | |||
| c151352910 | |||
| 52e2aaa20d | |||
| 9df87ba167 | |||
| b80f70fc54 | |||
| 939cdb019b | |||
| fde36fe238 | |||
|
|
116be93160 | ||
| 40e820cabb | |||
| d79387bd92 | |||
| 05f2a62cbb | |||
| 78a965dc90 | |||
| 5b603d6627 | |||
| e93d2046d3 | |||
| 995871db8a | |||
| 501535f196 | |||
| 91e854e99c | |||
| 9b05d8e774 | |||
| e70793c3bc | |||
| abb2603bea | |||
|
|
c91218be27 | ||
| 9eab44128a | |||
| f98101afa6 | |||
| 4fae7a8cb6 | |||
| 26cf1922db | |||
|
|
c087b0695f | ||
| 16930d2004 | |||
| 33939e7cfb | |||
| e759ef5865 | |||
| 2be1b1ece4 | |||
|
|
1820a29aed | ||
| ee9f85d53c | |||
| afbb1dbe86 | |||
| 9244bd6b0f | |||
| 6bb4403207 | |||
|
|
24ebd503d4 | ||
|
|
285a5eed35 | ||
| 26aebe6a0f | |||
| ca9f174171 | |||
| ffbced22b3 | |||
| 45ca459ceb | |||
|
|
5a539f0619 | ||
|
|
c6cef72cb7 | ||
| 8081d053cc | |||
| efdc8e5df0 | |||
| b4911a2b2f | |||
| 7b70210c9a | |||
| e3353df8bb | |||
| 25285fc059 | |||
| 7720f71e44 | |||
| 1657ac5357 | |||
| 299214aa1d | |||
|
|
5fd4488e08 | ||
| 87fcaf2651 | |||
| f6b9cf6f21 | |||
| eae18e61b7 | |||
| 96735f2543 | |||
|
|
54459d3b5c | ||
| 82444f9e7b | |||
| 3d8365a473 | |||
| e2dc9715d2 | |||
| d4f1230b37 | |||
| 7701693d37 | |||
| b6704fce4e | |||
| 39a68d4a2b | |||
| 94633899d7 | |||
| c45bc1554f | |||
|
|
789c8edc45 | ||
| e0b44f43e3 | |||
| b45069e377 | |||
| c3a156ce4b | |||
| ee5cc099ed | |||
| f189587153 | |||
| a1fb5ef6ed | |||
| 8c2550c39a | |||
| abd96a920f | |||
| 1d3f0fe96c | |||
| 49a6cdbda6 | |||
| 2700028da7 | |||
|
|
51a1564566 | ||
| bd068058c3 | |||
|
|
4591f77928 | ||
| 4f70a7d0fe | |||
|
|
6d67848096 | ||
| c145ed103a | |||
| f265db4037 | |||
| 62308f26bc | |||
|
|
2b4b47eff6 | ||
| 04ae0e2610 | |||
| cada9fae44 | |||
| 949aea951e | |||
| cfc49ab261 | |||
| 0e853a3119 | |||
| a0cad7e8e9 | |||
| 880f1866dc | |||
| 05d22344b5 | |||
| 7212f855d8 | |||
| 54171e4be2 | |||
| a820c01ba5 | |||
| 9d1ae891bb | |||
| 2df7247e83 | |||
| a109ebef28 | |||
| c0135b592d | |||
| b7a0aeea34 | |||
| 66375e25c6 | |||
| 85482a67f5 | |||
| d7e90969d0 | |||
| 9012baf51e | |||
| 3c8355790f | |||
| e9f1da85e4 | |||
| 58adccfd4a | |||
| 040f3e016e | |||
| 505d5f5cae | |||
| 2a99e2a295 | |||
| 49fae646e2 | |||
| f7780b6745 | |||
| 7a59d15e9c | |||
| 9b141bd9dd | |||
|
|
d0d062b6f4 | ||
| 375118fe02 | |||
| 7b59ceb4ae | |||
| 3aebe45a7d | |||
| 7c236c0fa0 | |||
| 7a7e650ca0 | |||
| 7a32f8efd6 | |||
| 2f8b55ceda | |||
| b77be20cc1 | |||
| 101135a99a | |||
| 746c1bd628 | |||
| b0502b1873 | |||
| 6801fc667a | |||
| 794db19b6d | |||
| d4637818be | |||
| 33b7dca782 | |||
| d7015cf2e6 | |||
| 5689fa8512 | |||
| 25e51a945c | |||
| bfd1306a56 | |||
| 839986db22 | |||
| ed443fe0d6 | |||
|
|
390981e10c | ||
| 56b3e6a993 | |||
| f86f2701ff | |||
| 63d3685652 | |||
| 0a93466999 | |||
| 81664bc776 | |||
| 6d60a6f833 | |||
| 4186894a39 | |||
|
|
cfeafacd07 | ||
|
|
9c2edf5c6a | ||
|
|
72396f0f96 | ||
|
|
45388b0d48 | ||
| a8ae36c403 | |||
| 164b39d8df | |||
| ccc95087a1 | |||
| b97f4f8bd7 | |||
| 76c1f47b20 | |||
| ce74e49236 | |||
| 977d39aeb1 | |||
| a821ea2752 | |||
| b97197c0fa | |||
| 474691927a | |||
| 81dcbedd17 | |||
| 4f5cfdee57 | |||
| 2c7725a8e4 | |||
| 0ba6482da5 | |||
| 4c1fd67925 | |||
| 25683cc0de | |||
| 087fd5a909 | |||
| fdfbab850a | |||
| f1c50b7fc3 | |||
| 959bf4a7f4 | |||
| 5dda915467 | |||
| 66d8fd6c2a | |||
|
|
06efe306c3 | ||
|
|
15d1b005f5 | ||
| 7efab75c3c | |||
| a9e4248c93 | |||
| 5338dc5540 | |||
| 09d3dc8e90 | |||
| 3bf95e566c | |||
| 958a5a3c4e | |||
|
|
7b48f6387f | ||
|
|
fcd2cdb136 | ||
|
|
6b59356bc5 | ||
|
|
1c7f098d9b | ||
|
|
103c15f000 | ||
|
|
0fd55f8b07 | ||
|
|
511c1e7b9d | ||
|
|
7df3bde2ce | ||
|
|
25e6b6e6c4 | ||
|
|
cb81f2c9ba | ||
|
|
6c25f0fd16 | ||
|
|
934f3d1814 | ||
|
|
e883fd6b1b | ||
|
|
e3b300e978 | ||
|
|
03a9f69739 | ||
|
|
be2dca8ee7 | ||
|
|
5c595ef289 | ||
|
|
d9534e5f23 | ||
|
|
00fa3806d8 | ||
|
|
6c5ef9acdc | ||
| 2db5914ba0 | |||
| 35a0dafb83 | |||
| 5f12688a90 | |||
| f8d9af871a | |||
| 08a2b4bbf0 | |||
| 27a5793fd0 | |||
|
|
b1151f82b5 | ||
| b6332f8313 | |||
| e036cd4332 | |||
| 174fedc2e6 | |||
| 4803e60db4 | |||
| abb62b93e3 | |||
| 95bb322cd7 | |||
| 670b4033cb | |||
| 715ae13997 | |||
| 77b88ab59d | |||
| 64235bd745 | |||
|
|
0a1960461a | ||
|
|
11249657ac | ||
| 188ac4f730 | |||
|
|
865116b3f4 | ||
| 3cd6520758 | |||
| da1a50bfeb | |||
| 9ec79f50fa | |||
|
|
b15392ea85 | ||
| 76f11859b2 | |||
| 5be21a19db | |||
| 043359dd40 | |||
| 1b95b00b44 |
15
LICENSE.txt
@@ -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:
|
||||
@@ -80,9 +83,10 @@ Public domain except as listed below:
|
||||
|
||||
|
||||
Installer:
|
||||
Launch4j 2.0.RC3:
|
||||
Copyright (C) 2005 Grzegorz Kowal
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
Launch4j 3.0.1:
|
||||
Copyright (c) 2004, 2008 Grzegorz Kowal
|
||||
See licenses/LICENSE-Launch4j.txt (in binary packages)
|
||||
See installer/lib/launch4j/LICENSE.txt (in source packages)
|
||||
The following projects are used by Launch4j...
|
||||
MinGW binutils (http://www.mingw.org/)
|
||||
|
||||
@@ -138,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
|
||||
@@ -175,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.
|
||||
@@ -207,6 +213,9 @@ The following applications and libraries are not used or bundled in
|
||||
binary packages, therefore the licenses are not included in binary
|
||||
distributions. See the source package for the additional license information.
|
||||
|
||||
Admin Manager:
|
||||
Public domain
|
||||
|
||||
Atalk:
|
||||
Public domain
|
||||
|
||||
|
||||
@@ -1,29 +1,38 @@
|
||||
These instructions are for the 1.5 Android SDK.
|
||||
These instructions are for a recent Android SDK (1.6 or later)..
|
||||
Should also still work with a 1.5 SDK.
|
||||
The build file is not compatible with the 1.1 SDK any more.
|
||||
1.6 and 2.0 SDKs are untested.
|
||||
|
||||
#Download the SDK from http://developer.android.com/sdk/index.html
|
||||
#Unzip the android SDK in ../../
|
||||
#So then the android tools will be in ../../android-sdk-linux_x86-1.5_r2/tools/
|
||||
#So then the android tools will be in ../../android-sdk-linux_86/tools/
|
||||
#
|
||||
# now go to the available packages tab, check the box and click refresh,
|
||||
# and download an SDK Platform
|
||||
# Since I2P is configured to run on 1.1 or higher
|
||||
# (API 2) download that one. Otherwise you must change the
|
||||
# target in default.properties from android-2 to andriod-x
|
||||
# where x is the API version.
|
||||
|
||||
# create a file local.properties with the following line:
|
||||
# sdk-location=/path/to/your/android-sdk-linux_x86-1.5_r2
|
||||
# sdk-location=/path/to/your/android-sdk-linux_86
|
||||
|
||||
#then build the android apk file:
|
||||
ant debug
|
||||
|
||||
# Create the android 1.5 virtual device
|
||||
# Create the android 1.1 (API 2) virtual device
|
||||
# (don't make a custom hardware profile)
|
||||
../../android-sdk-linux_x86-1.5_r2/tools/android create avd --name i2p --target 2
|
||||
# A AVD created with the 1.5 SDK will not work with the newer tools
|
||||
../../android-sdk-linux_86/tools/android create avd --name i2p --target 2
|
||||
|
||||
#then run the emulator:
|
||||
../../android-sdk-linux_x86-1.5_r2/tools/emulator -avd i2p &
|
||||
../../android-sdk-linux_86/tools/emulator -avd i2p &
|
||||
|
||||
#then wait a couple minutes until the emulator is up
|
||||
#then install the I2P app
|
||||
ant install
|
||||
|
||||
#then run the debugger
|
||||
../../android-sdk-linux_x86-1.5_r2/tools/ddms &
|
||||
../../android-sdk-linux_86/tools/ddms &
|
||||
|
||||
#to rebuild and reinstall to emulator:
|
||||
ant reinstall
|
||||
|
||||
@@ -113,6 +113,10 @@
|
||||
<delete file="${external-libs-folder}/crypto.jar" />
|
||||
</target>
|
||||
|
||||
<!-- fix for property name change sometime after SDK 1.5 -->
|
||||
<property name="android-jar" value="${android.jar}" />
|
||||
<property name="android-aidl" value="${android.aidl}" />
|
||||
|
||||
<!--
|
||||
================================================================================
|
||||
From here down copied from SDK platforms/android-1.1/templates/android_rules.xml
|
||||
|
||||
@@ -10,7 +10,13 @@ i2np.udp.maxConnections=30
|
||||
# no I2CP
|
||||
i2p.dummyClientFacade=true
|
||||
# for now
|
||||
i2np.ntcp.enable=false
|
||||
#i2np.ntcp.enable=false
|
||||
#
|
||||
# UDP crashes the JVM, don't know why
|
||||
#
|
||||
i2np.udp.enable=false
|
||||
# no COMM at all!!!
|
||||
#i2p.vmCommSystem=true
|
||||
# not on android
|
||||
i2np.upnp.enable=false
|
||||
routerconsole.geoip.enable=false
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
||||
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
|
||||
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/1">
|
||||
<file>file:/usblv/NetBeansProjects/i2p.i2p/apps/BOB/src/net/i2p/BOB/MUXlisten.java</file>
|
||||
<file>file:/usblv/NetBeansProjects/i2p.i2p/apps/BOB/src/net/i2p/BOB/Main.java</file>
|
||||
</open-files>
|
||||
</project-private>
|
||||
|
||||
@@ -256,11 +256,13 @@ public class BOB {
|
||||
listener = new ServerSocket(Integer.parseInt(props.getProperty(PROP_BOB_PORT)), 10, InetAddress.getByName(props.getProperty(PROP_BOB_HOST)));
|
||||
Socket server = null;
|
||||
listener.setSoTimeout(500); // .5 sec
|
||||
|
||||
while (spin.get()) {
|
||||
//DoCMDS connection;
|
||||
|
||||
try {
|
||||
server = listener.accept();
|
||||
server.setKeepAlive(true);
|
||||
g = true;
|
||||
} catch (ConnectException ce) {
|
||||
g = false;
|
||||
|
||||
@@ -50,7 +50,7 @@ public class DoCMDS implements Runnable {
|
||||
|
||||
// FIX ME
|
||||
// I need a better way to do versioning, but this will do for now.
|
||||
public static final String BMAJ = "00", BMIN = "00", BREV = "0A", BEXT = "";
|
||||
public static final String BMAJ = "00", BMIN = "00", BREV = "0C", BEXT = "";
|
||||
public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT;
|
||||
private Socket server;
|
||||
private Properties props;
|
||||
@@ -691,6 +691,7 @@ public class DoCMDS implements Runnable {
|
||||
try {
|
||||
prikey = new ByteArrayOutputStream();
|
||||
prikey.write(net.i2p.data.Base64.decode(Arg));
|
||||
d = new Destination();
|
||||
d.fromBase64(Arg);
|
||||
} catch (Exception ex) {
|
||||
Arg = "";
|
||||
|
||||
@@ -102,6 +102,7 @@ public class I2PtoTCP implements Runnable {
|
||||
break die;
|
||||
}
|
||||
sock = new Socket(host, port);
|
||||
sock.setKeepAlive(true);
|
||||
// make readers/writers
|
||||
in = sock.getInputStream();
|
||||
out = sock.getOutputStream();
|
||||
|
||||
@@ -311,6 +311,19 @@ public class MUXlisten implements Runnable {
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
|
||||
// Hopefully nuke stuff here...
|
||||
{
|
||||
String boner = tg.getName();
|
||||
try {
|
||||
_log.warn("destroySocketManager " + boner);
|
||||
socketManager.destroySocketManager();
|
||||
_log.warn("destroySocketManager Successful" + boner);
|
||||
} catch (Exception e) {
|
||||
// nop
|
||||
_log.warn("destroySocketManager Failed" + boner);
|
||||
_log.warn(e.toString());
|
||||
}
|
||||
}
|
||||
// zero out everything.
|
||||
try {
|
||||
wlock();
|
||||
|
||||
@@ -78,6 +78,7 @@ public class TCPlistener implements Runnable {
|
||||
while (lives.get()) {
|
||||
try {
|
||||
server = listener.accept();
|
||||
server.setKeepAlive(true);
|
||||
g = true;
|
||||
} catch (SocketTimeoutException ste) {
|
||||
g = false;
|
||||
|
||||
@@ -146,8 +146,14 @@ public class TCPtoI2P implements Runnable {
|
||||
input = line.toLowerCase();
|
||||
Destination dest = null;
|
||||
if (input.endsWith(".i2p")) {
|
||||
dest = I2PTunnel.destFromName(input);
|
||||
line = dest.toBase64();
|
||||
try {
|
||||
dest = I2PTunnel.destFromName(input);
|
||||
line = dest.toBase64();
|
||||
} catch (NullPointerException npe) {
|
||||
// Could not find the destination!?
|
||||
Emsg("Can't find destination: " + input, out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
dest = new Destination();
|
||||
dest.fromBase64(line);
|
||||
|
||||
@@ -39,6 +39,7 @@ import net.i2p.I2PAppContext;
|
||||
public class Daemon {
|
||||
public static final String VERSION = "2.0.3";
|
||||
private static final Daemon _instance = new Daemon();
|
||||
private boolean _running;
|
||||
|
||||
/**
|
||||
* Update the router and published address books using remote data from the
|
||||
@@ -126,6 +127,7 @@ public class Daemon {
|
||||
}
|
||||
|
||||
public void run(String[] args) {
|
||||
_running = true;
|
||||
String settingsLocation = "config.txt";
|
||||
File homeFile;
|
||||
if (args.length > 0) {
|
||||
@@ -166,7 +168,7 @@ public class Daemon {
|
||||
// Static method, and redundent Thread.currentThread().sleep(5*60*1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
|
||||
while (true) {
|
||||
while (_running) {
|
||||
long delay = Long.parseLong((String) settings.get("update_delay"));
|
||||
if (delay < 1) {
|
||||
delay = 1;
|
||||
@@ -179,6 +181,8 @@ public class Daemon {
|
||||
}
|
||||
} catch (InterruptedException exp) {
|
||||
}
|
||||
if (!_running)
|
||||
break;
|
||||
settings = ConfigParser.parse(settingsFile, defaultSettings);
|
||||
}
|
||||
}
|
||||
@@ -192,4 +196,9 @@ public class Daemon {
|
||||
_instance.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public static void stop() {
|
||||
_instance._running = false;
|
||||
wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,4 +51,9 @@ public class DaemonThread extends Thread {
|
||||
//}
|
||||
Daemon.main(this.args);
|
||||
}
|
||||
}
|
||||
|
||||
public void halt() {
|
||||
Daemon.stop();
|
||||
interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
*
|
||||
*/
|
||||
public class Servlet extends HttpServlet {
|
||||
private Thread thread;
|
||||
private DaemonThread thread;
|
||||
private String nonce;
|
||||
private static final String PROP_NONCE = "addressbook.nonce";
|
||||
|
||||
@@ -88,4 +88,9 @@ public class Servlet extends HttpServlet {
|
||||
//System.out.println("INFO: config root under " + args[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
this.thread.halt();
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
BIN
apps/i2psnark/_icons/application.png
Normal file
|
After Width: | Height: | Size: 464 B |
BIN
apps/i2psnark/_icons/book.png
Normal file
|
After Width: | Height: | Size: 593 B |
BIN
apps/i2psnark/_icons/bug.png
Normal file
|
After Width: | Height: | Size: 774 B |
BIN
apps/i2psnark/_icons/cancel.png
Normal file
|
After Width: | Height: | Size: 587 B |
BIN
apps/i2psnark/_icons/clock.png
Normal file
|
After Width: | Height: | Size: 882 B |
BIN
apps/i2psnark/_icons/compress.png
Normal file
|
After Width: | Height: | Size: 766 B |
BIN
apps/i2psnark/_icons/film.png
Normal file
|
After Width: | Height: | Size: 653 B |
BIN
apps/i2psnark/_icons/folder.png
Normal file
|
After Width: | Height: | Size: 537 B |
BIN
apps/i2psnark/_icons/html.png
Normal file
|
After Width: | Height: | Size: 578 B |
BIN
apps/i2psnark/_icons/music.png
Normal file
|
After Width: | Height: | Size: 385 B |
BIN
apps/i2psnark/_icons/package.png
Normal file
|
After Width: | Height: | Size: 853 B |
BIN
apps/i2psnark/_icons/page.png
Normal file
|
After Width: | Height: | Size: 635 B |
BIN
apps/i2psnark/_icons/page_white_acrobat.png
Normal file
|
After Width: | Height: | Size: 591 B |
BIN
apps/i2psnark/_icons/photo.png
Normal file
|
After Width: | Height: | Size: 589 B |
BIN
apps/i2psnark/_icons/plugin.png
Normal file
|
After Width: | Height: | Size: 591 B |
BIN
apps/i2psnark/_icons/tick.png
Normal file
|
After Width: | Height: | Size: 537 B |
@@ -52,8 +52,9 @@
|
||||
<classes dir="./build/obj" includes="**/I2PSnarkServlet*.class" />
|
||||
-->
|
||||
<target name="war" depends="jar, bundle">
|
||||
<war destfile="../i2psnark.war" webxml="../web.xml">
|
||||
<classes dir="./build/obj" includes="**/*.class" excludes="**/RunStandalone.class" />
|
||||
<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>
|
||||
</target>
|
||||
|
||||
|
||||
@@ -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 ]
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
@@ -36,7 +37,7 @@ import net.i2p.util.Log;
|
||||
*/
|
||||
public class ConnectionAcceptor implements Runnable
|
||||
{
|
||||
private Log _log = new Log(ConnectionAcceptor.class);
|
||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(ConnectionAcceptor.class);
|
||||
private I2PServerSocket serverSocket;
|
||||
private PeerAcceptor peeracceptor;
|
||||
private Thread thread;
|
||||
|
||||
@@ -4,7 +4,6 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -23,6 +22,7 @@ import net.i2p.client.streaming.I2PSocketManagerFactory;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.EepGet;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.Log;
|
||||
@@ -45,10 +45,10 @@ 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 _shitlist;
|
||||
private final Set<Hash> _shitlist;
|
||||
private int _maxUploaders;
|
||||
private int _maxUpBW;
|
||||
private int _maxConnections;
|
||||
@@ -65,9 +65,9 @@ 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 HashSet(64);
|
||||
_shitlist = new ConcurrentHashSet();
|
||||
_configured = false;
|
||||
_maxUploaders = Snark.MAX_TOTAL_UPLOADERS;
|
||||
_maxUpBW = DEFAULT_MAX_UP_BW;
|
||||
@@ -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; }
|
||||
@@ -187,18 +189,15 @@ public class I2PSnarkUtil {
|
||||
/** connect to the given destination */
|
||||
I2PSocket connect(PeerID peer) throws IOException {
|
||||
Hash dest = peer.getAddress().calculateHash();
|
||||
synchronized (_shitlist) {
|
||||
if (_shitlist.contains(dest))
|
||||
throw new IOException("Not trying to contact " + dest.toBase64() + ", as they are shitlisted");
|
||||
}
|
||||
if (_shitlist.contains(dest))
|
||||
throw new IOException("Not trying to contact " + dest.toBase64() + ", as they are shitlisted");
|
||||
try {
|
||||
I2PSocket rv = _manager.connect(peer.getAddress());
|
||||
if (rv != null) synchronized (_shitlist) { _shitlist.remove(dest); }
|
||||
if (rv != null)
|
||||
_shitlist.remove(dest);
|
||||
return rv;
|
||||
} catch (I2PException ie) {
|
||||
synchronized (_shitlist) {
|
||||
_shitlist.add(dest);
|
||||
}
|
||||
_shitlist.add(dest);
|
||||
SimpleScheduler.getInstance().addEvent(new Unshitlist(dest), 10*60*1000);
|
||||
throw new IOException("Unable to reach the peer " + peer + ": " + ie.getMessage());
|
||||
}
|
||||
@@ -207,7 +206,7 @@ public class I2PSnarkUtil {
|
||||
private class Unshitlist implements SimpleTimer.TimedEvent {
|
||||
private Hash _dest;
|
||||
public Unshitlist(Hash dest) { _dest = dest; }
|
||||
public void timeReached() { synchronized (_shitlist) { _shitlist.remove(_dest); } }
|
||||
public void timeReached() { _shitlist.remove(_dest); }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,7 +357,7 @@ public class I2PSnarkUtil {
|
||||
while (tok.hasMoreTokens())
|
||||
rv.add(tok.nextToken());
|
||||
|
||||
if (rv.size() <= 0)
|
||||
if (rv.isEmpty())
|
||||
return null;
|
||||
return rv;
|
||||
}
|
||||
@@ -431,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.SHA1;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.util.Log;
|
||||
@@ -47,7 +48,7 @@ import org.klomp.snark.bencode.InvalidBEncodingException;
|
||||
*/
|
||||
public class MetaInfo
|
||||
{
|
||||
private static final Log _log = new Log(MetaInfo.class);
|
||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(MetaInfo.class);
|
||||
private final String announce;
|
||||
private final byte[] info_hash;
|
||||
private final String name;
|
||||
|
||||
@@ -388,6 +388,7 @@ public class Peer implements Comparable
|
||||
* Sets whether or not we are interested in pieces from this peer.
|
||||
* Defaults to false. When interest is true and this peer unchokes
|
||||
* us then we start downloading from it. Has no effect when not connected.
|
||||
* @deprecated unused
|
||||
*/
|
||||
public void setInteresting(boolean interest)
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.io.OutputStream;
|
||||
import java.io.SequenceInputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
@@ -41,7 +42,7 @@ import net.i2p.util.Log;
|
||||
*/
|
||||
public class PeerAcceptor
|
||||
{
|
||||
private static final Log _log = new Log(PeerAcceptor.class);
|
||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerAcceptor.class);
|
||||
private final PeerCoordinator coordinator;
|
||||
final PeerCoordinatorSet coordinators;
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
@@ -105,15 +107,15 @@ class PeerCheckerTask extends TimerTask
|
||||
peer.resetCounters();
|
||||
|
||||
_util.debug(peer + ":", Snark.DEBUG);
|
||||
_util.debug(" ul: " + upload/KILOPERSECOND
|
||||
+ " dl: " + download/KILOPERSECOND
|
||||
_util.debug(" ul: " + upload*1024/KILOPERSECOND
|
||||
+ " dl: " + download*1024/KILOPERSECOND
|
||||
+ " i: " + peer.isInterested()
|
||||
+ " I: " + peer.isInteresting()
|
||||
+ " c: " + peer.isChoking()
|
||||
+ " 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()) ||
|
||||
|
||||
@@ -23,11 +23,12 @@ package org.klomp.snark;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
class PeerConnectionIn implements Runnable
|
||||
{
|
||||
private Log _log = new Log(PeerConnectionIn.class);
|
||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerConnectionIn.class);
|
||||
private final Peer peer;
|
||||
private final DataInputStream din;
|
||||
|
||||
@@ -129,7 +130,7 @@ class PeerConnectionIn implements Runnable
|
||||
din.readFully(bitmap);
|
||||
ps.bitfieldMessage(bitmap);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received bitmap from " + peer + " on " + peer.metainfo.getName() + ": size=" + (i-1) + ": " + ps.bitfield);
|
||||
_log.debug("Received bitmap from " + peer + " on " + peer.metainfo.getName() + ": size=" + (i-1) /* + ": " + ps.bitfield */ );
|
||||
break;
|
||||
case 6:
|
||||
piece = din.readInt();
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
@@ -33,7 +34,7 @@ import net.i2p.util.SimpleTimer;
|
||||
|
||||
class PeerConnectionOut implements Runnable
|
||||
{
|
||||
private Log _log = new Log(PeerConnectionOut.class);
|
||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerConnectionOut.class);
|
||||
private final Peer peer;
|
||||
private final DataOutputStream dout;
|
||||
|
||||
@@ -141,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);
|
||||
}
|
||||
@@ -151,7 +152,11 @@ class PeerConnectionOut implements Runnable
|
||||
{
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Send " + peer + ": " + m + " on " + peer.metainfo.getName());
|
||||
m.sendMessage(dout);
|
||||
|
||||
// This can block for quite a while.
|
||||
// To help get slow peers going, and track the bandwidth better,
|
||||
// move this _after_ state.uploaded() and see how it works.
|
||||
//m.sendMessage(dout);
|
||||
lastSent = System.currentTimeMillis();
|
||||
|
||||
// Remove all piece messages after sending a choke message.
|
||||
@@ -159,9 +164,22 @@ class PeerConnectionOut implements Runnable
|
||||
removeMessage(Message.PIECE);
|
||||
|
||||
// XXX - Should also register overhead...
|
||||
if (m.type == Message.PIECE)
|
||||
state.uploaded(m.len);
|
||||
// Don't let other clients requesting big chunks get an advantage
|
||||
// when we are seeding;
|
||||
// only count the rest of the upload after sendMessage().
|
||||
int remainder = 0;
|
||||
if (m.type == Message.PIECE) {
|
||||
if (m.len <= PeerState.PARTSIZE) {
|
||||
state.uploaded(m.len);
|
||||
} else {
|
||||
state.uploaded(PeerState.PARTSIZE);
|
||||
remainder = m.len - PeerState.PARTSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
m.sendMessage(dout);
|
||||
if (remainder > 0)
|
||||
state.uploaded(remainder);
|
||||
m = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Timer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@@ -37,7 +38,7 @@ import net.i2p.util.Log;
|
||||
*/
|
||||
public class PeerCoordinator implements PeerListener
|
||||
{
|
||||
private final Log _log = new Log(PeerCoordinator.class);
|
||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerCoordinator.class);
|
||||
final MetaInfo metainfo;
|
||||
final Storage storage;
|
||||
final Snark snark;
|
||||
@@ -96,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);
|
||||
}
|
||||
|
||||
@@ -240,15 +241,18 @@ public class PeerCoordinator implements PeerListener
|
||||
}
|
||||
}
|
||||
|
||||
/** reduce max if huge pieces to keep from ooming */
|
||||
/**
|
||||
* Reduce max if huge pieces to keep from ooming when leeching
|
||||
* @return 512K: 16; 1M: 11; 2M: 6
|
||||
*/
|
||||
private int getMaxConnections() {
|
||||
int size = metainfo.getPieceLength(0);
|
||||
int max = _util.getMaxConnections();
|
||||
if (size <= 1024*1024)
|
||||
if (size <= 512*1024 || completed())
|
||||
return max;
|
||||
if (size <= 2*1024*1024)
|
||||
return (max + 1) / 2;
|
||||
return (max + 3) / 4;
|
||||
if (size <= 1024*1024)
|
||||
return (max + max + 2) / 3;
|
||||
return (max + 2) / 3;
|
||||
}
|
||||
|
||||
public boolean halted() { return halted; }
|
||||
@@ -268,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);
|
||||
@@ -416,7 +420,7 @@ public class PeerCoordinator implements PeerListener
|
||||
count++;
|
||||
if (uploaders < maxUploaders)
|
||||
{
|
||||
if (!peer.isChoked())
|
||||
if (peer.isInteresting() && !peer.isChoked())
|
||||
interested.add(unchokedCount++, peer);
|
||||
else
|
||||
interested.add(peer);
|
||||
@@ -424,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))
|
||||
@@ -485,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.
|
||||
@@ -518,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();
|
||||
|
||||
@@ -24,11 +24,12 @@ import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
class PeerState
|
||||
{
|
||||
private Log _log = new Log(PeerState.class);
|
||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerState.class);
|
||||
final Peer peer;
|
||||
final PeerListener listener;
|
||||
final MetaInfo metainfo;
|
||||
@@ -152,7 +153,16 @@ class PeerState
|
||||
// XXX - Check for weird bitfield and disconnect?
|
||||
bitfield = new BitField(bitmap, metainfo.getPieces());
|
||||
}
|
||||
setInteresting(listener.gotBitField(peer, bitfield));
|
||||
boolean interest = listener.gotBitField(peer, bitfield);
|
||||
setInteresting(interest);
|
||||
if (bitfield.complete() && !interest) {
|
||||
// They are seeding and we are seeding,
|
||||
// why did they contact us? (robert)
|
||||
// Dump them quick before we send our whole bitmap
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Disconnecting seed that connects to seeds: " + peer);
|
||||
peer.disconnect(true);
|
||||
}
|
||||
}
|
||||
|
||||
void requestMessage(int piece, int begin, int length)
|
||||
@@ -186,6 +196,7 @@ class PeerState
|
||||
|
||||
// Limit total pipelined requests to MAX_PIPELINE bytes
|
||||
// to conserve memory and prevent DOS
|
||||
// Todo: limit number of requests also? (robert 64 x 4KB)
|
||||
if (out.queuedBytes() + length > MAX_PIPELINE_BYTES)
|
||||
{
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
|
||||
@@ -321,7 +321,7 @@ public class Snark
|
||||
// sixteen random bytes.
|
||||
byte snark = (((3 + 7 + 10) * (1000 - 8)) / 992) - 17;
|
||||
id = new byte[20];
|
||||
Random random = new Random();
|
||||
Random random = I2PAppContext.getGlobalContext().random();
|
||||
int i;
|
||||
for (i = 0; i < 9; i++)
|
||||
id[i] = 0;
|
||||
@@ -618,14 +618,14 @@ public class Snark
|
||||
command_interpreter = false;
|
||||
i++;
|
||||
}
|
||||
else if (args[i].equals("--eepproxy"))
|
||||
{
|
||||
String proxyHost = args[i+1];
|
||||
String proxyPort = args[i+2];
|
||||
if (!configured)
|
||||
util.setProxy(proxyHost, Integer.parseInt(proxyPort));
|
||||
i += 3;
|
||||
}
|
||||
//else if (args[i].equals("--eepproxy"))
|
||||
// {
|
||||
// String proxyHost = args[i+1];
|
||||
// String proxyPort = args[i+2];
|
||||
// if (!configured)
|
||||
// util.setProxy(proxyHost, Integer.parseInt(proxyPort));
|
||||
// i += 3;
|
||||
// }
|
||||
else if (args[i].equals("--i2cp"))
|
||||
{
|
||||
String i2cpHost = args[i+1];
|
||||
@@ -734,7 +734,7 @@ public class Snark
|
||||
//if (debug >= INFO && t != null)
|
||||
// t.printStackTrace();
|
||||
stopTorrent();
|
||||
throw new RuntimeException("die bart die");
|
||||
throw new RuntimeException(s + (t == null ? "" : ": " + t));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,8 +29,8 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
private static SnarkManager _instance = new SnarkManager();
|
||||
public static SnarkManager instance() { return _instance; }
|
||||
|
||||
/** map of (canonical) filename to Snark instance (unsynchronized) */
|
||||
private final Map _snarks;
|
||||
/** map of (canonical) filename of the .torrent file to Snark instance (unsynchronized) */
|
||||
private final Map<String, Snark> _snarks;
|
||||
private final Object _addSnarkLock;
|
||||
private /* FIXME final FIXME */ File _configFile;
|
||||
private Properties _config;
|
||||
@@ -40,12 +40,14 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
private I2PSnarkUtil _util;
|
||||
private PeerCoordinatorSet _peerCoordinatorSet;
|
||||
private ConnectionAcceptor _connectionAcceptor;
|
||||
private Thread _monitor;
|
||||
private boolean _running;
|
||||
|
||||
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";
|
||||
@@ -78,15 +80,22 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
* for i2cp host/port or i2psnark.dir
|
||||
*/
|
||||
public void start() {
|
||||
_running = true;
|
||||
_peerCoordinatorSet = new PeerCoordinatorSet();
|
||||
_connectionAcceptor = new ConnectionAcceptor(_util);
|
||||
int minutes = getStartupDelayMinutes();
|
||||
_messages.add(_("Adding torrents in {0} minutes", minutes));
|
||||
I2PAppThread monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor");
|
||||
monitor.setDaemon(true);
|
||||
monitor.start();
|
||||
_monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor", true);
|
||||
_monitor.start();
|
||||
_context.addShutdownTask(new SnarkManagerShutdown());
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
_running = false;
|
||||
_monitor.interrupt();
|
||||
_connectionAcceptor.halt();
|
||||
(new SnarkManagerShutdown()).run();
|
||||
}
|
||||
|
||||
/** hook to I2PSnarkUtil for the servlet */
|
||||
public I2PSnarkUtil util() { return _util; }
|
||||
@@ -148,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))
|
||||
@@ -189,15 +198,16 @@ 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);
|
||||
if (ot != null)
|
||||
_util.setOpenTrackerString(ot);
|
||||
// FIXME set util use open trackers property somehow
|
||||
getDataDir().mkdirs();
|
||||
}
|
||||
|
||||
@@ -216,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) {}
|
||||
@@ -298,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 {
|
||||
@@ -383,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()) {
|
||||
@@ -424,10 +455,27 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(sfile);
|
||||
} catch (IOException ioe) {
|
||||
// catch this here so we don't try do delete it below
|
||||
addMessage(_("Cannot open \"{0}\"", sfile.getName()) + ": " + ioe.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
MetaInfo info = new MetaInfo(fis);
|
||||
fis.close();
|
||||
fis = null;
|
||||
try {
|
||||
fis.close();
|
||||
fis = null;
|
||||
} catch (IOException e) {}
|
||||
|
||||
if (!TrackerClient.isValidAnnounce(info.getAnnounce())) {
|
||||
if (_util.shouldUseOpenTrackers() && _util.getOpenTrackers() != null) {
|
||||
addMessage(_("Warning - Ignoring non-i2p tracker in \"{0}\", will announce to i2p open trackers only", info.getName()));
|
||||
} else {
|
||||
addMessage(_("Warning - Ignoring non-i2p tracker in \"{0}\", and open trackers are disabled, you must enable open trackers before starting the torrent!", info.getName()));
|
||||
dontAutoStart = true;
|
||||
}
|
||||
}
|
||||
String rejectMessage = locked_validateTorrent(info);
|
||||
if (rejectMessage != null) {
|
||||
sfile.delete();
|
||||
@@ -551,12 +599,10 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning - does not validate announce URL - use TrackerClient.isValidAnnounce()
|
||||
*/
|
||||
private String locked_validateTorrent(MetaInfo info) throws IOException {
|
||||
String announce = info.getAnnounce();
|
||||
// basic validation of url
|
||||
if ((!announce.startsWith("http://")) ||
|
||||
(announce.indexOf(".i2p/") < 0)) // need to do better than this
|
||||
return _("Non-i2p tracker in \"{0}\", deleting it from our list of trackers!", info.getName());
|
||||
List files = info.getFiles();
|
||||
if ( (files != null) && (files.size() > MAX_FILES_PER_TORRENT) ) {
|
||||
return _("Too many files in \"{0}\" ({1}), deleting it!", info.getName(), files.size());
|
||||
@@ -567,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();
|
||||
@@ -661,7 +707,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
public void torrentComplete(Snark snark) {
|
||||
File f = new File(snark.torrent);
|
||||
long len = snark.meta.getTotalLength();
|
||||
addMessage(_("Download finished: \"{0}\"", f.getName()) + " (" + _("size: {0}B", DataHelper.formatSize(len)) + ')');
|
||||
addMessage(_("Download finished: \"{0}\"", f.getName()) + " (" + _("size: {0}B", DataHelper.formatSize2(len)) + ')');
|
||||
updateStatus(snark);
|
||||
}
|
||||
|
||||
|
||||
@@ -286,6 +286,50 @@ public class Storage
|
||||
return needed == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file canonical path (non-directory)
|
||||
* @return number of bytes remaining; -1 if unknown file
|
||||
* @since 0.7.14
|
||||
*/
|
||||
public long remaining(String file) {
|
||||
long bytes = 0;
|
||||
for (int i = 0; i < rafs.length; i++) {
|
||||
File f = RAFfile[i];
|
||||
// use canonical in case snark dir or sub dirs are symlinked
|
||||
String canonical = null;
|
||||
if (f != null) {
|
||||
try {
|
||||
canonical = f.getCanonicalPath();
|
||||
} catch (IOException ioe) {
|
||||
f = null;
|
||||
}
|
||||
}
|
||||
if (f != null && canonical.equals(file)) {
|
||||
if (complete())
|
||||
return 0;
|
||||
int psz = metainfo.getPieceLength(0);
|
||||
long start = bytes;
|
||||
long end = start + lengths[i];
|
||||
int pc = (int) (bytes / psz);
|
||||
long rv = 0;
|
||||
if (!bitfield.get(pc))
|
||||
rv = Math.min(psz - (start % psz), lengths[i]);
|
||||
int pieces = metainfo.getPieces();
|
||||
for (int j = pc + 1; (((long)j) * psz) < end && j < pieces; j++) {
|
||||
if (!bitfield.get(j)) {
|
||||
if (((long)(j+1))*psz < end)
|
||||
rv += psz;
|
||||
else
|
||||
rv += end - (((long)j) * psz);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
bytes += lengths[i];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The BitField that tells which pieces this storage contains.
|
||||
* Do not change this since this is the current state of the storage.
|
||||
@@ -295,6 +339,18 @@ public class Storage
|
||||
return bitfield;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base file or directory name of the data,
|
||||
* as specified in the .torrent file, but filtered to remove
|
||||
* illegal characters. This is where the data actually is,
|
||||
* relative to the snark base dir.
|
||||
*
|
||||
* @since 0.7.14
|
||||
*/
|
||||
public String getBaseName() {
|
||||
return filterName(metainfo.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates (and/or checks) all files from the metainfo file list.
|
||||
*/
|
||||
@@ -420,13 +476,29 @@ public class Storage
|
||||
}
|
||||
}
|
||||
|
||||
private static final char[] ILLEGAL = new char[] {
|
||||
'<', '>', ':', '"', '/', '\\', '|', '?', '*',
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
|
||||
|
||||
/**
|
||||
* Removes 'suspicious' characters from the give file name.
|
||||
* Removes 'suspicious' characters from the given file name.
|
||||
* http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx
|
||||
*/
|
||||
private static String filterName(String name)
|
||||
{
|
||||
// XXX - Is this enough?
|
||||
return name.replace(File.separatorChar, '_');
|
||||
if (name.equals(".") || name.equals(" "))
|
||||
return "_";
|
||||
String rv = name;
|
||||
if (rv.startsWith("."))
|
||||
rv = '_' + rv.substring(1);
|
||||
if (rv.endsWith(".") || rv.endsWith(" "))
|
||||
rv = rv.substring(0, rv.length() - 1) + '_';
|
||||
for (int i = 0; i < ILLEGAL.length; i++) {
|
||||
if (rv.indexOf(ILLEGAL[i]) >= 0)
|
||||
rv = rv.replace(ILLEGAL[i], '_');
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
private File createFileFromNames(File base, List names) throws IOException
|
||||
@@ -577,6 +649,9 @@ public class Storage
|
||||
if (rafs == null) return;
|
||||
for (int i = 0; i < rafs.length; i++)
|
||||
{
|
||||
// if we had an IOE in check(), the RAFlock may be null
|
||||
if (RAFlock[i] == null)
|
||||
continue;
|
||||
try {
|
||||
synchronized(RAFlock[i]) {
|
||||
closeRAF(i);
|
||||
|
||||
@@ -24,6 +24,8 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
@@ -31,6 +33,7 @@ import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@@ -42,7 +45,7 @@ import net.i2p.util.Log;
|
||||
*/
|
||||
public class TrackerClient extends I2PAppThread
|
||||
{
|
||||
private static final Log _log = new Log(TrackerClient.class);
|
||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(TrackerClient.class);
|
||||
private static final String NO_EVENT = "";
|
||||
private static final String STARTED_EVENT = "started";
|
||||
private static final String COMPLETED_EVENT = "completed";
|
||||
@@ -123,13 +126,19 @@ public class TrackerClient extends I2PAppThread
|
||||
// followed by the secondary open trackers
|
||||
// It's painful, but try to make sure if an open tracker is also
|
||||
// the primary tracker, that we don't add it twice.
|
||||
// todo: check for b32 matches as well
|
||||
trackers = new ArrayList(2);
|
||||
trackers.add(new Tracker(meta.getAnnounce(), true));
|
||||
String primary = meta.getAnnounce();
|
||||
if (isValidAnnounce(primary)) {
|
||||
trackers.add(new Tracker(meta.getAnnounce(), true));
|
||||
} else {
|
||||
_log.warn("Skipping invalid or non-i2p announce: " + primary);
|
||||
}
|
||||
List tlist = _util.getOpenTrackers();
|
||||
if (tlist != null) {
|
||||
for (int i = 0; i < tlist.size(); i++) {
|
||||
String url = (String)tlist.get(i);
|
||||
if (!url.startsWith("http://")) {
|
||||
if (!isValidAnnounce(url)) {
|
||||
_log.error("Bad announce URL: [" + url + "]");
|
||||
continue;
|
||||
}
|
||||
@@ -138,22 +147,29 @@ public class TrackerClient extends I2PAppThread
|
||||
_log.error("Bad announce URL: [" + url + "]");
|
||||
continue;
|
||||
}
|
||||
if (meta.getAnnounce().startsWith(url.substring(0, slash)))
|
||||
if (primary.startsWith(url.substring(0, slash)))
|
||||
continue;
|
||||
String dest = _util.lookup(url.substring(7, slash));
|
||||
if (dest == null) {
|
||||
_log.error("Announce host unknown: [" + url + "]");
|
||||
_log.error("Announce host unknown: [" + url.substring(7, slash) + "]");
|
||||
continue;
|
||||
}
|
||||
if (meta.getAnnounce().startsWith("http://" + dest))
|
||||
if (primary.startsWith("http://" + dest))
|
||||
continue;
|
||||
if (meta.getAnnounce().startsWith("http://i2p/" + dest))
|
||||
if (primary.startsWith("http://i2p/" + dest))
|
||||
continue;
|
||||
trackers.add(new Tracker(url, false));
|
||||
_log.debug("Additional announce: [" + url + "] for infoHash: " + infoHash);
|
||||
}
|
||||
}
|
||||
|
||||
if (tlist.isEmpty()) {
|
||||
// FIXME really need to get this message to the gui
|
||||
stop = true;
|
||||
_log.error("No valid trackers for infoHash: " + infoHash);
|
||||
return;
|
||||
}
|
||||
|
||||
long uploaded = coordinator.getUploaded();
|
||||
long downloaded = coordinator.getDownloaded();
|
||||
long left = coordinator.getLeft();
|
||||
@@ -167,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
|
||||
@@ -243,7 +259,7 @@ public class TrackerClient extends I2PAppThread
|
||||
tr.started = true;
|
||||
|
||||
Set peers = info.getPeers();
|
||||
tr.seenPeers = peers.size();
|
||||
tr.seenPeers = info.getPeerCount();
|
||||
if (coordinator.trackerSeenPeers < tr.seenPeers) // update rising number quickly
|
||||
coordinator.trackerSeenPeers = tr.seenPeers;
|
||||
if ( (left > 0) && (!completed) ) {
|
||||
@@ -254,6 +270,7 @@ public class TrackerClient extends I2PAppThread
|
||||
Iterator it = ordered.iterator();
|
||||
while (it.hasNext()) {
|
||||
Peer cur = (Peer)it.next();
|
||||
// FIXME if id == us || dest == us continue;
|
||||
// only delay if we actually make an attempt to add peer
|
||||
if(coordinator.addPeer(cur)) {
|
||||
int delay = DELAY_MUL;
|
||||
@@ -315,7 +332,7 @@ public class TrackerClient extends I2PAppThread
|
||||
// try to contact everybody we can
|
||||
// Don't try to restart I2CP connection just to say goodbye
|
||||
for (Iterator iter = trackers.iterator(); iter.hasNext(); ) {
|
||||
if (!verifyConnected()) return;
|
||||
if (!_util.connected()) return;
|
||||
Tracker tr = (Tracker)iter.next();
|
||||
if (tr.started && (!tr.stop) && tr.trackerProblems == null)
|
||||
doRequest(tr, infoHash, peerID, uploaded,
|
||||
@@ -341,6 +358,10 @@ public class TrackerClient extends I2PAppThread
|
||||
+ "&downloaded=" + downloaded
|
||||
+ "&left=" + left
|
||||
+ ((! event.equals(NO_EVENT)) ? ("&event=" + event) : "");
|
||||
if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
|
||||
s += "&numwant=0";
|
||||
else
|
||||
s += "&numwant=" + _util.getMaxConnections();
|
||||
_util.debug("Sending TrackerClient request: " + s, Snark.INFO);
|
||||
|
||||
tr.lastRequestTime = System.currentTimeMillis();
|
||||
@@ -399,7 +420,23 @@ public class TrackerClient extends I2PAppThread
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private class Tracker
|
||||
/**
|
||||
* @return true for i2p hosts only
|
||||
* @since 0.7.12
|
||||
*/
|
||||
static boolean isValidAnnounce(String ann) {
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(ann);
|
||||
} catch (MalformedURLException mue) {
|
||||
return false;
|
||||
}
|
||||
return url.getProtocol().equals("http") &&
|
||||
(url.getHost().endsWith(".i2p") || url.getHost().equals("i2p")) &&
|
||||
url.getPort() < 0;
|
||||
}
|
||||
|
||||
private static class Tracker
|
||||
{
|
||||
String announce;
|
||||
boolean isPrimary;
|
||||
|
||||
@@ -23,6 +23,7 @@ package org.klomp.snark;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -37,6 +38,8 @@ public class TrackerInfo
|
||||
private final String failure_reason;
|
||||
private final int interval;
|
||||
private final Set peers;
|
||||
private int complete;
|
||||
private int incomplete;
|
||||
|
||||
public TrackerInfo(InputStream in, byte[] my_id, MetaInfo metainfo)
|
||||
throws IOException
|
||||
@@ -68,11 +71,26 @@ public class TrackerInfo
|
||||
throw new InvalidBEncodingException("No interval given");
|
||||
else
|
||||
interval = beInterval.getInt();
|
||||
|
||||
BEValue bePeers = (BEValue)m.get("peers");
|
||||
if (bePeers == null)
|
||||
throw new InvalidBEncodingException("No peer list");
|
||||
peers = Collections.EMPTY_SET;
|
||||
else
|
||||
peers = getPeers(bePeers.getList(), my_id, metainfo);
|
||||
|
||||
BEValue bev = (BEValue)m.get("complete");
|
||||
if (bev != null) try {
|
||||
complete = bev.getInt();
|
||||
if (complete < 0)
|
||||
complete = 0;
|
||||
} catch (InvalidBEncodingException ibe) {}
|
||||
|
||||
bev = (BEValue)m.get("incomplete");
|
||||
if (bev != null) try {
|
||||
incomplete = bev.getInt();
|
||||
if (incomplete < 0)
|
||||
incomplete = 0;
|
||||
} catch (InvalidBEncodingException ibe) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +133,12 @@ public class TrackerInfo
|
||||
return peers;
|
||||
}
|
||||
|
||||
public int getPeerCount()
|
||||
{
|
||||
int pc = peers == null ? 0 : peers.size();
|
||||
return Math.max(pc, complete + incomplete - 1);
|
||||
}
|
||||
|
||||
public String getFailureReason()
|
||||
{
|
||||
return failure_reason;
|
||||
@@ -132,6 +156,8 @@ public class TrackerInfo
|
||||
return "TrackerInfo[FAILED: " + failure_reason + "]";
|
||||
else
|
||||
return "TrackerInfo[interval=" + interval
|
||||
+ (complete > 0 ? (", complete=" + complete) : "" )
|
||||
+ (incomplete > 0 ? (", incomplete=" + incomplete) : "" )
|
||||
+ ", peers=" + peers + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +279,9 @@ public class BDecoder
|
||||
public BEValue bdecodeMap() throws IOException
|
||||
{
|
||||
int c = getNextIndicator();
|
||||
if (c != 'd')
|
||||
if (c == '<')
|
||||
throw new InvalidBEncodingException("Expected a .torrent metainfo file but found HTML? Check URL or file!");
|
||||
else if (c != 'd')
|
||||
throw new InvalidBEncodingException("Expected 'd', not '"
|
||||
+ (char)c + "'");
|
||||
indicator = 0;
|
||||
|
||||
@@ -179,7 +179,7 @@ public class BEValue
|
||||
if (value instanceof byte[])
|
||||
{
|
||||
byte[] bs = (byte[])value;
|
||||
// XXX - Stupid heuristic...
|
||||
// XXX - Stupid heuristic... and not UTF-8
|
||||
if (bs.length <= 12)
|
||||
valueString = new String(bs);
|
||||
else
|
||||
|
||||
@@ -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,10 +68,82 @@ 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
|
||||
public void destroy() {
|
||||
_manager.stop();
|
||||
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");
|
||||
@@ -123,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) {
|
||||
@@ -157,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"));
|
||||
@@ -175,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>");
|
||||
@@ -186,10 +269,10 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
" <th align=\"left\" colspan=\"2\">");
|
||||
out.write(_("Totals"));
|
||||
out.write(" (");
|
||||
out.write(_("{0} torrents", snarks.size()));
|
||||
out.write(ngettext("1 torrent", "{0} torrents", snarks.size()));
|
||||
out.write(", ");
|
||||
out.write(DataHelper.formatSize(stats[5]) + "B, ");
|
||||
out.write(_("{0} connected peers", stats[4]));
|
||||
out.write(DataHelper.formatSize2(stats[5]) + "B, ");
|
||||
out.write(ngettext("1 connected peer", "{0} connected peers", (int) stats[4]));
|
||||
out.write(")</th>\n" +
|
||||
" <th> </th>\n" +
|
||||
" <th align=\"right\">" + formatSize(stats[0]) + "</th>\n" +
|
||||
@@ -251,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 {
|
||||
@@ -365,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;
|
||||
@@ -432,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);
|
||||
@@ -455,8 +580,11 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
int i = filename.lastIndexOf(".torrent");
|
||||
if (i > 0)
|
||||
filename = filename.substring(0, i);
|
||||
if (filename.length() > MAX_DISPLAYED_FILENAME_LENGTH)
|
||||
String fullFilename = filename;
|
||||
if (filename.length() > MAX_DISPLAYED_FILENAME_LENGTH) {
|
||||
fullFilename = new String(filename);
|
||||
filename = filename.substring(0, MAX_DISPLAYED_FILENAME_LENGTH) + "…";
|
||||
}
|
||||
long total = snark.meta.getTotalLength();
|
||||
// Early typecast, avoid possibly overflowing a temp integer
|
||||
long remaining = (long) snark.storage.needed() * (long) snark.meta.getPieceLength(0);
|
||||
@@ -496,17 +624,19 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
err = snark.coordinator.trackerProblems;
|
||||
curPeers = snark.coordinator.getPeerCount();
|
||||
stats[4] += curPeers;
|
||||
knownPeers = snark.coordinator.trackerSeenPeers;
|
||||
knownPeers = Math.max(curPeers, snark.coordinator.trackerSeenPeers);
|
||||
}
|
||||
|
||||
String statusString = _("Unknown");
|
||||
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) + "…";
|
||||
@@ -515,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
|
||||
@@ -546,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();
|
||||
@@ -590,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");
|
||||
@@ -627,13 +771,23 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
out.write("<a href=\"" + uri + "?action=Remove" + parameters
|
||||
+ "\" title=\"");
|
||||
out.write(_("Remove the torrent from the active list, deleting the .torrent file"));
|
||||
out.write("\">");
|
||||
out.write("\" onclick=\"if (!confirm('");
|
||||
// Can't figure out how to escape double quotes inside the onclick string.
|
||||
// Single quotes in translate strings with parameters must be doubled.
|
||||
// Then the remaining single quite must be escaped
|
||||
out.write(_("Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?", fullFilename));
|
||||
out.write("')) { return false; }\">");
|
||||
out.write(_("Remove"));
|
||||
out.write("</a><br>");
|
||||
out.write("<a href=\"" + uri + "?action=Delete" + parameters
|
||||
+ "\" title=\"");
|
||||
out.write(_("Delete the .torrent file and the associated data file(s)"));
|
||||
out.write("\">");
|
||||
out.write("\" onclick=\"if (!confirm('");
|
||||
// Can't figure out how to escape double quotes inside the onclick string.
|
||||
// Single quotes in translate strings with parameters must be doubled.
|
||||
// Then the remaining single quite must be escaped
|
||||
out.write(_("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", fullFilename));
|
||||
out.write("')) { return false; }\">");
|
||||
out.write(_("Delete"));
|
||||
out.write("</a>");
|
||||
}
|
||||
@@ -662,13 +816,13 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
client = "Azureus";
|
||||
else if ("CwsL".equals(ch))
|
||||
client = "I2PSnarkXL";
|
||||
else if ("ZV".equals(ch.substring(2,4)))
|
||||
client = "Robert";
|
||||
else if ("VUZP".equals(ch))
|
||||
else if ("ZV".equals(ch.substring(2,4)) || "VUZP".equals(ch))
|
||||
client = "Robert";
|
||||
else if (ch.startsWith("LV")) // LVCS 1.0.2?; LVRS 1.0.4
|
||||
client = "Transmission";
|
||||
else
|
||||
client = _("Unknown") + " (" + ch + ')';
|
||||
out.write(client + " " + peer.toString().substring(5, 9));
|
||||
out.write(client + " <tt>" + peer.toString().substring(5, 9)+ "</tt>");
|
||||
if (showDebug)
|
||||
out.write(" inactive " + (peer.getInactiveTime() / 1000) + "s");
|
||||
out.write("</td>\n\t");
|
||||
@@ -774,7 +928,7 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
//out.write("From file: <input type=\"file\" name=\"newFile\" size=\"50\" value=\"" + newFile + "\" /><br>\n");
|
||||
out.write(_("Data to seed"));
|
||||
out.write(":<td>" + _manager.getDataDir().getAbsolutePath() + File.separatorChar
|
||||
+ "<input type=\"text\" name=\"baseFile\" size=\"20\" value=\"" + baseFile
|
||||
+ "<input type=\"text\" name=\"baseFile\" size=\"40\" value=\"" + baseFile
|
||||
+ "\" title=\"");
|
||||
out.write(_("File or directory to seed (must be within the specified path)"));
|
||||
out.write("\" ><tr><td>\n");
|
||||
@@ -857,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\">");
|
||||
@@ -890,6 +1044,20 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
//out.write("port: <input type=\"text\" name=\"eepPort\" value=\""
|
||||
// + _manager.util().getEepProxyPort() + "\" size=\"5\" maxlength=\"5\" /><br>\n");
|
||||
|
||||
Map<String, String> options = new TreeMap(_manager.util().getI2CPOptions());
|
||||
out.write("<tr><td>");
|
||||
out.write(_("Inbound Settings"));
|
||||
out.write(":<td>");
|
||||
out.write(renderOptions(1, 6, options.remove("inbound.quantity"), "inbound.quantity", TUNNEL));
|
||||
out.write(" ");
|
||||
out.write(renderOptions(0, 4, options.remove("inbound.length"), "inbound.length", HOP));
|
||||
out.write("<tr><td>");
|
||||
out.write(_("Outbound Settings"));
|
||||
out.write(":<td>");
|
||||
out.write(renderOptions(1, 6, options.remove("outbound.quantity"), "outbound.quantity", TUNNEL));
|
||||
out.write(" ");
|
||||
out.write(renderOptions(0, 4, options.remove("outbound.length"), "outbound.length", HOP));
|
||||
|
||||
out.write("<tr><td>");
|
||||
out.write(_("I2CP host"));
|
||||
out.write(": <td><input type=\"text\" name=\"i2cpHost\" value=\""
|
||||
@@ -897,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();
|
||||
@@ -920,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);
|
||||
@@ -930,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" +
|
||||
@@ -953,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;
|
||||
@@ -967,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);
|
||||
@@ -995,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();
|
||||
|
||||
@@ -58,6 +58,23 @@
|
||||
<Arg>webapps/i2psnark.war</Arg>
|
||||
</Call>
|
||||
|
||||
<!-- this is so we can find the css -->
|
||||
<Call name="addContext">
|
||||
<Arg>
|
||||
<New class="org.mortbay.http.HttpContext">
|
||||
<Set name="contextPath">/themes</Set>
|
||||
<Set name="resourceBase">./docs/themes</Set>
|
||||
<Call name="addHandler">
|
||||
<Arg>
|
||||
<New class="org.mortbay.http.handler.ResourceHandler">
|
||||
<Set name="redirectWelcome">FALSE</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
<!-- =============================================================== -->
|
||||
<!-- Configure the Other Server Options -->
|
||||
<!-- =============================================================== -->
|
||||
|
||||
@@ -8,641 +8,756 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P i2psnark\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-12-14 03:19+0000\n"
|
||||
"PO-Revision-Date: 2009-12-20 07:03+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:241
|
||||
#: ../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:243
|
||||
#: ../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:255
|
||||
#: ../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:257
|
||||
#: ../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:301
|
||||
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:307
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:320
|
||||
msgid "Disconnecting old I2CP destination"
|
||||
msgstr "Рассоединяемся по старому адресу I2CP"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:311
|
||||
#: ../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:315
|
||||
#: ../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:319
|
||||
#: ../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:321
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:334
|
||||
msgid "Reconnected on the new I2CP destination"
|
||||
msgstr "Пересоединились по новому адресу I2CP"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:332
|
||||
#: ../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:343
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:356
|
||||
msgid "Enabled autostart"
|
||||
msgstr "Автостарт включен"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:345
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:358
|
||||
msgid "Disabled autostart"
|
||||
msgstr "Автостарт выключен"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:351
|
||||
#: ../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:353
|
||||
#: ../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:360
|
||||
#: ../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:367
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:380
|
||||
msgid "Configuration unchanged."
|
||||
msgstr "Настройки не изменились."
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:377
|
||||
#: ../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:395
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:408
|
||||
msgid "Connecting to I2P"
|
||||
msgstr "Устанавливается соединение с I2P"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:398
|
||||
#: ../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:407
|
||||
#: ../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:446
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:442
|
||||
#, java-format
|
||||
msgid "Cannot open \"{0}\""
|
||||
msgstr "Не удалось открыть \"{0}\""
|
||||
|
||||
#: ../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: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:476
|
||||
#, java-format
|
||||
msgid "Torrent in \"{0}\" is invalid"
|
||||
msgstr "Торрент в \"{0}\" некорректен"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:461
|
||||
#: ../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:463
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:493
|
||||
#, java-format
|
||||
msgid "Torrent added: \"{0}\""
|
||||
msgstr "Торрент добавлен: \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:559
|
||||
#, java-format
|
||||
msgid "Non-i2p tracker in \"{0}\", deleting it from our list of trackers!"
|
||||
msgstr "Обнаружен не-I2P трекер в торренте \"{0}\", удаляем его из нашего списка трекеров!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:562
|
||||
#: ../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:564
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:592
|
||||
#, java-format
|
||||
msgid "Torrent file \"{0}\" cannot end in '.torrent', deleting it!"
|
||||
msgstr "Торрент \"{0}\" содержит единственный файл заканчивающийся на '.torrent', удаляем его!"
|
||||
msgid "Torrent file \"{0}\" cannot end in \".torrent\", deleting it!"
|
||||
msgstr "Торрент \"{0}\" содержит единственный файл заканчивающийся на \".torrent\", удаляем его!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:566
|
||||
#: ../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:568
|
||||
#: ../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:570
|
||||
#: ../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:571
|
||||
#: ../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:579
|
||||
#: ../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:595
|
||||
#: ../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:616
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:644
|
||||
#, java-format
|
||||
msgid "Torrent stopped: \"{0}\""
|
||||
msgstr "Торрент остановлен: \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:631
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:659
|
||||
#, java-format
|
||||
msgid "Torrent removed: \"{0}\""
|
||||
msgstr "Торрент удален: \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:664
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:692
|
||||
#, java-format
|
||||
msgid "Download finished: \"{0}\""
|
||||
msgstr "Завершена загрузка: \"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:664
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:692
|
||||
#, java-format
|
||||
msgid "size: {0}B"
|
||||
msgstr "размер: {0}B"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:692
|
||||
#: ../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:656
|
||||
#: ../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:986
|
||||
#: ../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:988
|
||||
#: ../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:502
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:670
|
||||
#: ../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:505
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:509
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:513
|
||||
#: ../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:507
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:509
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:519
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:521
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:528
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:530
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:534
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:536
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:859
|
||||
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:517
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:521
|
||||
#: ../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:523
|
||||
#: ../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:526
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:530
|
||||
#: ../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:532
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:536
|
||||
#: ../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:538
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:672
|
||||
msgid "No Peers"
|
||||
msgstr "Нет Пиров"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:540
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:674
|
||||
msgid "Stopped"
|
||||
msgstr "Остановлен"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:553
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:689
|
||||
msgid "View files"
|
||||
msgstr "Открыть директорию"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:555
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:691
|
||||
msgid "Open file"
|
||||
msgstr "Открыть файл"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:579
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:779
|
||||
#: ../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:580
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:722
|
||||
msgid "Details"
|
||||
msgstr "Подробнее"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:614
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:756
|
||||
msgid "Stop the torrent"
|
||||
msgstr "Остановить торрент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:616
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:758
|
||||
msgid "Stop"
|
||||
msgstr "Остановить"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:622
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:764
|
||||
msgid "Start the torrent"
|
||||
msgstr "Запустить торрент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:624
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:766
|
||||
msgid "Start"
|
||||
msgstr "Запустить"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:629
|
||||
#: ../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:631
|
||||
#: ../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:778
|
||||
msgid "Remove"
|
||||
msgstr "Удалить"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:635
|
||||
#: ../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:637
|
||||
#: ../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:789
|
||||
msgid "Delete"
|
||||
msgstr "Стереть"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:680
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:832
|
||||
msgid "Seed"
|
||||
msgstr "Сид"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:698
|
||||
#: ../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:700
|
||||
#: ../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:714
|
||||
#: ../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:716
|
||||
#: ../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:742
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:895
|
||||
msgid "Add Torrent"
|
||||
msgstr "Добавить Торрент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:744
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:897
|
||||
msgid "From URL"
|
||||
msgstr "Из URL"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:749
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:902
|
||||
msgid "Add torrent"
|
||||
msgstr "Добавить торрент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:752
|
||||
#: ../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:754
|
||||
#: ../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:770
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:924
|
||||
msgid "Create Torrent"
|
||||
msgstr "Создать Торрент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:773
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:927
|
||||
msgid "Data to seed"
|
||||
msgstr "Файлы для раздачи"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:777
|
||||
#: ../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:781
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:935
|
||||
msgid "Select a tracker"
|
||||
msgstr "Выбрать трекер"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:794
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:948
|
||||
msgid "or"
|
||||
msgstr "или"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:797
|
||||
#: ../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:800
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:954
|
||||
msgid "Create torrent"
|
||||
msgstr "Создать торрент"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:817
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:972
|
||||
msgid "Configuration"
|
||||
msgstr "Настройки"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:820
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:975
|
||||
msgid "Data directory"
|
||||
msgstr "Директория для файлов"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:823
|
||||
#: ../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:825
|
||||
#: ../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:829
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:984
|
||||
msgid "Auto start"
|
||||
msgstr "Автозапуск"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:833
|
||||
#: ../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:856
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1011
|
||||
msgid "Total uploader limit"
|
||||
msgstr "Ограничение количества слотов отдачи"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:863
|
||||
# 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:866
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1021
|
||||
msgid "Half available bandwidth recommended."
|
||||
msgstr "Рекомендуется использовать половину от доступной пропускной способности."
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:868
|
||||
#: ../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:872
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1027
|
||||
msgid "Use open trackers also"
|
||||
msgstr "Дополнительно использовать открытые трекеры"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:876
|
||||
#: ../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:880
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1035
|
||||
msgid "Open tracker announce URLs"
|
||||
msgstr "URL открытых трекеров"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:891
|
||||
#: ../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:896
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1065
|
||||
msgid "I2CP port"
|
||||
msgstr "Порт I2CP"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:909
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1077
|
||||
msgid "I2CP options"
|
||||
msgstr "Параметры I2CP"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:914
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1082
|
||||
msgid "Save configuration"
|
||||
msgstr "Сохранить настройки"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:967
|
||||
#: ../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:994
|
||||
#: ../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:999
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1441
|
||||
#, java-format
|
||||
msgid "Torrent was not retrieved from {0}"
|
||||
msgstr "Не удалось получить торрент из: {0}"
|
||||
|
||||
#~ msgid "{0} torrents"
|
||||
#~ msgstr "{0} торрентов"
|
||||
#~ msgid "hops"
|
||||
#~ msgstr "хопа(-ов)"
|
||||
#~ msgid "tunnels"
|
||||
#~ msgstr "туннеля(-ей)"
|
||||
#~ msgid "Bytes"
|
||||
#~ msgstr "байт"
|
||||
#~ msgid "Cannot change the I2CP settings while torrents are active"
|
||||
#~ msgstr "Невозможно изменить настройки I2CP пока есть активные торренты"
|
||||
|
||||
|
||||
@@ -8,643 +8,739 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P i2psnark\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-12-18 06:45+0000\n"
|
||||
"PO-Revision-Date: 2009-12-27 10:47+0800\n"
|
||||
"Last-Translator: walking <zhazhenzhong@gmail.com>\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:241
|
||||
#: ../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:243
|
||||
#: ../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:255
|
||||
#: ../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:257
|
||||
#: ../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:301
|
||||
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:307
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:320
|
||||
msgid "Disconnecting old I2CP destination"
|
||||
msgstr "正在断开旧的I2CP目标"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:311
|
||||
#: ../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:315
|
||||
#: ../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:319
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:332
|
||||
msgid "Unable to reconnect with the old settings!"
|
||||
msgstr "旧设置也无法连接!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:321
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:334
|
||||
msgid "Reconnected on the new I2CP destination"
|
||||
msgstr "重新连接新I2CP目标"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:332
|
||||
#: ../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:343
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:356
|
||||
msgid "Enabled autostart"
|
||||
msgstr "启用自动启动"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:345
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:358
|
||||
msgid "Disabled autostart"
|
||||
msgstr "禁用自动启动"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:351
|
||||
#: ../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:353
|
||||
#: ../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:360
|
||||
#: ../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:367
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:380
|
||||
msgid "Configuration unchanged."
|
||||
msgstr "设置未改变"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:377
|
||||
#: ../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:395
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:408
|
||||
msgid "Connecting to I2P"
|
||||
msgstr "正在连接到I2P"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:398
|
||||
#: ../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:407
|
||||
#: ../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:446
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:442
|
||||
#, java-format
|
||||
msgid "Cannot open \"{0}\""
|
||||
msgstr "无法打开 \"{0}\""
|
||||
|
||||
#: ../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: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:476
|
||||
#, java-format
|
||||
msgid "Torrent in \"{0}\" is invalid"
|
||||
msgstr "无效种子 \"{0}\" "
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:461
|
||||
#: ../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:463
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:493
|
||||
#, java-format
|
||||
msgid "Torrent added: \"{0}\""
|
||||
msgstr "已添加种子:\"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:559
|
||||
#, java-format
|
||||
msgid "Non-i2p tracker in \"{0}\", deleting it from our list of trackers!"
|
||||
msgstr "【匿名性警告】\"{0}\" 中含有非I2P Tracker,程序将从Tracker列表中将其删除。"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:562
|
||||
#: ../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:564
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:592
|
||||
#, java-format
|
||||
msgid "Torrent file \"{0}\" cannot end in '.torrent', deleting it!"
|
||||
msgstr "种子文件 \"{0}\" 不以 \".torrent\"结尾,删除之!"
|
||||
msgid "Torrent file \"{0}\" cannot end in \".torrent\", deleting it!"
|
||||
msgstr "种子文件 \"{0}\" 不以 \".torrent\"结尾,正在删除!"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:566
|
||||
#: ../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:568
|
||||
#: ../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:570
|
||||
#: ../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:571
|
||||
#: ../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:579
|
||||
#: ../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:595
|
||||
#: ../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:616
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:644
|
||||
#, java-format
|
||||
msgid "Torrent stopped: \"{0}\""
|
||||
msgstr "种子已停止:\"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:631
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:659
|
||||
#, java-format
|
||||
msgid "Torrent removed: \"{0}\""
|
||||
msgstr "种子已删除:\"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:664
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:692
|
||||
#, java-format
|
||||
msgid "Download finished: \"{0}\""
|
||||
msgstr "下载已完成:\"{0}\""
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:664
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:692
|
||||
#, java-format
|
||||
msgid "size: {0}B"
|
||||
msgstr "大小:{0}B"
|
||||
|
||||
#: ../java/src/org/klomp/snark/SnarkManager.java:692
|
||||
#: ../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:656
|
||||
#: ../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:990
|
||||
#: ../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:992
|
||||
#: ../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:502
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:670
|
||||
#: ../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:505
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:509
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:513
|
||||
#: ../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:507
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:509
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:519
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:521
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:528
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:530
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:534
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:536
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:862
|
||||
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:517
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:521
|
||||
#: ../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:523
|
||||
#: ../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:526
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:530
|
||||
#: ../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:532
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:536
|
||||
#: ../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:538
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:672
|
||||
msgid "No Peers"
|
||||
msgstr "没有用户"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:540
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:674
|
||||
msgid "Stopped"
|
||||
msgstr "已停用"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:553
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:689
|
||||
msgid "View files"
|
||||
msgstr "浏览文件"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:555
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:691
|
||||
msgid "Open file"
|
||||
msgstr "打开文件"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:579
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:781
|
||||
#: ../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:580
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:722
|
||||
msgid "Details"
|
||||
msgstr "详情"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:614
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:756
|
||||
msgid "Stop the torrent"
|
||||
msgstr "停止种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:616
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:758
|
||||
msgid "Stop"
|
||||
msgstr "停止"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:622
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:764
|
||||
msgid "Start the torrent"
|
||||
msgstr "启动种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:624
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:766
|
||||
msgid "Start"
|
||||
msgstr "启动"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:629
|
||||
#: ../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:631
|
||||
#: ../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:778
|
||||
msgid "Remove"
|
||||
msgstr "移除"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:635
|
||||
#: ../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:637
|
||||
#: ../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:789
|
||||
msgid "Delete"
|
||||
msgstr "删除"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:680
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:832
|
||||
msgid "Seed"
|
||||
msgstr "种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:698
|
||||
#: ../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:700
|
||||
#: ../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:714
|
||||
#: ../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:716
|
||||
#: ../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:743
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:895
|
||||
msgid "Add Torrent"
|
||||
msgstr "添加种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:745
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:897
|
||||
msgid "From URL"
|
||||
msgstr "从URL"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:750
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:902
|
||||
msgid "Add torrent"
|
||||
msgstr "添加种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:753
|
||||
#: ../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:755
|
||||
#: ../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:772
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:924
|
||||
msgid "Create Torrent"
|
||||
msgstr "创建种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:775
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:927
|
||||
msgid "Data to seed"
|
||||
msgstr "做种数据"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:779
|
||||
#: ../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:783
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:935
|
||||
msgid "Select a tracker"
|
||||
msgstr "选择一个Tracker"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:796
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:948
|
||||
msgid "or"
|
||||
msgstr "或"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:799
|
||||
#: ../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:802
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:954
|
||||
msgid "Create torrent"
|
||||
msgstr "创建种子"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:820
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:972
|
||||
msgid "Configuration"
|
||||
msgstr "设置"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:823
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:975
|
||||
msgid "Data directory"
|
||||
msgstr "数据文件夹"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:826
|
||||
#: ../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:828
|
||||
#: ../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:832
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:984
|
||||
msgid "Auto start"
|
||||
msgstr "自动启动"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:836
|
||||
#: ../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:859
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1011
|
||||
msgid "Total uploader limit"
|
||||
msgstr ""
|
||||
msgstr "限制总上传种子数为"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:866
|
||||
#: ../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:869
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1021
|
||||
msgid "Half available bandwidth recommended."
|
||||
msgstr "推荐设置为可用带宽的一半。"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:871
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1023
|
||||
msgid "View or change router bandwidth"
|
||||
msgstr "浏览或修改路由器带宽"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:875
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1027
|
||||
msgid "Use open trackers also"
|
||||
msgstr "同时使用OpenTracker"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:879
|
||||
#: ../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:883
|
||||
#: ../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:894
|
||||
#: ../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:899
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1065
|
||||
msgid "I2CP port"
|
||||
msgstr "I2CP端口"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:912
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1077
|
||||
msgid "I2CP options"
|
||||
msgstr "I2CP选项"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:917
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1082
|
||||
msgid "Save configuration"
|
||||
msgstr "保存设置"
|
||||
|
||||
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:970
|
||||
#: ../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:998
|
||||
#: ../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:1003
|
||||
#: ../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列表中将其删除。"
|
||||
#~ msgid "Custom tracker URL"
|
||||
#~ msgstr "自定义TrackerURL"
|
||||
#~ msgid "Configure"
|
||||
|
||||
@@ -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 ]
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
|
||||
* (c) 2003 - 2004 mihi
|
||||
*/
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Read what i2ptunnel logs, and expose it in a buffer
|
||||
*
|
||||
*/
|
||||
class BufferLogger implements Logging {
|
||||
private final static Log _log = new Log(BufferLogger.class);
|
||||
private ByteArrayOutputStream _baos; // FIXME should be final and use a factory. FIXME
|
||||
private boolean _ignore;
|
||||
|
||||
/**
|
||||
* Constructs a buffered logger.
|
||||
*/
|
||||
public BufferLogger() {
|
||||
_baos = new ByteArrayOutputStream(512);
|
||||
_ignore = false;
|
||||
}
|
||||
|
||||
private final static String EMPTY = "";
|
||||
|
||||
/**
|
||||
* Retrieves the buffer
|
||||
* @return the buffer
|
||||
*/
|
||||
public String getBuffer() {
|
||||
if (_ignore)
|
||||
return EMPTY;
|
||||
|
||||
return new String(_baos.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* We don't care about anything else the logger receives. This is useful
|
||||
* for loggers passed in to servers and clients, since they will continue
|
||||
* to add info to the logger, but if we're instantiated by the tunnel manager,
|
||||
* its likely we only care about the first few messages it sends us.
|
||||
*
|
||||
*/
|
||||
public void ignoreFurtherActions() {
|
||||
_ignore = true;
|
||||
synchronized (_baos) {
|
||||
_baos.reset();
|
||||
}
|
||||
_baos = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass in some random data
|
||||
* @param s String containing what we're logging.
|
||||
*/
|
||||
public void log(String s) {
|
||||
if (_ignore) return;
|
||||
if (s != null) {
|
||||
_log.debug("logging [" + s + "]");
|
||||
try {
|
||||
_baos.write(s.getBytes());
|
||||
_baos.write('\n');
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error logging [" + s + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,13 +124,13 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
* Tweak that first HTTP response line (HTTP 200 OK, etc)
|
||||
*
|
||||
*/
|
||||
protected String filterResponseLine(String line) {
|
||||
protected static String filterResponseLine(String line) {
|
||||
return line;
|
||||
}
|
||||
|
||||
/** we ignore any potential \r, since we trim it on write anyway */
|
||||
private static final byte NL = '\n';
|
||||
private boolean isNL(byte b) { return (b == NL); }
|
||||
private static boolean isNL(byte b) { return (b == NL); }
|
||||
|
||||
/** ok, received, now munge & write it */
|
||||
private void writeHeader() throws IOException {
|
||||
@@ -275,7 +275,8 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
_context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, end-start);
|
||||
}
|
||||
}
|
||||
private class InternalGZIPInputStream extends GZIPInputStream {
|
||||
|
||||
private static class InternalGZIPInputStream extends GZIPInputStream {
|
||||
public InternalGZIPInputStream(InputStream in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
@@ -318,6 +319,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
return super.toString() + ": " + _in;
|
||||
}
|
||||
|
||||
/*******
|
||||
public static void main(String args[]) {
|
||||
String simple = "HTTP/1.1 200 OK\n" +
|
||||
"foo: bar\n" +
|
||||
@@ -367,7 +369,6 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
"A:\n" +
|
||||
"\n";
|
||||
|
||||
/* */
|
||||
test("Simple", simple, true);
|
||||
test("Filtered", filtered, true);
|
||||
test("Filtered windows", winfilter, true);
|
||||
@@ -382,7 +383,6 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
test("Invalid (bad headers)", invalid5, true);
|
||||
test("Invalid (bad headers2)", invalid6, false);
|
||||
test("Invalid (bad headers3)", invalid7, false);
|
||||
/* */
|
||||
}
|
||||
|
||||
private static void test(String name, String orig, boolean shouldPass) {
|
||||
@@ -401,4 +401,5 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
System.out.println("Properly fails with " + e.getMessage());
|
||||
}
|
||||
}
|
||||
******/
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.i2ptunnel.socks.I2PSOCKSIRCTunnel;
|
||||
import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel;
|
||||
import net.i2p.i2ptunnel.streamr.StreamrConsumer;
|
||||
import net.i2p.i2ptunnel.streamr.StreamrProducer;
|
||||
@@ -236,6 +237,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
runServer(args, l);
|
||||
} else if ("httpserver".equals(cmdname)) {
|
||||
runHttpServer(args, l);
|
||||
} else if ("httpbidirserver".equals(cmdname)) {
|
||||
runHttpBidirServer(args, l);
|
||||
} else if ("ircserver".equals(cmdname)) {
|
||||
runIrcServer(args, l);
|
||||
} else if ("textserver".equals(cmdname)) {
|
||||
@@ -300,6 +303,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("ping <args>");
|
||||
l.log("server <host> <port> <privkeyfile>");
|
||||
l.log("httpserver <host> <port> <spoofedhost> <privkeyfile>");
|
||||
l.log("httpbidirserver <host> <port> <proxyport> <spoofedhost> <privkeyfile>");
|
||||
l.log("textserver <host> <port> <privkey>");
|
||||
l.log("genkeys <privkeyfile> [<pubkeyfile>]");
|
||||
l.log("gentextkeys");
|
||||
@@ -503,6 +507,80 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the HTTP server pointing at the host and port specified using the private i2p
|
||||
* destination loaded from the specified file, replacing the HTTP headers
|
||||
* so that the Host: specified is the one spoofed. Also runs an HTTP proxy for
|
||||
* bidirectional communications on the same tunnel destination.<p />
|
||||
*
|
||||
* Sets the event "serverTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error)
|
||||
* Also sets the event "openServerResult" = "ok" or "error" (displaying "Ready!" on the logger after
|
||||
* 'ok'). So, success = serverTaskId != -1 and openServerResult = ok.
|
||||
*
|
||||
* @param args {hostname, portNumber, proxyPortNumber, spoofedHost, privKeyFilename}
|
||||
* @param l logger to receive events and output
|
||||
*/
|
||||
public void runHttpBidirServer(String args[], Logging l) {
|
||||
if (args.length == 5) {
|
||||
InetAddress serverHost = null;
|
||||
int portNum = -1;
|
||||
int port2Num = -1;
|
||||
File privKeyFile = null;
|
||||
try {
|
||||
serverHost = InetAddress.getByName(args[0]);
|
||||
} catch (UnknownHostException uhe) {
|
||||
l.log("unknown host");
|
||||
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
portNum = Integer.parseInt(args[1]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
port2Num = Integer.parseInt(args[2]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[2], nfe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
String spoofedHost = args[3];
|
||||
|
||||
privKeyFile = new File(args[4]);
|
||||
if (!privKeyFile.isAbsolute())
|
||||
privKeyFile = new File(_context.getConfigDir(), args[4]);
|
||||
if (!privKeyFile.canRead()) {
|
||||
l.log("private key file does not exist");
|
||||
_log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[4]);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
I2PTunnelHTTPBidirServer serv = new I2PTunnelHTTPBidirServer(serverHost, portNum, port2Num, privKeyFile, args[3], spoofedHost, l, (EventDispatcher) this, this);
|
||||
serv.setReadTimeout(readTimeout);
|
||||
serv.startRunning();
|
||||
addtask(serv);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(serv.getId()));
|
||||
return;
|
||||
} else {
|
||||
l.log("httpserver <host> <port> <proxyport> <spoofedhost> <privkeyfile>");
|
||||
l.log(" creates a bidirectional HTTP server that sends all incoming data\n"
|
||||
+ " of its destination to host:port., filtering the HTTP\n"
|
||||
+ " headers so it looks like the request is to the spoofed host,"
|
||||
+ " and listens to host:proxyport to proxy HTTP requests.");
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the server pointing at the host and port specified using the private i2p
|
||||
* destination loaded from the given base64 stream. <p />
|
||||
@@ -818,6 +896,39 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run an SOCKS IRC tunnel on the given port number
|
||||
* @since 0.7.12
|
||||
*/
|
||||
public void runSOCKSIRCTunnel(String args[], Logging l) {
|
||||
if (args.length >= 1 && args.length <= 2) {
|
||||
int _port = -1;
|
||||
try {
|
||||
_port = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
notifyEvent("sockstunnelTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isShared = false;
|
||||
if (args.length > 1)
|
||||
isShared = "true".equalsIgnoreCase(args[1].trim());
|
||||
|
||||
ownDest = !isShared;
|
||||
I2PTunnelTask task;
|
||||
task = new I2PSOCKSIRCTunnel(_port, l, ownDest, (EventDispatcher) this, this);
|
||||
addtask(task);
|
||||
notifyEvent("sockstunnelTaskId", Integer.valueOf(task.getId()));
|
||||
} else {
|
||||
l.log("sockstunnel <port>");
|
||||
l.log(" creates a tunnel that distributes SOCKS requests.");
|
||||
notifyEvent("sockstunnelTaskId", Integer.valueOf(-1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Streamr client
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
@@ -78,8 +78,9 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
i2ps.setReadTimeout(readTimeout);
|
||||
new I2PTunnelRunner(s, i2ps, sockLock, null, mySockets);
|
||||
} catch (Exception ex) {
|
||||
_log.info("Error connecting", ex);
|
||||
l.log(ex.getMessage());
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Error connecting", ex);
|
||||
//l.log("Error connecting: " + ex.getMessage());
|
||||
closeSocket(s);
|
||||
if (i2ps != null) {
|
||||
synchronized (sockLock) {
|
||||
|
||||
@@ -67,7 +67,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
// private Object conLock = new Object();
|
||||
|
||||
/** List of Socket for those accept()ed but not yet started up */
|
||||
private List _waitingSockets = new ArrayList(); // FIXME should be final and use a factory. FIXME
|
||||
protected final List _waitingSockets = new ArrayList(4); // FIXME should be final and use a factory. FIXME
|
||||
/** How many connections will we allow to be in the process of being built at once? */
|
||||
private int _numConnectionBuilders;
|
||||
/** How long will we allow sockets to sit in the _waitingSockets map before killing them? */
|
||||
@@ -89,12 +89,52 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
|
||||
private static final int DEFAULT_NUM_CONNECTION_BUILDERS = 5;
|
||||
private static final int DEFAULT_MAX_WAIT_TIME = 30*1000;
|
||||
|
||||
//public I2PTunnelClientBase(int localPort, boolean ownDest,
|
||||
// Logging l) {
|
||||
// I2PTunnelClientBase(localPort, ownDest, l, (EventDispatcher)null);
|
||||
//}
|
||||
|
||||
// true if we are chained from a server.
|
||||
private boolean chained = false;
|
||||
|
||||
public I2PTunnelClientBase(int localPort, Logging l, I2PSocketManager sktMgr,
|
||||
I2PTunnel tunnel, EventDispatcher notifyThis, long clientId )
|
||||
throws IllegalArgumentException {
|
||||
super(localPort + " (uninitialized)", notifyThis, tunnel);
|
||||
chained = true;
|
||||
sockMgr = sktMgr;
|
||||
_clientId = clientId;
|
||||
this.localPort = localPort;
|
||||
this.l = l;
|
||||
this.handlerName = handlerName + _clientId;
|
||||
_ownDest = true; // == ! shared client
|
||||
_context = tunnel.getContext();
|
||||
_context.statManager().createRateStat("i2ptunnel.client.closeBacklog", "How many pending sockets remain when we close one due to backlog?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.client.closeNoBacklog", "How many pending sockets remain when it was removed prior to backlog timeout?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.client.manageTime", "How long it takes to accept a socket and fire it into an i2ptunnel runner (or queue it for the pool)?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.client.buildRunTime", "How long it takes to run a queued socket into an i2ptunnel runner?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
|
||||
Thread t = new I2PAppThread(this);
|
||||
t.setName("Client " + _clientId);
|
||||
listenerReady = false;
|
||||
t.start();
|
||||
open = true;
|
||||
synchronized (this) {
|
||||
while (!listenerReady && open) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurePool(tunnel);
|
||||
|
||||
if (open && listenerReady) {
|
||||
l.log("Ready! Port " + getLocalPort());
|
||||
notifyEvent("openBaseClientResult", "ok");
|
||||
} else {
|
||||
l.log("Error listening - please see the logs!");
|
||||
notifyEvent("openBaseClientResult", "error");
|
||||
}
|
||||
}
|
||||
public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l,
|
||||
EventDispatcher notifyThis, String handlerName,
|
||||
I2PTunnel tunnel) throws IllegalArgumentException {
|
||||
@@ -188,7 +228,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
*
|
||||
*/
|
||||
private void configurePool(I2PTunnel tunnel) {
|
||||
_waitingSockets = new ArrayList(4);
|
||||
//_waitingSockets = new ArrayList(4);
|
||||
|
||||
Properties opts = tunnel.getClientOptions();
|
||||
String maxWait = opts.getProperty(PROP_MAX_WAIT_TIME, DEFAULT_MAX_WAIT_TIME+"");
|
||||
@@ -559,10 +599,12 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
}
|
||||
return false;
|
||||
}
|
||||
I2PSession session = sockMgr.getSession();
|
||||
if (session != null) {
|
||||
getTunnel().removeSession(session);
|
||||
}
|
||||
if (!chained) {
|
||||
I2PSession session = sockMgr.getSession();
|
||||
if (session != null) {
|
||||
getTunnel().removeSession(session);
|
||||
}
|
||||
} // else the app chaining to this one closes it!
|
||||
}
|
||||
l.log("Closing client " + toString());
|
||||
open = false;
|
||||
@@ -599,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);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
* Everyone is permitted to copy and distribute verbatim or modified
|
||||
* copies of this license document, and changing it is allowed as long
|
||||
* as the name is changed.
|
||||
*
|
||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
*
|
||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and liscense questions.
|
||||
*/
|
||||
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
// import java.util.ArrayList;
|
||||
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.util.EventDispatcher;
|
||||
|
||||
|
||||
/**
|
||||
* Reuse HTTP server's I2PSocketManager for a proxy with no outproxy capability.
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class I2PTunnelHTTPBidirProxy extends I2PTunnelHTTPClient implements Runnable {
|
||||
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if the I2PTunnel does not contain
|
||||
* valid config to contact the router
|
||||
*/
|
||||
public I2PTunnelHTTPBidirProxy(int localPort, Logging l, I2PSocketManager sockMgr, I2PTunnel tunnel, EventDispatcher notifyThis, long clientId) {
|
||||
super(localPort, l, sockMgr, tunnel, notifyThis, clientId);
|
||||
// proxyList = new ArrayList();
|
||||
|
||||
setName(getLocalPort() + " -> HTTPClient [NO PROXIES]");
|
||||
startRunning();
|
||||
|
||||
notifyEvent("openHTTPClientResult", "ok");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
|
||||
* (c) 2003 - 2004 mihi
|
||||
*/
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetAddress;
|
||||
|
||||
import net.i2p.util.EventDispatcher;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class I2PTunnelHTTPBidirServer extends I2PTunnelHTTPServer {
|
||||
private final static Log log = new Log(I2PTunnelHTTPBidirServer.class);
|
||||
|
||||
public I2PTunnelHTTPBidirServer(InetAddress host, int port, int proxyport, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privData, spoofHost, l, notifyThis, tunnel);
|
||||
finishSetupI2PTunnelHTTPBidirServer(l, proxyport);
|
||||
}
|
||||
|
||||
public I2PTunnelHTTPBidirServer(InetAddress host, int port, int proxyport, File privkey, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privkey, privkeyname, spoofHost, l, notifyThis, tunnel);
|
||||
finishSetupI2PTunnelHTTPBidirServer(l, proxyport);
|
||||
}
|
||||
|
||||
public I2PTunnelHTTPBidirServer(InetAddress host, int port, int proxyport, InputStream privData, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privData, privkeyname, spoofHost, l, notifyThis, tunnel);
|
||||
finishSetupI2PTunnelHTTPBidirServer(l, proxyport);
|
||||
}
|
||||
|
||||
private void finishSetupI2PTunnelHTTPBidirServer(Logging l, int proxyport) {
|
||||
|
||||
localPort = proxyport;
|
||||
bidir = true;
|
||||
|
||||
/* start the httpclient */
|
||||
task = new I2PTunnelHTTPBidirProxy(localPort, l, sockMgr, getTunnel(), getEventDispatcher(), __serverId);
|
||||
sockMgr.setName("Server"); // TO-DO: Need to change this to "Bidir"!
|
||||
getTunnel().addSession(sockMgr.getSession());
|
||||
l.log("Ready!");
|
||||
notifyEvent("openServerResult", "ok");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ import java.util.StringTokenizer;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.client.streaming.I2PSocketOptions;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
@@ -45,8 +47,14 @@ import net.i2p.util.Translate;
|
||||
* $method http://i2p/$b64key/$path $protocolVersion
|
||||
* or
|
||||
* $method /$site/$path $protocolVersion
|
||||
* or (deprecated)
|
||||
* $method /eepproxy/$site/$path $protocolVersion
|
||||
* </pre>
|
||||
*
|
||||
* Note that http://i2p/$b64key/... and /eepproxy/$site/... are not recommended
|
||||
* in browsers or other user-visible applications, as relative links will not
|
||||
* resolve correctly, cookies won't work, etc.
|
||||
*
|
||||
* If the $site resolves with the I2P naming service, then it is directed towards
|
||||
* that eepsite, otherwise it is directed towards this client's outproxy (typically
|
||||
* "squid.i2p"). Only HTTP is supported (no HTTPS, ftp, mailto, etc). Both GET
|
||||
@@ -56,7 +64,7 @@ import net.i2p.util.Translate;
|
||||
public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable {
|
||||
private static final Log _log = new Log(I2PTunnelHTTPClient.class);
|
||||
|
||||
private final List proxyList;
|
||||
protected final List proxyList = new ArrayList();
|
||||
|
||||
private HashMap addressHelpers = new HashMap();
|
||||
|
||||
@@ -150,7 +158,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
|
||||
private static final File _errorDir = new File(I2PAppContext.getGlobalContext().getBaseDir(), "docs");
|
||||
|
||||
public I2PTunnelHTTPClient(int localPort, Logging l, I2PSocketManager sockMgr, I2PTunnel tunnel, EventDispatcher notifyThis, long clientId) {
|
||||
super(localPort, l, sockMgr, tunnel, notifyThis, clientId);
|
||||
// proxyList = new ArrayList();
|
||||
|
||||
setName(getLocalPort() + " -> HTTPClient [NO PROXIES]");
|
||||
startRunning();
|
||||
|
||||
notifyEvent("openHTTPClientResult", "ok");
|
||||
}
|
||||
/**
|
||||
* @throws IllegalArgumentException if the I2PTunnel does not contain
|
||||
* valid config to contact the router
|
||||
@@ -160,7 +176,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
I2PTunnel tunnel) throws IllegalArgumentException {
|
||||
super(localPort, ownDest, l, notifyThis, "HTTPHandler " + (++__clientId), tunnel);
|
||||
|
||||
proxyList = new ArrayList();
|
||||
//proxyList = new ArrayList(); // We won't use outside of i2p
|
||||
if (waitEventValue("openBaseClientResult").equals("error")) {
|
||||
notifyEvent("openHTTPClientResult", "error");
|
||||
return;
|
||||
@@ -187,7 +203,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
if (size <= 0) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Proxy list is empty - no outproxy available");
|
||||
l.log("Proxy list is emtpy - no outproxy available");
|
||||
l.log("Proxy list is empty - no outproxy available");
|
||||
return null;
|
||||
}
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
@@ -251,6 +267,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
/**
|
||||
* Overridden to close internal socket too.
|
||||
*/
|
||||
@Override
|
||||
public boolean close(boolean forced) {
|
||||
boolean rv = super.close(forced);
|
||||
if (this.isr != null)
|
||||
@@ -293,14 +310,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
|
||||
if (method == null) { // first line (GET /base64/realaddr)
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix(requestId) + "Method is null for [" + line + "]");
|
||||
_log.debug(getPrefix(requestId) + "First line [" + line + "]");
|
||||
|
||||
int pos = line.indexOf(" ");
|
||||
if (pos == -1) break;
|
||||
method = line.substring(0, pos);
|
||||
// TODO use Java URL class to make all this simpler and more robust
|
||||
// That will also fix IPV6 [a:b:c]
|
||||
String request = line.substring(pos + 1);
|
||||
if (request.startsWith("/") && getTunnel().getClientOptions().getProperty("i2ptunnel.noproxy") != null) {
|
||||
// what is this for ???
|
||||
request = "http://i2p" + request;
|
||||
} else if (request.startsWith("/eepproxy/")) {
|
||||
// /eepproxy/foo.i2p/bar/baz.html HTTP/1.0
|
||||
@@ -312,6 +331,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
// "http://" + "foo.i2p/bar/baz.html" + " HTTP/1.0"
|
||||
request = "http://" + uri + subRequest.substring(protopos);
|
||||
} else if (request.toLowerCase().startsWith("http://i2p/")) {
|
||||
// http://i2p/b64key/bar/baz.html HTTP/1.0
|
||||
String subRequest = request.substring("http://i2p/".length());
|
||||
int protopos = subRequest.indexOf(" ");
|
||||
String uri = subRequest.substring(0, protopos);
|
||||
if (uri.indexOf("/") == -1) {
|
||||
uri = uri + "/";
|
||||
}
|
||||
// "http://" + "b64key/bar/baz.html" + " HTTP/1.0"
|
||||
request = "http://" + uri + subRequest.substring(protopos);
|
||||
}
|
||||
|
||||
pos = request.indexOf("//");
|
||||
@@ -324,6 +353,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
|
||||
targetRequest = request;
|
||||
|
||||
// pos is the start of the path
|
||||
pos = request.indexOf("/");
|
||||
if (pos == -1) {
|
||||
method = null;
|
||||
@@ -336,7 +366,17 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
int port = 80;
|
||||
if(posPort != -1) {
|
||||
String[] parts = host.split(":");
|
||||
try {
|
||||
host = parts[0];
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
if (out != null) {
|
||||
out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
|
||||
writeFooter(out);
|
||||
}
|
||||
s.close();
|
||||
return;
|
||||
|
||||
}
|
||||
try {
|
||||
port = Integer.parseInt(parts[1]);
|
||||
} catch(Exception exc) {
|
||||
@@ -344,9 +384,22 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
if (host.toLowerCase().equals("proxy.i2p")) {
|
||||
// Go through the various types of host names, set
|
||||
// the host and destination variables accordingly,
|
||||
// and transform the first line.
|
||||
// For all i2p network hosts, ensure that the host is a
|
||||
// Base 32 hostname so that we do not reveal our name for it
|
||||
// in our addressbook (all naming is local),
|
||||
// and it is removed from the request line.
|
||||
|
||||
if (host.length() >= 516 && host.indexOf(".") < 0) {
|
||||
// http://b64key/bar/baz.html
|
||||
destination = host;
|
||||
host = getHostName(destination);
|
||||
line = method + ' ' + request.substring(pos);
|
||||
} else if (host.toLowerCase().equals("proxy.i2p")) {
|
||||
// so we don't do any naming service lookups
|
||||
destination = "proxy.i2p";
|
||||
destination = host;
|
||||
usingInternalServer = true;
|
||||
} else if (host.toLowerCase().endsWith(".i2p")) {
|
||||
// Destination gets the host name
|
||||
@@ -382,6 +435,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
|
||||
// Key contains data, lets not ignore it
|
||||
if (ahelperKey != null) {
|
||||
// ahelperKey will be validated later
|
||||
|
||||
// Host resolvable only with addresshelper
|
||||
if ( (host == null) || ("i2p".equals(host)) )
|
||||
@@ -391,12 +445,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
} else {
|
||||
// Host resolvable from database, verify addresshelper key
|
||||
// Silently bypass correct keys, otherwise alert
|
||||
if (!host.equals(ahelperKey))
|
||||
String destB64 = null;
|
||||
try {
|
||||
Destination _dest = I2PTunnel.destFromName(host);
|
||||
if (_dest != null)
|
||||
destB64 = _dest.toBase64();
|
||||
} catch (DataFormatException dfe) {}
|
||||
if (destB64 != null && !destB64.equals(ahelperKey))
|
||||
{
|
||||
// Conflict: handle when URL reconstruction done
|
||||
ahelperConflict = true;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + "], trusted key [" + host + "], specified key [" + ahelperKey + "].");
|
||||
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + "], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -418,14 +478,22 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
{
|
||||
|
||||
if (out != null) {
|
||||
long alias = I2PAppContext.getGlobalContext().random().nextLong();
|
||||
String trustedURL = protocol + uriPath + urlEncoding;
|
||||
String conflictURL = protocol + alias + ".i2p/?" + initialFragments;
|
||||
byte[] header = getErrorPage("ahelper-conflict", ERR_AHELPER_CONFLICT);
|
||||
out.write(header);
|
||||
out.write(_("To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper link by temporarily giving it a random alias, click <a href=\"{1}\">here</a>.", trustedURL, conflictURL).getBytes("UTF-8"));
|
||||
out.write(("<p></div>").getBytes());
|
||||
writeFooter(out);
|
||||
// convert ahelperKey to b32
|
||||
String alias = getHostName(ahelperKey);
|
||||
if (alias.equals("i2p")) {
|
||||
// bad ahelperKey
|
||||
byte[] header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
|
||||
writeErrorMessage(header, out, targetRequest, false, destination, null);
|
||||
} else {
|
||||
String trustedURL = protocol + uriPath + urlEncoding;
|
||||
// Fixme - any path is lost
|
||||
String conflictURL = protocol + alias + '/' + urlEncoding;
|
||||
byte[] header = getErrorPage("ahelper-conflict", ERR_AHELPER_CONFLICT);
|
||||
out.write(header);
|
||||
out.write(_("To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper destination, click <a href=\"{1}\">here</a>.", trustedURL, conflictURL).getBytes("UTF-8"));
|
||||
out.write(("<p></div>").getBytes());
|
||||
writeFooter(out);
|
||||
}
|
||||
}
|
||||
s.close();
|
||||
return;
|
||||
@@ -440,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);
|
||||
@@ -472,6 +542,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix(requestId) + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!");
|
||||
} else {
|
||||
// what is left for here? a hostname with no dots, and != "i2p"
|
||||
// and not a destination ???
|
||||
// Perhaps something in privatehosts.txt ...
|
||||
request = request.substring(pos + 1);
|
||||
pos = request.indexOf("/");
|
||||
if (pos < 0) {
|
||||
@@ -484,31 +557,49 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
return;
|
||||
}
|
||||
destination = request.substring(0, pos);
|
||||
host = getHostName(destination);
|
||||
line = method + " " + request.substring(pos);
|
||||
} // end host name processing
|
||||
|
||||
if (port != 80 && !usingWWWProxy) {
|
||||
if (out != null) {
|
||||
out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
|
||||
writeFooter(out);
|
||||
}
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isValid = usingWWWProxy || usingInternalServer || isSupportedAddress(host, protocol);
|
||||
if (!isValid) {
|
||||
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "notValid(" + host + ")");
|
||||
method = null;
|
||||
destination = null;
|
||||
break;
|
||||
} else if ((!usingWWWProxy) && (!usingInternalServer)) {
|
||||
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "host=getHostName(" + destination + ")");
|
||||
host = getHostName(destination); // hide original host
|
||||
}
|
||||
|
||||
// don't do this, it forces yet another hostname lookup,
|
||||
// and in all cases host was already set above
|
||||
//if ((!usingWWWProxy) && (!usingInternalServer)) {
|
||||
// String oldhost = host;
|
||||
// host = getHostName(destination); // hide original host
|
||||
// if (_log.shouldLog(Log.INFO))
|
||||
// _log.info(getPrefix(requestId) + " oldhost " + oldhost + " newhost " + host + " dest " + destination);
|
||||
//}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug(getPrefix(requestId) + "METHOD:" + method + ":");
|
||||
_log.debug(getPrefix(requestId) + "PROTOC:" + protocol + ":");
|
||||
_log.debug(getPrefix(requestId) + "HOST :" + host + ":");
|
||||
_log.debug(getPrefix(requestId) + "DEST :" + destination + ":");
|
||||
_log.debug(getPrefix(requestId) + "METHOD: \"" + method + "\"");
|
||||
_log.debug(getPrefix(requestId) + "PROTOC: \"" + protocol + "\"");
|
||||
_log.debug(getPrefix(requestId) + "HOST : \"" + host + "\"");
|
||||
_log.debug(getPrefix(requestId) + "DEST : \"" + destination + "\"");
|
||||
}
|
||||
|
||||
// end first line processing
|
||||
|
||||
} else {
|
||||
if (lowercaseLine.startsWith("host: ") && !usingWWWProxy) {
|
||||
// Note that we only pass the original Host: line through to the outproxy
|
||||
// But we don't create a Host: line if it wasn't sent to us
|
||||
line = "Host: " + host;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix(requestId) + "Setting host = " + host);
|
||||
@@ -565,7 +656,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
_log.debug(getPrefix(requestId) + "NewRequest header: [" + newRequest.toString() + "]");
|
||||
|
||||
if (method == null || destination == null) {
|
||||
l.log("No HTTP method found in the request.");
|
||||
//l.log("No HTTP method found in the request.");
|
||||
if (out != null) {
|
||||
if ("http://".equalsIgnoreCase(protocol))
|
||||
out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
|
||||
@@ -588,7 +679,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
return;
|
||||
}
|
||||
|
||||
Destination clientDest = I2PTunnel.destFromName(destination);
|
||||
// If the host is "i2p", the getHostName() lookup failed, don't try to
|
||||
// look it up again as the naming service does not do negative caching
|
||||
// so it will be slow.
|
||||
|
||||
Destination clientDest;
|
||||
if ("i2p".equals(host))
|
||||
clientDest = null;
|
||||
else
|
||||
clientDest = I2PTunnel.destFromName(destination);
|
||||
|
||||
if (clientDest == null) {
|
||||
//l.log("Could not resolve " + destination + ".");
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
@@ -623,24 +723,27 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
I2PTunnelRunner runner = new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
|
||||
} catch (SocketException ex) {
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
//l.log("Error connecting: " + ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch (IOException ex) {
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
//l.log("Error connecting: " + ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch (I2PException ex) {
|
||||
_log.info("getPrefix(requestId) + Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("getPrefix(requestId) + Error trying to connect", ex);
|
||||
//l.log("Error connecting: " + ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch (OutOfMemoryError oom) {
|
||||
IOException ex = new IOException("OOM");
|
||||
_log.info("getPrefix(requestId) + Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
_log.error("getPrefix(requestId) + Error trying to connect", oom);
|
||||
//l.log("Error connecting: " + ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
}
|
||||
@@ -652,6 +755,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
* We can't use BufferedReader for POST because we can't have readahead,
|
||||
* since we are passing the stream on to I2PTunnelRunner for the POST data.
|
||||
*
|
||||
* Warning - BufferedReader removes \r, DataHelper does not
|
||||
* Warning - DataHelper limits line length, BufferedReader does not
|
||||
* Todo: Limit line length for buffered reads, or go back to unbuffered for all
|
||||
*/
|
||||
private static class InputReader {
|
||||
BufferedReader _br;
|
||||
@@ -669,12 +775,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return b32hash.b32.i2p, or "i2p" on lookup failure.
|
||||
* Prior to 0.7.12, returned b64 key
|
||||
*/
|
||||
private final static String getHostName(String host) {
|
||||
if (host == null) return null;
|
||||
if (host.length() == 60 && host.toLowerCase().endsWith(".b32.i2p"))
|
||||
return host;
|
||||
try {
|
||||
Destination dest = I2PTunnel.destFromName(host);
|
||||
if (dest == null) return "i2p";
|
||||
return dest.toBase64();
|
||||
return Base32.encode(dest.calculateHash().getData()) + ".b32.i2p";
|
||||
} catch (DataFormatException dfe) {
|
||||
return "i2p";
|
||||
}
|
||||
@@ -848,8 +960,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
|
||||
private final static String SUPPORTED_HOSTS[] = { "i2p", "www.i2p.com", "i2p."};
|
||||
|
||||
/** @param host ignored */
|
||||
private static boolean isSupportedAddress(String host, String protocol) {
|
||||
if ((host == null) || (protocol == null)) return false;
|
||||
|
||||
/****
|
||||
* Let's not look up the name _again_
|
||||
* and now that host is a b32, this was failing
|
||||
*
|
||||
boolean found = false;
|
||||
String lcHost = host.toLowerCase();
|
||||
for (int i = 0; i < SUPPORTED_HOSTS.length; i++) {
|
||||
@@ -866,7 +984,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
} catch (DataFormatException dfe) {
|
||||
}
|
||||
}
|
||||
|
||||
****/
|
||||
return protocol.equalsIgnoreCase("http://");
|
||||
}
|
||||
|
||||
|
||||
@@ -39,21 +39,21 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
|
||||
public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privData, l, notifyThis, tunnel);
|
||||
_spoofHost = spoofHost;
|
||||
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
|
||||
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpNullWorkaround", "How often an http server works around a streaming lib or i2ptunnel bug", "I2PTunnel", new long[] { 60*1000, 10*60*1000 });
|
||||
setupI2PTunnelHTTPServer(spoofHost);
|
||||
}
|
||||
|
||||
public I2PTunnelHTTPServer(InetAddress host, int port, File privkey, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privkey, privkeyname, l, notifyThis, tunnel);
|
||||
_spoofHost = spoofHost;
|
||||
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
|
||||
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpNullWorkaround", "How often an http server works around a streaming lib or i2ptunnel bug", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000 });
|
||||
setupI2PTunnelHTTPServer(spoofHost);
|
||||
}
|
||||
|
||||
public I2PTunnelHTTPServer(InetAddress host, int port, InputStream privData, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privData, privkeyname, l, notifyThis, tunnel);
|
||||
_spoofHost = spoofHost;
|
||||
setupI2PTunnelHTTPServer(spoofHost);
|
||||
}
|
||||
|
||||
private void setupI2PTunnelHTTPServer(String spoofHost) {
|
||||
_spoofHost = spoofHost;
|
||||
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
|
||||
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpNullWorkaround", "How often an http server works around a streaming lib or i2ptunnel bug", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000 });
|
||||
}
|
||||
@@ -321,6 +321,9 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** ridiculously long, just to prevent OOM DOS @since 0.7.13 */
|
||||
private static final int MAX_HEADERS = 60;
|
||||
|
||||
private Properties readHeaders(InputStream in, StringBuilder command) throws IOException {
|
||||
Properties headers = new Properties();
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
@@ -344,7 +347,10 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
if (trimmed > 0)
|
||||
getTunnel().getContext().statManager().addRateData("i2ptunnel.httpNullWorkaround", trimmed, 0);
|
||||
|
||||
int i = 0;
|
||||
while (true) {
|
||||
if (++i > MAX_HEADERS)
|
||||
throw new IOException("Too many header lines - max " + MAX_HEADERS);
|
||||
buf.setLength(0);
|
||||
ok = DataHelper.readLine(in, buf);
|
||||
if (!ok) throw new IOException("EOF reached before the end of the headers [" + buf.toString() + "]");
|
||||
|
||||
@@ -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;
|
||||
@@ -82,15 +82,15 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
try {
|
||||
i2ps = createI2PSocket(clientDest);
|
||||
i2ps.setReadTimeout(readTimeout);
|
||||
StringBuilder expectedPong = new StringBuilder();
|
||||
Thread in = new I2PAppThread(new IrcInboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " in");
|
||||
StringBuffer expectedPong = new StringBuffer();
|
||||
Thread in = new I2PAppThread(new IrcInboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " in", true);
|
||||
in.start();
|
||||
Thread out = new I2PAppThread(new IrcOutboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " out");
|
||||
Thread out = new I2PAppThread(new IrcOutboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " out", true);
|
||||
out.start();
|
||||
} catch (Exception ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error connecting", ex);
|
||||
l.log(ex.getMessage());
|
||||
//l.log("Error connecting: " + ex.getMessage());
|
||||
closeSocket(s);
|
||||
if (i2ps != null) {
|
||||
synchronized (sockLock) {
|
||||
@@ -117,13 +117,13 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
/*************************************************************************
|
||||
*
|
||||
*/
|
||||
private class IrcInboundFilter implements Runnable {
|
||||
public static class IrcInboundFilter implements Runnable {
|
||||
|
||||
private Socket local;
|
||||
private I2PSocket remote;
|
||||
private StringBuilder expectedPong;
|
||||
private StringBuffer expectedPong;
|
||||
|
||||
IrcInboundFilter(Socket _local, I2PSocket _remote, StringBuilder pong) {
|
||||
public IrcInboundFilter(Socket _local, I2PSocket _remote, StringBuffer pong) {
|
||||
local=_local;
|
||||
remote=_remote;
|
||||
expectedPong=pong;
|
||||
@@ -191,13 +191,13 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
/*************************************************************************
|
||||
*
|
||||
*/
|
||||
private class IrcOutboundFilter implements Runnable {
|
||||
public static class IrcOutboundFilter implements Runnable {
|
||||
|
||||
private Socket local;
|
||||
private I2PSocket remote;
|
||||
private StringBuilder expectedPong;
|
||||
private StringBuffer expectedPong;
|
||||
|
||||
IrcOutboundFilter(Socket _local, I2PSocket _remote, StringBuilder pong) {
|
||||
public IrcOutboundFilter(Socket _local, I2PSocket _remote, StringBuffer pong) {
|
||||
local=_local;
|
||||
remote=_remote;
|
||||
expectedPong=pong;
|
||||
@@ -266,7 +266,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
*
|
||||
*/
|
||||
|
||||
public String inboundFilter(String s, StringBuilder expectedPong) {
|
||||
public static String inboundFilter(String s, StringBuffer expectedPong) {
|
||||
|
||||
String field[]=s.split(" ",4);
|
||||
String command;
|
||||
@@ -353,7 +353,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
return null;
|
||||
}
|
||||
|
||||
public String outboundFilter(String s, StringBuilder expectedPong) {
|
||||
public static String outboundFilter(String s, StringBuffer expectedPong) {
|
||||
|
||||
String field[]=s.split(" ",3);
|
||||
String command;
|
||||
@@ -378,7 +378,8 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
"KICK",
|
||||
"HELPME",
|
||||
"RULES",
|
||||
"TOPIC"
|
||||
"TOPIC",
|
||||
"ISON" // jIRCii uses this for a ping (response is 303)
|
||||
};
|
||||
|
||||
if(field[0].length()==0)
|
||||
@@ -390,7 +391,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
|
||||
command = field[0].toUpperCase();
|
||||
|
||||
if ("PING".equalsIgnoreCase(command)) {
|
||||
if ("PING".equals(command)) {
|
||||
// Most clients just send a PING and are happy with any old PONG. Others,
|
||||
// like BitchX, actually expect certain behavior. It sends two different pings:
|
||||
// "PING :irc.freshcoffee.i2p" and "PING 1234567890 127.0.0.1" (where the IP is the proxy)
|
||||
@@ -426,19 +427,19 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
|
||||
return rv;
|
||||
}
|
||||
if ("PONG".equalsIgnoreCase(command))
|
||||
if ("PONG".equals(command))
|
||||
return "PONG 127.0.0.1"; // no way to know what the ircd to i2ptunnel server con is, so localhost works
|
||||
|
||||
// Allow all allowedCommands
|
||||
for(int i=0;i<allowedCommands.length;i++)
|
||||
{
|
||||
if(allowedCommands[i].equalsIgnoreCase(command))
|
||||
if(allowedCommands[i].equals(command))
|
||||
return s;
|
||||
}
|
||||
|
||||
// mIRC sends "NOTICE user :DCC Send file (IP)"
|
||||
// in addition to the CTCP version
|
||||
if("NOTICE".equalsIgnoreCase(command))
|
||||
if("NOTICE".equals(command))
|
||||
{
|
||||
String msg = field[2];
|
||||
if(msg.startsWith(":DCC "))
|
||||
@@ -447,7 +448,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
|
||||
// Allow PRIVMSG, but block CTCP (except ACTION).
|
||||
if("PRIVMSG".equalsIgnoreCase(command) || "NOTICE".equalsIgnoreCase(command))
|
||||
if("PRIVMSG".equals(command) || "NOTICE".equals(command))
|
||||
{
|
||||
String msg;
|
||||
msg = field[2];
|
||||
@@ -465,14 +466,16 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
return s;
|
||||
}
|
||||
|
||||
if("USER".equalsIgnoreCase(command)) {
|
||||
if("USER".equals(command)) {
|
||||
int idx = field[2].lastIndexOf(":");
|
||||
if(idx<0)
|
||||
return "USER user hostname localhost :realname";
|
||||
String realname = field[2].substring(idx+1);
|
||||
String ret = "USER "+field[1]+" hostname localhost :"+realname;
|
||||
return ret;
|
||||
} else if ("QUIT".equalsIgnoreCase(command)) {
|
||||
}
|
||||
|
||||
if ("QUIT".equals(command)) {
|
||||
return "QUIT :leaving";
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -124,11 +124,14 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
||||
OutputStream i2pout = i2ps.getOutputStream(); //new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
|
||||
if (initialI2PData != null) {
|
||||
synchronized (slock) {
|
||||
// this does not increment totalSent
|
||||
i2pout.write(initialI2PData);
|
||||
// do NOT flush here, it will block and then onTimeout.run() won't happen on fail.
|
||||
//i2pout.flush();
|
||||
}
|
||||
}
|
||||
if (initialSocketData != null) {
|
||||
// this does not increment totalReceived
|
||||
out.write(initialSocketData);
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -150,6 +153,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("runner has a timeout job, totalReceived = " + totalReceived
|
||||
+ " totalSent = " + totalSent + " job = " + onTimeout);
|
||||
// should we only look at totalReceived?
|
||||
if ( (totalSent <= 0) && (totalReceived <= 0) )
|
||||
onTimeout.run();
|
||||
}
|
||||
@@ -271,7 +275,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Flushing after sending " + len + " bytes through");
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(direction + ": " + len + " bytes flushed through to "
|
||||
_log.debug(direction + ": " + len + " bytes flushed through " + (_toI2P ? "to " : "from ")
|
||||
+ i2ps.getPeerDestination().calculateHash().toBase64().substring(0,6));
|
||||
try {
|
||||
Thread.sleep(I2PTunnel.PACKET_DELAY);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -48,26 +49,29 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
protected long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
|
||||
private static final boolean DEFAULT_USE_POOL = false;
|
||||
|
||||
protected static volatile long __serverId = 0;
|
||||
private static final String PROP_HANDLER_COUNT = "i2ptunnel.blockingHandlerCount";
|
||||
private static final int DEFAULT_HANDLER_COUNT = 10;
|
||||
|
||||
|
||||
|
||||
protected I2PTunnelTask task = null;
|
||||
protected boolean bidir = false;
|
||||
|
||||
private int DEFAULT_LOCALPORT = 4488;
|
||||
protected int localPort = DEFAULT_LOCALPORT;
|
||||
|
||||
public I2PTunnelServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host + ":" + port + " <- " + privData, notifyThis, tunnel);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(privData));
|
||||
String usePool = tunnel.getClientOptions().getProperty("i2ptunnel.usePool");
|
||||
if (usePool != null)
|
||||
_usePool = "true".equalsIgnoreCase(usePool);
|
||||
else
|
||||
_usePool = DEFAULT_USE_POOL;
|
||||
SetUsePool(tunnel);
|
||||
init(host, port, bais, privData, l);
|
||||
}
|
||||
|
||||
public I2PTunnelServer(InetAddress host, int port, File privkey, String privkeyname, Logging l,
|
||||
EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host + ":" + port + " <- " + privkeyname, notifyThis, tunnel);
|
||||
String usePool = tunnel.getClientOptions().getProperty("i2ptunnel.usePool");
|
||||
if (usePool != null)
|
||||
_usePool = "true".equalsIgnoreCase(usePool);
|
||||
else
|
||||
_usePool = DEFAULT_USE_POOL;
|
||||
SetUsePool(tunnel);
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(privkey);
|
||||
@@ -83,12 +87,17 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
|
||||
public I2PTunnelServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host + ":" + port + " <- " + privkeyname, notifyThis, tunnel);
|
||||
String usePool = tunnel.getClientOptions().getProperty("i2ptunnel.usePool");
|
||||
SetUsePool(tunnel);
|
||||
init(host, port, privData, privkeyname, l);
|
||||
}
|
||||
|
||||
|
||||
private void SetUsePool(I2PTunnel Tunnel) {
|
||||
String usePool = Tunnel.getClientOptions().getProperty("i2ptunnel.usePool");
|
||||
if (usePool != null)
|
||||
_usePool = "true".equalsIgnoreCase(usePool);
|
||||
else
|
||||
_usePool = DEFAULT_USE_POOL;
|
||||
init(host, port, privData, privkeyname, l);
|
||||
}
|
||||
|
||||
private void init(InetAddress host, int port, InputStream privData, String privkeyname, Logging l) {
|
||||
@@ -106,17 +115,29 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
// copy the privData to a new BAIS, so we can always reset() it if we have to retry
|
||||
ByteArrayInputStream privDataCopy;
|
||||
try {
|
||||
privDataCopy = copyOfInputStream(privData);
|
||||
} catch (IOException ioe) {
|
||||
_log.log(Log.CRIT, "Cannot read private key data for " + privkeyname, ioe);
|
||||
return;
|
||||
}
|
||||
|
||||
// Todo: Can't stop a tunnel from the UI while it's in this loop (no session yet)
|
||||
while (sockMgr == null) {
|
||||
synchronized (slock) {
|
||||
sockMgr = I2PSocketManagerFactory.createManager(privData, getTunnel().host, portNum,
|
||||
sockMgr = I2PSocketManagerFactory.createManager(privDataCopy, getTunnel().host, portNum,
|
||||
props);
|
||||
|
||||
}
|
||||
if (sockMgr == null) {
|
||||
_log.log(Log.CRIT, "Unable to create socket manager");
|
||||
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
|
||||
privDataCopy.reset();
|
||||
}
|
||||
}
|
||||
|
||||
sockMgr.setName("Server");
|
||||
getTunnel().addSession(sockMgr.getSession());
|
||||
l.log("Ready!");
|
||||
@@ -124,8 +145,24 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
open = true;
|
||||
}
|
||||
|
||||
|
||||
private static volatile long __serverId = 0;
|
||||
/**
|
||||
* Copy input stream to a byte array, so we can retry
|
||||
* @since 0.7.10
|
||||
*/
|
||||
private static ByteArrayInputStream copyOfInputStream(InputStream is) throws IOException {
|
||||
byte[] buf = new byte[128];
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(768);
|
||||
try {
|
||||
int read;
|
||||
while ((read = is.read(buf)) >= 0) {
|
||||
os.write(buf, 0, read);
|
||||
}
|
||||
} finally {
|
||||
try { is.close(); } catch (IOException ioe) {}
|
||||
// don't need to close BAOS
|
||||
}
|
||||
return new ByteArrayInputStream(os.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Start running the I2PTunnelServer.
|
||||
@@ -158,6 +195,9 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
|
||||
public boolean close(boolean forced) {
|
||||
if (!open) return true;
|
||||
if (task != null) {
|
||||
task.close(forced);
|
||||
}
|
||||
synchronized (lock) {
|
||||
if (!forced && sockMgr.listSockets().size() != 0) {
|
||||
l.log("There are still active connections!");
|
||||
@@ -173,7 +213,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
sockMgr.getSession().destroySession();
|
||||
} catch (I2PException ex) {
|
||||
_log.error("Error destroying the session", ex);
|
||||
System.exit(1);
|
||||
//System.exit(1);
|
||||
}
|
||||
l.log("Server shut down.");
|
||||
open = false;
|
||||
@@ -181,9 +221,6 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private static final String PROP_HANDLER_COUNT = "i2ptunnel.blockingHandlerCount";
|
||||
private static final int DEFAULT_HANDLER_COUNT = 10;
|
||||
|
||||
protected int getHandlerCount() {
|
||||
int rv = DEFAULT_HANDLER_COUNT;
|
||||
String cnt = getTunnel().getClientOptions().getProperty(PROP_HANDLER_COUNT);
|
||||
@@ -257,6 +294,8 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
}
|
||||
|
||||
protected void blockingHandle(I2PSocket socket) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Incoming connection to '" + toString() + "' from: " + socket.getPeerDestination().calculateHash().toBase64());
|
||||
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
|
||||
long afterSocket = -1;
|
||||
//local is fast, so synchronously. Does not need that many
|
||||
|
||||
@@ -144,6 +144,8 @@ public class TunnelController implements Logging {
|
||||
startIrcClient();
|
||||
} else if("sockstunnel".equals(type)) {
|
||||
startSocksClient();
|
||||
} else if("socksirctunnel".equals(type)) {
|
||||
startSocksIRCClient();
|
||||
} else if("connectclient".equals(type)) {
|
||||
startConnectClient();
|
||||
} else if ("client".equals(type)) {
|
||||
@@ -154,6 +156,8 @@ public class TunnelController implements Logging {
|
||||
startServer();
|
||||
} else if ("httpserver".equals(type)) {
|
||||
startHttpServer();
|
||||
} else if ("httpbidirserver".equals(type)) {
|
||||
startHttpBidirServer();
|
||||
} else if ("ircserver".equals(type)) {
|
||||
startIrcServer();
|
||||
} else if ("streamrserver".equals(type)) {
|
||||
@@ -209,6 +213,14 @@ public class TunnelController implements Logging {
|
||||
_tunnel.runSOCKSTunnel(new String[] { listenPort, sharedClient }, this);
|
||||
}
|
||||
|
||||
/** @since 0.7.12 */
|
||||
private void startSocksIRCClient() {
|
||||
setListenOn();
|
||||
String listenPort = getListenPort();
|
||||
String sharedClient = getSharedClient();
|
||||
_tunnel.runSOCKSIRCTunnel(new String[] { listenPort, sharedClient }, this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Streamr client is a UDP server, use the listenPort field for targetPort
|
||||
* and the listenOnInterface field for the targetHost
|
||||
@@ -294,6 +306,16 @@ public class TunnelController implements Logging {
|
||||
_tunnel.runHttpServer(new String[] { targetHost, targetPort, spoofedHost, privKeyFile }, this);
|
||||
}
|
||||
|
||||
private void startHttpBidirServer() {
|
||||
setListenOn();
|
||||
String targetHost = getTargetHost();
|
||||
String targetPort = getTargetPort();
|
||||
String listenPort = getListenPort();
|
||||
String spoofedHost = getSpoofedHost();
|
||||
String privKeyFile = getPrivKeyFile();
|
||||
_tunnel.runHttpBidirServer(new String[] { targetHost, targetPort, listenPort, spoofedHost, privKeyFile }, this);
|
||||
}
|
||||
|
||||
private void startIrcServer() {
|
||||
String targetHost = getTargetHost();
|
||||
String targetPort = getTargetPort();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/* I2PSOCKSTunnel is released under the terms of the GNU GPL,
|
||||
* with an additional exception. For further details, see the
|
||||
* licensing terms in I2PTunnel.java.
|
||||
*
|
||||
* Copyright (c) 2004 by human
|
||||
*/
|
||||
package net.i2p.i2ptunnel.socks;
|
||||
|
||||
import java.net.Socket;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.i2ptunnel.I2PTunnel;
|
||||
import net.i2p.i2ptunnel.I2PTunnelIRCClient;
|
||||
import net.i2p.i2ptunnel.Logging;
|
||||
import net.i2p.util.EventDispatcher;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/*
|
||||
* Pipe SOCKS IRC connections through I2PTunnelIRCClient filtering,
|
||||
* to get the best of both worlds:
|
||||
*
|
||||
* - SOCKS lets you specify the host so you don't have to set up
|
||||
* a tunnel for each IRC server in advance
|
||||
* - IRC filtering for security
|
||||
*
|
||||
* @since 0.7.12
|
||||
* @author zzz
|
||||
*/
|
||||
public class I2PSOCKSIRCTunnel extends I2PSOCKSTunnel {
|
||||
|
||||
private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(I2PSOCKSIRCTunnel.class);
|
||||
private static int __clientId = 0;
|
||||
|
||||
public I2PSOCKSIRCTunnel(int localPort, Logging l, boolean ownDest, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(localPort, l, ownDest, notifyThis, tunnel);
|
||||
setName(getLocalPort() + " -> SOCKSIRCTunnel");
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as in I2PSOCKSTunnel, but run the filters from I2PTunnelIRCClient
|
||||
* instead of I2PTunnelRunner
|
||||
*/
|
||||
@Override
|
||||
protected void clientConnectionRun(Socket s) {
|
||||
try {
|
||||
_log.error("SOCKS IRC Tunnel Start");
|
||||
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s);
|
||||
Socket clientSock = serv.getClientSocket();
|
||||
I2PSocket destSock = serv.getDestinationI2PSocket(this);
|
||||
StringBuffer expectedPong = new StringBuffer();
|
||||
Thread in = new I2PAppThread(new I2PTunnelIRCClient.IrcInboundFilter(clientSock, destSock, expectedPong), "SOCKS IRC Client " + (++__clientId) + " in", true);
|
||||
in.start();
|
||||
Thread out = new I2PAppThread(new I2PTunnelIRCClient.IrcOutboundFilter(clientSock, destSock, expectedPong), "SOCKS IRC Client " + __clientId + " out", true);
|
||||
out.start();
|
||||
} catch (SOCKSException e) {
|
||||
_log.error("Error from SOCKS connection", e);
|
||||
closeSocket(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -89,10 +89,10 @@ public class SOCKS5Server extends SOCKSServer {
|
||||
int method = Method.NO_ACCEPTABLE_METHODS;
|
||||
|
||||
for (int i = 0; i < nMethods; ++i) {
|
||||
method = in.readByte() & 0xff;
|
||||
if (method == Method.NO_AUTH_REQUIRED) {
|
||||
int meth = in.readByte() & 0xff;
|
||||
if (meth == Method.NO_AUTH_REQUIRED) {
|
||||
// That's fine, we do support this method
|
||||
break;
|
||||
method = meth;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ public class SOCKS5Server extends SOCKSServer {
|
||||
int socksVer = in.readByte() & 0xff;
|
||||
if (socksVer != SOCKS_VERSION_5) {
|
||||
_log.debug("error in SOCKS5 request (protocol != 5? wtf?)");
|
||||
throw new SOCKSException("Invalid protocol version in request");
|
||||
throw new SOCKSException("Invalid protocol version in request: " + socksVer);
|
||||
}
|
||||
|
||||
int command = in.readByte() & 0xff;
|
||||
@@ -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 {
|
||||
|
||||
@@ -50,7 +50,7 @@ public class SOCKSUDPUnwrapper implements Source, Sink {
|
||||
|
||||
int headerlen = h.getBytes().length;
|
||||
byte unwrapped[] = new byte[data.length - headerlen];
|
||||
System.arraycopy(unwrapped, 0, data, headerlen, unwrapped.length);
|
||||
System.arraycopy(data, headerlen, unwrapped, 0, unwrapped.length);
|
||||
this.sink.send(dest, unwrapped);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@ public class SOCKSUDPWrapper implements Source, Sink {
|
||||
|
||||
byte[] header = h.getBytes();
|
||||
byte wrapped[] = new byte[header.length + data.length];
|
||||
System.arraycopy(wrapped, 0, header, 0, header.length);
|
||||
System.arraycopy(wrapped, header.length, data, 0, data.length);
|
||||
System.arraycopy(header, 0, wrapped, 0, header.length);
|
||||
System.arraycopy(data, 0, wrapped, header.length, data.length);
|
||||
this.sink.send(from, wrapped);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ public class Pinger implements Source, Runnable {
|
||||
|
||||
public void start() {
|
||||
this.running = true;
|
||||
this.waitlock = new Object();
|
||||
//this.waitlock = new Object();
|
||||
this.thread.start();
|
||||
}
|
||||
|
||||
@@ -54,6 +54,6 @@ public class Pinger implements Source, Runnable {
|
||||
|
||||
protected Sink sink;
|
||||
protected Thread thread;
|
||||
protected Object waitlock; // FIXME should be final and use a factory. FIXME
|
||||
private final Object waitlock = new Object();
|
||||
protected boolean running;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class I2PSink implements Sink {
|
||||
|
||||
// create maker
|
||||
if (!raw)
|
||||
this.maker = new I2PDatagramMaker(this.sess);
|
||||
this.maker.setI2PDatagramMaker(this.sess);
|
||||
}
|
||||
|
||||
/** @param src ignored */
|
||||
@@ -54,20 +54,8 @@ public class I2PSink implements Sink {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
protected boolean raw;
|
||||
protected I2PSession sess;
|
||||
protected Destination dest;
|
||||
protected I2PDatagramMaker maker; // FIXME should be final and use a factory. FIXME
|
||||
protected final I2PDatagramMaker maker= new I2PDatagramMaker(); // FIXME should be final and use a factory. FIXME
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public class I2PSinkAnywhere implements Sink {
|
||||
|
||||
// create maker
|
||||
if (!raw)
|
||||
this.maker = new I2PDatagramMaker(this.sess);
|
||||
this.maker.setI2PDatagramMaker(this.sess);
|
||||
}
|
||||
|
||||
/** @param to - where it's going */
|
||||
@@ -52,20 +52,8 @@ public class I2PSinkAnywhere implements Sink {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
protected boolean raw;
|
||||
protected I2PSession sess;
|
||||
protected Destination dest;
|
||||
protected I2PDatagramMaker maker; // FIXME should be final and use a factory. FIXME
|
||||
protected final I2PDatagramMaker maker = new I2PDatagramMaker();
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@ public class IndexBean {
|
||||
private String _action;
|
||||
private int _tunnel;
|
||||
private long _prevNonce;
|
||||
private long _prevNonce2;
|
||||
private long _curNonce;
|
||||
private long _nextNonce;
|
||||
private String _passphrase;
|
||||
|
||||
private String _type;
|
||||
private String _name;
|
||||
@@ -79,8 +79,11 @@ public class IndexBean {
|
||||
public static final int NOT_RUNNING = 3;
|
||||
public static final int STANDBY = 4;
|
||||
|
||||
public static final String PROP_TUNNEL_PASSPHRASE = "i2ptunnel.passphrase";
|
||||
/** deprecated unimplemented, now using routerconsole realm */
|
||||
//public static final String PROP_TUNNEL_PASSPHRASE = "i2ptunnel.passphrase";
|
||||
public static final String PROP_TUNNEL_PASSPHRASE = "consolePassword";
|
||||
static final String PROP_NONCE = IndexBean.class.getName() + ".nonce";
|
||||
static final String PROP_NONCE_OLD = PROP_NONCE + '2';
|
||||
static final String CLIENT_NICKNAME = "shared clients";
|
||||
|
||||
public static final String PROP_THEME_NAME = "routerconsole.theme";
|
||||
@@ -96,10 +99,16 @@ public class IndexBean {
|
||||
_tunnel = -1;
|
||||
_curNonce = -1;
|
||||
_prevNonce = -1;
|
||||
_prevNonce2 = -1;
|
||||
try {
|
||||
String nonce2 = System.getProperty(PROP_NONCE_OLD);
|
||||
if (nonce2 != null)
|
||||
_prevNonce2 = Long.parseLong(nonce2);
|
||||
String nonce = System.getProperty(PROP_NONCE);
|
||||
if (nonce != null)
|
||||
if (nonce != null) {
|
||||
_prevNonce = Long.parseLong(nonce);
|
||||
System.setProperty(PROP_NONCE_OLD, nonce);
|
||||
}
|
||||
} catch (NumberFormatException nfe) {}
|
||||
_nextNonce = _context.random().nextLong();
|
||||
System.setProperty(PROP_NONCE, Long.toString(_nextNonce));
|
||||
@@ -117,8 +126,8 @@ public class IndexBean {
|
||||
}
|
||||
}
|
||||
|
||||
/** deprecated unimplemented, now using routerconsole realm */
|
||||
public void setPassphrase(String phrase) {
|
||||
_passphrase = phrase;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
@@ -134,19 +143,16 @@ public class IndexBean {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validPassphrase(String proposed) {
|
||||
if (proposed == null) return false;
|
||||
/** just check if console password option is set, jetty will do auth */
|
||||
private boolean validPassphrase() {
|
||||
String pass = _context.getProperty(PROP_TUNNEL_PASSPHRASE);
|
||||
if ( (pass != null) && (pass.trim().length() > 0) )
|
||||
return pass.trim().equals(proposed.trim());
|
||||
else
|
||||
return false;
|
||||
return pass != null && pass.trim().length() > 0;
|
||||
}
|
||||
|
||||
private String processAction() {
|
||||
if ( (_action == null) || (_action.trim().length() <= 0) || ("Cancel".equals(_action)))
|
||||
return "";
|
||||
if ( (_prevNonce != _curNonce) && (!validPassphrase(_passphrase)) )
|
||||
if ( (_prevNonce != _curNonce) && (_prevNonce2 != _curNonce) && (!validPassphrase()) )
|
||||
return "Invalid form submission, probably because you used the 'back' or 'reload' button on your browser. Please resubmit.";
|
||||
if ("Stop all".equals(_action))
|
||||
return stopAll();
|
||||
@@ -349,6 +355,7 @@ public class IndexBean {
|
||||
return ( ("client".equals(type)) ||
|
||||
("httpclient".equals(type)) ||
|
||||
("sockstunnel".equals(type)) ||
|
||||
("socksirctunnel".equals(type)) ||
|
||||
("connectclient".equals(type)) ||
|
||||
("streamrclient".equals(type)) ||
|
||||
("ircclient".equals(type)));
|
||||
@@ -385,10 +392,12 @@ public class IndexBean {
|
||||
else if ("server".equals(internalType)) return _("Standard server");
|
||||
else if ("httpserver".equals(internalType)) return _("HTTP server");
|
||||
else if ("sockstunnel".equals(internalType)) return _("SOCKS 4/4a/5 proxy");
|
||||
else if ("socksirctunnel".equals(internalType)) return _("SOCKS IRC proxy");
|
||||
else if ("connectclient".equals(internalType)) return _("CONNECT/SSL/HTTPS proxy");
|
||||
else if ("ircserver".equals(internalType)) return _("IRC server");
|
||||
else if ("streamrclient".equals(internalType)) return _("Streamr client");
|
||||
else if ("streamrserver".equals(internalType)) return _("Streamr server");
|
||||
else if ("httpbidirserver".equals(internalType)) return _("HTTP bidir");
|
||||
else return internalType;
|
||||
}
|
||||
|
||||
@@ -779,8 +788,11 @@ public class IndexBean {
|
||||
config.setProperty("listenPort", _port);
|
||||
if (_reachableByOther != null)
|
||||
config.setProperty("interface", _reachableByOther);
|
||||
else
|
||||
else if (_reachableBy != null)
|
||||
config.setProperty("interface", _reachableBy);
|
||||
else
|
||||
config.setProperty("interface", "");
|
||||
|
||||
config.setProperty("sharedClient", _sharedClient + "");
|
||||
for (String p : _booleanClientOpts)
|
||||
config.setProperty("option." + p, "" + _booleanOptions.contains(p));
|
||||
@@ -806,11 +818,22 @@ public class IndexBean {
|
||||
} else if ("ircclient".equals(_type) || "client".equals(_type) || "streamrclient".equals(_type)) {
|
||||
if (_targetDestination != null)
|
||||
config.setProperty("targetDestination", _targetDestination);
|
||||
} else if ("httpserver".equals(_type)) {
|
||||
} else if ("httpserver".equals(_type) || "httpbidirserver".equals(_type)) {
|
||||
if (_spoofedHost != null)
|
||||
config.setProperty("spoofedHost", _spoofedHost);
|
||||
}
|
||||
|
||||
if ("httpbidirserver".equals(_type)) {
|
||||
if (_port != null)
|
||||
config.setProperty("listenPort", _port);
|
||||
if (_reachableByOther != null)
|
||||
config.setProperty("interface", _reachableByOther);
|
||||
else if (_reachableBy != null)
|
||||
config.setProperty("interface", _reachableBy);
|
||||
else if (_targetHost != null)
|
||||
config.setProperty("interface", _targetHost);
|
||||
else
|
||||
config.setProperty("interface", "");
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
@@ -424,9 +424,9 @@
|
||||
<span class="comment"><%=intl._("NOTE: If tunnel is currently running, most changes will not take effect until tunnel is stopped and restarted.")%></span>
|
||||
<div class="separator"><hr /></div>
|
||||
<input type="hidden" value="true" name="removeConfirm" />
|
||||
<button id="controlSave" accesskey="S" class="control" type="submit" name="action" value="Save changes" title="Save Changes"><%=intl._("Save")%>(<span class="accessKey">S</span>)</button>
|
||||
<button id="controlDelete" <%=(editBean.allowJS() ? "onclick=\"if (!confirm('Are you sure you want to delete?')) { return false; }\" " : "")%>accesskey="D" class="control" type="submit" name="action" value="Delete this proxy" title="Delete this Proxy"><%=intl._("Delete")%>(<span class="accessKey">D</span>)</button>
|
||||
<button id="controlCancel" class="control" type="submit" name="action" value="" title="Cancel"><%=intl._("Cancel")%></button>
|
||||
<button id="controlDelete" <%=(editBean.allowJS() ? "onclick=\"if (!confirm('Are you sure you want to delete?')) { return false; }\" " : "")%>accesskey="D" class="control" type="submit" name="action" value="Delete this proxy" title="Delete this Proxy"><%=intl._("Delete")%>(<span class="accessKey">D</span>)</button>
|
||||
<button id="controlSave" accesskey="S" class="control" type="submit" name="action" value="Save changes" title="Save Changes"><%=intl._("Save")%>(<span class="accessKey">S</span>)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -113,11 +113,58 @@
|
||||
<input type="text" size="6" maxlength="5" id="targetPort" name="targetPort" title="Target Port Number" value="<%=editBean.getTargetPort(curTunnel)%>" class="freetext" />
|
||||
</div>
|
||||
|
||||
<% if ("httpbidirserver".equals(tunnelType)) {
|
||||
%>
|
||||
<div class="subdivider">
|
||||
<hr />
|
||||
</div>
|
||||
<div id="accessField" class="rowItem">
|
||||
<label><%=intl._("Access Point")%>:</label>
|
||||
</div>
|
||||
<div id="portField" class="rowItem">
|
||||
<label for="port" accesskey="P">
|
||||
<span class="accessKey">P</span>ort:
|
||||
<% String value4 = editBean.getClientPort(curTunnel);
|
||||
if (value4 == null || "".equals(value4.trim())) {
|
||||
out.write(" <font color=\"red\">(");
|
||||
out.write(intl._("required"));
|
||||
out.write(")</font>");
|
||||
}
|
||||
%>
|
||||
</label>
|
||||
<input type="text" size="6" maxlength="5" id="port" name="port" title="Access Port Number" value="<%=editBean.getClientPort(curTunnel)%>" class="freetext" />
|
||||
</div>
|
||||
<% String otherInterface = "";
|
||||
String clientInterface = editBean.getClientInterface(curTunnel);
|
||||
%>
|
||||
<div id="reachField" class="rowItem">
|
||||
<label for="reachableBy" accesskey="r">
|
||||
<%=intl._("Reachable by")%>(<span class="accessKey">R</span>):
|
||||
</label>
|
||||
<select id="reachableBy" name="reachableBy" title="Valid IP for Client Access" class="selectbox">
|
||||
<% if (!("127.0.0.1".equals(clientInterface)) &&
|
||||
!("0.0.0.0".equals(clientInterface)) &&
|
||||
(clientInterface != null) &&
|
||||
(clientInterface.trim().length() > 0)) {
|
||||
otherInterface = clientInterface;
|
||||
}
|
||||
%><option value="127.0.0.1"<%=("127.0.0.1".equals(clientInterface) ? " selected=\"selected\"" : "")%>><%=intl._("Locally (127.0.0.1)")%></option>
|
||||
<option value="0.0.0.0"<%=("0.0.0.0".equals(clientInterface) ? " selected=\"selected\"" : "")%>><%=intl._("Everyone (0.0.0.0)")%></option>
|
||||
<option value="other"<%=(!("".equals(otherInterface)) ? " selected=\"selected\"" : "")%>><%=intl._("LAN Hosts (Please specify your LAN address)")%></option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="otherField" class="rowItem">
|
||||
<label for="reachableByOther" accesskey="O">
|
||||
<%=intl._("Other")%>(<span class="accessKey">O</span>):
|
||||
</label>
|
||||
<input type="text" size="20" id="reachableByOther" name="reachableByOther" title="Alternative IP for Client Access" value="<%=otherInterface%>" class="freetext" />
|
||||
</div>
|
||||
<% } %>
|
||||
<div class="subdivider">
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
<% if ("httpserver".equals(tunnelType)) {
|
||||
<% if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) {
|
||||
%><div id="websiteField" class="rowItem">
|
||||
<label for="spoofedHost" accesskey="W">
|
||||
<%=intl._("Website name")%>(<span class="accessKey">W</span>):
|
||||
@@ -129,8 +176,8 @@
|
||||
%><div id="privKeyField" class="rowItem">
|
||||
<label for="privKeyFile" accesskey="k">
|
||||
<%=intl._("Private key file")%>(<span class="accessKey">k</span>):
|
||||
<% String value2 = editBean.getPrivateKeyFile(curTunnel);
|
||||
if (value2 == null || "".equals(value2.trim())) {
|
||||
<% String value3 = editBean.getPrivateKeyFile(curTunnel);
|
||||
if (value3 == null || "".equals(value3.trim())) {
|
||||
out.write(" <font color=\"red\">(");
|
||||
out.write(intl._("required"));
|
||||
out.write(")</font>");
|
||||
@@ -139,6 +186,7 @@
|
||||
</label>
|
||||
<input type="text" size="30" id="privKeyFile" name="privKeyFile" title="Path to Private Key File" value="<%=editBean.getPrivateKeyFile(curTunnel)%>" class="freetext" />
|
||||
</div>
|
||||
|
||||
<% if (!"streamrserver".equals(tunnelType)) { %>
|
||||
<div id="profileField" class="rowItem">
|
||||
<label for="profile" accesskey="f">
|
||||
@@ -293,7 +341,7 @@
|
||||
|
||||
<div id="optionsField" class="rowItem">
|
||||
<label for="access" accesskey="s">
|
||||
<%=intl._("Restricted Access List")%>(<span class="accessKey">s</span>): <i><%=intl._("Unimplemented")%></i>
|
||||
<%=intl._("Restricted Access List")%>(<span class="accessKey">s</span>):
|
||||
</label>
|
||||
</div>
|
||||
<div id="portField" class="rowItem">
|
||||
@@ -409,9 +457,9 @@
|
||||
<span class="comment"><%=intl._("NOTE: If tunnel is currently running, most changes will not take effect until tunnel is stopped and restarted.")%></span>
|
||||
<div class="separator"><hr /></div>
|
||||
<input type="hidden" value="true" name="removeConfirm" />
|
||||
<button id="controlSave" accesskey="S" class="control" type="submit" name="action" value="Save changes" title="Save Changes"><%=intl._("Save")%>(<span class="accessKey">S</span>)</button>
|
||||
<button id="controlDelete" <%=(editBean.allowJS() ? "onclick=\"if (!confirm('Are you sure you want to delete?')) { return false; }\" " : "")%>accesskey="D" class="control" type="submit" name="action" value="Delete this proxy" title="Delete this Proxy"><%=intl._("Delete")%>(<span class="accessKey">D</span>)</button>
|
||||
<button id="controlCancel" class="control" type="submit" name="action" value="" title="Cancel"><%=intl._("Cancel")%></button>
|
||||
<button id="controlDelete" <%=(editBean.allowJS() ? "onclick=\"if (!confirm('Are you sure you want to delete?')) { return false; }\" " : "")%>accesskey="D" class="control" type="submit" name="action" value="Delete this proxy" title="Delete this Proxy"><%=intl._("Delete")%>(<span class="accessKey">D</span>)</button>
|
||||
<button id="controlSave" accesskey="S" class="control" type="submit" name="action" value="Save changes" title="Save Changes"><%=intl._("Save")%>(<span class="accessKey">S</span>)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -107,9 +107,9 @@
|
||||
</div>
|
||||
<div class="targetField rowItem">
|
||||
<%
|
||||
if ("httpserver".equals(indexBean.getInternalType(curServer)) && indexBean.getTunnelStatus(curServer) == IndexBean.RUNNING) {
|
||||
if (("httpserver".equals(indexBean.getInternalType(curServer)) || ("httpbidirserver".equals(indexBean.getInternalType(curServer)))) && indexBean.getTunnelStatus(curServer) == IndexBean.RUNNING) {
|
||||
%><label><%=intl._("Preview")%>:</label>
|
||||
<a class="control" title="Test HTTP server through I2P" href="http://<%=indexBean.getDestHashBase32(curServer)%>.b32.i2p"><%=intl._("Preview")%></a>
|
||||
<a class="control" title="Test HTTP server through I2P" href="http://<%=indexBean.getDestHashBase32(curServer)%>.b32.i2p"><%=intl._("Preview")%></a>
|
||||
<%
|
||||
} else if (indexBean.getTunnelStatus(curServer) == IndexBean.RUNNING) {
|
||||
%><span class="text"><%=intl._("Base32 Address")%>:<br /><%=indexBean.getDestHashBase32(curServer)%>.b32.i2p</span>
|
||||
@@ -164,6 +164,7 @@
|
||||
<select name="type">
|
||||
<option value="server"><%=intl._("Standard")%></option>
|
||||
<option value="httpserver">HTTP</option>
|
||||
<option value="httpbidirserver">HTTP bidir</option>
|
||||
<option value="ircserver">IRC</option>
|
||||
<option value="streamrserver">Streamr</option>
|
||||
</select>
|
||||
@@ -249,7 +250,8 @@
|
||||
}
|
||||
%></div>
|
||||
|
||||
<% if (!"sockstunnel".equals(indexBean.getInternalType(curClient))) { %>
|
||||
<% if (!("sockstunnel".equals(indexBean.getInternalType(curClient)) ||
|
||||
"socksirctunnel".equals(indexBean.getInternalType(curClient)))) { %>
|
||||
<div class="destinationField rowItem">
|
||||
<label>
|
||||
<% if ("httpclient".equals(indexBean.getInternalType(curClient)) || "connectclient".equals(indexBean.getInternalType(curClient))) { %>
|
||||
@@ -287,6 +289,7 @@
|
||||
<option value="httpclient">HTTP</option>
|
||||
<option value="ircclient">IRC</option>
|
||||
<option value="sockstunnel">SOCKS 4/4a/5</option>
|
||||
<option value="socksirctunnel">SOCKS IRC</option>
|
||||
<option value="connectclient">CONNECT</option>
|
||||
<option value="streamrclient">Streamr</option>
|
||||
</select>
|
||||
|
||||
@@ -8,8 +8,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P i2ptunnel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2010-01-02 04:59+0000\n"
|
||||
"PO-Revision-Date: 2010-01-02 07:05+0000\n"
|
||||
"POT-Creation-Date: 2010-03-06 00:05+0000\n"
|
||||
"PO-Revision-Date: 2010-03-06 00:08+0000\n"
|
||||
"Last-Translator: 4get <forget@mail.i2p>\n"
|
||||
"Language-Team: foo <foo@bar>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -17,59 +17,67 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Poedit-Language: Russian\n"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:430
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:483
|
||||
#, java-format
|
||||
msgid "To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper link by temporarily giving it a random alias, click <a href=\"{1}\">here</a>."
|
||||
msgstr "Для перехода по ссылке из локальной адресной книги, нажмите <a href=\"{0}\">здесь</a>. Для перехода по новой addresshelper-ссылке с временным присвоением ей случайного имени, нажмите <a href=\"{1}\">здесь</a>."
|
||||
msgid "To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper destination, click <a href=\"{1}\">here</a>."
|
||||
msgstr "Для перехода по ссылке из локальной адресной книги, нажмите <a href=\"{0}\">здесь</a>. Для перехода по новой addresshelper-ссылке, нажмите <a href=\"{1}\">здесь</a>."
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:800
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:886
|
||||
msgid "Click a link below to look for an address helper by using a \"jump\" service:"
|
||||
msgstr "Jump-сервисы, которые, возможно, знают нужную Вам addresshelper-ссылку:"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:362
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:363
|
||||
msgid "New Tunnel"
|
||||
msgstr "Новый туннель"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:382
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:383
|
||||
msgid "Standard client"
|
||||
msgstr "Обычный клиент"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:383
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:384
|
||||
msgid "HTTP client"
|
||||
msgstr "HTTP-клиент"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:384
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:385
|
||||
msgid "IRC client"
|
||||
msgstr "IRC-клиент"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:385
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:386
|
||||
msgid "Standard server"
|
||||
msgstr "Обычный сервер"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:386
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:387
|
||||
msgid "HTTP server"
|
||||
msgstr "HTTP-сервер"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:387
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:388
|
||||
msgid "SOCKS 4/4a/5 proxy"
|
||||
msgstr "SOCKS 4/4a/5 прокси"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:388
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:389
|
||||
msgid "SOCKS IRC proxy"
|
||||
msgstr "SOCKS IRC прокси"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:390
|
||||
msgid "CONNECT/SSL/HTTPS proxy"
|
||||
msgstr "CONNECT/SSL/HTTPS прокси"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:389
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:391
|
||||
msgid "IRC server"
|
||||
msgstr "IRC-сервер"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:390
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:392
|
||||
msgid "Streamr client"
|
||||
msgstr "Streamr-клиент"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:391
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:393
|
||||
msgid "Streamr server"
|
||||
msgstr "Streamr-сервер"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:394
|
||||
msgid "HTTP bidir"
|
||||
msgstr "HTTP bidir (экспериментальный двунаправленный режим, инструкцию спрашивайте у sponge)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:73
|
||||
msgid "I2P Tunnel Manager - Edit Client Tunnel"
|
||||
msgstr "Менеджер Туннелей I2P — Редактирование Клиентского Туннеля"
|
||||
@@ -101,7 +109,7 @@ msgstr "Тип"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:120
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:120
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:226
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:357
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:358
|
||||
msgid "Description"
|
||||
msgstr "Описание"
|
||||
|
||||
@@ -112,6 +120,7 @@ msgstr "Точка доступа"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:130
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:132
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:167
|
||||
msgid "Access Point"
|
||||
msgstr "Точка доступа"
|
||||
|
||||
@@ -119,28 +128,34 @@ msgstr "Точка доступа"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:179
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:207
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:157
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:181
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:172
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:228
|
||||
msgid "required"
|
||||
msgstr "*"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:150
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:142
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:183
|
||||
msgid "Reachable by"
|
||||
msgstr "Кому будет доступно (Сетевой интерфейс)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:162
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:195
|
||||
msgid "Locally (127.0.0.1)"
|
||||
msgstr "Только в пределах этого компьютера (127.0.0.1)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:166
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:199
|
||||
msgid "Everyone (0.0.0.0)"
|
||||
msgstr "Всем (0.0.0.0)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:170
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:203
|
||||
msgid "LAN Hosts (Please specify your LAN address)"
|
||||
msgstr "Только из локальной сети (Введите свой LAN-адрес)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:186
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:205
|
||||
msgid "Other"
|
||||
msgstr "Адрес сетевого интерфейса"
|
||||
|
||||
@@ -157,17 +172,17 @@ msgid "name or destination"
|
||||
msgstr "имя или адрес"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:220
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:190
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:237
|
||||
msgid "Profile"
|
||||
msgstr "Режим"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:227
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:197
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:244
|
||||
msgid "interactive connection"
|
||||
msgstr "оптимизировать для малых задержек (irc)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:231
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:201
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:248
|
||||
msgid "bulk connection (downloads/websites/BT)"
|
||||
msgstr "оптимизировать для большого обьема (www/bittorrent)"
|
||||
|
||||
@@ -198,7 +213,7 @@ msgid "(Check the Box for 'YES')"
|
||||
msgstr "(поставьте галочку для включения)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:253
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:219
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:266
|
||||
msgid "Advanced networking options"
|
||||
msgstr "Расширенные сетевые настройки"
|
||||
|
||||
@@ -207,151 +222,151 @@ msgid "(NOTE: when this client proxy is configured to share tunnels, then these
|
||||
msgstr "(ПРИМЕЧАНИЕ: при коллективном использовании туннелей эти опции будут применяться ко всем коллективным прокси-клиентам!)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:257
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:221
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:268
|
||||
msgid "Tunnel Options"
|
||||
msgstr "Параметры туннеля"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:259
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:223
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:270
|
||||
msgid "Length"
|
||||
msgstr "Длина"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:266
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:230
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:277
|
||||
msgid "0 hop tunnel (low anonymity, low latency)"
|
||||
msgstr "0 хопов (низкая анонимность, малые задержки)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:270
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:234
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:281
|
||||
msgid "1 hop tunnel (medium anonymity, medium latency)"
|
||||
msgstr "1 хоп (умеренная анонимность, умеренные задержки)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:274
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:238
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:285
|
||||
msgid "2 hop tunnel (high anonymity, high latency)"
|
||||
msgstr "2 хопа (высокая анонимность, высокие задержки)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:278
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:242
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:289
|
||||
msgid "3 hop tunnel (very high anonymity, poor performance)"
|
||||
msgstr "3 хопа (очень высокая анонимность, низкая производительность)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:287
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:251
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:298
|
||||
msgid "hop tunnel (very poor performance)"
|
||||
msgstr "хопов (очень низкая производительность)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:292
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:256
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:303
|
||||
msgid "Variance"
|
||||
msgstr "Разброс"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:299
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:263
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:310
|
||||
msgid "0 hop variance (no randomisation, consistant performance)"
|
||||
msgstr "нулевой разброс (без рандомизации, фиксированная производительность)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:303
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:267
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:314
|
||||
msgid "+ 0-1 hop variance (medium additive randomisation, subtractive performance)"
|
||||
msgstr "+ 0-1 разброс (умеренно повышенная рандомизация, пониженная производительность)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:307
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:271
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:318
|
||||
msgid "+ 0-2 hop variance (high additive randomisation, subtractive performance)"
|
||||
msgstr "+ 0-2 разброс (сильно повышенная рандомизация, пониженная производительность)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:311
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:275
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:322
|
||||
msgid "+/- 0-1 hop variance (standard randomisation, standard performance)"
|
||||
msgstr "+/- 0-1 разброс (стандартная рандомизация, стандартная производительность)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:315
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:279
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:326
|
||||
msgid "+/- 0-2 hop variance (not recommended)"
|
||||
msgstr "+/- 0-2 разброс (не рекомендуется)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:327
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:291
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:338
|
||||
msgid "hop variance"
|
||||
msgstr "разброс"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:332
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:296
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:343
|
||||
msgid "Count"
|
||||
msgstr "Количество"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:339
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:303
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:350
|
||||
msgid "1 inbound, 1 outbound tunnel (low bandwidth usage, less reliability)"
|
||||
msgstr "1 входящий, 1 исходящий туннель (низкая пропускная способность, низкая надежность) "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:343
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:307
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:354
|
||||
msgid "2 inbound, 2 outbound tunnels (standard bandwidth usage, standard reliability)"
|
||||
msgstr "2 входящих, 2 исходящих туннеля (стандартная пропускная способность, стандартная надежность)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:347
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:311
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:358
|
||||
msgid "3 inbound, 3 outbound tunnels (higher bandwidth usage, higher reliability)"
|
||||
msgstr "3 входящих, 3 исходящих туннеля (высокая пропускная способность, высокая надежность)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:356
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:320
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:367
|
||||
msgid "tunnels"
|
||||
msgstr "туннелей"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:361
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:325
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:372
|
||||
msgid "Backup Count"
|
||||
msgstr "Резервное количество"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:368
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:332
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:379
|
||||
msgid "0 backup tunnels (0 redundancy, no added resource usage)"
|
||||
msgstr "без резервных туннелей (отсутствие избыточности, отсутствие дополнительной нагрузки на систему)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:372
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:336
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:383
|
||||
msgid "1 backup tunnel each direction (low redundancy, low resource usage)"
|
||||
msgstr "1 резервный туннель в каждом направлении (низкая избыточность, низкая нагрузка на систему)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:376
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:340
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:387
|
||||
msgid "2 backup tunnels each direction (medium redundancy, medium resource usage)"
|
||||
msgstr "2 резервных туннеля в каждом направлении (умеренная избыточность, умеренная нагрузка на систему)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:380
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:344
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:391
|
||||
msgid "3 backup tunnels each direction (high redundancy, high resource usage)"
|
||||
msgstr "3 резервных туннеля в каждом направлении (высокая избыточность, высокая нагрузка на систему)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:389
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:353
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:400
|
||||
msgid "backup tunnels"
|
||||
msgstr "резервных туннелей"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:394
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:358
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:405
|
||||
msgid "I2CP Options"
|
||||
msgstr "Параметры I2CP"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:396
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:146
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:360
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:407
|
||||
msgid "Host"
|
||||
msgstr "Адрес"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:400
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:152
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:364
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:411
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:244
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:266
|
||||
msgid "Port"
|
||||
msgstr "Порт"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:406
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:398
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:445
|
||||
msgid "Reduce tunnel quantity when idle"
|
||||
msgstr "Снижать количество туннелей при простое"
|
||||
|
||||
@@ -360,20 +375,20 @@ msgstr "Снижать количество туннелей при просто
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:430
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:442
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:452
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:370
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:388
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:400
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:417
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:435
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:447
|
||||
msgid "Enable"
|
||||
msgstr "Включить"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:412
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:404
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:451
|
||||
msgid "Reduced tunnel count"
|
||||
msgstr "Количество туннелей"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:416
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:436
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:408
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:455
|
||||
msgid "Idle minutes"
|
||||
msgstr "Минут простоя"
|
||||
|
||||
@@ -402,7 +417,7 @@ msgid "File"
|
||||
msgstr "Файл"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:460
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:205
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:252
|
||||
msgid "Local destination"
|
||||
msgstr "Локальный адрес назначения"
|
||||
|
||||
@@ -411,27 +426,27 @@ msgid "(if known)"
|
||||
msgstr "(если известен)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:468
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:444
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:491
|
||||
msgid "Custom options"
|
||||
msgstr "Дополнительные параметры"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:472
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:448
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:495
|
||||
msgid "NOTE: If tunnel is currently running, most changes will not take effect until tunnel is stopped and restarted."
|
||||
msgstr "ПРИМЕЧАНИЕ: для вступления в силу измененных настроек потребуется остановка и перезапуск туннеля"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:474
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:450
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:497
|
||||
msgid "Save"
|
||||
msgstr "Сохранить"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:478
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:454
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:501
|
||||
msgid "Delete"
|
||||
msgstr "Удалить"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:480
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:456
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:503
|
||||
msgid "Cancel"
|
||||
msgstr "Отмена"
|
||||
|
||||
@@ -447,92 +462,92 @@ msgstr "Редактирование настроек серверного ту
|
||||
msgid "New server settings"
|
||||
msgstr "Настройки нового серверного туннеля"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:167
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:214
|
||||
msgid "Website name"
|
||||
msgstr "Имя веб-сайта"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:171
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:218
|
||||
msgid "(leave blank for outproxies)"
|
||||
msgstr "(не заполнять для outproxy)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:176
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:223
|
||||
msgid "Private key file"
|
||||
msgstr "Файл секретного ключа"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:215
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:262
|
||||
msgid "Add to local addressbook"
|
||||
msgstr "Добавить в локальную адресную книгу"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:368
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:415
|
||||
msgid "Encrypt Leaseset"
|
||||
msgstr "Шифровать LeaseSet"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:374
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:421
|
||||
msgid "Encryption Key"
|
||||
msgstr "Ключ шифрования"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:378
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:425
|
||||
msgid "Generate New Key"
|
||||
msgstr "Сгенерировать новый ключ"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:380
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:427
|
||||
msgid "Generate"
|
||||
msgstr "Сгенерировать"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:382
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:442
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:429
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:489
|
||||
msgid "(Tunnel must be stopped first)"
|
||||
msgstr "(Туннель перед этим следует остановить)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:384
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:431
|
||||
msgid "Restricted Access List"
|
||||
msgstr "Ограниченный доступ"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:386
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:433
|
||||
msgid "Unimplemented"
|
||||
msgstr "не реализовано"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:392
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:439
|
||||
msgid "Access List"
|
||||
msgstr "Список доступа"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:396
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:443
|
||||
msgid "(Restrict to these clients only)"
|
||||
msgstr "(Разрешить доступ только перечисленным клиентам)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:412
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:459
|
||||
msgid "New Certificate type"
|
||||
msgstr "Создать новый сертификат. Тип"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:414
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:461
|
||||
msgid "None"
|
||||
msgstr "Без"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:418
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:465
|
||||
msgid "Hashcash (effort)"
|
||||
msgstr "Hashcash (экспериментальный)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:424
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:471
|
||||
msgid "Hashcash Calc Time"
|
||||
msgstr "Время генерации hashcash-сертификата"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:426
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:473
|
||||
msgid "Estimate"
|
||||
msgstr "Прогноз"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:428
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:475
|
||||
msgid "Hidden"
|
||||
msgstr "Скрытый"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:432
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:479
|
||||
msgid "Signed (signed by)"
|
||||
msgstr "Подписанный (указать кем подписан)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:438
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:485
|
||||
msgid "Modify Certificate"
|
||||
msgstr "Изменить сертификат"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:440
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:487
|
||||
msgid "Modify"
|
||||
msgstr "Изменить"
|
||||
|
||||
@@ -627,12 +642,12 @@ msgid "New server tunnel"
|
||||
msgstr "Новый серверный туннель"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:236
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:367
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:368
|
||||
msgid "Standard"
|
||||
msgstr "Стандартный"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:238
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:369
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:370
|
||||
msgid "Create"
|
||||
msgstr "Создать"
|
||||
|
||||
@@ -650,15 +665,15 @@ msgid "Standby"
|
||||
msgstr "Режим ожидания"
|
||||
|
||||
# This term intentionally left in English
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:345
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:346
|
||||
msgid "Outproxy"
|
||||
msgstr "Outproxy"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:349
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:350
|
||||
msgid "Destination"
|
||||
msgstr "Адрес назначения"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:365
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:366
|
||||
msgid "New client tunnel"
|
||||
msgstr "Новый клиентский туннель"
|
||||
|
||||
|
||||
@@ -8,68 +8,76 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P i2ptunnel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2010-01-02 08:14+0000\n"
|
||||
"PO-Revision-Date: 2010-01-02 23:43+0800\n"
|
||||
"Last-Translator: walking <zhazhenzhong@gmail.com>\n"
|
||||
"POT-Creation-Date: 2010-05-29 02:35+0000\n"
|
||||
"PO-Revision-Date: 2010-05-29 10:57+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"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:426
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:493
|
||||
#, java-format
|
||||
msgid "To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper link by temporarily giving it a random alias, click <a href=\"{1}\">here</a>."
|
||||
msgstr "要访问您本地【地址簿】中规定的主机(相当与IP),请点击<a href=\"{0}\">这里</a>。要访问【地址助手】返回的主机请点<a href=\"{1}\">这里</a>(主机的域名会被临时强制替换)。"
|
||||
msgid "To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper destination, click <a href=\"{1}\">here</a>."
|
||||
msgstr "域名冲突:要访问您本地【地址簿】中设置的目标主机(相当与IP),请点击<a href=\"{0}\">这里</a>。要访问【地址助手】返回的目标主机请点<a href=\"{1}\">这里</a>。"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:792
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:904
|
||||
msgid "Click a link below to look for an address helper by using a \"jump\" service:"
|
||||
msgstr "请点击下面的链接通过【跳转(Jump)】服务提供的【地址助手】链接跳转至域名对应的主机:"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:362
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:369
|
||||
msgid "New Tunnel"
|
||||
msgstr "新建隧道"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:382
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:389
|
||||
msgid "Standard client"
|
||||
msgstr "标准客户端"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:383
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:390
|
||||
msgid "HTTP client"
|
||||
msgstr "HTTP 客户端"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:384
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:391
|
||||
msgid "IRC client"
|
||||
msgstr "IRC 客户端"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:385
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:392
|
||||
msgid "Standard server"
|
||||
msgstr "标准服务器"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:386
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:393
|
||||
msgid "HTTP server"
|
||||
msgstr "HTTP 服务器"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:387
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:394
|
||||
msgid "SOCKS 4/4a/5 proxy"
|
||||
msgstr "SOCKS4/4A/5 代理"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:388
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:395
|
||||
msgid "SOCKS IRC proxy"
|
||||
msgstr "SOCKS IRC 代理"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:396
|
||||
msgid "CONNECT/SSL/HTTPS proxy"
|
||||
msgstr "CONNECT/SSL/HTTPS 代理"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:389
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:397
|
||||
msgid "IRC server"
|
||||
msgstr "IRC 服务器"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:390
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:398
|
||||
msgid "Streamr client"
|
||||
msgstr "Streamr 客户端"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:391
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:399
|
||||
msgid "Streamr server"
|
||||
msgstr "Streamr 服务器"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:400
|
||||
msgid "HTTP bidir"
|
||||
msgstr "双向http"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:73
|
||||
msgid "I2P Tunnel Manager - Edit Client Tunnel"
|
||||
msgstr "I2P 隧道管理器 - 编辑客户端隧道"
|
||||
@@ -101,7 +109,7 @@ msgstr "类型"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:120
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:120
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:226
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:357
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:358
|
||||
msgid "Description"
|
||||
msgstr "描述"
|
||||
|
||||
@@ -112,6 +120,7 @@ msgstr "目标"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:130
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:132
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:167
|
||||
msgid "Access Point"
|
||||
msgstr "接入点"
|
||||
|
||||
@@ -119,28 +128,34 @@ msgstr "接入点"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:179
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:207
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:157
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:181
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:172
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:228
|
||||
msgid "required"
|
||||
msgstr "必要"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:150
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:142
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:183
|
||||
msgid "Reachable by"
|
||||
msgstr "访问地址"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:162
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:195
|
||||
msgid "Locally (127.0.0.1)"
|
||||
msgstr "本地(127.0.0.1)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:166
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:199
|
||||
msgid "Everyone (0.0.0.0)"
|
||||
msgstr "任何人(0.0.0.0)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:170
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:203
|
||||
msgid "LAN Hosts (Please specify your LAN address)"
|
||||
msgstr "局域网(请指定LAN地址)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:186
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:205
|
||||
msgid "Other"
|
||||
msgstr "其他"
|
||||
|
||||
@@ -157,17 +172,17 @@ msgid "name or destination"
|
||||
msgstr "名称或描述"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:220
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:190
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:237
|
||||
msgid "Profile"
|
||||
msgstr "连接类型"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:227
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:197
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:244
|
||||
msgid "interactive connection"
|
||||
msgstr "速度连接"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:231
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:201
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:248
|
||||
msgid "bulk connection (downloads/websites/BT)"
|
||||
msgstr "效率连接(下载/WEB/BT)"
|
||||
|
||||
@@ -198,7 +213,7 @@ msgid "(Check the Box for 'YES')"
|
||||
msgstr "(选中表示\"是\")"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:253
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:219
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:266
|
||||
msgid "Advanced networking options"
|
||||
msgstr "高级网络设置"
|
||||
|
||||
@@ -207,151 +222,151 @@ msgid "(NOTE: when this client proxy is configured to share tunnels, then these
|
||||
msgstr "(注意:此客户代理被设置使用共享隧道时,这些设置将影响所有使用共享隧道的客户端!)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:257
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:221
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:268
|
||||
msgid "Tunnel Options"
|
||||
msgstr "隧道选项"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:259
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:223
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:270
|
||||
msgid "Length"
|
||||
msgstr "长度"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:266
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:230
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:277
|
||||
msgid "0 hop tunnel (low anonymity, low latency)"
|
||||
msgstr "直连(匿名性无,延迟低)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:270
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:234
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:281
|
||||
msgid "1 hop tunnel (medium anonymity, medium latency)"
|
||||
msgstr "隧道跳点x1(匿名性中,延迟中)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:274
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:238
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:285
|
||||
msgid "2 hop tunnel (high anonymity, high latency)"
|
||||
msgstr "隧道跳点x2(匿名性高,延迟高)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:278
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:242
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:289
|
||||
msgid "3 hop tunnel (very high anonymity, poor performance)"
|
||||
msgstr "隧道跳点x3(匿名性优,影响性能)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:287
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:251
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:298
|
||||
msgid "hop tunnel (very poor performance)"
|
||||
msgstr "跳点隧道(严重影响性能)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:292
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:256
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:303
|
||||
msgid "Variance"
|
||||
msgstr "随机变化"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:299
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:263
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:310
|
||||
msgid "0 hop variance (no randomisation, consistant performance)"
|
||||
msgstr "隧道长度恒定(随机性无,性能稳定)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:303
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:267
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:314
|
||||
msgid "+ 0-1 hop variance (medium additive randomisation, subtractive performance)"
|
||||
msgstr "隧道长度+ 0-1(随机性中,影响性能)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:307
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:271
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:318
|
||||
msgid "+ 0-2 hop variance (high additive randomisation, subtractive performance)"
|
||||
msgstr "隧道长度+ 0-2(随机性高,影响性能)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:311
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:275
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:322
|
||||
msgid "+/- 0-1 hop variance (standard randomisation, standard performance)"
|
||||
msgstr "隧道长度+/- 0-1(随机性标准,正常性能)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:315
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:279
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:326
|
||||
msgid "+/- 0-2 hop variance (not recommended)"
|
||||
msgstr "隧道程度+/- 0-2(不推荐)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:327
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:291
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:338
|
||||
msgid "hop variance"
|
||||
msgstr "节点数量"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:332
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:296
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:343
|
||||
msgid "Count"
|
||||
msgstr "计数"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:339
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:303
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:350
|
||||
msgid "1 inbound, 1 outbound tunnel (low bandwidth usage, less reliability)"
|
||||
msgstr "出/入站隧道x1(带宽低,低可靠性)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:343
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:307
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:354
|
||||
msgid "2 inbound, 2 outbound tunnels (standard bandwidth usage, standard reliability)"
|
||||
msgstr "出/入站隧道x2(带宽标准,标准稳定性)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:347
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:311
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:358
|
||||
msgid "3 inbound, 3 outbound tunnels (higher bandwidth usage, higher reliability)"
|
||||
msgstr "出/入站隧道x3(带宽高,高稳定性)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:356
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:320
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:367
|
||||
msgid "tunnels"
|
||||
msgstr "隧道"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:361
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:325
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:372
|
||||
msgid "Backup Count"
|
||||
msgstr "备用数量"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:368
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:332
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:379
|
||||
msgid "0 backup tunnels (0 redundancy, no added resource usage)"
|
||||
msgstr "无备用隧道(无冗余,不增加资源占用)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:372
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:336
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:383
|
||||
msgid "1 backup tunnel each direction (low redundancy, low resource usage)"
|
||||
msgstr "备用隧道对x1 (低冗余,低资源占用)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:376
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:340
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:387
|
||||
msgid "2 backup tunnels each direction (medium redundancy, medium resource usage)"
|
||||
msgstr "备用隧道对x2 (中冗余,中资源占用)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:380
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:344
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:391
|
||||
msgid "3 backup tunnels each direction (high redundancy, high resource usage)"
|
||||
msgstr "备用隧道对x3 (高冗余,高资源占用)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:389
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:353
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:400
|
||||
msgid "backup tunnels"
|
||||
msgstr "备用隧道"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:394
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:358
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:405
|
||||
msgid "I2CP Options"
|
||||
msgstr "I2CP选项"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:396
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:146
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:360
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:407
|
||||
msgid "Host"
|
||||
msgstr "主机"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:400
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:152
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:364
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:411
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:244
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:266
|
||||
msgid "Port"
|
||||
msgstr "端口"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:406
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:398
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:443
|
||||
msgid "Reduce tunnel quantity when idle"
|
||||
msgstr "空闲时缩减隧道数量"
|
||||
|
||||
@@ -360,20 +375,20 @@ msgstr "空闲时缩减隧道数量"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:430
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:442
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:452
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:370
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:388
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:400
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:417
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:433
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:445
|
||||
msgid "Enable"
|
||||
msgstr "启用"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:412
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:404
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:449
|
||||
msgid "Reduced tunnel count"
|
||||
msgstr "削减后的隧道数量"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:416
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:436
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:408
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:453
|
||||
msgid "Idle minutes"
|
||||
msgstr "空闲时间(分钟)"
|
||||
|
||||
@@ -402,7 +417,7 @@ msgid "File"
|
||||
msgstr "文件"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:460
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:205
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:252
|
||||
msgid "Local destination"
|
||||
msgstr "本地目标"
|
||||
|
||||
@@ -411,29 +426,29 @@ msgid "(if known)"
|
||||
msgstr "(如果已知)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:468
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:444
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:489
|
||||
msgid "Custom options"
|
||||
msgstr "自定义选项"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:472
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:448
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:493
|
||||
msgid "NOTE: If tunnel is currently running, most changes will not take effect until tunnel is stopped and restarted."
|
||||
msgstr "注意:如果当前隧道已经启动,设置需要【停止】并重新【启动】相应隧道后才能生效。"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:474
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:450
|
||||
msgid "Save"
|
||||
msgstr "保存"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:495
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:478
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:454
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:499
|
||||
msgid "Delete"
|
||||
msgstr "删除"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:480
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:456
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:501
|
||||
msgid "Save"
|
||||
msgstr "保存"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:73
|
||||
msgid "I2P Tunnel Manager - Edit Server Tunnel"
|
||||
@@ -447,92 +462,88 @@ msgstr "服务器隧道设置"
|
||||
msgid "New server settings"
|
||||
msgstr "新建服务器设置"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:167
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:214
|
||||
msgid "Website name"
|
||||
msgstr "网站名称"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:171
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:218
|
||||
msgid "(leave blank for outproxies)"
|
||||
msgstr "(出口代理这里请置空)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:176
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:223
|
||||
msgid "Private key file"
|
||||
msgstr "私钥文件"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:215
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:262
|
||||
msgid "Add to local addressbook"
|
||||
msgstr "添加至本地地址簿"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:368
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:415
|
||||
msgid "Encrypt Leaseset"
|
||||
msgstr "加密赁集"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:374
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:421
|
||||
msgid "Encryption Key"
|
||||
msgstr "加密密钥"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:378
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:425
|
||||
msgid "Generate New Key"
|
||||
msgstr "生成新密钥"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:380
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:427
|
||||
msgid "Generate"
|
||||
msgstr "生成"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:382
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:442
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:429
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:487
|
||||
msgid "(Tunnel must be stopped first)"
|
||||
msgstr "(必须先停止隧道)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:384
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:431
|
||||
msgid "Restricted Access List"
|
||||
msgstr "限制访问列表"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:386
|
||||
msgid "Unimplemented"
|
||||
msgstr "尚未实现"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:392
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:437
|
||||
msgid "Access List"
|
||||
msgstr "访问列表"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:396
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:441
|
||||
msgid "(Restrict to these clients only)"
|
||||
msgstr "(仅允许这些客户访问)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:412
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:457
|
||||
msgid "New Certificate type"
|
||||
msgstr "新建证书类型"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:414
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:459
|
||||
msgid "None"
|
||||
msgstr "无"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:418
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:463
|
||||
msgid "Hashcash (effort)"
|
||||
msgstr "Hashcash (强度)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:424
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:469
|
||||
msgid "Hashcash Calc Time"
|
||||
msgstr "Hashcash 计算时间"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:426
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:471
|
||||
msgid "Estimate"
|
||||
msgstr "估算"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:428
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:473
|
||||
msgid "Hidden"
|
||||
msgstr "隐藏"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:432
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:477
|
||||
msgid "Signed (signed by)"
|
||||
msgstr "签名(签名者)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:438
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:483
|
||||
msgid "Modify Certificate"
|
||||
msgstr "修改证书"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:440
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:485
|
||||
msgid "Modify"
|
||||
msgstr "修改"
|
||||
|
||||
@@ -627,12 +638,12 @@ msgid "New server tunnel"
|
||||
msgstr "新建服务器隧道"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:236
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:367
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:368
|
||||
msgid "Standard"
|
||||
msgstr "标准"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:238
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:369
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:370
|
||||
msgid "Create"
|
||||
msgstr "创建"
|
||||
|
||||
@@ -649,15 +660,18 @@ msgstr "网络接口"
|
||||
msgid "Standby"
|
||||
msgstr "等待"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:345
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:346
|
||||
msgid "Outproxy"
|
||||
msgstr "出口代理"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:349
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:350
|
||||
msgid "Destination"
|
||||
msgstr "目标"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:365
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:366
|
||||
msgid "New client tunnel"
|
||||
msgstr "新建客户隧道"
|
||||
|
||||
#~ msgid "Unimplemented"
|
||||
#~ msgstr "尚未实现"
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="all" name="jetty">
|
||||
|
||||
<property name="jetty.sha1" value="021164f84da7304bd1ff07c268b45aa3e0b13322" />
|
||||
<property name="jetty.md5" value="a61adc832be6baf2678935506743cfc3" />
|
||||
<property name="jetty.url" value="http://dist.codehaus.org/jetty/jetty-5.1.x/jetty-5.1.12.zip" />
|
||||
<property name="jetty.filename" value="jetty-5.1.12.zip" />
|
||||
<property name="jetty.base" value="jetty-5.1.15" />
|
||||
<property name="jetty.sha1" value="3a7a3de50f86f0cdb23c33aec632ea7f44132c5e" />
|
||||
<property name="jetty.filename" value="${jetty.base}.tgz" />
|
||||
<property name="jetty.url" value="http://dist.codehaus.org/jetty/jetty-5.1.x/${jetty.filename}" />
|
||||
<property name="verified.filename" value="verified.txt" />
|
||||
<property name="javac.compilerargs" value="" />
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
<echo message="Even if you deploy the Jetty archive manually, the build script will" />
|
||||
<echo message="still attempt to verify its checksums, which must be:" />
|
||||
<echo message="SHA1 ${jetty.sha1}" />
|
||||
<echo message="MD5 ${jetty.md5}" />
|
||||
<echo message="" />
|
||||
<input message="Download Jetty archive automatically?" validargs="y,n" addproperty="jetty.download" />
|
||||
<fail message="Aborting as requested. Please deploy the Jetty archive manually." >
|
||||
@@ -43,16 +42,18 @@
|
||||
<get src="${jetty.url}" verbose="true" dest="${jetty.filename}" />
|
||||
</target>
|
||||
|
||||
<uptodate property="verified.already" srcfile="${jetty.filename}" targetfile="${verified.filename}" />
|
||||
<condition property="verified.already" >
|
||||
<and>
|
||||
<available file="${jetty.filename}" />
|
||||
<uptodate property="foo.bar.baz" srcfile="${jetty.filename}" targetfile="${verified.filename}" />
|
||||
</and>
|
||||
</condition>
|
||||
|
||||
<target name="verifyJettylib" unless="verified.already" >
|
||||
<condition property="jetty.zip.verified" >
|
||||
<and>
|
||||
<checksum file="${jetty.filename}" algorithm="SHA" property="${jetty.sha1}" />
|
||||
<checksum file="${jetty.filename}" algorithm="MD5" property="${jetty.md5}" />
|
||||
</and>
|
||||
</condition>
|
||||
<fail message="Jetty archive does not match its checksums!" >
|
||||
<fail message="Jetty archive does not match its checksum!" >
|
||||
<condition>
|
||||
<not>
|
||||
<istrue value="${jetty.zip.verified}" />
|
||||
@@ -63,15 +64,16 @@
|
||||
</target>
|
||||
|
||||
<target name="extractJettylib" unless="jetty.zip.extracted" >
|
||||
<unzip src="${jetty.filename}" dest="." />
|
||||
<gunzip src="${jetty.filename}" dest="jetty.tar" />
|
||||
<untar src="jetty.tar" dest="." />
|
||||
<mkdir dir="jettylib" />
|
||||
<copy todir="jettylib">
|
||||
<fileset dir="jetty-5.1.12/lib">
|
||||
<fileset dir="${jetty.base}/lib">
|
||||
<include name="*.jar" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy todir="jettylib">
|
||||
<fileset dir="jetty-5.1.12/ext">
|
||||
<fileset dir="${jetty.base}/ext">
|
||||
<include name="ant.jar" />
|
||||
<include name="commons-el.jar" />
|
||||
<include name="commons-logging.jar" />
|
||||
@@ -81,7 +83,8 @@
|
||||
<include name="org.mortbay.jetty.jar" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<delete dir="jetty-5.1.12" />
|
||||
<delete file="jetty.tar" />
|
||||
<delete dir="${jetty.base}" />
|
||||
</target>
|
||||
|
||||
<target name="build" depends="jar" />
|
||||
@@ -107,10 +110,10 @@
|
||||
</target>
|
||||
<target name="cleandep" depends="clean" />
|
||||
<target name="distclean" depends="clean">
|
||||
<delete dir="./jettylib" />
|
||||
<echo message="Not actually deleting the jetty libs (since they're so large)" />
|
||||
</target>
|
||||
<target name="reallyclean" depends="distclean">
|
||||
<delete dir="./jettylib" />
|
||||
</target>
|
||||
<target name="totallyclean" depends="clean">
|
||||
<delete dir="./jettylib" />
|
||||
@@ -128,9 +131,9 @@
|
||||
<mkdir dir="./build/javadoc" />
|
||||
<unzip src="${jetty.filename}" dest="./build/javadoc" >
|
||||
<patternset>
|
||||
<include name="jetty-5.1.12/javadoc/" />
|
||||
<include name="${jetty.base}/javadoc/" />
|
||||
</patternset>
|
||||
<mapper type="glob" from="jetty-5.1.12/javadoc/*" to="javadoc/*" />
|
||||
<mapper type="glob" from="${jetty.base}/javadoc/*" to="javadoc/*" />
|
||||
</unzip>
|
||||
</target>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
617
apps/jetty/java/src/org/mortbay/jetty/Server.java
Normal file
@@ -0,0 +1,617 @@
|
||||
// ========================================================================
|
||||
// $Id: Server.java,v 1.40 2005/10/21 13:52:11 gregwilkins Exp $
|
||||
// Copyright 2002-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.jetty;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.mortbay.log.LogFactory;
|
||||
import org.mortbay.http.HttpContext;
|
||||
import org.mortbay.http.HttpServer;
|
||||
import org.mortbay.jetty.servlet.ServletHttpContext;
|
||||
import org.mortbay.jetty.servlet.WebApplicationContext;
|
||||
import org.mortbay.util.LogSupport;
|
||||
import org.mortbay.util.Resource;
|
||||
import org.mortbay.xml.XmlConfiguration;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** The Jetty HttpServer.
|
||||
*
|
||||
* This specialization of org.mortbay.http.HttpServer adds knowledge
|
||||
* about servlets and their specialized contexts. It also included
|
||||
* support for initialization from xml configuration files
|
||||
* that follow the XmlConfiguration dtd.
|
||||
*
|
||||
* HttpContexts created by Server are of the type
|
||||
* org.mortbay.jetty.servlet.ServletHttpContext unless otherwise
|
||||
* specified.
|
||||
*
|
||||
* This class also provides a main() method which starts a server for
|
||||
* each config file passed on the command line. If the system
|
||||
* property JETTY_NO_SHUTDOWN_HOOK is not set to true, then a shutdown
|
||||
* hook is thread is registered to stop these servers.
|
||||
*
|
||||
* @see org.mortbay.xml.XmlConfiguration
|
||||
* @see org.mortbay.jetty.servlet.ServletHttpContext
|
||||
* @version $Revision: 1.40 $
|
||||
* @author Greg Wilkins (gregw)
|
||||
*/
|
||||
public class Server extends HttpServer
|
||||
{
|
||||
static Log log = LogFactory.getLog(Server.class);
|
||||
private String[] _webAppConfigurationClassNames =
|
||||
new String[]{"org.mortbay.jetty.servlet.XMLConfiguration", "org.mortbay.jetty.servlet.JettyWebConfiguration"};
|
||||
private String _configuration;
|
||||
private String _rootWebApp;
|
||||
private static ShutdownHookThread hookThread = new ShutdownHookThread();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Constructor.
|
||||
*/
|
||||
public Server()
|
||||
{
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Constructor.
|
||||
* @param configuration The filename or URL of the XML
|
||||
* configuration file.
|
||||
*/
|
||||
public Server(String configuration)
|
||||
throws IOException
|
||||
{
|
||||
this(Resource.newResource(configuration).getURL());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Constructor.
|
||||
* @param configuration The filename or URL of the XML
|
||||
* configuration file.
|
||||
*/
|
||||
public Server(Resource configuration)
|
||||
throws IOException
|
||||
{
|
||||
this(configuration.getURL());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Constructor.
|
||||
* @param configuration The filename or URL of the XML
|
||||
* configuration file.
|
||||
*/
|
||||
public Server(URL configuration)
|
||||
throws IOException
|
||||
{
|
||||
_configuration=configuration.toString();
|
||||
Server.hookThread.add(this);
|
||||
try
|
||||
{
|
||||
XmlConfiguration config=new XmlConfiguration(configuration);
|
||||
config.configure(this);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
catch(InvocationTargetException e)
|
||||
{
|
||||
log.warn(LogSupport.EXCEPTION,e.getTargetException());
|
||||
throw new IOException("Jetty configuration problem: "+e.getTargetException());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
log.warn(LogSupport.EXCEPTION,e);
|
||||
throw new IOException("Jetty configuration problem: "+e);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean getStopAtShutdown()
|
||||
{
|
||||
return hookThread.contains(this);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void setStopAtShutdown(boolean stop)
|
||||
{
|
||||
if (stop)
|
||||
hookThread.add(this);
|
||||
else
|
||||
hookThread.remove(this);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the root webapp name.
|
||||
* @return The name of the root webapp (eg. "root" for root.war).
|
||||
*/
|
||||
public String getRootWebApp()
|
||||
{
|
||||
return _rootWebApp;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Set the root webapp name.
|
||||
* @param rootWebApp The name of the root webapp (eg. "root" for root.war).
|
||||
*/
|
||||
public void setRootWebApp(String rootWebApp)
|
||||
{
|
||||
_rootWebApp = rootWebApp;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Configure the server from an XML file.
|
||||
* @param configuration The filename or URL of the XML
|
||||
* configuration file.
|
||||
*/
|
||||
public void configure(String configuration)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
URL url=Resource.newResource(configuration).getURL();
|
||||
if (_configuration!=null && _configuration.equals(url.toString()))
|
||||
return;
|
||||
if (_configuration!=null)
|
||||
throw new IllegalStateException("Already configured with "+_configuration);
|
||||
try
|
||||
{
|
||||
XmlConfiguration config=new XmlConfiguration(url);
|
||||
_configuration=url.toString();
|
||||
config.configure(this);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
log.warn(LogSupport.EXCEPTION,e);
|
||||
throw new IOException("Jetty configuration problem: "+e);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public String getConfiguration()
|
||||
{
|
||||
return _configuration;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Create a new ServletHttpContext.
|
||||
* Ths method is called by HttpServer to creat new contexts. Thus
|
||||
* calls to addContext or getContext that result in a new Context
|
||||
* being created will return an
|
||||
* org.mortbay.jetty.servlet.ServletHttpContext instance.
|
||||
* @return ServletHttpContext
|
||||
*/
|
||||
protected HttpContext newHttpContext()
|
||||
{
|
||||
return new ServletHttpContext();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Create a new WebApplicationContext.
|
||||
* Ths method is called by Server to creat new contexts for web
|
||||
* applications. Thus calls to addWebApplication that result in
|
||||
* a new Context being created will return an correct class instance.
|
||||
* Derived class can override this method to create instance of its
|
||||
* own class derived from WebApplicationContext in case it needs more
|
||||
* functionality.
|
||||
* @param webApp The Web application directory or WAR file.
|
||||
* @return WebApplicationContext
|
||||
*/
|
||||
protected WebApplicationContext newWebApplicationContext(
|
||||
String webApp
|
||||
)
|
||||
{
|
||||
return new WebApplicationContext(webApp);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Add Web Application.
|
||||
* @param contextPathSpec The context path spec. Which must be of
|
||||
* the form / or /path/*
|
||||
* @param webApp The Web application directory or WAR file.
|
||||
* @return The WebApplicationContext
|
||||
* @exception IOException
|
||||
*/
|
||||
public WebApplicationContext addWebApplication(String contextPathSpec,
|
||||
String webApp)
|
||||
throws IOException
|
||||
{
|
||||
return addWebApplication(null,contextPathSpec,webApp);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Add Web Application.
|
||||
* @param virtualHost Virtual host name or null
|
||||
* @param contextPathSpec The context path spec. Which must be of
|
||||
* the form / or /path/*
|
||||
* @param webApp The Web application directory or WAR file.
|
||||
* @return The WebApplicationContext
|
||||
* @exception IOException
|
||||
*/
|
||||
public WebApplicationContext addWebApplication(String virtualHost,
|
||||
String contextPathSpec,
|
||||
String webApp)
|
||||
throws IOException
|
||||
{
|
||||
WebApplicationContext appContext =
|
||||
newWebApplicationContext(webApp);
|
||||
appContext.setContextPath(contextPathSpec);
|
||||
addContext(virtualHost,appContext);
|
||||
if(log.isDebugEnabled())log.debug("Web Application "+appContext+" added");
|
||||
return appContext;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Add Web Applications.
|
||||
* Add auto webapplications to the server. The name of the
|
||||
* webapp directory or war is used as the context name. If a
|
||||
* webapp is called "root" it is added at "/".
|
||||
* @param webapps Directory file name or URL to look for auto webapplication.
|
||||
* @exception IOException
|
||||
*/
|
||||
public WebApplicationContext[] addWebApplications(String webapps)
|
||||
throws IOException
|
||||
{
|
||||
return addWebApplications(null,webapps,null,false);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Add Web Applications.
|
||||
* Add auto webapplications to the server. The name of the
|
||||
* webapp directory or war is used as the context name. If the
|
||||
* webapp matches the rootWebApp it is added as the "/" context.
|
||||
* @param host Virtual host name or null
|
||||
* @param webapps Directory file name or URL to look for auto webapplication.
|
||||
* @exception IOException
|
||||
*/
|
||||
public WebApplicationContext[] addWebApplications(String host,
|
||||
String webapps)
|
||||
throws IOException
|
||||
{
|
||||
return addWebApplications(host,webapps,null,false);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Add Web Applications.
|
||||
* Add auto webapplications to the server. The name of the
|
||||
* webapp directory or war is used as the context name. If the
|
||||
* webapp matches the rootWebApp it is added as the "/" context.
|
||||
* @param host Virtual host name or null
|
||||
* @param webapps Directory file name or URL to look for auto
|
||||
* webapplication.
|
||||
* @param extract If true, extract war files
|
||||
* @exception IOException
|
||||
*/
|
||||
public WebApplicationContext[] addWebApplications(String host,
|
||||
String webapps,
|
||||
boolean extract)
|
||||
throws IOException
|
||||
{
|
||||
return addWebApplications(host,webapps,null,extract);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Add Web Applications.
|
||||
* Add auto webapplications to the server. The name of the
|
||||
* webapp directory or war is used as the context name. If the
|
||||
* webapp matches the rootWebApp it is added as the "/" context.
|
||||
* @param host Virtual host name or null
|
||||
* @param webapps Directory file name or URL to look for auto
|
||||
* webapplication.
|
||||
* @param defaults The defaults xml filename or URL which is
|
||||
* loaded before any in the web app. Must respect the web.dtd.
|
||||
* If null the default defaults file is used. If the empty string, then
|
||||
* no defaults file is used.
|
||||
* @param extract If true, extract war files
|
||||
* @exception IOException
|
||||
*/
|
||||
public WebApplicationContext[] addWebApplications(String host,
|
||||
String webapps,
|
||||
String defaults,
|
||||
boolean extract)
|
||||
throws IOException
|
||||
{
|
||||
return addWebApplications(host,webapps,defaults,extract,true,null);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Add Web Applications.
|
||||
* Add auto webapplications to the server. The name of the
|
||||
* webapp directory or war is used as the context name. If the
|
||||
* webapp matches the rootWebApp it is added as the "/" context.
|
||||
* @param host Virtual host name or null
|
||||
* @param webapps Directory file name or URL to look for auto
|
||||
* webapplication.
|
||||
* @param defaults The defaults xml filename or URL which is
|
||||
* loaded before any in the web app. Must respect the web.dtd.
|
||||
* If null the default defaults file is used. If the empty string, then
|
||||
* no defaults file is used.
|
||||
* @param extract If true, extract war files
|
||||
* @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications
|
||||
* @exception IOException
|
||||
*/
|
||||
public WebApplicationContext[] addWebApplications(String host,
|
||||
String webapps,
|
||||
String defaults,
|
||||
boolean extract,
|
||||
boolean java2CompliantClassLoader)
|
||||
throws IOException
|
||||
{
|
||||
return addWebApplications(host,webapps,defaults,extract,java2CompliantClassLoader,null);
|
||||
|
||||
}
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Add Web Applications.
|
||||
* Add auto webapplications to the server. The name of the
|
||||
* webapp directory or war is used as the context name. If the
|
||||
* webapp matches the rootWebApp it is added as the "/" context.
|
||||
* @param host Virtual host name or null
|
||||
* @param webapps Directory file name or URL to look for auto
|
||||
* webapplication.
|
||||
* @param defaults The defaults xml filename or URL which is
|
||||
* loaded before any in the web app. Must respect the web.dtd.
|
||||
* If null the default defaults file is used. If the empty string, then
|
||||
* no defaults file is used.
|
||||
* @param extract If true, extract war files
|
||||
* @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications
|
||||
* @param Attributes[] A set of attributes to pass to setAttribute, format is first item is the key, second item is the value.
|
||||
* @exception IOException
|
||||
*/
|
||||
public WebApplicationContext[] addWebApplications(String host,
|
||||
String webapps,
|
||||
String defaults,
|
||||
boolean extract,
|
||||
boolean java2CompliantClassLoader,
|
||||
String Attributes[])
|
||||
throws IOException
|
||||
{
|
||||
ArrayList wacs = new ArrayList();
|
||||
Resource r=Resource.newResource(webapps);
|
||||
if (!r.exists())
|
||||
throw new IllegalArgumentException("No such webapps resource "+r);
|
||||
|
||||
if (!r.isDirectory())
|
||||
throw new IllegalArgumentException("Not directory webapps resource "+r);
|
||||
if(Attributes != null) {
|
||||
if(((Attributes.length / 2) * 2) != Attributes.length) {
|
||||
throw new IllegalArgumentException("Attributes must be in pairs of key,value.");
|
||||
}
|
||||
}
|
||||
String[] files=r.list();
|
||||
|
||||
for (int f=0;files!=null && f<files.length;f++)
|
||||
{
|
||||
String context=files[f];
|
||||
|
||||
if (context.equalsIgnoreCase("CVS/") ||
|
||||
context.equalsIgnoreCase("CVS") ||
|
||||
context.startsWith("."))
|
||||
continue;
|
||||
|
||||
|
||||
String app = r.addPath(r.encode(files[f])).toString();
|
||||
if (context.toLowerCase().endsWith(".war") ||
|
||||
context.toLowerCase().endsWith(".jar"))
|
||||
{
|
||||
context=context.substring(0,context.length()-4);
|
||||
Resource unpacked=r.addPath(context);
|
||||
if (unpacked!=null && unpacked.exists() && unpacked.isDirectory())
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_rootWebApp!=null && (context.equals(_rootWebApp)||context.equals(_rootWebApp+"/")))
|
||||
context="/";
|
||||
else
|
||||
context="/"+context;
|
||||
|
||||
WebApplicationContext wac= addWebApplication(host,
|
||||
context,
|
||||
app);
|
||||
wac.setExtractWAR(extract);
|
||||
wac.setClassLoaderJava2Compliant(java2CompliantClassLoader);
|
||||
if (defaults!=null)
|
||||
{
|
||||
if (defaults.length()==0)
|
||||
wac.setDefaultsDescriptor(null);
|
||||
else
|
||||
wac.setDefaultsDescriptor(defaults);
|
||||
}
|
||||
if(Attributes != null) {
|
||||
for(int i = 0; i < Attributes.length; i++, i++) {
|
||||
wac.setAttribute(Attributes[i],Attributes[i + 1]);
|
||||
}
|
||||
}
|
||||
wacs.add(wac);
|
||||
}
|
||||
|
||||
return (WebApplicationContext[])wacs.toArray(new WebApplicationContext[wacs.size()]);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** setWebApplicationConfigurationClasses
|
||||
* Set up the list of classnames of WebApplicationContext.Configuration
|
||||
* implementations that will be applied to configure every webapp.
|
||||
* The list can be overridden by individual WebApplicationContexts.
|
||||
* @param configurationClasses
|
||||
*/
|
||||
public void setWebApplicationConfigurationClassNames (String[] configurationClassNames)
|
||||
{
|
||||
if (configurationClassNames != null)
|
||||
{
|
||||
_webAppConfigurationClassNames = new String[configurationClassNames.length];
|
||||
System.arraycopy(configurationClassNames, 0, _webAppConfigurationClassNames, 0, configurationClassNames.length);
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getWebApplicationConfigurationClassNames ()
|
||||
{
|
||||
return _webAppConfigurationClassNames;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
public static void main(String[] arg)
|
||||
{
|
||||
String[] dftConfig={"etc/jetty.xml"};
|
||||
|
||||
if (arg.length==0)
|
||||
{
|
||||
log.info("Using default configuration: etc/jetty.xml");
|
||||
arg=dftConfig;
|
||||
}
|
||||
|
||||
final Server[] servers=new Server[arg.length];
|
||||
|
||||
// create and start the servers.
|
||||
for (int i=0;i<arg.length;i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
servers[i] = new Server(arg[i]);
|
||||
servers[i].setStopAtShutdown(true);
|
||||
servers[i].start();
|
||||
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
log.warn(LogSupport.EXCEPTION,e);
|
||||
}
|
||||
}
|
||||
|
||||
// create and start the servers.
|
||||
for (int i=0;i<arg.length;i++)
|
||||
{
|
||||
try{servers[i].join();}
|
||||
catch (Exception e){LogSupport.ignore(log,e);}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ShutdownHook thread for stopping all servers.
|
||||
*
|
||||
* Thread is hooked first time list of servers is changed.
|
||||
*/
|
||||
private static class ShutdownHookThread extends Thread {
|
||||
private boolean hooked = false;
|
||||
private ArrayList servers = new ArrayList();
|
||||
|
||||
/**
|
||||
* Hooks this thread for shutdown.
|
||||
* @see java.lang.Runtime#addShutdownHook(java.lang.Thread)
|
||||
*/
|
||||
private void createShutdownHook() {
|
||||
if (!Boolean.getBoolean("JETTY_NO_SHUTDOWN_HOOK") && !hooked) {
|
||||
try {
|
||||
Method shutdownHook = java.lang.Runtime.class.getMethod("addShutdownHook",
|
||||
new Class[] { java.lang.Thread.class });
|
||||
shutdownHook.invoke(Runtime.getRuntime(), new Object[] { this });
|
||||
this.hooked = true;
|
||||
} catch (Exception e) {
|
||||
if (log.isDebugEnabled()) log.debug("No shutdown hook in JVM ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Server to servers list.
|
||||
*/
|
||||
public boolean add(Server server) {
|
||||
createShutdownHook();
|
||||
return this.servers.add(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains Server in servers list?
|
||||
*/
|
||||
public boolean contains(Server server) {
|
||||
return this.servers.contains(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append all Servers from Collection
|
||||
*/
|
||||
public boolean addAll(Collection c) {
|
||||
createShutdownHook();
|
||||
return this.servers.addAll(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear list of Servers.
|
||||
*/
|
||||
public void clear() {
|
||||
createShutdownHook();
|
||||
this.servers.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Server from list.
|
||||
*/
|
||||
public boolean remove(Server server) {
|
||||
createShutdownHook();
|
||||
return this.servers.remove(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all Servers in Collection from list.
|
||||
*/
|
||||
public boolean removeAll(Collection c) {
|
||||
createShutdownHook();
|
||||
return this.servers.removeAll(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all Servers in list.
|
||||
*/
|
||||
public void run() {
|
||||
setName("Shutdown");
|
||||
log.info("Shutdown hook executing");
|
||||
Iterator it = servers.iterator();
|
||||
while (it.hasNext()) {
|
||||
Server svr = (Server) it.next();
|
||||
if (svr == null) continue;
|
||||
try {
|
||||
svr.stop();
|
||||
} catch (Exception e) {
|
||||
log.warn(LogSupport.EXCEPTION, e);
|
||||
}
|
||||
log.info("Shutdown hook complete");
|
||||
|
||||
// Try to avoid JVM crash
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (Exception e) {
|
||||
log.warn(LogSupport.EXCEPTION, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
431
apps/jetty/java/src/org/mortbay/util/Resource.java
Normal file
@@ -0,0 +1,431 @@
|
||||
// ========================================================================
|
||||
// $Id: Resource.java,v 1.32 2009/05/16 01:53:36 gregwilkins Exp $
|
||||
// Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========================================================================
|
||||
package org.mortbay.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.mortbay.log.LogFactory;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Abstract resource class.
|
||||
*
|
||||
* @version $Id: Resource.java,v 1.32 2009/05/16 01:53:36 gregwilkins Exp $
|
||||
* @author Nuno Preguica
|
||||
* @author Greg Wilkins (gregw)
|
||||
*/
|
||||
public abstract class Resource implements Serializable
|
||||
{
|
||||
private static Log log = LogFactory.getLog(Resource.class);
|
||||
|
||||
Object _associate;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Construct a resource from a url.
|
||||
* @param url A URL.
|
||||
* @return A Resource object.
|
||||
*/
|
||||
public static Resource newResource(URL url)
|
||||
throws IOException
|
||||
{
|
||||
if (url==null)
|
||||
return null;
|
||||
|
||||
String urls=url.toExternalForm();
|
||||
if( urls.startsWith( "file:"))
|
||||
{
|
||||
try
|
||||
{
|
||||
FileResource fileResource= new FileResource(url);
|
||||
return fileResource;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
log.debug(LogSupport.EXCEPTION,e);
|
||||
return new BadResource(url,e.toString());
|
||||
}
|
||||
}
|
||||
else if( urls.startsWith( "jar:file:"))
|
||||
{
|
||||
return new JarFileResource(url);
|
||||
}
|
||||
else if( urls.startsWith( "jar:"))
|
||||
{
|
||||
return new JarResource(url);
|
||||
}
|
||||
|
||||
return new URLResource(url,null);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Construct a resource from a string.
|
||||
* @param resource A URL or filename.
|
||||
* @return A Resource object.
|
||||
*/
|
||||
public static Resource newResource(String resource)
|
||||
throws MalformedURLException, IOException
|
||||
{
|
||||
URL url=null;
|
||||
try
|
||||
{
|
||||
// Try to format as a URL?
|
||||
url = new URL(resource);
|
||||
}
|
||||
catch(MalformedURLException e)
|
||||
{
|
||||
if(!resource.startsWith("ftp:") &&
|
||||
!resource.startsWith("file:") &&
|
||||
!resource.startsWith("jar:"))
|
||||
{
|
||||
try
|
||||
{
|
||||
// It's a file.
|
||||
if (resource.startsWith("./"))
|
||||
resource=resource.substring(2);
|
||||
|
||||
File file=new File(resource).getCanonicalFile();
|
||||
url=file.toURI().toURL();
|
||||
|
||||
URLConnection connection=url.openConnection();
|
||||
FileResource fileResource= new FileResource(url,connection,file);
|
||||
return fileResource;
|
||||
}
|
||||
catch(Exception e2)
|
||||
{
|
||||
log.debug(LogSupport.EXCEPTION,e2);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("Bad Resource: "+resource);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
String nurl=url.toString();
|
||||
if (nurl.length()>0 &&
|
||||
nurl.charAt(nurl.length()-1)!=
|
||||
resource.charAt(resource.length()-1))
|
||||
{
|
||||
if ((nurl.charAt(nurl.length()-1)!='/' ||
|
||||
nurl.charAt(nurl.length()-2)!=resource.charAt(resource.length()-1))
|
||||
&&
|
||||
(resource.charAt(resource.length()-1)!='/' ||
|
||||
resource.charAt(resource.length()-2)!=nurl.charAt(nurl.length()-1)
|
||||
))
|
||||
{
|
||||
return new BadResource(url,"Trailing special characters stripped by URL in "+resource);
|
||||
}
|
||||
}
|
||||
return newResource(url);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Construct a system resource from a string.
|
||||
* The resource is tried as classloader resource before being
|
||||
* treated as a normal resource.
|
||||
*/
|
||||
public static Resource newSystemResource(String resource)
|
||||
throws IOException
|
||||
{
|
||||
URL url=null;
|
||||
// Try to format as a URL?
|
||||
ClassLoader
|
||||
loader=Thread.currentThread().getContextClassLoader();
|
||||
if (loader!=null)
|
||||
{
|
||||
url=loader.getResource(resource);
|
||||
if (url==null && resource.startsWith("/"))
|
||||
url=loader.getResource(resource.substring(1));
|
||||
}
|
||||
if (url==null)
|
||||
{
|
||||
loader=Resource.class.getClassLoader();
|
||||
if (loader!=null)
|
||||
{
|
||||
url=loader.getResource(resource);
|
||||
if (url==null && resource.startsWith("/"))
|
||||
url=loader.getResource(resource.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
if (url==null)
|
||||
{
|
||||
url=ClassLoader.getSystemResource(resource);
|
||||
if (url==null && resource.startsWith("/"))
|
||||
url=loader.getResource(resource.substring(1));
|
||||
}
|
||||
|
||||
if (url==null)
|
||||
return null;
|
||||
|
||||
return newResource(url);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void finalize()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Release any resources held by the resource.
|
||||
*/
|
||||
public abstract void release();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns true if the respresened resource exists.
|
||||
*/
|
||||
public abstract boolean exists();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns true if the respresenetd resource is a container/directory.
|
||||
* If the resource is not a file, resources ending with "/" are
|
||||
* considered directories.
|
||||
*/
|
||||
public abstract boolean isDirectory();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns the last modified time
|
||||
*/
|
||||
public abstract long lastModified();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Return the length of the resource
|
||||
*/
|
||||
public abstract long length();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns an URL representing the given resource
|
||||
*/
|
||||
public abstract URL getURL();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns an File representing the given resource or NULL if this
|
||||
* is not possible.
|
||||
*/
|
||||
public abstract File getFile()
|
||||
throws IOException;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns the name of the resource
|
||||
*/
|
||||
public abstract String getName();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns an input stream to the resource
|
||||
*/
|
||||
public abstract InputStream getInputStream()
|
||||
throws java.io.IOException;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns an output stream to the resource
|
||||
*/
|
||||
public abstract OutputStream getOutputStream()
|
||||
throws java.io.IOException, SecurityException;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Deletes the given resource
|
||||
*/
|
||||
public abstract boolean delete()
|
||||
throws SecurityException;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Rename the given resource
|
||||
*/
|
||||
public abstract boolean renameTo( Resource dest)
|
||||
throws SecurityException;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns a list of resource names contained in the given resource
|
||||
* The resource names are not URL encoded.
|
||||
*/
|
||||
public abstract String[] list();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns the resource contained inside the current resource with the
|
||||
* given name.
|
||||
* @param path The path segment to add, which should be encoded by the
|
||||
* encode method.
|
||||
*/
|
||||
public abstract Resource addPath(String path)
|
||||
throws IOException,MalformedURLException;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Encode according to this resource type.
|
||||
* The default implementation calls URI.encodePath(uri)
|
||||
* @param uri
|
||||
* @return String encoded for this resource type.
|
||||
*/
|
||||
public String encode(String uri)
|
||||
{
|
||||
return URI.encodePath(uri);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Object getAssociate()
|
||||
{
|
||||
return _associate;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void setAssociate(Object o)
|
||||
{
|
||||
_associate=o;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return The canonical Alias of this resource or null if none.
|
||||
*/
|
||||
public URL getAlias()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public CachedResource cache()
|
||||
throws IOException
|
||||
{
|
||||
return new CachedResource(this);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the resource list as a HTML directory listing.
|
||||
* @param base The base URL
|
||||
* @param parent True if the parent directory should be included
|
||||
* @return String of HTML
|
||||
*/
|
||||
public String getListHTML(String base,
|
||||
boolean parent)
|
||||
throws IOException
|
||||
{
|
||||
if (!isDirectory())
|
||||
return null;
|
||||
|
||||
|
||||
String[] ls = list();
|
||||
if (ls==null)
|
||||
return null;
|
||||
Arrays.sort(ls);
|
||||
|
||||
String title = "Directory: "+URI.decodePath(base);
|
||||
title=StringUtil.replace(StringUtil.replace(title,"<","<"),">",">");
|
||||
StringBuffer buf=new StringBuffer(4096);
|
||||
buf.append("<HTML><HEAD><TITLE>");
|
||||
buf.append(title);
|
||||
buf.append("</TITLE></HEAD><BODY>\n<H1>");
|
||||
buf.append(title);
|
||||
buf.append("</H1><TABLE BORDER=0>");
|
||||
|
||||
if (parent)
|
||||
{
|
||||
buf.append("<TR><TD><A HREF=");
|
||||
buf.append(URI.encodePath(URI.addPaths(base,"../")));
|
||||
buf.append(">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
|
||||
}
|
||||
|
||||
DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
|
||||
DateFormat.MEDIUM);
|
||||
for (int i=0 ; i< ls.length ; i++)
|
||||
{
|
||||
String encoded=URI.encodePath(ls[i]);
|
||||
// bugfix for I2P - Backport from Jetty 6 (zero file lengths and last-modified times)
|
||||
// http://jira.codehaus.org/browse/JETTY-361?page=com.atlassian.jira.plugin.system.issuetabpanels%3Achangehistory-tabpanel#issue-tabs
|
||||
// See resource.diff attachment
|
||||
//Resource item = addPath(encoded);
|
||||
Resource item = addPath(ls[i]);
|
||||
|
||||
buf.append("<TR><TD><A HREF=\"");
|
||||
|
||||
String path=URI.addPaths(base,encoded);
|
||||
|
||||
if (item.isDirectory() && !path.endsWith("/"))
|
||||
path=URI.addPaths(path,"/");
|
||||
buf.append(path);
|
||||
buf.append("\">");
|
||||
buf.append(StringUtil.replace(StringUtil.replace(ls[i],"<","<"),">",">"));
|
||||
buf.append(" ");
|
||||
buf.append("</TD><TD ALIGN=right>");
|
||||
buf.append(item.length());
|
||||
buf.append(" bytes </TD><TD>");
|
||||
buf.append(dfmt.format(new Date(item.lastModified())));
|
||||
buf.append("</TD></TR>\n");
|
||||
}
|
||||
buf.append("</TABLE>\n");
|
||||
buf.append("</BODY></HTML>\n");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param out
|
||||
* @param start First byte to write
|
||||
* @param count Bytes to write or -1 for all of them.
|
||||
*/
|
||||
public void writeTo(OutputStream out,long start,long count)
|
||||
throws IOException
|
||||
{
|
||||
InputStream in = getInputStream();
|
||||
try
|
||||
{
|
||||
in.skip(start);
|
||||
if (count<0)
|
||||
IO.copy(in,out);
|
||||
else
|
||||
IO.copy(in,out,(int)count);
|
||||
}
|
||||
finally
|
||||
{
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ class I2PServerSocketImpl implements I2PServerSocket {
|
||||
I2PSocket ret = null;
|
||||
|
||||
while ( (ret == null) && (!closing) ){
|
||||
while (pendingSockets.size() <= 0) {
|
||||
while (pendingSockets.isEmpty()) {
|
||||
if (closing) throw new ConnectException("I2PServerSocket closed");
|
||||
try {
|
||||
synchronized(socketAddedLock) {
|
||||
@@ -78,7 +78,7 @@ class I2PServerSocketImpl implements I2PServerSocket {
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
synchronized (pendingSockets) {
|
||||
if (pendingSockets.size() > 0) {
|
||||
if (!pendingSockets.isEmpty()) {
|
||||
ret = (I2PSocket)pendingSockets.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,13 +64,16 @@
|
||||
<target name="jar" depends="compile">
|
||||
<jar destfile="./build/routerconsole.jar" basedir="./build/obj" includes="**/*.class">
|
||||
<manifest>
|
||||
<attribute name="Class-Path" value="i2p.jar router.jar" />
|
||||
<!-- top level installer will rename to jrobin.jar -->
|
||||
<attribute name="Class-Path" value="i2p.jar router.jar jrobin.jar" />
|
||||
</manifest>
|
||||
</jar>
|
||||
<delete dir="./tmpextract" />
|
||||
<!-- jrobin taken out of routerconsole.jar in 0.7.12
|
||||
<unjar src="../../jrobin/jrobin-1.4.0.jar" dest="./tmpextract" />
|
||||
<jar destfile="./build/routerconsole.jar" basedir="./tmpextract" update="true" />
|
||||
<delete dir="./tmpextract" />
|
||||
-->
|
||||
|
||||
<ant target="war" />
|
||||
|
||||
|
||||
@@ -39,7 +39,11 @@ ROUTERFILES="\
|
||||
../../../router/java/src/net/i2p/router/transport/TransportManager.java \
|
||||
../../../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/transport/ntcp/EstablishState.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"
|
||||
@@ -78,7 +82,7 @@ do
|
||||
# To start a new translation, copy the header from an old translation to the new .po file,
|
||||
# then ant distclean updater.
|
||||
find $JPATHS -name *.java > $TMPFILE
|
||||
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 \
|
||||
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 --add-comments\
|
||||
--keyword=_ --keyword=_x --keyword=intl._ --keyword=intl.title \
|
||||
--keyword=handler._ --keyword=formhandler._ \
|
||||
--keyword=net.i2p.router.web.Messages.getString \
|
||||
|
||||
@@ -19,6 +19,9 @@ public class CSSHelper extends HelperBase {
|
||||
if (userAgent != null && userAgent.contains("MSIE")) {
|
||||
url += FORCE + "/";
|
||||
} else {
|
||||
// This is the first thing to use _context on most pages
|
||||
if (_context == null)
|
||||
throw new IllegalStateException("No contexts. This is usually because the router is either starting up or shutting down.");
|
||||
String theme = _context.getProperty(PROP_THEME_NAME, DEFAULT_THEME);
|
||||
url += theme + "/";
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -11,24 +11,26 @@ import java.util.Set;
|
||||
|
||||
import net.i2p.router.startup.ClientAppConfig;
|
||||
import net.i2p.router.startup.LoadClientAppsJob;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import org.mortbay.http.HttpListener;
|
||||
import org.mortbay.jetty.Server;
|
||||
|
||||
/**
|
||||
* Saves changes to clients.config or webapps.config
|
||||
*/
|
||||
public class ConfigClientsHandler extends FormHandler {
|
||||
private Log configClient_log;
|
||||
private Map _settings;
|
||||
|
||||
public ConfigClientsHandler() {
|
||||
configClient_log = ContextHelper.getContext(null).logManager().getLog(ConfigClientsHandler.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processForm() {
|
||||
// set action for when CR is hit in a text input box
|
||||
if (_action.length() <= 0) {
|
||||
String url = getJettyString("pluginURL");
|
||||
if (url != null && url.length() > 0)
|
||||
_action = "Install Plugin";
|
||||
else
|
||||
_action = "Save Client Configuration";
|
||||
}
|
||||
|
||||
if (_action.equals(_("Save Client Configuration"))) {
|
||||
saveClientChanges();
|
||||
return;
|
||||
@@ -37,6 +39,14 @@ public class ConfigClientsHandler extends FormHandler {
|
||||
saveWebAppChanges();
|
||||
return;
|
||||
}
|
||||
if (_action.equals(_("Save Plugin Configuration"))) {
|
||||
savePluginChanges();
|
||||
return;
|
||||
}
|
||||
if (_action.equals(_("Install Plugin"))) {
|
||||
installPlugin();
|
||||
return;
|
||||
}
|
||||
// value
|
||||
if (_action.startsWith("Start ")) {
|
||||
String app = _action.substring(6);
|
||||
@@ -44,12 +54,67 @@ public class ConfigClientsHandler extends FormHandler {
|
||||
try {
|
||||
appnum = Integer.parseInt(app);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
if (appnum >= 0)
|
||||
if (appnum >= 0) {
|
||||
startClient(appnum);
|
||||
else
|
||||
startWebApp(app);
|
||||
} else {
|
||||
List<String> plugins = PluginStarter.getPlugins();
|
||||
if (plugins.contains(app))
|
||||
startPlugin(app);
|
||||
else
|
||||
startWebApp(app);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// value
|
||||
if (_action.startsWith("Delete ")) {
|
||||
String app = _action.substring(7);
|
||||
int appnum = -1;
|
||||
try {
|
||||
appnum = Integer.parseInt(app);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
if (appnum >= 0) {
|
||||
deleteClient(appnum);
|
||||
} else {
|
||||
try {
|
||||
PluginStarter.stopPlugin(_context, app);
|
||||
PluginStarter.deletePlugin(_context, app);
|
||||
addFormNotice(_("Deleted plugin {0}", app));
|
||||
} catch (Throwable e) {
|
||||
addFormError(_("Error deleting plugin {0}", app) + ": " + e);
|
||||
_log.error("Error deleting plugin " + app, e);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// value
|
||||
if (_action.startsWith("Stop ")) {
|
||||
String app = _action.substring(5);
|
||||
try {
|
||||
PluginStarter.stopPlugin(_context, app);
|
||||
addFormNotice(_("Stopped plugin {0}", app));
|
||||
} catch (Throwable e) {
|
||||
addFormError(_("Error stopping plugin {0}", app) + ": " + e);
|
||||
_log.error("Error stopping plugin " + app, e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// value
|
||||
if (_action.startsWith("Update ")) {
|
||||
String app = _action.substring(7);
|
||||
updatePlugin(app);
|
||||
return;
|
||||
}
|
||||
|
||||
// value
|
||||
if (_action.startsWith("Check ")) {
|
||||
String app = _action.substring(6);
|
||||
checkPlugin(app);
|
||||
return;
|
||||
}
|
||||
|
||||
// label (IE)
|
||||
String xStart = _("Start");
|
||||
if (_action.toLowerCase().startsWith(xStart + "<span class=hide> ") &&
|
||||
@@ -60,40 +125,99 @@ public class ConfigClientsHandler extends FormHandler {
|
||||
try {
|
||||
appnum = Integer.parseInt(app);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
if (appnum >= 0)
|
||||
if (appnum >= 0) {
|
||||
startClient(appnum);
|
||||
else
|
||||
startWebApp(app);
|
||||
} else {
|
||||
List<String> plugins = PluginStarter.getPlugins();
|
||||
if (plugins.contains(app))
|
||||
startPlugin(app);
|
||||
else
|
||||
startWebApp(app);
|
||||
}
|
||||
} else {
|
||||
addFormError(_("Unsupported") + ' ' + _action + '.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setSettings(Map settings) { _settings = new HashMap(settings); }
|
||||
|
||||
private void saveClientChanges() {
|
||||
List clients = ClientAppConfig.getClientApps(_context);
|
||||
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(_context);
|
||||
for (int cur = 0; cur < clients.size(); cur++) {
|
||||
ClientAppConfig ca = (ClientAppConfig) clients.get(cur);
|
||||
ClientAppConfig ca = clients.get(cur);
|
||||
Object val = _settings.get(cur + ".enabled");
|
||||
if (! ("webConsole".equals(ca.clientName) || "Web console".equals(ca.clientName)))
|
||||
ca.disabled = val == null;
|
||||
// edit of an existing entry
|
||||
String desc = getJettyString("desc" + cur);
|
||||
if (desc != null) {
|
||||
int spc = desc.indexOf(" ");
|
||||
String clss = desc;
|
||||
String args = null;
|
||||
if (spc >= 0) {
|
||||
clss = desc.substring(0, spc);
|
||||
args = desc.substring(spc + 1);
|
||||
}
|
||||
ca.className = clss;
|
||||
ca.args = args;
|
||||
ca.clientName = getJettyString("name" + cur);
|
||||
}
|
||||
}
|
||||
|
||||
int newClient = clients.size();
|
||||
String newDesc = getJettyString("desc" + newClient);
|
||||
if (newDesc != null && newDesc.trim().length() > 0) {
|
||||
// new entry
|
||||
int spc = newDesc.indexOf(" ");
|
||||
String clss = newDesc;
|
||||
String args = null;
|
||||
if (spc >= 0) {
|
||||
clss = newDesc.substring(0, spc);
|
||||
args = newDesc.substring(spc + 1);
|
||||
}
|
||||
String name = getJettyString("name" + newClient);
|
||||
if (name == null || name.trim().length() <= 0) name = "new client";
|
||||
ClientAppConfig ca = new ClientAppConfig(clss, name, args, 2*60*1000,
|
||||
_settings.get(newClient + ".enabled") != null);
|
||||
clients.add(ca);
|
||||
addFormNotice(_("New client added") + ": " + name + " (" + clss + ").");
|
||||
}
|
||||
|
||||
ClientAppConfig.writeClientAppConfig(_context, clients);
|
||||
addFormNotice(_("Client configuration saved successfully - restart required to take effect."));
|
||||
}
|
||||
|
||||
/** curses Jetty for returning arrays */
|
||||
private String getJettyString(String key) {
|
||||
String[] arr = (String[]) _settings.get(key);
|
||||
if (arr == null)
|
||||
return null;
|
||||
return arr[0];
|
||||
}
|
||||
|
||||
private void startClient(int i) {
|
||||
List clients = ClientAppConfig.getClientApps(_context);
|
||||
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(_context);
|
||||
if (i >= clients.size()) {
|
||||
addFormError(_("Bad client index."));
|
||||
return;
|
||||
}
|
||||
ClientAppConfig ca = (ClientAppConfig) clients.get(i);
|
||||
LoadClientAppsJob.runClient(ca.className, ca.clientName, LoadClientAppsJob.parseArgs(ca.args), configClient_log);
|
||||
ClientAppConfig ca = clients.get(i);
|
||||
LoadClientAppsJob.runClient(ca.className, ca.clientName, LoadClientAppsJob.parseArgs(ca.args), _log);
|
||||
addFormNotice(_("Client") + ' ' + _(ca.clientName) + ' ' + _("started") + '.');
|
||||
}
|
||||
|
||||
private void deleteClient(int i) {
|
||||
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(_context);
|
||||
if (i < 0 || i >= clients.size()) {
|
||||
addFormError(_("Bad client index."));
|
||||
return;
|
||||
}
|
||||
ClientAppConfig ca = clients.remove(i);
|
||||
ClientAppConfig.writeClientAppConfig(_context, clients);
|
||||
addFormNotice(_("Client") + ' ' + _(ca.clientName) + ' ' + _("deleted") + '.');
|
||||
}
|
||||
|
||||
private void saveWebAppChanges() {
|
||||
Properties props = RouterConsoleRunner.webAppProperties();
|
||||
Set keys = props.keySet();
|
||||
@@ -108,32 +232,109 @@ public class ConfigClientsHandler extends FormHandler {
|
||||
props.setProperty(name, "" + (val != null));
|
||||
}
|
||||
RouterConsoleRunner.storeWebAppProperties(props);
|
||||
addFormNotice(_("WebApp configuration saved successfully - restart required to take effect."));
|
||||
addFormNotice(_("WebApp configuration saved."));
|
||||
}
|
||||
|
||||
// Big hack for the moment, not using properties for directory and port
|
||||
// Go through all the Jetty servers, find the one serving port 7657,
|
||||
// requested and add the .war to that one
|
||||
private void savePluginChanges() {
|
||||
Properties props = PluginStarter.pluginProperties();
|
||||
Set keys = props.keySet();
|
||||
int cur = 0;
|
||||
for (Iterator iter = keys.iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
if (! (name.startsWith(PluginStarter.PREFIX) && name.endsWith(PluginStarter.ENABLED)))
|
||||
continue;
|
||||
String app = name.substring(PluginStarter.PREFIX.length(), name.lastIndexOf(PluginStarter.ENABLED));
|
||||
Object val = _settings.get(app + ".enabled");
|
||||
props.setProperty(name, "" + (val != null));
|
||||
}
|
||||
PluginStarter.storePluginProperties(props);
|
||||
addFormNotice(_("Plugin configuration saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Big hack for the moment, not using properties for directory and port
|
||||
* Go through all the Jetty servers, find the one serving port 7657,
|
||||
* requested and add the .war to that one
|
||||
*/
|
||||
private void startWebApp(String app) {
|
||||
Collection c = Server.getHttpServers();
|
||||
for (int i = 0; i < c.size(); i++) {
|
||||
Server s = (Server) c.toArray()[i];
|
||||
HttpListener[] hl = s.getListeners();
|
||||
for (int j = 0; j < hl.length; j++) {
|
||||
if (hl[j].getPort() == 7657) {
|
||||
Server s = WebAppStarter.getConsoleServer();
|
||||
if (s != null) {
|
||||
try {
|
||||
File path = new File(_context.getBaseDir(), "webapps");
|
||||
path = new File(path, app + ".war");
|
||||
s.addWebApplication("/"+ app, path.getAbsolutePath()).start();
|
||||
// no passwords... initialize(wac);
|
||||
WebAppStarter.startWebApp(_context, s, app, path.getAbsolutePath());
|
||||
addFormNotice(_("WebApp") + " <a href=\"/" + app + "/\">" + _(app) + "</a> " + _("started") + '.');
|
||||
} catch (Exception ioe) {
|
||||
addFormError(_("Failed to start") + ' ' + _(app) + " " + ioe + '.');
|
||||
} catch (Throwable e) {
|
||||
addFormError(_("Failed to start") + ' ' + _(app) + " " + e + '.');
|
||||
_log.error("Failed to start webapp " + app, e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
addFormError(_("Failed to find server."));
|
||||
}
|
||||
|
||||
private void installPlugin() {
|
||||
String url = getJettyString("pluginURL");
|
||||
if (url == null || url.length() <= 0) {
|
||||
addFormError(_("No plugin URL specified."));
|
||||
return;
|
||||
}
|
||||
installPlugin(url);
|
||||
}
|
||||
|
||||
private void updatePlugin(String app) {
|
||||
Properties props = PluginStarter.pluginProperties(_context, app);
|
||||
String url = props.getProperty("updateURL");
|
||||
if (url == null) {
|
||||
addFormError(_("No update URL specified for {0}",app));
|
||||
return;
|
||||
}
|
||||
installPlugin(url);
|
||||
}
|
||||
|
||||
private void installPlugin(String url) {
|
||||
if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) {
|
||||
addFormError(_("Plugin or update download already in progress."));
|
||||
return;
|
||||
}
|
||||
PluginUpdateHandler puh = PluginUpdateHandler.getInstance(_context);
|
||||
if (puh.isRunning()) {
|
||||
addFormError(_("Plugin or update download already in progress."));
|
||||
return;
|
||||
}
|
||||
puh.update(url);
|
||||
addFormNotice(_("Downloading plugin from {0}", url));
|
||||
// So that update() will post a status to the summary bar before we reload
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
|
||||
private void checkPlugin(String app) {
|
||||
if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) {
|
||||
addFormError(_("Plugin or update download already in progress."));
|
||||
return;
|
||||
}
|
||||
PluginUpdateChecker puc = PluginUpdateChecker.getInstance(_context);
|
||||
if (puc.isRunning()) {
|
||||
addFormError(_("Plugin or update download already in progress."));
|
||||
return;
|
||||
}
|
||||
puc.update(app);
|
||||
addFormNotice(_("Checking plugin {0} for updates", app));
|
||||
// So that update() will post a status to the summary bar before we reload
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
|
||||
private void startPlugin(String app) {
|
||||
try {
|
||||
PluginStarter.startPlugin(_context, app);
|
||||
addFormNotice(_("Started plugin {0}", app));
|
||||
} catch (Throwable e) {
|
||||
addFormError(_("Error starting plugin {0}", app) + ": " + e);
|
||||
_log.error("Error starting plugin " + app, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
@@ -9,21 +11,43 @@ import java.util.TreeSet;
|
||||
import net.i2p.router.startup.ClientAppConfig;
|
||||
|
||||
public class ConfigClientsHelper extends HelperBase {
|
||||
private String _edit;
|
||||
|
||||
public ConfigClientsHelper() {}
|
||||
|
||||
public void setEdit(String edit) {
|
||||
if (edit == null)
|
||||
return;
|
||||
String xStart = _("Edit");
|
||||
if (edit.startsWith(xStart + "<span class=hide> ") &&
|
||||
edit.endsWith("</span>")) {
|
||||
// IE sucks
|
||||
_edit = edit.substring(xStart.length() + 18, edit.length() - 7);
|
||||
} else if (edit.startsWith("Edit ")) {
|
||||
_edit = edit.substring(5);
|
||||
} else if (edit.startsWith(xStart + ' ')) {
|
||||
_edit = edit.substring(xStart.length() + 1);
|
||||
} else if ((_("Add Client")).equals(edit)) {
|
||||
_edit = "new";
|
||||
}
|
||||
}
|
||||
|
||||
public String getForm1() {
|
||||
StringBuilder buf = new StringBuilder(1024);
|
||||
buf.append("<table>\n");
|
||||
buf.append("<tr><th align=\"right\">" + _("Client") + "</th><th>" + _("Run at Startup?") + "</th><th>" + _("Start Now") + "</th><th align=\"left\">" + _("Class and arguments") + "</th></tr>\n");
|
||||
buf.append("<tr><th align=\"right\">" + _("Client") + "</th><th>" + _("Run at Startup?") + "</th><th>" + _("Control") + "</th><th align=\"left\">" + _("Class and arguments") + "</th></tr>\n");
|
||||
|
||||
List clients = ClientAppConfig.getClientApps(_context);
|
||||
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(_context);
|
||||
for (int cur = 0; cur < clients.size(); cur++) {
|
||||
ClientAppConfig ca = (ClientAppConfig) clients.get(cur);
|
||||
ClientAppConfig ca = clients.get(cur);
|
||||
renderForm(buf, ""+cur, ca.clientName, false, !ca.disabled,
|
||||
"webConsole".equals(ca.clientName) || "Web console".equals(ca.clientName),
|
||||
ca.className + ((ca.args != null) ? " " + ca.args : ""));
|
||||
ca.className + ((ca.args != null) ? " " + ca.args : ""), (""+cur).equals(_edit),
|
||||
true, false, false, true, ca.disabled);
|
||||
}
|
||||
|
||||
if ("new".equals(_edit))
|
||||
renderForm(buf, "" + clients.size(), "", false, false, false, "", true, false, false, false, false, false);
|
||||
buf.append("</table>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
@@ -31,30 +55,128 @@ public class ConfigClientsHelper extends HelperBase {
|
||||
public String getForm2() {
|
||||
StringBuilder buf = new StringBuilder(1024);
|
||||
buf.append("<table>\n");
|
||||
buf.append("<tr><th align=\"right\">" + _("WebApp") + "</th><th>" + _("Run at Startup?") + "</th><th>" + _("Start Now") + "</th><th align=\"left\">" + _("Description") + "</th></tr>\n");
|
||||
buf.append("<tr><th align=\"right\">" + _("WebApp") + "</th><th>" + _("Run at Startup?") + "</th><th>" + _("Control") + "</th><th align=\"left\">" + _("Description") + "</th></tr>\n");
|
||||
Properties props = RouterConsoleRunner.webAppProperties();
|
||||
Set keys = new TreeSet(props.keySet());
|
||||
for (Iterator iter = keys.iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
Set<String> keys = new TreeSet(props.keySet());
|
||||
for (Iterator<String> iter = keys.iterator(); iter.hasNext(); ) {
|
||||
String name = iter.next();
|
||||
if (name.startsWith(RouterConsoleRunner.PREFIX) && name.endsWith(RouterConsoleRunner.ENABLED)) {
|
||||
String app = name.substring(RouterConsoleRunner.PREFIX.length(), name.lastIndexOf(RouterConsoleRunner.ENABLED));
|
||||
String val = props.getProperty(name);
|
||||
renderForm(buf, app, app, !"addressbook".equals(app), "true".equals(val), RouterConsoleRunner.ROUTERCONSOLE.equals(app), app + ".war");
|
||||
renderForm(buf, app, app, !"addressbook".equals(app),
|
||||
"true".equals(val), RouterConsoleRunner.ROUTERCONSOLE.equals(app), app + ".war",
|
||||
false, false, false, false, false, true);
|
||||
}
|
||||
}
|
||||
buf.append("</table>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private void renderForm(StringBuilder buf, String index, String name, boolean urlify, boolean enabled, boolean ro, String desc) {
|
||||
public boolean showPlugins() {
|
||||
return PluginStarter.pluginsEnabled(_context);
|
||||
}
|
||||
|
||||
public String getForm3() {
|
||||
StringBuilder buf = new StringBuilder(1024);
|
||||
buf.append("<table>\n");
|
||||
buf.append("<tr><th align=\"right\">" + _("Plugin") + "</th><th>" + _("Run at Startup?") + "</th><th>" + _("Control") + "</th><th align=\"left\">" + _("Description") + "</th></tr>\n");
|
||||
Properties props = PluginStarter.pluginProperties();
|
||||
Set<String> keys = new TreeSet(props.keySet());
|
||||
for (Iterator<String> iter = keys.iterator(); iter.hasNext(); ) {
|
||||
String name = iter.next();
|
||||
if (name.startsWith(PluginStarter.PREFIX) && name.endsWith(PluginStarter.ENABLED)) {
|
||||
String app = name.substring(PluginStarter.PREFIX.length(), name.lastIndexOf(PluginStarter.ENABLED));
|
||||
String val = props.getProperty(name);
|
||||
Properties appProps = PluginStarter.pluginProperties(_context, app);
|
||||
if (appProps.isEmpty())
|
||||
continue;
|
||||
StringBuilder desc = new StringBuilder(256);
|
||||
desc.append("<table border=\"0\">")
|
||||
.append("<tr><td><b>").append(_("Version")).append("<td>").append(stripHTML(appProps, "version"))
|
||||
.append("<tr><td><b>")
|
||||
.append(_("Signed by")).append("<td>");
|
||||
String s = stripHTML(appProps, "signer");
|
||||
if (s != null) {
|
||||
if (s.indexOf("@") > 0)
|
||||
desc.append("<a href=\"mailto:").append(s).append("\">").append(s).append("</a>");
|
||||
else
|
||||
desc.append(s);
|
||||
}
|
||||
s = stripHTML(appProps, "date");
|
||||
if (s != null) {
|
||||
long ms = 0;
|
||||
try {
|
||||
ms = Long.parseLong(s);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
if (ms > 0) {
|
||||
String date = (new SimpleDateFormat("yyyy-MM-dd HH:mm")).format(new Date(ms));
|
||||
desc.append("<tr><td><b>")
|
||||
.append(_("Date")).append("<td>").append(date);
|
||||
}
|
||||
}
|
||||
s = stripHTML(appProps, "author");
|
||||
if (s != null) {
|
||||
desc.append("<tr><td><b>")
|
||||
.append(_("Author")).append("<td>");
|
||||
if (s.indexOf("@") > 0)
|
||||
desc.append("<a href=\"mailto:").append(s).append("\">").append(s).append("</a>");
|
||||
else
|
||||
desc.append(s);
|
||||
}
|
||||
s = stripHTML(appProps, "description_" + Messages.getLanguage(_context));
|
||||
if (s == null)
|
||||
s = stripHTML(appProps, "description");
|
||||
if (s != null) {
|
||||
desc.append("<tr><td><b>")
|
||||
.append(_("Description")).append("<td>").append(s);
|
||||
}
|
||||
s = stripHTML(appProps, "license");
|
||||
if (s != null) {
|
||||
desc.append("<tr><td><b>")
|
||||
.append(_("License")).append("<td>").append(s);
|
||||
}
|
||||
s = stripHTML(appProps, "websiteURL");
|
||||
if (s != null) {
|
||||
desc.append("<tr><td>")
|
||||
.append("<a href=\"").append(s).append("\">").append(_("Website")).append("</a><td> ");
|
||||
}
|
||||
String updateURL = stripHTML(appProps, "updateURL");
|
||||
if (updateURL != null) {
|
||||
desc.append("<tr><td>")
|
||||
.append("<a href=\"").append(updateURL).append("\">").append(_("Update link")).append("</a><td> ");
|
||||
}
|
||||
desc.append("</table>");
|
||||
boolean enableStop = !Boolean.valueOf(appProps.getProperty("disableStop")).booleanValue();
|
||||
enableStop &= PluginStarter.isPluginRunning(app, _context);
|
||||
boolean enableStart = !PluginStarter.isPluginRunning(app, _context);
|
||||
renderForm(buf, app, app, false,
|
||||
"true".equals(val), false, desc.toString(), false, false,
|
||||
updateURL != null, enableStop, true, enableStart);
|
||||
}
|
||||
}
|
||||
buf.append("</table>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** ro trumps edit and showEditButton */
|
||||
private void renderForm(StringBuilder buf, String index, String name, boolean urlify,
|
||||
boolean enabled, boolean ro, String desc, boolean edit,
|
||||
boolean showEditButton, boolean showUpdateButton, boolean showStopButton,
|
||||
boolean showDeleteButton, boolean showStartButton) {
|
||||
buf.append("<tr><td class=\"mediumtags\" align=\"right\" width=\"25%\">");
|
||||
if (urlify && enabled) {
|
||||
String link = "/";
|
||||
if (! RouterConsoleRunner.ROUTERCONSOLE.equals(name))
|
||||
link += name + "/";
|
||||
buf.append("<a href=\"").append(link).append("\">").append(_(name)).append("</a>");
|
||||
} else if (edit && !ro) {
|
||||
buf.append("<input type=\"text\" name=\"name").append(index).append("\" value=\"");
|
||||
if (name.length() > 0)
|
||||
buf.append(_(name));
|
||||
buf.append("\" >");
|
||||
} else {
|
||||
buf.append(_(name));
|
||||
if (name.length() > 0)
|
||||
buf.append(_(name));
|
||||
}
|
||||
buf.append("</td><td align=\"center\" width=\"10%\"><input type=\"checkbox\" class=\"optbox\" name=\"").append(index).append(".enabled\" value=\"true\" ");
|
||||
if (enabled) {
|
||||
@@ -62,10 +184,45 @@ public class ConfigClientsHelper extends HelperBase {
|
||||
if (ro)
|
||||
buf.append("disabled=\"true\" ");
|
||||
}
|
||||
buf.append("/></td><td align=\"center\" width=\"15%\">");
|
||||
if (!enabled) {
|
||||
buf.append("></td><td align=\"center\" width=\"15%\">");
|
||||
if (showStartButton && (!ro) && !edit) {
|
||||
buf.append("<button type=\"submit\" name=\"action\" value=\"Start ").append(index).append("\" >" + _("Start") + "<span class=hide> ").append(index).append("</span></button>");
|
||||
}
|
||||
buf.append("</td><td align=\"left\" width=\"50%\">").append(desc).append("</td></tr>\n");
|
||||
if (showEditButton && (!edit) && !ro)
|
||||
buf.append("<button type=\"submit\" name=\"edit\" value=\"Edit ").append(index).append("\" >" + _("Edit") + "<span class=hide> ").append(index).append("</span></button>");
|
||||
if (showStopButton && (!edit))
|
||||
buf.append("<button type=\"submit\" name=\"action\" value=\"Stop ").append(index).append("\" >" + _("Stop") + "<span class=hide> ").append(index).append("</span></button>");
|
||||
if (showUpdateButton && (!edit) && !ro) {
|
||||
buf.append("<button type=\"submit\" name=\"action\" value=\"Check ").append(index).append("\" >" + _("Check for updates") + "<span class=hide> ").append(index).append("</span></button>");
|
||||
buf.append("<button type=\"submit\" name=\"action\" value=\"Update ").append(index).append("\" >" + _("Update") + "<span class=hide> ").append(index).append("</span></button>");
|
||||
}
|
||||
if (showDeleteButton && (!edit) && !ro) {
|
||||
buf.append("<button type=\"submit\" name=\"action\" value=\"Delete ").append(index)
|
||||
.append("\" onclick=\"if (!confirm('")
|
||||
.append(_("Are you sure you want to delete {0}?", _(name)))
|
||||
.append("')) { return false; }\">")
|
||||
.append(_("Delete")).append("<span class=hide> ").append(index).append("</span></button>");
|
||||
}
|
||||
buf.append("</td><td align=\"left\" width=\"50%\">");
|
||||
if (edit && !ro) {
|
||||
buf.append("<input type=\"text\" size=\"80\" name=\"desc").append(index).append("\" value=\"");
|
||||
buf.append(desc);
|
||||
buf.append("\" >");
|
||||
} else {
|
||||
buf.append(desc);
|
||||
}
|
||||
buf.append("</td></tr>\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Like in DataHelper but doesn't convert null to ""
|
||||
* There's a lot worse things a plugin could do but...
|
||||
*/
|
||||
static String stripHTML(Properties props, String key) {
|
||||
String orig = props.getProperty(key);
|
||||
if (orig == null) return null;
|
||||
String t1 = orig.replace('<', ' ');
|
||||
String rv = t1.replace('>', ' ');
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -298,6 +301,9 @@ public class ConfigNetHandler extends FormHandler {
|
||||
_context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART);
|
||||
}
|
||||
|
||||
private static final int DEF_BURST_PCT = 10;
|
||||
private static final int DEF_BURST_TIME = 20;
|
||||
|
||||
private void updateRates() {
|
||||
boolean updated = false;
|
||||
|
||||
@@ -310,14 +316,27 @@ public class ConfigNetHandler extends FormHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// Since burst is now hidden in the gui, set burst to +10% for 20 seconds
|
||||
if ( (_inboundRate != null) && (_inboundRate.length() > 0) &&
|
||||
!_inboundRate.equals(_context.getProperty(FIFOBandwidthRefiller.PROP_INBOUND_BANDWIDTH, "" + FIFOBandwidthRefiller.DEFAULT_INBOUND_BANDWIDTH))) {
|
||||
_context.router().setConfigSetting(FIFOBandwidthRefiller.PROP_INBOUND_BANDWIDTH, _inboundRate);
|
||||
try {
|
||||
int rate = Integer.parseInt(_inboundRate) * (100 + DEF_BURST_PCT) / 100;
|
||||
int kb = DEF_BURST_TIME * rate;
|
||||
_context.router().setConfigSetting(FIFOBandwidthRefiller.PROP_INBOUND_BURST_BANDWIDTH, "" + rate);
|
||||
_context.router().setConfigSetting(FIFOBandwidthRefiller.PROP_INBOUND_BANDWIDTH_PEAK, "" + kb);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
updated = true;
|
||||
}
|
||||
if ( (_outboundRate != null) && (_outboundRate.length() > 0) &&
|
||||
!_outboundRate.equals(_context.getProperty(FIFOBandwidthRefiller.PROP_OUTBOUND_BANDWIDTH, "" + FIFOBandwidthRefiller.DEFAULT_OUTBOUND_BANDWIDTH))) {
|
||||
_context.router().setConfigSetting(FIFOBandwidthRefiller.PROP_OUTBOUND_BANDWIDTH, _outboundRate);
|
||||
try {
|
||||
int rate = Integer.parseInt(_outboundRate) * (100 + DEF_BURST_PCT) / 100;
|
||||
int kb = DEF_BURST_TIME * rate;
|
||||
_context.router().setConfigSetting(FIFOBandwidthRefiller.PROP_OUTBOUND_BURST_BANDWIDTH, "" + rate);
|
||||
_context.router().setConfigSetting(FIFOBandwidthRefiller.PROP_OUTBOUND_BANDWIDTH_PEAK, "" + kb);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
updated = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +166,8 @@ public class ConfigNetHelper extends HelperBase {
|
||||
return kbytesToBits(getShareBandwidth());
|
||||
}
|
||||
private String kbytesToBits(int kbytes) {
|
||||
return DataHelper.formatSize(kbytes * 8 * 1024) + ' ' + _("bits per second");
|
||||
return DataHelper.formatSize(kbytes * 8 * 1024) + ' ' + _("bits per second") +
|
||||
' ' + _("or {0} bytes per month maximum", DataHelper.formatSize(kbytes * 1024l * 60 * 60 * 24 * 31));
|
||||
}
|
||||
public String getInboundBurstRate() {
|
||||
return "" + _context.bandwidthLimiter().getInboundBurstKBytesPerSecond();
|
||||
|
||||
@@ -58,7 +58,7 @@ public class ConfigPeerHandler extends FormHandler {
|
||||
}
|
||||
addFormError(_("Invalid peer"));
|
||||
} else if (_action.startsWith("Check")) {
|
||||
addFormError("Unsupported");
|
||||
addFormError(_("Unsupported"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -139,9 +139,6 @@ public class ConfigStatsHelper extends HelperBase {
|
||||
public boolean getCurrentCanBeGraphed() { return _currentCanBeGraphed; }
|
||||
public String getExplicitFilter() { return _filter; }
|
||||
public boolean getIsFull() {
|
||||
String f = _context.getProperty(StatManager.PROP_STAT_FULL);
|
||||
if (f != null && f.equals("true"))
|
||||
return true;
|
||||
return false;
|
||||
return _context.getBooleanPropertyDefaultTrue(StatManager.PROP_STAT_FULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -19,6 +20,8 @@ public class ConfigUIHelper extends HelperBase {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
static final String PROP_THEME_PFX = "routerconsole.theme.";
|
||||
|
||||
/** @return standard and user-installed themes, sorted (untranslated) */
|
||||
private Set<String> themeSet() {
|
||||
Set<String> rv = new TreeSet();
|
||||
@@ -33,6 +36,13 @@ public class ConfigUIHelper extends HelperBase {
|
||||
if (files[i].isDirectory() && ! name.equals("images"))
|
||||
rv.add(name);
|
||||
}
|
||||
// user themes
|
||||
Set props = _context.getPropertyNames();
|
||||
for (Iterator iter = props.iterator(); iter.hasNext(); ) {
|
||||
String prop = (String) iter.next();
|
||||
if (prop.startsWith(PROP_THEME_PFX) && prop.length() > PROP_THEME_PFX.length())
|
||||
rv.add(prop.substring(PROP_THEME_PFX.length()));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||