forked from I2P_Developers/i2p.i2p
Compare commits
424 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eafca84717 | |||
| 0bfe8ff41d | |||
|
|
804f0294bb | ||
|
|
7a4430856d | ||
|
|
6bd40e253a | ||
|
|
c2d178efc3 | ||
| 97da508df5 | |||
|
|
211128f128 | ||
|
|
fc461931bd | ||
|
|
e5a8a6aba4 | ||
|
|
da835fbd6b | ||
| 95870df45b | |||
| e329742c8d | |||
| 5695d0e94a | |||
| 5a964dacbb | |||
|
|
580c940d42 | ||
|
|
7ea8cd4a09 | ||
|
|
a6ca962fcb | ||
|
|
b816ecc7e3 | ||
| 8df2a2d00a | |||
| 184220f4c5 | |||
| 5d6d27907d | |||
|
|
cb56b76ef9 | ||
|
|
eff238e85c | ||
|
|
a436e60fb8 | ||
|
|
2c570f8d4e | ||
|
|
6f23bdd331 | ||
|
|
b797f9e26d | ||
|
|
2b13973eca | ||
|
|
9331b229fe | ||
|
|
ccd0795a4e | ||
|
|
1f98493dbd | ||
|
|
f20d906b67 | ||
| 65757dee1c | |||
| b259a3ac3d | |||
| ca1f816ad9 | |||
| 1b154551a2 | |||
| c419016a12 | |||
| f10478ceef | |||
| d477773054 | |||
| 8ed280ebf4 | |||
| 762e96b8a6 | |||
| 23c77fbe4b | |||
| e99dd72cb6 | |||
| b095b7e769 | |||
| 6b97e1bfaf | |||
| 3ceb83d40e | |||
| d80340f0ae | |||
| 3acc2fb160 | |||
| 034db1a282 | |||
| b07b9bf0b9 | |||
| 97460e7d99 | |||
| ddc750469c | |||
| 0448537509 | |||
| 583463ab42 | |||
| b20e298f6e | |||
| 090d59fcb7 | |||
| 1d174d6797 | |||
| 15a47b5612 | |||
| 4d1ea6e4cd | |||
| 13ef00cb2e | |||
| d2c1641569 | |||
| a1873e74e5 | |||
|
|
8be86fe80c | ||
| 4dc90ef5da | |||
| e130264254 | |||
| 93039a6813 | |||
| 07b3c8a7b4 | |||
| 83fe635438 | |||
| 3ee96fb663 | |||
| 6684ba1b1d | |||
| 466778875d | |||
| a71e8fae00 | |||
| f58bf3028a | |||
| 595556c39f | |||
| eeaa4fbbb4 | |||
| 49b11e1f84 | |||
| e3133d88d7 | |||
| 1a50b6243d | |||
| 076558d4f5 | |||
| fb5d0cd760 | |||
| 7c8ba61f03 | |||
| 20e463e41b | |||
| 5d3984e353 | |||
| 941aea80bb | |||
| 0533aa7f6f | |||
| 568e2d5063 | |||
| 86c7aa8b8a | |||
| f61e7a193f | |||
|
|
567dae8ced | ||
|
|
02f483a873 | ||
| 7051e1c5f6 | |||
| 87295b4bfd | |||
| 23ca6b4fac | |||
| 9e3559625c | |||
| 351d582c8f | |||
| 5b1ea6187f | |||
| 211782fae4 | |||
|
|
44466aa769 | ||
|
|
d27d014eb0 | ||
| e884ca54ef | |||
| 336420cf50 | |||
| f16e83f21b | |||
| 0eedc3aa19 | |||
| f232775161 | |||
| bd57463d42 | |||
| 2c4910e9e7 | |||
| 2b14d32bea | |||
|
|
ee66747def | ||
| 259c28f8c1 | |||
| b6a5360390 | |||
| 0b7b947786 | |||
| 147e257cee | |||
| ccb8483766 | |||
| 68ccb3a944 | |||
| b317eca5e3 | |||
| 5ffacccdd7 | |||
| a41936af94 | |||
|
|
0991adc291 | ||
| b9aceb895d | |||
| 8633ef9513 | |||
|
|
7820cef60a | ||
| 4666454482 | |||
| db42d9ec37 | |||
| d7b48a2256 | |||
| 50ec279917 | |||
| e8a8f3c210 | |||
| e0fc642fc3 | |||
| 835ed6d9bb | |||
| 3781928693 | |||
|
|
cb39006f6c | ||
|
|
52447096f2 | ||
| 2f98d05e7c | |||
| 74e753934c | |||
| 9bc54f27cf | |||
| d9e6c06b22 | |||
| e02d82981a | |||
| 98da06cd83 | |||
| 0d62266008 | |||
| 1ae0c2e312 | |||
| 61629080b2 | |||
| 4cf104720c | |||
| 2c866e205b | |||
| ca91ad3188 | |||
| 33de6beab3 | |||
| 871f046755 | |||
| aef021dcd1 | |||
| 489f43529c | |||
| 78203aac9a | |||
| 3c95f0b66b | |||
| 3347788712 | |||
| 0c5b4c05c6 | |||
|
|
5056706742 | ||
| b8949eafe2 | |||
| 9286d6a7b8 | |||
| 9fd2f1e6a7 | |||
| b98474880d | |||
| 5347d296dc | |||
| 666a387d1b | |||
| bb66e16b69 | |||
| 2cddf1405f | |||
| 8575437626 | |||
| c965a3dca0 | |||
| c48aca8d5c | |||
| 4360284355 | |||
| f44eeaf7dd | |||
| a0418bec59 | |||
| 5eff26e40e | |||
| 4e78517651 | |||
| 10d9eb70c8 | |||
|
|
0ba3aad666 | ||
| 8bfbe855a1 | |||
| 3fbf60ee21 | |||
| 6bfd916fef | |||
| a5e4b15349 | |||
| 94f370e76c | |||
| 7cc353ab04 | |||
| 506626d6b1 | |||
| 26898f38ad | |||
| 4fdff1bf13 | |||
|
|
7d4a6e74d2 | ||
|
|
b33a01cf26 | ||
| 0689b03603 | |||
| ee8cd29da9 | |||
| c4a3159b33 | |||
|
|
a4511ca2ab | ||
| 17b4ab6151 | |||
| d2a7af2884 | |||
| d05f1ca2c8 | |||
| 832d66bfb9 | |||
| c8a46dac5d | |||
| 7005376061 | |||
| ab213f45e2 | |||
| fa504ae8a3 | |||
| d305eb6a9c | |||
| f8bc6f8612 | |||
| 9099937119 | |||
| b827468e2f | |||
| 587795552e | |||
|
|
0a1ff9b6bd | ||
| b01cf32321 | |||
| 9ba6c293ed | |||
| 99681e1d1e | |||
|
|
96775acf5a | ||
|
|
ba992067ad | ||
| 2552d99308 | |||
| e99e25b3b9 | |||
| 70820d7be6 | |||
| 38fda46d44 | |||
| 9d383d6aef | |||
| ba0408a741 | |||
| 07c21c3bfd | |||
| 5ffefd2a19 | |||
| e3e15850bb | |||
| 54b367b153 | |||
| b61127270e | |||
| 1d41c2fd19 | |||
| 7c7e131dc0 | |||
| 85fbbf8980 | |||
| 612fab1b2a | |||
| fbd8c69eea | |||
| 8fcac04aad | |||
| 7d902cca1e | |||
|
|
ddc1d7c6bc | ||
|
|
5bb90c6185 | ||
| 9452547204 | |||
| 34c09583b4 | |||
| 38b0927d01 | |||
| 715bde5ecf | |||
| 6c2eb317fe | |||
| 05516f3260 | |||
|
|
264df83943 | ||
| 3a546612d9 | |||
| 3cac01ff27 | |||
|
|
e01521618f | ||
| ee63f3b86d | |||
| a900511d5e | |||
| 3fe092d788 | |||
| e2fe5004e7 | |||
| 442af031eb | |||
| e22882bd02 | |||
| 523d39b3bb | |||
| 65efefb094 | |||
| 44edf70842 | |||
| 16a46b3211 | |||
| e9cc85141c | |||
| cfcafd2ba3 | |||
| e67dd15308 | |||
| a76f840ff8 | |||
| 269a36c549 | |||
|
|
36bf248385 | ||
|
|
046135f8e3 | ||
|
|
97e469da7b | ||
| 01beb015dc | |||
| 50d5692884 | |||
|
|
0ea6513e9c | ||
| e2b683556b | |||
| 14587ebb59 | |||
| be3cf44608 | |||
| 1538cd84a9 | |||
|
|
f5b808b997 | ||
| f92d8aed3d | |||
| f6c769187e | |||
| c70e3727be | |||
| a6a0228ef8 | |||
|
|
d2a5595df2 | ||
|
|
e9c07a123a | ||
|
|
1e8e2a197b | ||
| 39d9d3f5b6 | |||
| 8bada7f882 | |||
|
|
a940062255 | ||
|
|
93efd31a5b | ||
|
|
2e9fdc6d9f | ||
| b9f5f230a2 | |||
| 0a751a303f | |||
| b2da629034 | |||
| 37a542c009 | |||
| 0451ee7f08 | |||
| d8dd76c6e0 | |||
| 9cee0ee504 | |||
|
|
58a545d30c | ||
|
|
dfb0b7801d | ||
|
|
a21175d903 | ||
|
|
9c7f4cc604 | ||
|
|
3017e4f51a | ||
|
|
5ed1ec681f | ||
|
|
0a4031cd7b | ||
|
|
31ea4a7093 | ||
|
|
0ca2d33ee1 | ||
|
|
48bcd3a8c2 | ||
| 1ab8200c7f | |||
| 91e61dbd5c | |||
| fb4ef57148 | |||
| ced0129e03 | |||
|
|
740b6501cd | ||
| 67f16b0de4 | |||
| fd3d92d3b2 | |||
| 5ba5d537b5 | |||
| 4efa87d6bf | |||
|
|
442897ba5b | ||
|
|
2b79da5c35 | ||
|
|
cc3a8e5d62 | ||
| 280a708afe | |||
|
|
f5a348a863 | ||
|
|
85a4e9cb5c | ||
| 4715dbdbd0 | |||
| afad77af19 | |||
| b4a50ed03a | |||
| 00f9fea98c | |||
| 501651125f | |||
| 18e8d35910 | |||
| 9e4d231285 | |||
| 2972e79f9e | |||
| 4d32eaa036 | |||
| e4f141b94c | |||
| ccf36abd30 | |||
|
|
d147db3382 | ||
|
|
9d29dc6b68 | ||
|
|
6562b33bbc | ||
|
|
f58f297cdb | ||
|
|
376b991b63 | ||
|
|
120d31244e | ||
|
|
679549cbf2 | ||
|
|
a623d924fa | ||
|
|
95fb141ad9 | ||
|
|
fad6f54794 | ||
|
|
e1525d98cd | ||
|
|
3d69d2bf63 | ||
|
|
cb2dd03e77 | ||
|
|
3253f82900 | ||
| 33a00efd82 | |||
| 8bcbf24713 | |||
|
|
52ba727664 | ||
|
|
a1cfacd8da | ||
|
|
5b6e7ba91d | ||
|
|
691ce6fec7 | ||
|
|
618f214a4f | ||
|
|
48df91f796 | ||
|
|
d27d0bd2e4 | ||
|
|
39d954d56a | ||
|
|
639253e9bb | ||
|
|
f8fe2a295f | ||
| 4298958952 | |||
| 54a80d6bdc | |||
| aba655a9c7 | |||
|
|
314817242b | ||
|
|
945a0f30aa | ||
|
|
a7c8a7201a | ||
|
|
6be94658a7 | ||
|
|
490dcc5020 | ||
|
|
8e6bade42b | ||
|
|
3c4f1b7814 | ||
|
|
ce024ff006 | ||
|
|
e603b120c3 | ||
|
|
b17af505c2 | ||
|
|
5d5a3b80e5 | ||
|
|
c8a73b63fd | ||
| e2588a5379 | |||
| dba3fee477 | |||
| 50fba8fc8d | |||
| 5eab417134 | |||
| fe53501990 | |||
| e497859587 | |||
| d5a1e0b1c6 | |||
| 5883b7344e | |||
| 8522779df1 | |||
| f8e470c7f4 | |||
| c6d1c552f8 | |||
| c2137a2a80 | |||
| 44da37f009 | |||
| d0b967388a | |||
| 41096c7f23 | |||
| fe2b97c941 | |||
| 6a1b90f8f8 | |||
| 3f40487c99 | |||
| a6f7761544 | |||
| d5cb443925 | |||
| 121491a3be | |||
| 152b2152cb | |||
| f8c185d09f | |||
| 558bb2f4f3 | |||
| 7b07eb89a3 | |||
| bec33cad87 | |||
| df00725077 | |||
| 26846d592c | |||
| 21466e017f | |||
| b033db969c | |||
| d18e4d430c | |||
| 14ac5ac03e | |||
| b2846de94f | |||
|
|
c89e127d8a | ||
|
|
104bfa8784 | ||
|
|
e99749097a | ||
|
|
ce8cd91d72 | ||
|
|
0cefaba925 | ||
|
|
ba3bc9e2ed | ||
|
|
f164951848 | ||
|
|
bfaf72a547 | ||
|
|
377aa9bca1 | ||
|
|
67da35ab35 | ||
|
|
136d77a8aa | ||
|
|
bf0b59b3b3 | ||
|
|
f19bc6a4b0 | ||
|
|
79ab065500 | ||
|
|
9d07bc241c | ||
|
|
786a261a70 | ||
|
|
a226d25dc6 | ||
|
|
d436c846ac | ||
|
|
24268c5130 | ||
|
|
22900a0d91 | ||
|
|
e7922c4ded | ||
|
|
f19ef3e486 | ||
|
|
4148aa54f3 | ||
|
|
204440b06b | ||
|
|
6a26c0b621 | ||
|
|
c955adf7f6 | ||
|
|
c68d53faf3 | ||
|
|
ceda7c9ca0 | ||
|
|
849c407712 | ||
|
|
a2217b2b36 | ||
|
|
c7d473a7eb | ||
|
|
d1a03f500f | ||
|
|
8a32aad6c3 | ||
| d8080278b3 |
@@ -17,10 +17,10 @@ _jsp\.java$
|
||||
\.war$
|
||||
\.zip$
|
||||
^\.
|
||||
^build/
|
||||
^build
|
||||
^pkg-temp/
|
||||
~$
|
||||
/build/
|
||||
/build
|
||||
/classes/
|
||||
^debian/copyright
|
||||
override.properties
|
||||
|
||||
@@ -10,6 +10,7 @@ trans.fr = apps/i2ptunnel/locale/messages_fr.po
|
||||
trans.hu = apps/i2ptunnel/locale/messages_hu.po
|
||||
trans.it = apps/i2ptunnel/locale/messages_it.po
|
||||
trans.nl = apps/i2ptunnel/locale/messages_nl.po
|
||||
trans.pt = apps/i2ptunnel/locale/messages_pt.po
|
||||
trans.ru = apps/i2ptunnel/locale/messages_ru.po
|
||||
trans.sv_SE = apps/i2ptunnel/locale/messages_sv.po
|
||||
trans.uk_UA = apps/i2ptunnel/locale/messages_uk.po
|
||||
@@ -32,6 +33,7 @@ trans.hu = apps/routerconsole/locale/messages_hu.po
|
||||
trans.it = apps/routerconsole/locale/messages_it.po
|
||||
trans.nl = apps/routerconsole/locale/messages_nl.po
|
||||
trans.pl = apps/routerconsole/locale/messages_pl.po
|
||||
trans.pt = apps/routerconsole/locale/messages_pt.po
|
||||
trans.ru = apps/routerconsole/locale/messages_ru.po
|
||||
trans.sv_SE = apps/routerconsole/locale/messages_sv.po
|
||||
trans.uk_UA = apps/routerconsole/locale/messages_uk.po
|
||||
@@ -70,6 +72,7 @@ trans.hu = apps/susidns/locale/messages_hu.po
|
||||
trans.it = apps/susidns/locale/messages_it.po
|
||||
trans.nl = apps/susidns/locale/messages_nl.po
|
||||
trans.pl = apps/susidns/locale/messages_pl.po
|
||||
trans.pt = apps/susidns/locale/messages_pt.po
|
||||
trans.ru = apps/susidns/locale/messages_ru.po
|
||||
trans.sv_SE = apps/susidns/locale/messages_sv.po
|
||||
trans.uk_UA = apps/susidns/locale/messages_uk.po
|
||||
|
||||
@@ -79,11 +79,13 @@ Public domain except as listed below:
|
||||
From freenet
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
UPnP subsystem 1.7:
|
||||
Copyright (C) 2003-2006 Satoshi Konno
|
||||
UPnP subsystem (CyberLink) 2.1:
|
||||
Copyright (C) 2003-2010 Satoshi Konno
|
||||
See licenses/LICENSE-UPnP.txt
|
||||
|
||||
GeoIP data free to use, courtesy http://www.maxmind.com/
|
||||
GeoIP: GeoLite databases are licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
|
||||
http://creativecommons.org/licenses/by-sa/3.0/
|
||||
This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com/
|
||||
|
||||
|
||||
Installer:
|
||||
|
||||
@@ -41,7 +41,6 @@ public class I2Plistener implements Runnable {
|
||||
|
||||
private NamedDB info, database;
|
||||
private Log _log;
|
||||
// private int tgwatch;
|
||||
public I2PSocketManager socketManager;
|
||||
public I2PServerSocket serverSocket;
|
||||
private AtomicBoolean lives;
|
||||
@@ -103,7 +102,7 @@ public class I2Plistener implements Runnable {
|
||||
serverSocket.close();
|
||||
} catch (I2PException ex) {
|
||||
}
|
||||
// System.out.println("I2Plistener: Close");
|
||||
// System.out.println("I2Plistener: Close");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,16 +108,6 @@ public class MUXlisten implements Runnable {
|
||||
this.listener = new ServerSocket(port, backlog, host);
|
||||
}
|
||||
socketManager = I2PSocketManagerFactory.createManager(prikey, Q);
|
||||
// I2PException, IOException, RuntimeException
|
||||
// To bad we can't just catch and enumerate....
|
||||
// } catch (I2PException e) {
|
||||
// Something went bad.
|
||||
// this.database.getWriteLock();
|
||||
// this.info.getWriteLock();
|
||||
// this.info.add("STARTING", new Boolean(false));
|
||||
// this.info.releaseWriteLock();
|
||||
// this.database.releaseWriteLock();
|
||||
// throw new I2PException(e);
|
||||
} catch (IOException e) {
|
||||
// Something went bad.
|
||||
this.database.getWriteLock();
|
||||
@@ -194,7 +184,6 @@ public class MUXlisten implements Runnable {
|
||||
lock.set(false);
|
||||
return;
|
||||
}
|
||||
// socketManager.addDisconnectListener(new DisconnectListener());
|
||||
lives.set(true);
|
||||
lock.set(false);
|
||||
quit:
|
||||
@@ -347,7 +336,6 @@ public class MUXlisten implements Runnable {
|
||||
String boner = tg.getName();
|
||||
// System.out.println("BOB: MUXlisten: Starting thread collection for: " + boner);
|
||||
_log.warn("BOB: MUXlisten: Starting thread collection for: " + boner);
|
||||
// tg.interrupt(); // give my stuff a small smack again.
|
||||
if (tg.activeCount() + tg.activeGroupCount() != 0) {
|
||||
// visit(tg, 0, boner);
|
||||
int foo = tg.activeCount() + tg.activeGroupCount();
|
||||
|
||||
@@ -95,14 +95,9 @@ public class TCPio implements Runnable {
|
||||
if (b > 0) {
|
||||
Aout.write(a, 0, b);
|
||||
} else if (b == 0) {
|
||||
// Will this die? We'll see.
|
||||
while(Ain.available() == 0) {
|
||||
Thread.sleep(20);
|
||||
}
|
||||
// Thread.yield(); // this should act like a mini sleep.
|
||||
// if (Ain.available() == 0) {
|
||||
// Thread.sleep(10);
|
||||
// }
|
||||
} else {
|
||||
/* according to the specs:
|
||||
*
|
||||
|
||||
@@ -27,8 +27,6 @@ import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
// import net.i2p.client.I2PSession;
|
||||
// import net.i2p.client.I2PSessionException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
|
||||
@@ -108,20 +108,18 @@ public class TCPtoI2P implements Runnable {
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
private void Emsg(String e, OutputStream out) throws IOException {
|
||||
// Debugging System.out.println("ERROR TCPtoI2P: " + e);
|
||||
// Debugging System.out.println("ERROR TCPtoI2P: " + e);
|
||||
out.write("ERROR ".concat(e).getBytes());
|
||||
out.write(13);
|
||||
out.write(10);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
// private void rlock() throws Exception {
|
||||
private void rlock() {
|
||||
database.getReadLock();
|
||||
info.getReadLock();
|
||||
}
|
||||
|
||||
// private void runlock() throws Exception {
|
||||
private void runlock() {
|
||||
info.releaseReadLock();
|
||||
database.releaseReadLock();
|
||||
|
||||
@@ -111,7 +111,7 @@ public class UDPIOthread implements I2PSessionListener, Runnable {
|
||||
* @param size
|
||||
*/
|
||||
public void messageAvailable(I2PSession session, int msgId, long size) {
|
||||
// _log.debug("Message available: id = " + msgId + " size = " + size);
|
||||
// _log.debug("Message available: id = " + msgId + " size = " + size);
|
||||
try {
|
||||
byte msg[] = session.receiveMessage(msgId);
|
||||
out.write(msg);
|
||||
|
||||
@@ -37,6 +37,7 @@ import java.util.Map;
|
||||
|
||||
import net.i2p.util.SecureFile;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
* Utility class providing methods to parse and write files in config file
|
||||
@@ -49,7 +50,7 @@ import net.i2p.util.SecureFileOutputStream;
|
||||
*/
|
||||
class ConfigParser {
|
||||
|
||||
private static final boolean isWindows = System.getProperty("os.name").startsWith("Win");
|
||||
private static final boolean isWindows = SystemVersion.isWindows();
|
||||
|
||||
/**
|
||||
* Strip the comments from a String. Lines that begin with '#' and ';' are
|
||||
|
||||
@@ -230,7 +230,7 @@ public class Daemon {
|
||||
*/
|
||||
public static void update(Map<String, String> settings, String home) {
|
||||
File published = null;
|
||||
boolean should_publish = Boolean.valueOf(settings.get("should_publish")).booleanValue();
|
||||
boolean should_publish = Boolean.parseBoolean(settings.get("should_publish"));
|
||||
if (should_publish)
|
||||
published = new File(home, settings
|
||||
.get("published_addressbook"));
|
||||
@@ -261,7 +261,7 @@ public class Daemon {
|
||||
|
||||
// If false, add hosts via naming service; if true, write hosts.txt file directly
|
||||
// Default false
|
||||
if (Boolean.valueOf(settings.get("update_direct")).booleanValue()) {
|
||||
if (Boolean.parseBoolean(settings.get("update_direct"))) {
|
||||
// Direct hosts.txt access
|
||||
File routerFile = new File(home, settings.get("router_addressbook"));
|
||||
AddressBook master;
|
||||
|
||||
76
apps/i2psnark/java/src/net/i2p/kademlia/KBucket.java
Normal file
76
apps/i2psnark/java/src/net/i2p/kademlia/KBucket.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package net.i2p.kademlia;
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by jrandom in 2003 and released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
* It probably won't make your computer catch on fire, or eat
|
||||
* your children, but it might. Use at your own risk.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
|
||||
/**
|
||||
* Group, without inherent ordering, a set of keys a certain distance away from
|
||||
* a local key, using XOR as the distance metric
|
||||
*
|
||||
* Refactored from net.i2p.router.networkdb.kademlia
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public interface KBucket<T extends SimpleDataStructure> {
|
||||
|
||||
/**
|
||||
* Lowest order high bit for difference keys.
|
||||
* The lower-bounds distance of this bucket is 2**begin.
|
||||
* If begin == 0, this is the closest bucket.
|
||||
*/
|
||||
public int getRangeBegin();
|
||||
|
||||
/**
|
||||
* Highest high bit for the difference keys.
|
||||
* The upper-bounds distance of this bucket is (2**(end+1)) - 1.
|
||||
* If begin == end, the bucket cannot be split further.
|
||||
* If end == (numbits - 1), this is the furthest bucket.
|
||||
*/
|
||||
public int getRangeEnd();
|
||||
|
||||
/**
|
||||
* Number of keys already contained in this kbucket
|
||||
*/
|
||||
public int getKeyCount();
|
||||
|
||||
/**
|
||||
* Add the peer to the bucket
|
||||
*
|
||||
* @return true if added
|
||||
*/
|
||||
public boolean add(T key);
|
||||
|
||||
/**
|
||||
* Remove the key from the bucket
|
||||
* @return true if the key existed in the bucket before removing it, else false
|
||||
*/
|
||||
public boolean remove(T key);
|
||||
|
||||
/**
|
||||
* Update the last-changed timestamp to now.
|
||||
*/
|
||||
public void setLastChanged();
|
||||
|
||||
/**
|
||||
* The last-changed timestamp
|
||||
*/
|
||||
public long getLastChanged();
|
||||
|
||||
/**
|
||||
* Retrieve all routing table entries stored in the bucket
|
||||
* @return set of Hash structures
|
||||
*/
|
||||
public Set<T> getEntries();
|
||||
|
||||
public void getEntries(SelectionCollector<T> collector);
|
||||
|
||||
public void clear();
|
||||
}
|
||||
150
apps/i2psnark/java/src/net/i2p/kademlia/KBucketImpl.java
Normal file
150
apps/i2psnark/java/src/net/i2p/kademlia/KBucketImpl.java
Normal file
@@ -0,0 +1,150 @@
|
||||
package net.i2p.kademlia;
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by jrandom in 2003 and released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
* It probably won't make your computer catch on fire, or eat
|
||||
* your children, but it might. Use at your own risk.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
|
||||
/**
|
||||
* A concurrent implementation using ConcurrentHashSet.
|
||||
* The max size (K) may be temporarily exceeded due to concurrency,
|
||||
* a pending split, or the behavior of the supplied trimmer,
|
||||
* as explained below.
|
||||
* The creator is responsible for splits.
|
||||
*
|
||||
* This class has no knowledge of the DHT base used for XORing,
|
||||
* and thus there are no validity checks in add/remove.
|
||||
*
|
||||
* The begin and end values are immutable.
|
||||
* All entries in this bucket will have at least one bit different
|
||||
* from us in the range [begin, end] inclusive.
|
||||
* Splits must be implemented by creating two new buckets
|
||||
* and discarding this one.
|
||||
*
|
||||
* The keys are kept in a Set and are NOT sorted by last-seen.
|
||||
* Per-key last-seen-time, failures, etc. must be tracked elsewhere.
|
||||
*
|
||||
* If this bucket is full (i.e. begin == end && size == max)
|
||||
* then add() will call KBucketTrimmer.trim() do
|
||||
* (possibly) remove older entries, and indicate whether
|
||||
* to add the new entry. If the trimmer returns true without
|
||||
* removing entries, this KBucket will exceed the max size.
|
||||
*
|
||||
* Refactored from net.i2p.router.networkdb.kademlia
|
||||
* @since 0.9.2
|
||||
*/
|
||||
class KBucketImpl<T extends SimpleDataStructure> implements KBucket<T> {
|
||||
/**
|
||||
* set of Hash objects for the peers in the kbucket
|
||||
*/
|
||||
private final Set<T> _entries;
|
||||
/** include if any bits equal or higher to this bit (in big endian order) */
|
||||
private final int _begin;
|
||||
/** include if no bits higher than this bit (inclusive) are set */
|
||||
private final int _end;
|
||||
private final int _max;
|
||||
private final KBucketTrimmer _trimmer;
|
||||
/** when did we last shake things up */
|
||||
private long _lastChanged;
|
||||
private final I2PAppContext _context;
|
||||
|
||||
/**
|
||||
* All entries in this bucket will have at least one bit different
|
||||
* from us in the range [begin, end] inclusive.
|
||||
*/
|
||||
public KBucketImpl(I2PAppContext context, int begin, int end, int max, KBucketTrimmer trimmer) {
|
||||
if (begin > end)
|
||||
throw new IllegalArgumentException(begin + " > " + end);
|
||||
_context = context;
|
||||
_entries = new ConcurrentHashSet(max + 4);
|
||||
_begin = begin;
|
||||
_end = end;
|
||||
_max = max;
|
||||
_trimmer = trimmer;
|
||||
}
|
||||
|
||||
public int getRangeBegin() { return _begin; }
|
||||
|
||||
public int getRangeEnd() { return _end; }
|
||||
|
||||
public int getKeyCount() {
|
||||
return _entries.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an unmodifiable view; not a copy
|
||||
*/
|
||||
public Set<T> getEntries() {
|
||||
return Collections.unmodifiableSet(_entries);
|
||||
}
|
||||
|
||||
public void getEntries(SelectionCollector<T> collector) {
|
||||
for (T h : _entries) {
|
||||
collector.add(h);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
_entries.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets last-changed if rv is true OR if the peer is already present.
|
||||
* Calls the trimmer if begin == end and we are full.
|
||||
* If begin != end then add it and caller must do bucket splitting.
|
||||
* @return true if added
|
||||
*/
|
||||
public boolean add(T peer) {
|
||||
if (_begin != _end || _entries.size() < _max ||
|
||||
_entries.contains(peer) || _trimmer.trim(this, peer)) {
|
||||
// do this even if already contains, to call setLastChanged()
|
||||
boolean rv = _entries.add(peer);
|
||||
setLastChanged();
|
||||
return rv;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if removed. Does NOT set lastChanged.
|
||||
*/
|
||||
public boolean remove(T peer) {
|
||||
boolean rv = _entries.remove(peer);
|
||||
//if (rv)
|
||||
// setLastChanged();
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the last-changed timestamp to now.
|
||||
*/
|
||||
public void setLastChanged() {
|
||||
_lastChanged = _context.clock().now();
|
||||
}
|
||||
|
||||
/**
|
||||
* The last-changed timestamp, which actually indicates last-added or last-seen.
|
||||
*/
|
||||
public long getLastChanged() {
|
||||
return _lastChanged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(1024);
|
||||
buf.append(_entries.size());
|
||||
buf.append(" entries in (").append(_begin).append(',').append(_end);
|
||||
buf.append(") : ").append(_entries.toString());
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
777
apps/i2psnark/java/src/net/i2p/kademlia/KBucketSet.java
Normal file
777
apps/i2psnark/java/src/net/i2p/kademlia/KBucketSet.java
Normal file
@@ -0,0 +1,777 @@
|
||||
package net.i2p.kademlia;
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by jrandom in 2003 and released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
* It probably won't make your computer catch on fire, or eat
|
||||
* your children, but it might. Use at your own risk.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
import net.i2p.util.LHMCache;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* In-memory storage of buckets sorted by the XOR metric from the base (us)
|
||||
* passed in via the constructor.
|
||||
* This starts with one bucket covering the whole key space, and
|
||||
* may eventually be split to a max of the number of bits in the data type
|
||||
* (160 for SHA1Hash or 256 for Hash),
|
||||
* times 2**(B-1) for Kademlia value B.
|
||||
*
|
||||
* Refactored from net.i2p.router.networkdb.kademlia
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public class KBucketSet<T extends SimpleDataStructure> {
|
||||
private final Log _log;
|
||||
private final I2PAppContext _context;
|
||||
private final T _us;
|
||||
|
||||
/**
|
||||
* The bucket list is locked by _bucketsLock, however the individual
|
||||
* buckets are not locked. Users may see buckets that have more than
|
||||
* the maximum k entries, or may have adds and removes silently fail
|
||||
* when they appear to succeed.
|
||||
*
|
||||
* Closest values are in bucket 0, furthest are in the last bucket.
|
||||
*/
|
||||
private final List<KBucket> _buckets;
|
||||
private final Range<T> _rangeCalc;
|
||||
private final KBucketTrimmer _trimmer;
|
||||
|
||||
/**
|
||||
* Locked for reading only when traversing all the buckets.
|
||||
* Locked for writing only when splitting a bucket.
|
||||
* Adds/removes/gets from individual buckets are not locked.
|
||||
*/
|
||||
private final ReentrantReadWriteLock _bucketsLock = new ReentrantReadWriteLock(false);
|
||||
|
||||
private final int KEYSIZE_BITS;
|
||||
private final int NUM_BUCKETS;
|
||||
private final int BUCKET_SIZE;
|
||||
private final int B_VALUE;
|
||||
private final int B_FACTOR;
|
||||
|
||||
/**
|
||||
* Use the default trim strategy, which removes a random entry.
|
||||
* @param us the local identity (typically a SHA1Hash or Hash)
|
||||
* The class must have a zero-argument constructor.
|
||||
* @param max the Kademlia value "k", the max per bucket, k >= 4
|
||||
* @param b the Kademlia value "b", split buckets an extra 2**(b-1) times,
|
||||
* b > 0, use 1 for bittorrent, Kademlia paper recommends 5
|
||||
*/
|
||||
public KBucketSet(I2PAppContext context, T us, int max, int b) {
|
||||
this(context, us, max, b, new RandomTrimmer(context, max));
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the supplied trim strategy.
|
||||
*/
|
||||
public KBucketSet(I2PAppContext context, T us, int max, int b, KBucketTrimmer trimmer) {
|
||||
_us = us;
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(KBucketSet.class);
|
||||
_trimmer = trimmer;
|
||||
if (max <= 4 || b <= 0 || b > 8)
|
||||
throw new IllegalArgumentException();
|
||||
KEYSIZE_BITS = us.length() * 8;
|
||||
B_VALUE = b;
|
||||
B_FACTOR = 1 << (b - 1);
|
||||
NUM_BUCKETS = KEYSIZE_BITS * B_FACTOR;
|
||||
BUCKET_SIZE = max;
|
||||
_buckets = createBuckets();
|
||||
_rangeCalc = new Range(us, B_VALUE);
|
||||
// this verifies the zero-argument constructor
|
||||
makeKey(new byte[us.length()]);
|
||||
}
|
||||
|
||||
private void getReadLock() {
|
||||
_bucketsLock.readLock().lock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the lock if we can. Non-blocking.
|
||||
* @return true if the lock was acquired
|
||||
*/
|
||||
private boolean tryReadLock() {
|
||||
return _bucketsLock.readLock().tryLock();
|
||||
}
|
||||
|
||||
private void releaseReadLock() {
|
||||
_bucketsLock.readLock().unlock();
|
||||
}
|
||||
|
||||
/** @return true if the lock was acquired */
|
||||
private boolean getWriteLock() {
|
||||
try {
|
||||
boolean rv = _bucketsLock.writeLock().tryLock(3000, TimeUnit.MILLISECONDS);
|
||||
if ((!rv) && _log.shouldLog(Log.WARN))
|
||||
_log.warn("no lock, size is: " + _bucketsLock.getQueueLength(), new Exception("rats"));
|
||||
return rv;
|
||||
} catch (InterruptedException ie) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void releaseWriteLock() {
|
||||
_bucketsLock.writeLock().unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the peer is new to the bucket it goes in, or false if it was
|
||||
* already in it. Always returns false on an attempt to add ourselves.
|
||||
*
|
||||
*/
|
||||
public boolean add(T peer) {
|
||||
KBucket bucket;
|
||||
getReadLock();
|
||||
try {
|
||||
bucket = getBucket(peer);
|
||||
} finally { releaseReadLock(); }
|
||||
if (bucket != null) {
|
||||
if (bucket.add(peer)) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Peer " + peer + " added to bucket " + bucket);
|
||||
if (shouldSplit(bucket)) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Splitting bucket " + bucket);
|
||||
split(bucket.getRangeBegin());
|
||||
//testAudit(this, _log);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Peer " + peer + " NOT added to bucket " + bucket);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Failed to add, probably us: " + peer);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No lock required.
|
||||
* FIXME will split the closest buckets too far if B > 1 and K < 2**B
|
||||
* Won't ever really happen and if it does it still works.
|
||||
*/
|
||||
private boolean shouldSplit(KBucket b) {
|
||||
return
|
||||
b.getRangeBegin() != b.getRangeEnd() &&
|
||||
b.getKeyCount() > BUCKET_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs the write lock.
|
||||
* Caller must NOT have the read lock.
|
||||
* The bucket should be splittable (range start != range end).
|
||||
* @param r the range start of the bucket to be split
|
||||
*/
|
||||
private void split(int r) {
|
||||
if (!getWriteLock())
|
||||
return;
|
||||
try {
|
||||
locked_split(r);
|
||||
} finally { releaseWriteLock(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates two or more new buckets. The old bucket is replaced and discarded.
|
||||
*
|
||||
* Caller must hold write lock
|
||||
* The bucket should be splittable (range start != range end).
|
||||
* @param r the range start of the bucket to be split
|
||||
*/
|
||||
private void locked_split(int r) {
|
||||
int b = pickBucket(r);
|
||||
while (shouldSplit(_buckets.get(b))) {
|
||||
KBucket<T> b0 = _buckets.get(b);
|
||||
// Each bucket gets half the keyspace.
|
||||
// When B_VALUE = 1, or the bucket is larger than B_FACTOR, then
|
||||
// e.g. 0-159 => 0-158, 159-159
|
||||
// When B_VALUE > 1, and the bucket is smaller than B_FACTOR, then
|
||||
// e.g. 1020-1023 => 1020-1021, 1022-1023
|
||||
int s1, e1, s2, e2;
|
||||
s1 = b0.getRangeBegin();
|
||||
e2 = b0.getRangeEnd();
|
||||
if (B_FACTOR > 1 &&
|
||||
(s1 & (B_FACTOR - 1)) == 0 &&
|
||||
((e2 + 1) & (B_FACTOR - 1)) == 0 &&
|
||||
e2 > s1 + B_FACTOR) {
|
||||
// The bucket is a "whole" kbucket with a range > B_FACTOR,
|
||||
// so it should be split into two "whole" kbuckets each with
|
||||
// a range >= B_FACTOR.
|
||||
// Log split
|
||||
s2 = e2 + 1 - B_FACTOR;
|
||||
} else {
|
||||
// The bucket is the smallest "whole" kbucket with a range == B_FACTOR,
|
||||
// or B_VALUE > 1 and the bucket has already been split.
|
||||
// Start or continue splitting down to a depth B_VALUE.
|
||||
// Linear split
|
||||
s2 = s1 + ((1 + e2 - s1) / 2);
|
||||
}
|
||||
e1 = s2 - 1;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Splitting (" + s1 + ',' + e2 + ") -> (" + s1 + ',' + e1 + ") (" + s2 + ',' + e2 + ')');
|
||||
KBucket<T> b1 = createBucket(s1, e1);
|
||||
KBucket<T> b2 = createBucket(s2, e2);
|
||||
for (T key : b0.getEntries()) {
|
||||
if (getRange(key) < s2)
|
||||
b1.add(key);
|
||||
else
|
||||
b2.add(key);
|
||||
}
|
||||
_buckets.set(b, b1);
|
||||
_buckets.add(b + 1, b2);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Split bucket at idx " + b +
|
||||
":\n" + b0 +
|
||||
"\ninto: " + b1 +
|
||||
"\nand: " + b2);
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("State is now: " + toString());
|
||||
|
||||
if (b2.getKeyCount() > BUCKET_SIZE) {
|
||||
// should be rare... too hard to call _trimmer from here
|
||||
// (and definitely not from inside the write lock)
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("All went into 2nd bucket after split");
|
||||
}
|
||||
// loop if all the entries went in the first bucket
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The current number of entries.
|
||||
*/
|
||||
public int size() {
|
||||
int rv = 0;
|
||||
getReadLock();
|
||||
try {
|
||||
for (KBucket b : _buckets) {
|
||||
rv += b.getKeyCount();
|
||||
}
|
||||
} finally { releaseReadLock(); }
|
||||
return rv;
|
||||
}
|
||||
|
||||
public boolean remove(T entry) {
|
||||
KBucket kbucket;
|
||||
getReadLock();
|
||||
try {
|
||||
kbucket = getBucket(entry);
|
||||
} finally { releaseReadLock(); }
|
||||
boolean removed = kbucket.remove(entry);
|
||||
return removed;
|
||||
}
|
||||
|
||||
/** @since 0.8.8 */
|
||||
public void clear() {
|
||||
getReadLock();
|
||||
try {
|
||||
for (KBucket b : _buckets) {
|
||||
b.clear();
|
||||
}
|
||||
} finally { releaseReadLock(); }
|
||||
_rangeCalc.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a copy in a new set
|
||||
*/
|
||||
public Set<T> getAll() {
|
||||
Set<T> all = new HashSet(256);
|
||||
getReadLock();
|
||||
try {
|
||||
for (KBucket b : _buckets) {
|
||||
all.addAll(b.getEntries());
|
||||
}
|
||||
} finally { releaseReadLock(); }
|
||||
return all;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a copy in a new set
|
||||
*/
|
||||
public Set<T> getAll(Set<T> toIgnore) {
|
||||
Set<T> all = getAll();
|
||||
all.removeAll(toIgnore);
|
||||
return all;
|
||||
}
|
||||
|
||||
public void getAll(SelectionCollector<T> collector) {
|
||||
getReadLock();
|
||||
try {
|
||||
for (KBucket b : _buckets) {
|
||||
b.getEntries(collector);
|
||||
}
|
||||
} finally { releaseReadLock(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* The keys closest to us.
|
||||
* Returned list will never contain us.
|
||||
* @return non-null, closest first
|
||||
*/
|
||||
public List<T> getClosest(int max) {
|
||||
return getClosest(max, Collections.EMPTY_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* The keys closest to us.
|
||||
* Returned list will never contain us.
|
||||
* @return non-null, closest first
|
||||
*/
|
||||
public List<T> getClosest(int max, Collection<T> toIgnore) {
|
||||
List<T> rv = new ArrayList(max);
|
||||
int count = 0;
|
||||
getReadLock();
|
||||
try {
|
||||
// start at first (closest) bucket
|
||||
for (int i = 0; i < _buckets.size() && count < max; i++) {
|
||||
Set<T> entries = _buckets.get(i).getEntries();
|
||||
// add the whole bucket except for ignores,
|
||||
// extras will be trimmed after sorting
|
||||
for (T e : entries) {
|
||||
if (!toIgnore.contains(e)) {
|
||||
rv.add(e);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally { releaseReadLock(); }
|
||||
Comparator comp = new XORComparator(_us);
|
||||
Collections.sort(rv, comp);
|
||||
int sz = rv.size();
|
||||
for (int i = sz - 1; i >= max; i--) {
|
||||
rv.remove(i);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* The keys closest to the key.
|
||||
* Returned list will never contain us.
|
||||
* @return non-null, closest first
|
||||
*/
|
||||
public List<T> getClosest(T key, int max) {
|
||||
return getClosest(key, max, Collections.EMPTY_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* The keys closest to the key.
|
||||
* Returned list will never contain us.
|
||||
* @return non-null, closest first
|
||||
*/
|
||||
public List<T> getClosest(T key, int max, Collection<T> toIgnore) {
|
||||
if (key.equals(_us))
|
||||
return getClosest(max, toIgnore);
|
||||
List<T> rv = new ArrayList(max);
|
||||
int count = 0;
|
||||
getReadLock();
|
||||
try {
|
||||
int start = pickBucket(key);
|
||||
// start at closest bucket, then to the smaller (closer to us) buckets
|
||||
for (int i = start; i >= 0 && count < max; i--) {
|
||||
Set<T> entries = _buckets.get(i).getEntries();
|
||||
for (T e : entries) {
|
||||
if (!toIgnore.contains(e)) {
|
||||
rv.add(e);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// then the farther from us buckets if necessary
|
||||
for (int i = start + 1; i < _buckets.size() && count < max; i++) {
|
||||
Set<T> entries = _buckets.get(i).getEntries();
|
||||
for (T e : entries) {
|
||||
if (!toIgnore.contains(e)) {
|
||||
rv.add(e);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally { releaseReadLock(); }
|
||||
Comparator comp = new XORComparator(key);
|
||||
Collections.sort(rv, comp);
|
||||
int sz = rv.size();
|
||||
for (int i = sz - 1; i >= max; i--) {
|
||||
rv.remove(i);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* The bucket number (NOT the range number) that the xor of the key goes in
|
||||
* Caller must hold read lock
|
||||
* @return 0 to max-1 or -1 for us
|
||||
*/
|
||||
private int pickBucket(T key) {
|
||||
int range = getRange(key);
|
||||
if (range < 0)
|
||||
return -1;
|
||||
int rv = pickBucket(range);
|
||||
if (rv >= 0) {
|
||||
return rv;
|
||||
}
|
||||
_log.error("Key does not fit in any bucket?! WTF!\nKey : ["
|
||||
+ DataHelper.toHexString(key.getData()) + "]"
|
||||
+ "\nUs : " + _us
|
||||
+ "\nDelta: ["
|
||||
+ DataHelper.toHexString(DataHelper.xor(_us.getData(), key.getData()))
|
||||
+ "]", new Exception("WTF"));
|
||||
_log.error(toString());
|
||||
throw new IllegalStateException("pickBucket returned " + rv);
|
||||
//return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returned list is a copy of the bucket list, closest first,
|
||||
* with the actual buckets (not a copy).
|
||||
*
|
||||
* Primarily for testing. You shouldn't ever need to get all the buckets.
|
||||
* Use getClosest() or getAll() instead to get the keys.
|
||||
*
|
||||
* @return non-null
|
||||
*/
|
||||
List<KBucket<T>> getBuckets() {
|
||||
getReadLock();
|
||||
try {
|
||||
return new ArrayList(_buckets);
|
||||
} finally { releaseReadLock(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* The bucket that the xor of the key goes in
|
||||
* Caller must hold read lock
|
||||
* @return null if key is us
|
||||
*/
|
||||
private KBucket getBucket(T key) {
|
||||
int bucket = pickBucket(key);
|
||||
if (bucket < 0)
|
||||
return null;
|
||||
return _buckets.get(bucket);
|
||||
}
|
||||
|
||||
/**
|
||||
* The bucket number that contains this range number
|
||||
* Caller must hold read lock or write lock
|
||||
* @return 0 to max-1 or -1 for us
|
||||
*/
|
||||
private int pickBucket(int range) {
|
||||
// If B is small, a linear search from back to front
|
||||
// is most efficient since most of the keys are at the end...
|
||||
// If B is larger, there's a lot of sub-buckets
|
||||
// of equal size to be checked so a binary search is better
|
||||
if (B_VALUE <= 3) {
|
||||
for (int i = _buckets.size() - 1; i >= 0; i--) {
|
||||
KBucket b = _buckets.get(i);
|
||||
if (range >= b.getRangeBegin() && range <= b.getRangeEnd())
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
} else {
|
||||
KBucket dummy = new DummyBucket(range);
|
||||
return Collections.binarySearch(_buckets, dummy, new BucketComparator());
|
||||
}
|
||||
}
|
||||
|
||||
private List<KBucket> createBuckets() {
|
||||
// just an initial size
|
||||
List<KBucket> buckets = new ArrayList(4 * B_FACTOR);
|
||||
buckets.add(createBucket(0, NUM_BUCKETS -1));
|
||||
return buckets;
|
||||
}
|
||||
|
||||
private KBucket createBucket(int start, int end) {
|
||||
if (end - start >= B_FACTOR &&
|
||||
(((end + 1) & B_FACTOR - 1) != 0 ||
|
||||
(start & B_FACTOR - 1) != 0))
|
||||
throw new IllegalArgumentException("Sub-bkt crosses K-bkt boundary: " + start + '-' + end);
|
||||
KBucket bucket = new KBucketImpl(_context, start, end, BUCKET_SIZE, _trimmer);
|
||||
return bucket;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of bits minus 1 (range number) for the xor of the key.
|
||||
* Package private for testing only. Others shouldn't need this.
|
||||
* @return 0 to max-1 or -1 for us
|
||||
*/
|
||||
int getRange(T key) {
|
||||
return _rangeCalc.getRange(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* For every bucket that hasn't been updated in this long,
|
||||
* or isn't close to full,
|
||||
* generate a random key that would be a member of that bucket.
|
||||
* The returned keys may be searched for to "refresh" the buckets.
|
||||
* @return non-null, closest first
|
||||
*/
|
||||
public List<T> getExploreKeys(long age) {
|
||||
List<T> rv = new ArrayList(_buckets.size());
|
||||
long old = _context.clock().now() - age;
|
||||
getReadLock();
|
||||
try {
|
||||
for (KBucket b : _buckets) {
|
||||
if (b.getLastChanged() < old || b.getKeyCount() < BUCKET_SIZE * 3 / 4)
|
||||
rv.add(generateRandomKey(b));
|
||||
}
|
||||
} finally { releaseReadLock(); }
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random key to go within this bucket
|
||||
* Package private for testing only. Others shouldn't need this.
|
||||
*/
|
||||
T generateRandomKey(KBucket bucket) {
|
||||
int begin = bucket.getRangeBegin();
|
||||
int end = bucket.getRangeEnd();
|
||||
// number of fixed bits, out of B_VALUE - 1 bits
|
||||
int fixed = 0;
|
||||
int bsz = 1 + end - begin;
|
||||
// compute fixed = B_VALUE - log2(bsz)
|
||||
// e.g for B=4, B_FACTOR=8, sz 4-> fixed 1, sz 2->fixed 2, sz 1 -> fixed 3
|
||||
while (bsz < B_FACTOR) {
|
||||
fixed++;
|
||||
bsz <<= 1;
|
||||
}
|
||||
int fixedBits = 0;
|
||||
if (fixed > 0) {
|
||||
// 0x01, 03, 07, 0f, ...
|
||||
int mask = (1 << fixed) - 1;
|
||||
// fixed bits masked from begin
|
||||
fixedBits = (begin >> (B_VALUE - (fixed + 1))) & mask;
|
||||
}
|
||||
int obegin = begin;
|
||||
int oend = end;
|
||||
begin >>= (B_VALUE - 1);
|
||||
end >>= (B_VALUE - 1);
|
||||
// we need randomness for [0, begin) bits
|
||||
BigInteger variance;
|
||||
// 00000000rrrr
|
||||
if (begin > 0)
|
||||
variance = new BigInteger(begin - fixed, _context.random());
|
||||
else
|
||||
variance = BigInteger.ZERO;
|
||||
// we need nonzero randomness for [begin, end] bits
|
||||
int numNonZero = 1 + end - begin;
|
||||
if (numNonZero == 1) {
|
||||
// 00001000rrrr
|
||||
variance = variance.setBit(begin);
|
||||
// fixed bits as the 'main' bucket is split
|
||||
// 00001fffrrrr
|
||||
if (fixed > 0)
|
||||
variance = variance.or(BigInteger.valueOf(fixedBits).shiftLeft(begin - fixed));
|
||||
} else {
|
||||
// dont span main bucket boundaries with depth > 1
|
||||
if (fixed > 0)
|
||||
throw new IllegalStateException("WTF " + bucket);
|
||||
BigInteger nonz;
|
||||
if (numNonZero <= 62) {
|
||||
// add one to ensure nonzero
|
||||
long nz = 1 + _context.random().nextLong((1l << numNonZero) - 1);
|
||||
nonz = BigInteger.valueOf(nz);
|
||||
} else {
|
||||
// loop to ensure nonzero
|
||||
do {
|
||||
nonz = new BigInteger(numNonZero, _context.random());
|
||||
} while (nonz.equals(BigInteger.ZERO));
|
||||
}
|
||||
// shift left and or-in the nonzero randomness
|
||||
if (begin > 0)
|
||||
nonz = nonz.shiftLeft(begin);
|
||||
// 0000nnnnrrrr
|
||||
variance = variance.or(nonz);
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("SB(" + obegin + ',' + oend + ") KB(" + begin + ',' + end + ") fixed=" + fixed + " fixedBits=" + fixedBits + " numNonZ=" + numNonZero);
|
||||
byte data[] = variance.toByteArray();
|
||||
T key = makeKey(data);
|
||||
byte[] hash = DataHelper.xor(key.getData(), _us.getData());
|
||||
T rv = makeKey(hash);
|
||||
|
||||
// DEBUG
|
||||
//int range = getRange(rv);
|
||||
//if (range < obegin || range > oend) {
|
||||
// throw new IllegalStateException("Generate random key failed range=" + range + " for " + rv + " meant for bucket " + bucket);
|
||||
//}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a new SimpleDataStrucure from the data
|
||||
* @param data size <= SDS length, else throws IAE
|
||||
* Can be 1 bigger if top byte is zero
|
||||
*/
|
||||
private T makeKey(byte[] data) {
|
||||
int len = _us.length();
|
||||
int dlen = data.length;
|
||||
if (dlen > len + 1 ||
|
||||
(dlen == len + 1 && data[0] != 0))
|
||||
throw new IllegalArgumentException("bad length " + dlen + " > " + len);
|
||||
T rv;
|
||||
try {
|
||||
rv = (T) _us.getClass().newInstance();
|
||||
} catch (Exception e) {
|
||||
_log.error("fail", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (dlen == len) {
|
||||
rv.setData(data);
|
||||
} else {
|
||||
byte[] ndata = new byte[len];
|
||||
if (dlen == len + 1) {
|
||||
// one bigger
|
||||
System.arraycopy(data, 1, ndata, 0, len);
|
||||
} else {
|
||||
// smaller
|
||||
System.arraycopy(data, 0, ndata, len - dlen, dlen);
|
||||
}
|
||||
rv.setData(ndata);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
private static class Range<T extends SimpleDataStructure> {
|
||||
private final int _bValue;
|
||||
private final BigInteger _bigUs;
|
||||
private final Map<T, Integer> _distanceCache;
|
||||
|
||||
public Range(T us, int bValue) {
|
||||
_bValue = bValue;
|
||||
_bigUs = new BigInteger(1, us.getData());
|
||||
_distanceCache = new LHMCache(256);
|
||||
}
|
||||
|
||||
/** @return 0 to max-1 or -1 for us */
|
||||
public int getRange(T key) {
|
||||
Integer rv;
|
||||
synchronized (_distanceCache) {
|
||||
rv = _distanceCache.get(key);
|
||||
if (rv == null) {
|
||||
// easy way when _bValue == 1
|
||||
//rv = Integer.valueOf(_bigUs.xor(new BigInteger(1, key.getData())).bitLength() - 1);
|
||||
BigInteger xor = _bigUs.xor(new BigInteger(1, key.getData()));
|
||||
int range = xor.bitLength() - 1;
|
||||
if (_bValue > 1) {
|
||||
int toShift = range + 1 - _bValue;
|
||||
int highbit = range;
|
||||
range <<= _bValue - 1;
|
||||
if (toShift >= 0) {
|
||||
int extra = xor.clearBit(highbit).shiftRight(toShift).intValue();
|
||||
range += extra;
|
||||
//Log log = I2PAppContext.getGlobalContext().logManager().getLog(KBucketSet.class);
|
||||
//if (log.shouldLog(Log.DEBUG))
|
||||
// log.debug("highbit " + highbit + " toshift " + toShift + " extra " + extra + " new " + range);
|
||||
}
|
||||
}
|
||||
rv = Integer.valueOf(range);
|
||||
_distanceCache.put(key, rv);
|
||||
}
|
||||
}
|
||||
return rv.intValue();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
synchronized (_distanceCache) {
|
||||
_distanceCache.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For Collections.binarySearch.
|
||||
* getRangeBegin == getRangeEnd.
|
||||
*/
|
||||
private static class DummyBucket<T extends SimpleDataStructure> implements KBucket<T> {
|
||||
private final int r;
|
||||
|
||||
public DummyBucket(int range) {
|
||||
r = range;
|
||||
}
|
||||
|
||||
public int getRangeBegin() { return r; }
|
||||
public int getRangeEnd() { return r; }
|
||||
|
||||
public int getKeyCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Set<T> getEntries() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void getEntries(SelectionCollector<T> collector) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void clear() {}
|
||||
|
||||
public boolean add(T peer) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean remove(T peer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setLastChanged() {}
|
||||
|
||||
public long getLastChanged() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For Collections.binarySearch.
|
||||
* Returns equal for any overlap.
|
||||
*/
|
||||
private static class BucketComparator implements Comparator<KBucket> {
|
||||
public int compare(KBucket l, KBucket r) {
|
||||
if (l.getRangeEnd() < r.getRangeBegin())
|
||||
return -1;
|
||||
if (l.getRangeBegin() > r.getRangeEnd())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(1024);
|
||||
buf.append("Bucket set rooted on: ").append(_us.toString())
|
||||
.append(" K= ").append(BUCKET_SIZE)
|
||||
.append(" B= ").append(B_VALUE)
|
||||
.append(" with ").append(size())
|
||||
.append(" keys in ").append(_buckets.size()).append(" buckets:\n");
|
||||
getReadLock();
|
||||
try {
|
||||
int len = _buckets.size();
|
||||
for (int i = 0; i < len; i++) {
|
||||
KBucket b = _buckets.get(i);
|
||||
buf.append("* Bucket ").append(i).append("/").append(len).append(": ");
|
||||
buf.append(b.toString()).append("\n");
|
||||
}
|
||||
} finally { releaseReadLock(); }
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
20
apps/i2psnark/java/src/net/i2p/kademlia/KBucketTrimmer.java
Normal file
20
apps/i2psnark/java/src/net/i2p/kademlia/KBucketTrimmer.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package net.i2p.kademlia;
|
||||
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
|
||||
/**
|
||||
* Called when a kbucket can no longer be split and is too big
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public interface KBucketTrimmer<K extends SimpleDataStructure> {
|
||||
/**
|
||||
* Called from add() just before adding the entry.
|
||||
* You may call getEntries() and/or remove() from here.
|
||||
* Do NOT call add().
|
||||
* To always discard a newer entry, always return false.
|
||||
*
|
||||
* @param kbucket the kbucket that is now too big
|
||||
* @return true to actually add the entry.
|
||||
*/
|
||||
public boolean trim(KBucket<K> kbucket, K toAdd);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package net.i2p.kademlia;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
|
||||
/**
|
||||
* Removes a random element, but only if the bucket hasn't changed in 5 minutes.
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public class RandomIfOldTrimmer<T extends SimpleDataStructure> extends RandomTrimmer<T> {
|
||||
|
||||
public RandomIfOldTrimmer(I2PAppContext ctx, int max) {
|
||||
super(ctx, max);
|
||||
}
|
||||
|
||||
public boolean trim(KBucket<T> kbucket, T toAdd) {
|
||||
if (kbucket.getLastChanged() > _ctx.clock().now() - 5*60*1000)
|
||||
return false;
|
||||
return super.trim(kbucket, toAdd);
|
||||
}
|
||||
}
|
||||
31
apps/i2psnark/java/src/net/i2p/kademlia/RandomTrimmer.java
Normal file
31
apps/i2psnark/java/src/net/i2p/kademlia/RandomTrimmer.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package net.i2p.kademlia;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
|
||||
/**
|
||||
* Removes a random element. Not resistant to flooding.
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public class RandomTrimmer<T extends SimpleDataStructure> implements KBucketTrimmer<T> {
|
||||
protected final I2PAppContext _ctx;
|
||||
private final int _max;
|
||||
|
||||
public RandomTrimmer(I2PAppContext ctx, int max) {
|
||||
_ctx = ctx;
|
||||
_max = max;
|
||||
}
|
||||
|
||||
public boolean trim(KBucket<T> kbucket, T toAdd) {
|
||||
List<T> e = new ArrayList(kbucket.getEntries());
|
||||
int sz = e.size();
|
||||
// concurrency
|
||||
if (sz < _max)
|
||||
return true;
|
||||
T toRemove = e.get(_ctx.random().nextInt(sz));
|
||||
return kbucket.remove(toRemove);
|
||||
}
|
||||
}
|
||||
13
apps/i2psnark/java/src/net/i2p/kademlia/RejectTrimmer.java
Normal file
13
apps/i2psnark/java/src/net/i2p/kademlia/RejectTrimmer.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package net.i2p.kademlia;
|
||||
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
|
||||
/**
|
||||
* Removes nothing and always rejects the add. Flood resistant..
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public class RejectTrimmer<T extends SimpleDataStructure> implements KBucketTrimmer<T> {
|
||||
public boolean trim(KBucket<T> kbucket, T toAdd) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.i2p.kademlia;
|
||||
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
|
||||
/**
|
||||
* Visit kbuckets, gathering matches
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public interface SelectionCollector<T extends SimpleDataStructure> {
|
||||
public void add(T entry);
|
||||
}
|
||||
28
apps/i2psnark/java/src/net/i2p/kademlia/XORComparator.java
Normal file
28
apps/i2psnark/java/src/net/i2p/kademlia/XORComparator.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package net.i2p.kademlia;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
|
||||
/**
|
||||
* Help sort Hashes in relation to a base key using the XOR metric
|
||||
*
|
||||
* @since 0.9.2
|
||||
*/
|
||||
class XORComparator<T extends SimpleDataStructure> implements Comparator<T> {
|
||||
private final byte[] _base;
|
||||
|
||||
/**
|
||||
* @param target key to compare distances with
|
||||
*/
|
||||
public XORComparator(T target) {
|
||||
_base = target.getData();
|
||||
}
|
||||
|
||||
public int compare(T lhs, T rhs) {
|
||||
byte lhsDelta[] = DataHelper.xor(lhs.getData(), _base);
|
||||
byte rhsDelta[] = DataHelper.xor(rhs.getData(), _base);
|
||||
return DataHelper.compareTo(lhsDelta, rhsDelta);
|
||||
}
|
||||
}
|
||||
6
apps/i2psnark/java/src/net/i2p/kademlia/package.html
Normal file
6
apps/i2psnark/java/src/net/i2p/kademlia/package.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<html><body><p>
|
||||
This is a major rewrite of KBucket, KBucketSet, and KBucketImpl from net.i2p.router.networkdb.kademlia.
|
||||
The classes are now generic to support SHA1. SHA256, or other key lengths.
|
||||
The long-term goal is to prove out this new implementation in i2psnark,
|
||||
then move it to core, then convert the network database to use it.
|
||||
</p></body></html>
|
||||
@@ -39,7 +39,6 @@ public class BitField
|
||||
this.size = size;
|
||||
int arraysize = ((size-1)/8)+1;
|
||||
bitfield = new byte[arraysize];
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,9 +98,11 @@ public class BitField
|
||||
throw new IndexOutOfBoundsException(Integer.toString(bit));
|
||||
int index = bit/8;
|
||||
int mask = 128 >> (bit % 8);
|
||||
if ((bitfield[index] & mask) == 0) {
|
||||
count++;
|
||||
bitfield[index] |= mask;
|
||||
synchronized(this) {
|
||||
if ((bitfield[index] & mask) == 0) {
|
||||
count++;
|
||||
bitfield[index] |= mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ public class ConnectionAcceptor implements Runnable
|
||||
thread = new I2PAppThread(this, "I2PSnark acceptor");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
SimpleScheduler.getInstance().addPeriodicEvent(new Cleaner(), BAD_CLEAN_INTERVAL);
|
||||
_util.getContext().simpleScheduler().addPeriodicEvent(new Cleaner(), BAD_CLEAN_INTERVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ public class ConnectionAcceptor implements Runnable
|
||||
thread = new I2PAppThread(this, "I2PSnark acceptor");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
SimpleScheduler.getInstance().addPeriodicEvent(new Cleaner(), BAD_CLEAN_INTERVAL);
|
||||
_util.getContext().simpleScheduler().addPeriodicEvent(new Cleaner(), BAD_CLEAN_INTERVAL);
|
||||
}
|
||||
|
||||
public void halt()
|
||||
@@ -146,7 +146,7 @@ public class ConnectionAcceptor implements Runnable
|
||||
}
|
||||
} else {
|
||||
if (socket.getPeerDestination().equals(_util.getMyDestination())) {
|
||||
_util.debug("Incoming connection from myself", Snark.ERROR);
|
||||
_log.error("Incoming connection from myself");
|
||||
try { socket.close(); } catch (IOException ioe) {}
|
||||
continue;
|
||||
}
|
||||
@@ -163,13 +163,13 @@ public class ConnectionAcceptor implements Runnable
|
||||
catch (I2PException ioe)
|
||||
{
|
||||
if (!socketChanged) {
|
||||
_util.debug("Error while accepting: " + ioe, Snark.ERROR);
|
||||
_log.error("Error while accepting", ioe);
|
||||
stop = true;
|
||||
}
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
_util.debug("Error while accepting: " + ioe, Snark.ERROR);
|
||||
_log.error("Error while accepting", ioe);
|
||||
stop = true;
|
||||
}
|
||||
// catch oom?
|
||||
|
||||
@@ -40,4 +40,5 @@ interface CoordinatorListener
|
||||
public boolean overUploadLimit(int uploaders);
|
||||
public boolean overUpBWLimit();
|
||||
public boolean overUpBWLimit(long total);
|
||||
public void addMessage(String message);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.klomp.snark;
|
||||
|
||||
import net.i2p.data.ByteArray;
|
||||
|
||||
/**
|
||||
* Callback used to fetch data
|
||||
* @since 0.8.2
|
||||
@@ -10,5 +12,5 @@ interface DataLoader
|
||||
* This is the callback that PeerConnectionOut calls to get the data from disk
|
||||
* @return bytes or null for errors
|
||||
*/
|
||||
public byte[] loadData(int piece, int begin, int length);
|
||||
public ByteArray loadData(int piece, int begin, int length);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ abstract class ExtensionHandler {
|
||||
public static final int ID_PEX = 2;
|
||||
/** not ut_pex since the compact format is different */
|
||||
public static final String TYPE_PEX = "i2p_pex";
|
||||
public static final int ID_DHT = 3;
|
||||
/** not using the option bit since the compact format is different */
|
||||
public static final String TYPE_DHT = "i2p_dht";
|
||||
/** Pieces * SHA1 Hash length, + 25% extra for file names, benconding overhead, etc */
|
||||
private static final int MAX_METADATA_SIZE = Storage.MAX_PIECES * 20 * 5 / 4;
|
||||
private static final int PARALLEL_REQUESTS = 3;
|
||||
@@ -36,9 +39,10 @@ abstract class ExtensionHandler {
|
||||
/**
|
||||
* @param metasize -1 if unknown
|
||||
* @param pexAndMetadata advertise these capabilities
|
||||
* @param dht advertise DHT capability
|
||||
* @return bencoded outgoing handshake message
|
||||
*/
|
||||
public static byte[] getHandshake(int metasize, boolean pexAndMetadata) {
|
||||
public static byte[] getHandshake(int metasize, boolean pexAndMetadata, boolean dht) {
|
||||
Map<String, Object> handshake = new HashMap();
|
||||
Map<String, Integer> m = new HashMap();
|
||||
if (pexAndMetadata) {
|
||||
@@ -47,6 +51,9 @@ abstract class ExtensionHandler {
|
||||
if (metasize >= 0)
|
||||
handshake.put("metadata_size", Integer.valueOf(metasize));
|
||||
}
|
||||
if (dht) {
|
||||
m.put(TYPE_DHT, Integer.valueOf(ID_DHT));
|
||||
}
|
||||
// include the map even if empty so the far-end doesn't NPE
|
||||
handshake.put("m", m);
|
||||
handshake.put("p", Integer.valueOf(6881));
|
||||
@@ -65,6 +72,8 @@ abstract class ExtensionHandler {
|
||||
handleMetadata(peer, listener, bs, log);
|
||||
else if (id == ID_PEX)
|
||||
handlePEX(peer, listener, bs, log);
|
||||
else if (id == ID_DHT)
|
||||
handleDHT(peer, listener, bs, log);
|
||||
else if (log.shouldLog(Log.INFO))
|
||||
log.info("Unknown extension msg " + id + " from " + peer);
|
||||
}
|
||||
@@ -87,6 +96,12 @@ abstract class ExtensionHandler {
|
||||
// peer state calls peer listener calls sendPEX()
|
||||
}
|
||||
|
||||
if (msgmap.get(TYPE_DHT) != null) {
|
||||
if (log.shouldLog(Log.DEBUG))
|
||||
log.debug("Peer supports DHT extension: " + peer);
|
||||
// peer state calls peer listener calls sendDHT()
|
||||
}
|
||||
|
||||
MagnetState state = peer.getMagnetState();
|
||||
|
||||
if (msgmap.get(TYPE_METADATA) == null) {
|
||||
@@ -323,7 +338,7 @@ abstract class ExtensionHandler {
|
||||
System.arraycopy(ids, off, hash, 0, HASH_LENGTH);
|
||||
if (DataHelper.eq(hash, peer.getPeerID().getDestHash()))
|
||||
continue;
|
||||
PeerID pID = new PeerID(hash);
|
||||
PeerID pID = new PeerID(hash, listener.getUtil());
|
||||
peers.add(pID);
|
||||
}
|
||||
// could include ourselves, listener must remove
|
||||
@@ -335,6 +350,28 @@ abstract class ExtensionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the DHT port numbers
|
||||
* @since DHT
|
||||
*/
|
||||
private static void handleDHT(Peer peer, PeerListener listener, byte[] bs, Log log) {
|
||||
if (log.shouldLog(Log.DEBUG))
|
||||
log.debug("Got DHT msg from " + peer);
|
||||
try {
|
||||
InputStream is = new ByteArrayInputStream(bs);
|
||||
BDecoder dec = new BDecoder(is);
|
||||
BEValue bev = dec.bdecodeMap();
|
||||
Map<String, BEValue> map = bev.getMap();
|
||||
int qport = map.get("port").getInt();
|
||||
int rport = map.get("rport").getInt();
|
||||
listener.gotPort(peer, qport, rport);
|
||||
} catch (Exception e) {
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("DHT msg exception from " + peer, e);
|
||||
//peer.disconnect(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* added.f and dropped unsupported
|
||||
* @param pList non-null
|
||||
@@ -362,4 +399,22 @@ abstract class ExtensionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the DHT port numbers
|
||||
* @since DHT
|
||||
*/
|
||||
public static void sendDHT(Peer peer, int qport, int rport) {
|
||||
Map<String, Object> map = new HashMap();
|
||||
map.put("port", Integer.valueOf(qport));
|
||||
map.put("rport", Integer.valueOf(rport));
|
||||
byte[] payload = BEncoder.bencode(map);
|
||||
try {
|
||||
int hisMsgCode = peer.getHandshakeMap().get("m").getMap().get(TYPE_DHT).getInt();
|
||||
peer.sendExtension(hisMsgCode, payload);
|
||||
} catch (Exception e) {
|
||||
// NPE, no DHT caps
|
||||
//if (log.shouldLog(Log.INFO))
|
||||
// log.info("DHT msg exception to " + peer, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import net.i2p.util.SimpleTimer;
|
||||
import net.i2p.util.Translate;
|
||||
|
||||
import org.klomp.snark.dht.DHT;
|
||||
//import org.klomp.snark.dht.KRPC;
|
||||
import org.klomp.snark.dht.KRPC;
|
||||
|
||||
/**
|
||||
* I2P specific helpers for I2PSnark
|
||||
@@ -65,6 +65,7 @@ public class I2PSnarkUtil {
|
||||
private final File _tmpDir;
|
||||
private int _startupDelay;
|
||||
private boolean _shouldUseOT;
|
||||
private boolean _shouldUseDHT;
|
||||
private boolean _areFilesPublic;
|
||||
private List<String> _openTrackers;
|
||||
private DHT _dht;
|
||||
@@ -77,7 +78,7 @@ public class I2PSnarkUtil {
|
||||
public static final int DEFAULT_MAX_UP_BW = 8; //KBps
|
||||
public static final int MAX_CONNECTIONS = 16; // per torrent
|
||||
public static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond";
|
||||
//private static final boolean ENABLE_DHT = true;
|
||||
public static final boolean DEFAULT_USE_DHT = true;
|
||||
|
||||
public I2PSnarkUtil(I2PAppContext ctx) {
|
||||
_context = ctx;
|
||||
@@ -86,7 +87,6 @@ public class I2PSnarkUtil {
|
||||
//setProxy("127.0.0.1", 4444);
|
||||
setI2CPConfig("127.0.0.1", 7654, null);
|
||||
_shitlist = new ConcurrentHashSet();
|
||||
_configured = false;
|
||||
_maxUploaders = Snark.MAX_TOTAL_UPLOADERS;
|
||||
_maxUpBW = DEFAULT_MAX_UP_BW;
|
||||
_maxConnections = MAX_CONNECTIONS;
|
||||
@@ -94,6 +94,7 @@ public class I2PSnarkUtil {
|
||||
_shouldUseOT = DEFAULT_USE_OPENTRACKERS;
|
||||
// FIXME split if default has more than one
|
||||
_openTrackers = Collections.singletonList(DEFAULT_OPENTRACKERS);
|
||||
_shouldUseDHT = DEFAULT_USE_DHT;
|
||||
// This is used for both announce replies and .torrent file downloads,
|
||||
// so it must be available even if not connected to I2CP.
|
||||
// so much for multiple instances
|
||||
@@ -241,12 +242,13 @@ public class I2PSnarkUtil {
|
||||
opts.setProperty("i2p.streaming.maxTotalConnsPerMinute", "8");
|
||||
if (opts.getProperty("i2p.streaming.maxConnsPerHour") == null)
|
||||
opts.setProperty("i2p.streaming.maxConnsPerHour", "20");
|
||||
if (opts.getProperty("i2p.streaming.enforceProtocol") == null)
|
||||
opts.setProperty("i2p.streaming.enforceProtocol", "true");
|
||||
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
|
||||
_connecting = false;
|
||||
}
|
||||
// FIXME this only instantiates krpc once, left stuck with old manager
|
||||
//if (ENABLE_DHT && _manager != null && _dht == null)
|
||||
// _dht = new KRPC(_context, _manager.getSession());
|
||||
if (_shouldUseDHT && _manager != null && _dht == null)
|
||||
_dht = new KRPC(_context, _manager.getSession());
|
||||
return (_manager != null);
|
||||
}
|
||||
|
||||
@@ -273,7 +275,11 @@ public class I2PSnarkUtil {
|
||||
/**
|
||||
* Destroy the destination itself
|
||||
*/
|
||||
public void disconnect() {
|
||||
public synchronized void disconnect() {
|
||||
if (_dht != null) {
|
||||
_dht.stop();
|
||||
_dht = null;
|
||||
}
|
||||
I2PSocketManager mgr = _manager;
|
||||
// FIXME this can cause race NPEs elsewhere
|
||||
_manager = null;
|
||||
@@ -309,7 +315,7 @@ public class I2PSnarkUtil {
|
||||
return rv;
|
||||
} catch (I2PException ie) {
|
||||
_shitlist.add(dest);
|
||||
SimpleScheduler.getInstance().addEvent(new Unshitlist(dest), 10*60*1000);
|
||||
_context.simpleScheduler().addEvent(new Unshitlist(dest), 10*60*1000);
|
||||
throw new IOException("Unable to reach the peer " + peer + ": " + ie.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -447,7 +453,8 @@ public class I2PSnarkUtil {
|
||||
if (sess != null) {
|
||||
byte[] b = Base32.decode(ip.substring(0, BASE32_HASH_LENGTH));
|
||||
if (b != null) {
|
||||
Hash h = new Hash(b);
|
||||
//Hash h = new Hash(b);
|
||||
Hash h = Hash.create(b);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Using existing session for lookup of " + ip);
|
||||
try {
|
||||
@@ -522,6 +529,22 @@ public class I2PSnarkUtil {
|
||||
public boolean shouldUseOpenTrackers() {
|
||||
return _shouldUseOT;
|
||||
}
|
||||
|
||||
/** @since DHT */
|
||||
public synchronized void setUseDHT(boolean yes) {
|
||||
_shouldUseDHT = yes;
|
||||
if (yes && _manager != null && _dht == null) {
|
||||
_dht = new KRPC(_context, _manager.getSession());
|
||||
} else if (!yes && _dht != null) {
|
||||
_dht.stop();
|
||||
_dht = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** @since DHT */
|
||||
public boolean shouldUseDHT() {
|
||||
return _shouldUseDHT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like DataHelper.toHexString but ensures no loss of leading zero bytes
|
||||
@@ -538,40 +561,6 @@ public class I2PSnarkUtil {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** hook between snark's logger and an i2p log */
|
||||
void debug(String msg, int snarkDebugLevel) {
|
||||
debug(msg, snarkDebugLevel, null);
|
||||
}
|
||||
void debug(String msg, int snarkDebugLevel, Throwable t) {
|
||||
if (t instanceof OutOfMemoryError) {
|
||||
try { Thread.sleep(100); } catch (InterruptedException ie) {}
|
||||
try {
|
||||
t.printStackTrace();
|
||||
} catch (Throwable tt) {}
|
||||
try {
|
||||
System.out.println("OOM thread: " + Thread.currentThread().getName());
|
||||
} catch (Throwable tt) {}
|
||||
}
|
||||
switch (snarkDebugLevel) {
|
||||
case 0:
|
||||
case 1:
|
||||
_log.error(msg, t);
|
||||
break;
|
||||
case 2:
|
||||
_log.warn(msg, t);
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
_log.info(msg, t);
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
default:
|
||||
_log.debug(msg, t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String BUNDLE_NAME = "org.klomp.snark.web.messages";
|
||||
|
||||
/** lang in routerconsole.lang property, else current locale */
|
||||
|
||||
@@ -23,8 +23,13 @@ package org.klomp.snark;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
// Used to queue outgoing connections
|
||||
// sendMessage() should be used to translate them to wire format.
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.util.ByteCache;
|
||||
|
||||
/**
|
||||
* Used to queue outgoing connections
|
||||
* sendMessage() should be used to translate them to wire format.
|
||||
*/
|
||||
class Message
|
||||
{
|
||||
final static byte KEEP_ALIVE = -1;
|
||||
@@ -69,6 +74,9 @@ class Message
|
||||
// now unused
|
||||
//SimpleTimer.TimedEvent expireEvent;
|
||||
|
||||
private static final int BUFSIZE = PeerState.PARTSIZE;
|
||||
private static final ByteCache _cache = ByteCache.getInstance(16, BUFSIZE);
|
||||
|
||||
/** Utility method for sending a message through a DataStream. */
|
||||
void sendMessage(DataOutputStream dos) throws IOException
|
||||
{
|
||||
@@ -79,11 +87,15 @@ class Message
|
||||
return;
|
||||
}
|
||||
|
||||
ByteArray ba;
|
||||
// Get deferred data
|
||||
if (data == null && dataLoader != null) {
|
||||
data = dataLoader.loadData(piece, begin, length);
|
||||
if (data == null)
|
||||
ba = dataLoader.loadData(piece, begin, length);
|
||||
if (ba == null)
|
||||
return; // hmm will get retried, but shouldn't happen
|
||||
data = ba.getData();
|
||||
} else {
|
||||
ba = null;
|
||||
}
|
||||
|
||||
// Calculate the total length in bytes
|
||||
@@ -139,6 +151,10 @@ class Message
|
||||
// Send actual data
|
||||
if (type == BITFIELD || type == PIECE || type == EXTENSION)
|
||||
dos.write(data, off, len);
|
||||
|
||||
// Was pulled from cache in Storage.getPiece() via dataLoader
|
||||
if (ba != null && ba.getData().length == BUFSIZE)
|
||||
_cache.release(ba, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,6 +9,8 @@ import java.security.MessageDigest;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.SHA1;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFile;
|
||||
|
||||
@@ -42,6 +44,9 @@ class PartialPiece implements Comparable {
|
||||
private final int pclen;
|
||||
private final File tempDir;
|
||||
|
||||
private static final int BUFSIZE = PeerState.PARTSIZE;
|
||||
private static final ByteCache _cache = ByteCache.getInstance(16, BUFSIZE);
|
||||
|
||||
// Any bigger than this, use temp file instead of heap
|
||||
private static final int MAX_IN_MEM = 128 * 1024;
|
||||
// May be reduced on OOM
|
||||
@@ -154,7 +159,16 @@ class PartialPiece implements Comparable {
|
||||
sha1.update(bs);
|
||||
} else {
|
||||
int read = 0;
|
||||
byte[] buf = new byte[Math.min(pclen, 16384)];
|
||||
int buflen = Math.min(pclen, BUFSIZE);
|
||||
ByteArray ba;
|
||||
byte[] buf;
|
||||
if (buflen == BUFSIZE) {
|
||||
ba = _cache.acquire();
|
||||
buf = ba.getData();
|
||||
} else {
|
||||
ba = null;
|
||||
buf = new byte[buflen];
|
||||
}
|
||||
synchronized (this) {
|
||||
if (raf == null)
|
||||
throw new IOException();
|
||||
@@ -167,6 +181,8 @@ class PartialPiece implements Comparable {
|
||||
sha1.update(buf, 0, rd);
|
||||
}
|
||||
}
|
||||
if (ba != null)
|
||||
_cache.release(ba, false);
|
||||
if (read < pclen)
|
||||
throw new IOException();
|
||||
}
|
||||
@@ -182,7 +198,15 @@ class PartialPiece implements Comparable {
|
||||
din.readFully(bs, off, len);
|
||||
} else {
|
||||
// read in fully before synching on raf
|
||||
byte[] tmp = new byte[len];
|
||||
ByteArray ba;
|
||||
byte[] tmp;
|
||||
if (len == BUFSIZE) {
|
||||
ba = _cache.acquire();
|
||||
tmp = ba.getData();
|
||||
} else {
|
||||
ba = null;
|
||||
tmp = new byte[len];
|
||||
}
|
||||
din.readFully(tmp);
|
||||
synchronized (this) {
|
||||
if (raf == null)
|
||||
@@ -190,6 +214,8 @@ class PartialPiece implements Comparable {
|
||||
raf.seek(off);
|
||||
raf.write(tmp);
|
||||
}
|
||||
if (ba != null)
|
||||
_cache.release(ba, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +234,16 @@ class PartialPiece implements Comparable {
|
||||
out.write(bs, offset, len);
|
||||
} else {
|
||||
int read = 0;
|
||||
byte[] buf = new byte[Math.min(len, 16384)];
|
||||
int buflen = Math.min(len, BUFSIZE);
|
||||
ByteArray ba;
|
||||
byte[] buf;
|
||||
if (buflen == BUFSIZE) {
|
||||
ba = _cache.acquire();
|
||||
buf = ba.getData();
|
||||
} else {
|
||||
ba = null;
|
||||
buf = new byte[buflen];
|
||||
}
|
||||
synchronized (this) {
|
||||
if (raf == null)
|
||||
throw new IOException();
|
||||
@@ -220,6 +255,8 @@ class PartialPiece implements Comparable {
|
||||
out.write(buf, 0, rd);
|
||||
}
|
||||
}
|
||||
if (ba != null)
|
||||
_cache.release(ba, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,9 @@ public class Peer implements Comparable
|
||||
static final long OPTION_FAST = 0x0000000000000004l;
|
||||
static final long OPTION_DHT = 0x0000000000000001l;
|
||||
/** we use a different bit since the compact format is different */
|
||||
/* no, let's use an extension message
|
||||
static final long OPTION_I2P_DHT = 0x0000000040000000l;
|
||||
*/
|
||||
static final long OPTION_AZMP = 0x1000000000000000l;
|
||||
private long options;
|
||||
|
||||
@@ -269,15 +271,17 @@ public class Peer implements Comparable
|
||||
_log.debug("Peer supports extensions, sending reply message");
|
||||
int metasize = metainfo != null ? metainfo.getInfoBytes().length : -1;
|
||||
boolean pexAndMetadata = metainfo == null || !metainfo.isPrivate();
|
||||
out.sendExtension(0, ExtensionHandler.getHandshake(metasize, pexAndMetadata));
|
||||
boolean dht = util.getDHT() != null;
|
||||
out.sendExtension(0, ExtensionHandler.getHandshake(metasize, pexAndMetadata, dht));
|
||||
}
|
||||
|
||||
if ((options & OPTION_I2P_DHT) != 0 && util.getDHT() != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Peer supports DHT, sending PORT message");
|
||||
int port = util.getDHT().getPort();
|
||||
out.sendPort(port);
|
||||
}
|
||||
// Old DHT PORT message
|
||||
//if ((options & OPTION_I2P_DHT) != 0 && util.getDHT() != null) {
|
||||
// if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Peer supports DHT, sending PORT message");
|
||||
// int port = util.getDHT().getPort();
|
||||
// out.sendPort(port);
|
||||
//}
|
||||
|
||||
// Send our bitmap
|
||||
if (bitfield != null)
|
||||
|
||||
@@ -117,9 +117,8 @@ public class PeerAcceptor
|
||||
}
|
||||
} else {
|
||||
// multitorrent capable, so lets see what we can handle
|
||||
for (Iterator iter = coordinators.iterator(); iter.hasNext(); ) {
|
||||
PeerCoordinator cur = (PeerCoordinator)iter.next();
|
||||
|
||||
PeerCoordinator cur = coordinators.get(peerInfoHash);
|
||||
if (cur != null) {
|
||||
if (DataHelper.eq(cur.getInfoHash(), peerInfoHash)) {
|
||||
if (cur.needPeers())
|
||||
{
|
||||
|
||||
@@ -28,6 +28,8 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import org.klomp.snark.dht.DHT;
|
||||
|
||||
/**
|
||||
* TimerTask that checks for good/bad up/downloader. Works together
|
||||
* with the PeerCoordinator to select which Peers get (un)choked.
|
||||
@@ -74,6 +76,7 @@ class PeerCheckerTask implements Runnable
|
||||
List<Peer> removed = new ArrayList();
|
||||
int uploadLimit = coordinator.allowedUploaders();
|
||||
boolean overBWLimit = coordinator.overUpBWLimit();
|
||||
DHT dht = _util.getDHT();
|
||||
for (Peer peer : peerList) {
|
||||
|
||||
// Remove dying peers
|
||||
@@ -218,8 +221,8 @@ class PeerCheckerTask implements Runnable
|
||||
if (coordinator.getNeededLength() > 0 || !peer.isCompleted())
|
||||
peer.keepAlive();
|
||||
// announce them to local tracker (TrackerClient does this too)
|
||||
if (_util.getDHT() != null && (_runCount % 5) == 0) {
|
||||
_util.getDHT().announce(coordinator.getInfoHash(), peer.getPeerID().getDestHash());
|
||||
if (dht != null && (_runCount % 5) == 0) {
|
||||
dht.announce(coordinator.getInfoHash(), peer.getPeerID().getDestHash());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,8 +270,8 @@ class PeerCheckerTask implements Runnable
|
||||
}
|
||||
|
||||
// announce ourselves to local tracker (TrackerClient does this too)
|
||||
if (_util.getDHT() != null && (_runCount % 16) == 0) {
|
||||
_util.getDHT().announce(coordinator.getInfoHash());
|
||||
if (dht != null && (_runCount % 16) == 0) {
|
||||
dht.announce(coordinator.getInfoHash());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,6 +437,7 @@ class PeerConnectionOut implements Runnable
|
||||
*/
|
||||
void sendPiece(int piece, int begin, int length, DataLoader loader)
|
||||
{
|
||||
/****
|
||||
boolean sendNow = false;
|
||||
// are there any cases where we should?
|
||||
|
||||
@@ -447,6 +448,7 @@ class PeerConnectionOut implements Runnable
|
||||
sendPiece(piece, begin, length, bytes);
|
||||
return;
|
||||
}
|
||||
****/
|
||||
|
||||
// queue a fake message... set everything up,
|
||||
// except save the PeerState instead of the bytes.
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
@@ -160,7 +161,7 @@ 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
|
||||
timer = new CheckEvent(new PeerCheckerTask(_util, this));
|
||||
timer = new CheckEvent(_util.getContext(), new PeerCheckerTask(_util, this));
|
||||
timer.schedule((CHECK_PERIOD / 2) + _random.nextInt((int) CHECK_PERIOD));
|
||||
}
|
||||
|
||||
@@ -170,8 +171,8 @@ class PeerCoordinator implements PeerListener
|
||||
*/
|
||||
private static class CheckEvent extends SimpleTimer2.TimedEvent {
|
||||
private final PeerCheckerTask _task;
|
||||
public CheckEvent(PeerCheckerTask task) {
|
||||
super(SimpleTimer2.getInstance());
|
||||
public CheckEvent(I2PAppContext ctx, PeerCheckerTask task) {
|
||||
super(ctx.simpleTimer2());
|
||||
_task = task;
|
||||
}
|
||||
public void timeReached() {
|
||||
@@ -214,7 +215,7 @@ class PeerCoordinator implements PeerListener
|
||||
|
||||
public Storage getStorage() { return storage; }
|
||||
|
||||
// for web page detailed stats
|
||||
/** for web page detailed stats */
|
||||
public List<Peer> peerList()
|
||||
{
|
||||
return new ArrayList(peers);
|
||||
@@ -445,6 +446,12 @@ class PeerCoordinator implements PeerListener
|
||||
synchronized (downloaded_old) {
|
||||
Arrays.fill(downloaded_old, 0);
|
||||
}
|
||||
// failsafe
|
||||
synchronized(wantedPieces) {
|
||||
for (Piece pc : wantedPieces) {
|
||||
pc.clear();
|
||||
}
|
||||
}
|
||||
timer.schedule((CHECK_PERIOD / 2) + _random.nextInt((int) CHECK_PERIOD));
|
||||
}
|
||||
|
||||
@@ -648,10 +655,15 @@ class PeerCoordinator implements PeerListener
|
||||
if (listener != null)
|
||||
listener.peerChange(this, peer);
|
||||
|
||||
synchronized(wantedPieces)
|
||||
{
|
||||
return wantedPieces.contains(new Piece(piece));
|
||||
}
|
||||
synchronized(wantedPieces) {
|
||||
for (Piece pc : wantedPieces) {
|
||||
if (pc.getId() == piece) {
|
||||
pc.addPeer(peer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -663,20 +675,17 @@ class PeerCoordinator implements PeerListener
|
||||
if (listener != null)
|
||||
listener.peerChange(this, peer);
|
||||
|
||||
synchronized(wantedPieces)
|
||||
{
|
||||
Iterator<Piece> it = wantedPieces.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Piece p = it.next();
|
||||
boolean rv = false;
|
||||
synchronized(wantedPieces) {
|
||||
for (Piece p : wantedPieces) {
|
||||
int i = p.getId();
|
||||
if (bitfield.get(i)) {
|
||||
p.addPeer(peer);
|
||||
return true;
|
||||
rv = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -732,7 +741,19 @@ class PeerCoordinator implements PeerListener
|
||||
break;
|
||||
if (havePieces.get(p.getId()) && !p.isRequested())
|
||||
{
|
||||
piece = p;
|
||||
// never ever choose one that's in partialPieces, or we
|
||||
// will create a second one and leak
|
||||
boolean hasPartial = false;
|
||||
for (PartialPiece pp : partialPieces) {
|
||||
if (pp.getPiece() == p.getId()) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("wantPiece() skipping partial for " + peer + ": piece = " + pp);
|
||||
hasPartial = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasPartial)
|
||||
piece = p;
|
||||
}
|
||||
else if (p.isRequested())
|
||||
{
|
||||
@@ -747,8 +768,12 @@ class PeerCoordinator implements PeerListener
|
||||
// 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 (wantedSize > END_GAME_THRESHOLD)
|
||||
if (wantedSize > END_GAME_THRESHOLD) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Nothing to request, " + requested.size() + " being requested and " +
|
||||
wantedSize + " still wanted");
|
||||
return null; // nothing to request and not in end game
|
||||
}
|
||||
// let's not all get on the same piece
|
||||
// Even better would be to sort by number of requests
|
||||
if (record)
|
||||
@@ -784,7 +809,8 @@ class PeerCoordinator implements PeerListener
|
||||
}
|
||||
if (record) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(peer + " is now requesting: piece " + piece + " priority " + piece.getPriority());
|
||||
_log.info("Now requesting from " + peer + ": piece " + piece + " priority " + piece.getPriority() +
|
||||
" peers " + piece.getPeerCount() + '/' + peers.size());
|
||||
piece.setRequested(peer, true);
|
||||
}
|
||||
return piece;
|
||||
@@ -874,7 +900,7 @@ class PeerCoordinator implements PeerListener
|
||||
*
|
||||
* @throws RuntimeException on IOE getting the data
|
||||
*/
|
||||
public byte[] gotRequest(Peer peer, int piece, int off, int len)
|
||||
public ByteArray gotRequest(Peer peer, int piece, int off, int len)
|
||||
{
|
||||
if (halted)
|
||||
return null;
|
||||
@@ -890,8 +916,10 @@ class PeerCoordinator implements PeerListener
|
||||
snark.stopTorrent();
|
||||
String msg = "Error reading the storage (piece " + piece + ") for " + metainfo.getName() + ": " + ioe;
|
||||
_log.error(msg, ioe);
|
||||
SnarkManager.instance().addMessage(msg);
|
||||
SnarkManager.instance().addMessage("Fatal storage error: Stopping torrent " + metainfo.getName());
|
||||
if (listener != null) {
|
||||
listener.addMessage(msg);
|
||||
listener.addMessage("Fatal storage error: Stopping torrent " + metainfo.getName());
|
||||
}
|
||||
throw new RuntimeException(msg, ioe);
|
||||
}
|
||||
}
|
||||
@@ -927,11 +955,14 @@ class PeerCoordinator implements PeerListener
|
||||
*/
|
||||
public boolean gotPiece(Peer peer, PartialPiece pp)
|
||||
{
|
||||
if (metainfo == null || storage == null)
|
||||
if (metainfo == null || storage == null) {
|
||||
pp.release();
|
||||
return true;
|
||||
}
|
||||
int piece = pp.getPiece();
|
||||
if (halted) {
|
||||
_log.info("Got while-halted piece " + piece + "/" + metainfo.getPieces() +" from " + peer + " for " + metainfo.getName());
|
||||
pp.release();
|
||||
return true; // We don't actually care anymore.
|
||||
}
|
||||
|
||||
@@ -946,8 +977,10 @@ class PeerCoordinator implements PeerListener
|
||||
// Assume we got a good piece, we don't really care anymore.
|
||||
// Well, this could be caused by a change in priorities, so
|
||||
// only return true if we already have it, otherwise might as well keep it.
|
||||
if (storage.getBitField().get(piece))
|
||||
if (storage.getBitField().get(piece)) {
|
||||
pp.release();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
@@ -970,8 +1003,10 @@ class PeerCoordinator implements PeerListener
|
||||
snark.stopTorrent();
|
||||
String msg = "Error writing storage (piece " + piece + ") for " + metainfo.getName() + ": " + ioe;
|
||||
_log.error(msg, ioe);
|
||||
SnarkManager.instance().addMessage(msg);
|
||||
SnarkManager.instance().addMessage("Fatal storage error: Stopping torrent " + metainfo.getName());
|
||||
if (listener != null) {
|
||||
listener.addMessage(msg);
|
||||
listener.addMessage("Fatal storage error: Stopping torrent " + metainfo.getName());
|
||||
}
|
||||
throw new RuntimeException(msg, ioe);
|
||||
}
|
||||
wantedPieces.remove(p);
|
||||
@@ -1070,11 +1105,11 @@ class PeerCoordinator implements PeerListener
|
||||
/** Called when a peer is removed, to prevent it from being used in
|
||||
* rarest-first calculations.
|
||||
*/
|
||||
public void removePeerFromPieces(Peer peer) {
|
||||
private void removePeerFromPieces(Peer peer) {
|
||||
synchronized(wantedPieces) {
|
||||
for(Iterator<Piece> iter = wantedPieces.iterator(); iter.hasNext(); ) {
|
||||
Piece piece = iter.next();
|
||||
for (Piece piece : wantedPieces) {
|
||||
piece.removePeer(peer);
|
||||
piece.setRequested(peer, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1167,11 +1202,24 @@ class PeerCoordinator implements PeerListener
|
||||
for(Piece piece : wantedPieces) {
|
||||
if (piece.getId() == savedPiece) {
|
||||
if (peer.isCompleted() && piece.getPeerCount() > 1) {
|
||||
// Try to preserve rarest-first when we have only one seeder
|
||||
// by not preferring a partial piece that others have too
|
||||
// Try to preserve rarest-first
|
||||
// by not requesting a partial piece that non-seeders also have
|
||||
// from a seeder
|
||||
skipped = true;
|
||||
break;
|
||||
boolean nonSeeds = false;
|
||||
for (Peer pr : peers) {
|
||||
PeerState state = pr.state;
|
||||
if (state == null) continue;
|
||||
BitField bf = state.bitfield;
|
||||
if (bf == null) continue;
|
||||
if (bf.get(savedPiece) && !pr.isCompleted()) {
|
||||
nonSeeds = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nonSeeds) {
|
||||
skipped = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
iter.remove();
|
||||
piece.setRequested(peer, true);
|
||||
@@ -1246,6 +1294,7 @@ class PeerCoordinator implements PeerListener
|
||||
PartialPiece pp = iter.next();
|
||||
if (pp.getPiece() == piece) {
|
||||
iter.remove();
|
||||
pp.release();
|
||||
// there should be only one but keep going to be sure
|
||||
}
|
||||
}
|
||||
@@ -1287,6 +1336,7 @@ class PeerCoordinator implements PeerListener
|
||||
}
|
||||
} else if (id == ExtensionHandler.ID_HANDSHAKE) {
|
||||
sendPeers(peer);
|
||||
sendDHT(peer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1315,6 +1365,26 @@ class PeerCoordinator implements PeerListener
|
||||
} catch (InvalidBEncodingException ibee) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a DHT message to the peer, if we both support DHT.
|
||||
* @since DHT
|
||||
*/
|
||||
void sendDHT(Peer peer) {
|
||||
DHT dht = _util.getDHT();
|
||||
if (dht == null)
|
||||
return;
|
||||
Map<String, BEValue> handshake = peer.getHandshakeMap();
|
||||
if (handshake == null)
|
||||
return;
|
||||
BEValue bev = handshake.get("m");
|
||||
if (bev == null)
|
||||
return;
|
||||
try {
|
||||
if (bev.getMap().get(ExtensionHandler.TYPE_DHT) != null)
|
||||
ExtensionHandler.sendDHT(peer, dht.getPort(), dht.getRPort());
|
||||
} catch (InvalidBEncodingException ibee) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the storage after transition out of magnet mode
|
||||
* Snark calls this after we call gotMetaInfo()
|
||||
@@ -1332,11 +1402,13 @@ class PeerCoordinator implements PeerListener
|
||||
/**
|
||||
* PeerListener callback
|
||||
* Tell the DHT to ping it, this will get back the node info
|
||||
* @param rport must be port + 1
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public void gotPort(Peer peer, int port) {
|
||||
public void gotPort(Peer peer, int port, int rport) {
|
||||
DHT dht = _util.getDHT();
|
||||
if (dht != null)
|
||||
if (dht != null &&
|
||||
port > 0 && port < 65535 && rport == port + 1)
|
||||
dht.ping(peer.getDestination(), port);
|
||||
}
|
||||
|
||||
@@ -1408,5 +1480,13 @@ class PeerCoordinator implements PeerListener
|
||||
return listener.overUpBWLimit(total * 1000 / CHECK_PERIOD);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public I2PSnarkUtil getUtil() {
|
||||
return _util;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package org.klomp.snark;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.crypto.SHA1Hash;
|
||||
|
||||
/**
|
||||
* Hmm, any guesses as to what this is? Used by the multitorrent functionality
|
||||
@@ -12,26 +13,28 @@ import java.util.Set;
|
||||
* from it there too)
|
||||
*/
|
||||
public class PeerCoordinatorSet {
|
||||
private final Set _coordinators;
|
||||
private final Map<SHA1Hash, PeerCoordinator> _coordinators;
|
||||
|
||||
public PeerCoordinatorSet() {
|
||||
_coordinators = new HashSet();
|
||||
_coordinators = new ConcurrentHashMap();
|
||||
}
|
||||
|
||||
public Iterator iterator() {
|
||||
synchronized (_coordinators) {
|
||||
return new ArrayList(_coordinators).iterator();
|
||||
}
|
||||
|
||||
public Iterator<PeerCoordinator> iterator() {
|
||||
return _coordinators.values().iterator();
|
||||
}
|
||||
|
||||
|
||||
public void add(PeerCoordinator coordinator) {
|
||||
synchronized (_coordinators) {
|
||||
_coordinators.add(coordinator);
|
||||
}
|
||||
_coordinators.put(new SHA1Hash(coordinator.getInfoHash()), coordinator);
|
||||
}
|
||||
|
||||
public void remove(PeerCoordinator coordinator) {
|
||||
synchronized (_coordinators) {
|
||||
_coordinators.remove(coordinator);
|
||||
}
|
||||
_coordinators.remove(new SHA1Hash(coordinator.getInfoHash()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public PeerCoordinator get(byte[] infoHash) {
|
||||
return _coordinators.get(new SHA1Hash(infoHash));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ public class PeerID implements Comparable
|
||||
/** whether we have tried to get the dest from the hash - only do once */
|
||||
private boolean triedDestLookup;
|
||||
private final int hash;
|
||||
private final I2PSnarkUtil util;
|
||||
|
||||
public PeerID(byte[] id, Destination address)
|
||||
{
|
||||
@@ -60,6 +61,7 @@ public class PeerID implements Comparable
|
||||
this.port = 6881;
|
||||
this.destHash = address.calculateHash().getData();
|
||||
hash = calculateHash();
|
||||
util = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,13 +95,15 @@ public class PeerID implements Comparable
|
||||
port = 6881;
|
||||
this.destHash = address.calculateHash().getData();
|
||||
hash = calculateHash();
|
||||
util = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PeerID from a destHash
|
||||
* @param util for eventual destination lookup
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public PeerID(byte[] dest_hash) throws InvalidBEncodingException
|
||||
public PeerID(byte[] dest_hash, I2PSnarkUtil util) throws InvalidBEncodingException
|
||||
{
|
||||
// id and address remain null
|
||||
port = 6881;
|
||||
@@ -107,6 +111,7 @@ public class PeerID implements Comparable
|
||||
throw new InvalidBEncodingException("bad hash length");
|
||||
destHash = dest_hash;
|
||||
hash = DataHelper.hashCode(dest_hash);
|
||||
this.util = util;
|
||||
}
|
||||
|
||||
public byte[] getID()
|
||||
@@ -131,7 +136,7 @@ public class PeerID implements Comparable
|
||||
{
|
||||
if (address == null && destHash != null && !triedDestLookup) {
|
||||
String b32 = Base32.encode(destHash) + ".b32.i2p";
|
||||
address = I2PAppContext.getGlobalContext().namingService().lookup(b32);
|
||||
address = util.getDestination(b32);
|
||||
triedDestLookup = true;
|
||||
}
|
||||
return address;
|
||||
|
||||
@@ -22,6 +22,8 @@ package org.klomp.snark;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.data.ByteArray;
|
||||
|
||||
/**
|
||||
* Listener for Peer events.
|
||||
*/
|
||||
@@ -114,7 +116,7 @@ interface PeerListener
|
||||
* @return a byte array containing the piece or null when the piece
|
||||
* is not available (which is a protocol error).
|
||||
*/
|
||||
byte[] gotRequest(Peer peer, int piece, int off, int len);
|
||||
ByteArray gotRequest(Peer peer, int piece, int off, int len);
|
||||
|
||||
/**
|
||||
* Called when a (partial) piece has been downloaded from the peer.
|
||||
@@ -190,13 +192,14 @@ interface PeerListener
|
||||
void gotExtension(Peer peer, int id, byte[] bs);
|
||||
|
||||
/**
|
||||
* Called when a port message is received.
|
||||
* Called when a DHT port message is received.
|
||||
*
|
||||
* @param peer the Peer that got the message.
|
||||
* @param port the port
|
||||
* @param port the query port
|
||||
* @param rport the response port
|
||||
* @since 0.8.4
|
||||
*/
|
||||
void gotPort(Peer peer, int port);
|
||||
void gotPort(Peer peer, int port, int rport);
|
||||
|
||||
/**
|
||||
* Called when peers are received via PEX
|
||||
@@ -206,4 +209,10 @@ interface PeerListener
|
||||
* @since 0.8.4
|
||||
*/
|
||||
void gotPeers(Peer peer, List<PeerID> pIDList);
|
||||
|
||||
/**
|
||||
* Convenience
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public I2PSnarkUtil getUtil();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
class PeerState implements DataLoader
|
||||
@@ -245,8 +246,8 @@ class PeerState implements DataLoader
|
||||
* @return bytes or null for errors
|
||||
* @since 0.8.2
|
||||
*/
|
||||
public byte[] loadData(int piece, int begin, int length) {
|
||||
byte[] pieceBytes = listener.gotRequest(peer, piece, begin, length);
|
||||
public ByteArray loadData(int piece, int begin, int length) {
|
||||
ByteArray pieceBytes = listener.gotRequest(peer, piece, begin, length);
|
||||
if (pieceBytes == null)
|
||||
{
|
||||
// XXX - Protocol error-> diconnect?
|
||||
@@ -256,7 +257,7 @@ class PeerState implements DataLoader
|
||||
}
|
||||
|
||||
// More sanity checks
|
||||
if (length != pieceBytes.length)
|
||||
if (length != pieceBytes.getData().length)
|
||||
{
|
||||
// XXX - Protocol error-> disconnect?
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
@@ -526,10 +527,14 @@ class PeerState implements DataLoader
|
||||
setInteresting(true);
|
||||
}
|
||||
|
||||
/** @since 0.8.4 */
|
||||
/**
|
||||
* Unused
|
||||
* @since 0.8.4
|
||||
*/
|
||||
void portMessage(int port)
|
||||
{
|
||||
listener.gotPort(peer, port);
|
||||
// for compatibility with old DHT PORT message
|
||||
listener.gotPort(peer, port, port + 1);
|
||||
}
|
||||
|
||||
void unknownMessage(int type, byte[] bs)
|
||||
@@ -587,6 +592,7 @@ class PeerState implements DataLoader
|
||||
// Send cancel even when we are choked to make sure that it is
|
||||
// really never ever send.
|
||||
out.sendCancel(req);
|
||||
req.getPartialPiece().release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -677,6 +683,7 @@ class PeerState implements DataLoader
|
||||
_log.debug(peer + " addRequest() we are choked, delaying requestNextPiece()");
|
||||
return;
|
||||
}
|
||||
// huh? rv unused
|
||||
more_pieces = requestNextPiece();
|
||||
} else if (more_pieces) // We want something
|
||||
{
|
||||
@@ -706,6 +713,8 @@ class PeerState implements DataLoader
|
||||
}
|
||||
|
||||
// failsafe
|
||||
// However this is bad as it thrashes the peer when we change our mind
|
||||
// Ticket 691 cause here?
|
||||
if (interesting && lastRequest == null && outstandingRequests.isEmpty())
|
||||
setInteresting(false);
|
||||
|
||||
@@ -733,6 +742,10 @@ class PeerState implements DataLoader
|
||||
out.sendRequest(r);
|
||||
lastRequest = r;
|
||||
return true;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Got dup from coord: " + pp);
|
||||
pp.release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -779,6 +792,8 @@ class PeerState implements DataLoader
|
||||
}
|
||||
|
||||
// failsafe
|
||||
// However this is bad as it thrashes the peer when we change our mind
|
||||
// Ticket 691 cause here?
|
||||
if (outstandingRequests.isEmpty())
|
||||
lastRequest = null;
|
||||
|
||||
|
||||
@@ -12,13 +12,13 @@ class Piece implements Comparable {
|
||||
private final int id;
|
||||
private final Set<PeerID> peers;
|
||||
/** @since 0.8.3 */
|
||||
private Set<PeerID> requests;
|
||||
private volatile Set<PeerID> requests;
|
||||
/** @since 0.8.1 */
|
||||
private int priority;
|
||||
|
||||
public Piece(int id) {
|
||||
this.id = id;
|
||||
this.peers = new HashSet(I2PSnarkUtil.MAX_CONNECTIONS);
|
||||
this.peers = new HashSet(I2PSnarkUtil.MAX_CONNECTIONS / 2);
|
||||
// defer creating requests to save memory
|
||||
}
|
||||
|
||||
@@ -54,7 +54,10 @@ class Piece implements Comparable {
|
||||
/** caller must synchronize */
|
||||
public boolean addPeer(Peer peer) { return this.peers.add(peer.getPeerID()); }
|
||||
|
||||
/** caller must synchronize */
|
||||
/**
|
||||
* Caller must synchronize.
|
||||
* @return true if removed
|
||||
*/
|
||||
public boolean removePeer(Peer peer) { return this.peers.remove(peer.getPeerID()); }
|
||||
|
||||
/**
|
||||
@@ -104,6 +107,17 @@ class Piece implements Comparable {
|
||||
public int getRequestCount() {
|
||||
return this.requests == null ? 0 : this.requests.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all knowledge of peers
|
||||
* Caller must synchronize
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public void clear() {
|
||||
peers.clear();
|
||||
if (requests != null)
|
||||
requests.clear();
|
||||
}
|
||||
|
||||
/** @return default 0 @since 0.8.1 */
|
||||
public int getPriority() { return this.priority; }
|
||||
|
||||
@@ -35,6 +35,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Main Snark program startup class.
|
||||
@@ -47,29 +48,6 @@ public class Snark
|
||||
private final static int MIN_PORT = 6881;
|
||||
private final static int MAX_PORT = 6889;
|
||||
|
||||
// Error messages (non-fatal)
|
||||
public final static int ERROR = 1;
|
||||
|
||||
// Warning messages
|
||||
public final static int WARNING = 2;
|
||||
|
||||
// Notices (peer level)
|
||||
public final static int NOTICE = 3;
|
||||
|
||||
// Info messages (protocol policy level)
|
||||
public final static int INFO = 4;
|
||||
|
||||
// Debug info (protocol level)
|
||||
public final static int DEBUG = 5;
|
||||
|
||||
// Very low level stuff (network level)
|
||||
public final static int ALL = 6;
|
||||
|
||||
/**
|
||||
* What level of debug info to show.
|
||||
*/
|
||||
//public static int debug = NOTICE;
|
||||
|
||||
// Whether or not to ask the user for commands while sharing
|
||||
//private static boolean command_interpreter = true;
|
||||
|
||||
@@ -252,9 +230,10 @@ public class Snark
|
||||
private volatile boolean stopped;
|
||||
private volatile boolean starting;
|
||||
private byte[] id;
|
||||
private byte[] infoHash;
|
||||
private final byte[] infoHash;
|
||||
private String additionalTrackerURL;
|
||||
private final I2PSnarkUtil _util;
|
||||
private final Log _log;
|
||||
private final PeerCoordinatorSet _peerCoordinatorSet;
|
||||
private String trackerProblems;
|
||||
private int trackerSeenPeers;
|
||||
@@ -308,6 +287,7 @@ public class Snark
|
||||
|
||||
completeListener = complistener;
|
||||
_util = util;
|
||||
_log = util.getContext().logManager().getLog(Snark.class);
|
||||
_peerCoordinatorSet = peerCoordinatorSet;
|
||||
acceptor = connectionAcceptor;
|
||||
|
||||
@@ -318,7 +298,8 @@ public class Snark
|
||||
activity = "Network setup";
|
||||
|
||||
id = generateID();
|
||||
debug("My peer id: " + PeerID.idencode(id), Snark.INFO);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("My peer id: " + PeerID.idencode(id));
|
||||
|
||||
/*
|
||||
* Don't start a tunnel if the torrent isn't going to be started.
|
||||
@@ -340,6 +321,7 @@ public class Snark
|
||||
meta = null;
|
||||
File f = null;
|
||||
InputStream in = null;
|
||||
byte[] x_infoHash = null;
|
||||
try
|
||||
{
|
||||
f = new File(torrent);
|
||||
@@ -362,7 +344,7 @@ public class Snark
|
||||
throw new IOException("not found");
|
||||
}
|
||||
meta = new MetaInfo(in);
|
||||
infoHash = meta.getInfoHash();
|
||||
x_infoHash = meta.getInfoHash();
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
@@ -403,7 +385,9 @@ public class Snark
|
||||
try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
debug(meta.toString(), INFO);
|
||||
infoHash = x_infoHash; // final
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(meta.toString());
|
||||
|
||||
// When the metainfo torrent was created from an existing file/dir
|
||||
// it already exists.
|
||||
@@ -464,6 +448,7 @@ public class Snark
|
||||
{
|
||||
completeListener = complistener;
|
||||
_util = util;
|
||||
_log = util.getContext().logManager().getLog(Snark.class);
|
||||
_peerCoordinatorSet = peerCoordinatorSet;
|
||||
acceptor = connectionAcceptor;
|
||||
this.torrent = torrent;
|
||||
@@ -531,9 +516,11 @@ public class Snark
|
||||
fatal("Unable to listen for I2P connections");
|
||||
else {
|
||||
Destination d = serversocket.getManager().getSession().getMyDestination();
|
||||
debug("Listening on I2P destination " + d.toBase64() + " / " + d.calculateHash().toBase64(), NOTICE);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Listening on I2P destination " + d.toBase64() + " / " + d.calculateHash().toBase64());
|
||||
}
|
||||
debug("Starting PeerCoordinator, ConnectionAcceptor, and TrackerClient", NOTICE);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Starting PeerCoordinator, ConnectionAcceptor, and TrackerClient");
|
||||
activity = "Collecting pieces";
|
||||
coordinator = new PeerCoordinator(_util, id, infoHash, meta, storage, this, this);
|
||||
if (_peerCoordinatorSet != null) {
|
||||
@@ -573,7 +560,8 @@ public class Snark
|
||||
}
|
||||
trackerclient.start();
|
||||
} else {
|
||||
debug("NOT starting TrackerClient???", NOTICE);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("NOT starting TrackerClient???");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,6 +688,22 @@ public class Snark
|
||||
starting = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* File checking in progress.
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public boolean isChecking() {
|
||||
return storage != null && storage.isChecking();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disk allocation (ballooning) in progress.
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public boolean isAllocating() {
|
||||
return storage != null && storage.isAllocating();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.8.4
|
||||
*/
|
||||
@@ -1017,22 +1021,13 @@ public class Snark
|
||||
private static void usage()
|
||||
{
|
||||
System.out.println
|
||||
("Usage: snark [--debug [level]] [--no-commands] [--port <port>]");
|
||||
("Usage: snark [--no-commands] [--port <port>]");
|
||||
System.out.println
|
||||
(" [--eepproxy hostname portnum]");
|
||||
System.out.println
|
||||
(" [--i2cp routerHost routerPort ['name=val name=val name=val']]");
|
||||
System.out.println
|
||||
(" (<url>|<file>)");
|
||||
System.out.println
|
||||
(" --debug\tShows some extra info and stacktraces");
|
||||
System.out.println
|
||||
(" level\tHow much debug details to show");
|
||||
System.out.println
|
||||
(" \t(defaults to "
|
||||
+ NOTICE + ", with --debug to "
|
||||
+ INFO + ", highest level is "
|
||||
+ ALL + ").");
|
||||
System.out.println
|
||||
(" --no-commands\tDon't read interactive commands or show usage info.");
|
||||
System.out.println
|
||||
@@ -1071,7 +1066,7 @@ public class Snark
|
||||
*/
|
||||
private void fatal(String s, Throwable t)
|
||||
{
|
||||
_util.debug(s, ERROR, t);
|
||||
_log.error(s, t);
|
||||
//System.err.println("snark: " + s + ((t == null) ? "" : (": " + t)));
|
||||
//if (debug >= INFO && t != null)
|
||||
// t.printStackTrace();
|
||||
@@ -1083,14 +1078,6 @@ public class Snark
|
||||
throw new RuntimeException(s, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show debug info if debug is true.
|
||||
*/
|
||||
private void debug(String s, int level)
|
||||
{
|
||||
_util.debug(s, level, null);
|
||||
}
|
||||
|
||||
/** CoordinatorListener - this does nothing */
|
||||
public void peerChange(PeerCoordinator coordinator, Peer peer)
|
||||
{
|
||||
@@ -1168,9 +1155,10 @@ public class Snark
|
||||
// + " pieces: ");
|
||||
checking = true;
|
||||
}
|
||||
if (!checking)
|
||||
debug("Got " + (checked ? "" : "BAD ") + "piece: " + num,
|
||||
Snark.INFO);
|
||||
if (!checking) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Got " + (checked ? "" : "BAD ") + "piece: " + num);
|
||||
}
|
||||
}
|
||||
|
||||
public void storageAllChecked(Storage storage)
|
||||
@@ -1186,7 +1174,8 @@ public class Snark
|
||||
|
||||
public void storageCompleted(Storage storage)
|
||||
{
|
||||
debug("Completely received " + torrent, Snark.INFO);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Completely received " + torrent);
|
||||
//storage.close();
|
||||
//System.out.println("Completely received: " + torrent);
|
||||
if (completeListener != null)
|
||||
@@ -1206,6 +1195,15 @@ public class Snark
|
||||
//System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* StorageListener and CoordinatorListener callback
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public void addMessage(String message) {
|
||||
if (completeListener != null)
|
||||
completeListener.addMessage(this, message);
|
||||
}
|
||||
|
||||
public interface CompleteListener {
|
||||
public void torrentComplete(Snark snark);
|
||||
public void updateStatus(Snark snark);
|
||||
@@ -1225,6 +1223,11 @@ public class Snark
|
||||
*/
|
||||
public void fatal(Snark snark, String error);
|
||||
|
||||
/**
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public void addMessage(Snark snark, String message);
|
||||
|
||||
// not really listeners but the easiest way to get back to an optional SnarkManager
|
||||
public long getSavedTorrentTime(Snark snark);
|
||||
public BitField getSavedTorrentBitField(Snark snark);
|
||||
@@ -1239,8 +1242,8 @@ public class Snark
|
||||
if (_peerCoordinatorSet == null || uploaders <= 0)
|
||||
return false;
|
||||
int totalUploaders = 0;
|
||||
for (Iterator iter = _peerCoordinatorSet.iterator(); iter.hasNext(); ) {
|
||||
PeerCoordinator c = (PeerCoordinator)iter.next();
|
||||
for (Iterator<PeerCoordinator> iter = _peerCoordinatorSet.iterator(); iter.hasNext(); ) {
|
||||
PeerCoordinator c = iter.next();
|
||||
if (!c.halted())
|
||||
totalUploaders += c.uploaders;
|
||||
}
|
||||
@@ -1253,13 +1256,14 @@ public class Snark
|
||||
if (_peerCoordinatorSet == null)
|
||||
return false;
|
||||
long total = 0;
|
||||
for (Iterator iter = _peerCoordinatorSet.iterator(); iter.hasNext(); ) {
|
||||
PeerCoordinator c = (PeerCoordinator)iter.next();
|
||||
for (Iterator<PeerCoordinator> iter = _peerCoordinatorSet.iterator(); iter.hasNext(); ) {
|
||||
PeerCoordinator c = iter.next();
|
||||
if (!c.halted())
|
||||
total += c.getCurrentUploadRate();
|
||||
}
|
||||
long limit = 1024l * _util.getMaxUpBW();
|
||||
debug("Total up bw: " + total + " Limit: " + limit, Snark.NOTICE);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Total up bw: " + total + " Limit: " + limit);
|
||||
return total > limit;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
|
||||
import org.klomp.snark.dht.DHT;
|
||||
|
||||
/**
|
||||
* Manage multiple snarks
|
||||
*/
|
||||
@@ -85,11 +87,14 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
//public static final String DEFAULT_LINK_PREFIX = "file:///";
|
||||
public static final String PROP_STARTUP_DELAY = "i2psnark.startupDelay";
|
||||
public static final String PROP_REFRESH_DELAY = "i2psnark.refreshSeconds";
|
||||
public static final String RC_PROP_THEME = "routerconsole.theme";
|
||||
public static final String RC_PROP_UNIVERSAL_THEMING = "routerconsole.universal.theme";
|
||||
public static final String PROP_THEME = "i2psnark.theme";
|
||||
public static final String DEFAULT_THEME = "ubergine";
|
||||
private static final String PROP_USE_OPENTRACKERS = "i2psnark.useOpentrackers";
|
||||
public static final String PROP_OPENTRACKERS = "i2psnark.opentrackers";
|
||||
public static final String PROP_PRIVATETRACKERS = "i2psnark.privatetrackers";
|
||||
private static final String PROP_USE_DHT = "i2psnark.enableDHT";
|
||||
|
||||
public static final int MIN_UP_BW = 2;
|
||||
public static final int DEFAULT_MAX_UP_BW = 10;
|
||||
@@ -120,15 +125,11 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
/** comma delimited list of name=announceURL=baseURL for the trackers to be displayed */
|
||||
public static final String PROP_TRACKERS = "i2psnark.trackers";
|
||||
|
||||
private static final SnarkManager _instance = new SnarkManager();
|
||||
|
||||
public static SnarkManager instance() { return _instance; }
|
||||
|
||||
private SnarkManager() {
|
||||
public SnarkManager(I2PAppContext ctx) {
|
||||
_snarks = new ConcurrentHashMap();
|
||||
_magnets = new ConcurrentHashSet();
|
||||
_addSnarkLock = new Object();
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_context = ctx;
|
||||
_log = _context.logManager().getLog(SnarkManager.class);
|
||||
_messages = new LinkedBlockingQueue();
|
||||
_util = new I2PSnarkUtil(_context);
|
||||
@@ -199,11 +200,11 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
* @since 0.8.9
|
||||
*/
|
||||
public boolean areFilesPublic() {
|
||||
return Boolean.valueOf(_config.getProperty(PROP_FILES_PUBLIC)).booleanValue();
|
||||
return Boolean.parseBoolean(_config.getProperty(PROP_FILES_PUBLIC));
|
||||
}
|
||||
|
||||
public boolean shouldAutoStart() {
|
||||
return Boolean.valueOf(_config.getProperty(PROP_AUTO_START, DEFAULT_AUTO_START)).booleanValue();
|
||||
return Boolean.parseBoolean(_config.getProperty(PROP_AUTO_START, DEFAULT_AUTO_START));
|
||||
}
|
||||
|
||||
/****
|
||||
@@ -288,6 +289,9 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
_config.setProperty(PROP_STARTUP_DELAY, Integer.toString(DEFAULT_STARTUP_DELAY));
|
||||
if (!_config.containsKey(PROP_THEME))
|
||||
_config.setProperty(PROP_THEME, DEFAULT_THEME);
|
||||
// no, so we can switch default to true later
|
||||
//if (!_config.containsKey(PROP_USE_DHT))
|
||||
// _config.setProperty(PROP_USE_DHT, Boolean.toString(I2PSnarkUtil.DEFAULT_USE_DHT));
|
||||
updateConfig();
|
||||
}
|
||||
/**
|
||||
@@ -296,6 +300,26 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
*/
|
||||
public String getTheme() {
|
||||
String theme = _config.getProperty(PROP_THEME);
|
||||
boolean universalTheming = _context.getBooleanProperty(RC_PROP_UNIVERSAL_THEMING);
|
||||
if (universalTheming) {
|
||||
// Fetch routerconsole theme (or use our default if it doesn't exist)
|
||||
theme = _context.getProperty(RC_PROP_THEME, DEFAULT_THEME);
|
||||
// Ensure that theme exists
|
||||
String[] themes = getThemes();
|
||||
boolean themeExists = false;
|
||||
for (int i = 0; i < themes.length; i++) {
|
||||
if (themes[i].equals(theme))
|
||||
themeExists = true;
|
||||
}
|
||||
if (!themeExists) {
|
||||
// Since the default is not "light", explicitly check if universal theme is "classic"
|
||||
if (theme.equals("classic"))
|
||||
theme = "light";
|
||||
else
|
||||
theme = DEFAULT_THEME;
|
||||
_config.setProperty(PROP_THEME, DEFAULT_THEME);
|
||||
}
|
||||
}
|
||||
return theme;
|
||||
}
|
||||
|
||||
@@ -360,8 +384,11 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
if (ot != null)
|
||||
_util.setOpenTrackers(getOpenTrackers());
|
||||
String useOT = _config.getProperty(PROP_USE_OPENTRACKERS);
|
||||
boolean bOT = useOT == null || Boolean.valueOf(useOT).booleanValue();
|
||||
boolean bOT = useOT == null || Boolean.parseBoolean(useOT);
|
||||
_util.setUseOpenTrackers(bOT);
|
||||
// careful, so we can switch default to true later
|
||||
_util.setUseDHT(Boolean.parseBoolean(_config.getProperty(PROP_USE_DHT,
|
||||
Boolean.toString(I2PSnarkUtil.DEFAULT_USE_DHT))));
|
||||
getDataDir().mkdirs();
|
||||
initTrackerMap();
|
||||
}
|
||||
@@ -380,7 +407,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
public void updateConfig(String dataDir, boolean filesPublic, boolean autoStart, String refreshDelay,
|
||||
String startDelay, String seedPct, String eepHost,
|
||||
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
|
||||
String upLimit, String upBW, boolean useOpenTrackers, String theme) {
|
||||
String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme) {
|
||||
boolean changed = false;
|
||||
//if (eepHost != null) {
|
||||
// // unused, we use socket eepget
|
||||
@@ -564,6 +591,17 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
_util.setUseOpenTrackers(useOpenTrackers);
|
||||
changed = true;
|
||||
}
|
||||
if (_util.shouldUseDHT() != useDHT) {
|
||||
_config.setProperty(PROP_USE_DHT, Boolean.toString(useDHT));
|
||||
if (useDHT)
|
||||
addMessage(_("Enabled DHT."));
|
||||
else
|
||||
addMessage(_("Disabled DHT."));
|
||||
if (_util.connected())
|
||||
addMessage(_("DHT change requires tunnel shutdown and reopen"));
|
||||
_util.setUseDHT(useDHT);
|
||||
changed = true;
|
||||
}
|
||||
if (theme != null) {
|
||||
if(!theme.equals(_config.getProperty(PROP_THEME))) {
|
||||
_config.setProperty(PROP_THEME, theme);
|
||||
@@ -669,7 +707,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
public Properties getConfig() { return _config; }
|
||||
|
||||
/** hardcoded for sanity. perhaps this should be customizable, for people who increase their ulimit, etc. */
|
||||
private static final int MAX_FILES_PER_TORRENT = 512;
|
||||
public static final int MAX_FILES_PER_TORRENT = 512;
|
||||
|
||||
/**
|
||||
* Set of canonical .torrent filenames that we are dealing with.
|
||||
@@ -787,14 +825,14 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
if (!TrackerClient.isValidAnnounce(info.getAnnounce())) {
|
||||
if (info.isPrivate()) {
|
||||
addMessage(_("ERROR - No I2P trackers in private torrent \"{0}\"", info.getName()));
|
||||
} else if (_util.shouldUseOpenTrackers() && _util.getOpenTrackers() != null) {
|
||||
//addMessage(_("Warning - No I2P trackers in \"{0}\", will announce to I2P open trackers and DHT only.", info.getName()));
|
||||
addMessage(_("Warning - No I2P trackers in \"{0}\", will announce to I2P open trackers only.", info.getName()));
|
||||
//} else if (_util.getDHT() != null) {
|
||||
// addMessage(_("Warning - No I2P trackers in \"{0}\", and open trackers are disabled, will announce to DHT only.", info.getName()));
|
||||
} else if (!_util.getOpenTrackers().isEmpty()) {
|
||||
addMessage(_("Warning - No I2P trackers in \"{0}\", will announce to I2P open trackers and DHT only.", info.getName()));
|
||||
//addMessage(_("Warning - No I2P trackers in \"{0}\", will announce to I2P open trackers only.", info.getName()));
|
||||
} else if (_util.shouldUseDHT()) {
|
||||
addMessage(_("Warning - No I2P trackers in \"{0}\", and open trackers are disabled, will announce to DHT only.", info.getName()));
|
||||
} else {
|
||||
//addMessage(_("Warning - No I2P trackers in \"{0}\", and DHT and open trackers are disabled, you should enable open trackers or DHT before starting the torrent.", info.getName()));
|
||||
addMessage(_("Warning - No I2P Trackers found in \"{0}\". Make sure Open Tracker is enabled before starting this torrent.", info.getName()));
|
||||
addMessage(_("Warning - No I2P trackers in \"{0}\", and DHT and open trackers are disabled, you should enable open trackers or DHT before starting the torrent.", info.getName()));
|
||||
//addMessage(_("Warning - No I2P Trackers found in \"{0}\". Make sure Open Tracker is enabled before starting this torrent.", info.getName()));
|
||||
dontAutoStart = true;
|
||||
}
|
||||
}
|
||||
@@ -870,10 +908,13 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
if (shouldAutoStart()) {
|
||||
torrent.startTorrent();
|
||||
addMessage(_("Fetching {0}", name));
|
||||
boolean haveSavedPeers = false;
|
||||
if ((_util.connected()) && !haveSavedPeers) {
|
||||
addMessage(_("We have no saved peers and no other torrents are running. " +
|
||||
"Fetch of {0} will not succeed until you start another torrent.", name));
|
||||
DHT dht = _util.getDHT();
|
||||
boolean shouldWarn = _util.connected() &&
|
||||
_util.getOpenTrackers().isEmpty() &&
|
||||
((!_util.shouldUseDHT()) || dht == null || dht.size() <= 0);
|
||||
if (shouldWarn) {
|
||||
addMessage(_("Open trackers are disabled and we have no DHT peers. " +
|
||||
"Fetch of {0} may not succeed until you start another torrent, enable open trackers, or enable DHT.", name));
|
||||
}
|
||||
} else {
|
||||
addMessage(_("Adding {0}", name));
|
||||
@@ -1329,6 +1370,8 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
} catch (Exception e) {
|
||||
_log.error("Error in the DirectoryMonitor", e);
|
||||
}
|
||||
if (!_snarks.isEmpty())
|
||||
addMessage(_("Up bandwidth limit is {0} KBps", _util.getMaxUpBW()));
|
||||
}
|
||||
try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
|
||||
}
|
||||
@@ -1420,6 +1463,14 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
addMessage(_("Error on torrent {0}", snark.getName()) + ": " + error);
|
||||
}
|
||||
|
||||
/**
|
||||
* A Snark.CompleteListener method.
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public void addMessage(Snark snark, String message) {
|
||||
addMessage(message);
|
||||
}
|
||||
|
||||
// End Snark.CompleteListeners
|
||||
|
||||
/**
|
||||
@@ -1435,7 +1486,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
byte[] ih = Base64.decode(b64);
|
||||
// ignore value - TODO put tracker URL in value
|
||||
if (ih != null && ih.length == 20)
|
||||
addMagnet("* " + _("Magnet") + ' ' + I2PSnarkUtil.toHex(ih), ih, null, false);
|
||||
addMagnet(_("Magnet") + ' ' + I2PSnarkUtil.toHex(ih), ih, null, false);
|
||||
// else remove from config?
|
||||
}
|
||||
}
|
||||
@@ -1708,9 +1759,12 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
}
|
||||
if (_util.connected()) {
|
||||
if (count > 0) {
|
||||
DHT dht = _util.getDHT();
|
||||
if (dht != null)
|
||||
dht.stop();
|
||||
// Schedule this even for final shutdown, as there's a chance
|
||||
// that it's just this webapp that is stopping.
|
||||
SimpleScheduler.getInstance().addEvent(new Disconnector(), 60*1000);
|
||||
_context.simpleScheduler().addEvent(new Disconnector(), 60*1000);
|
||||
addMessage(_("Closing I2P tunnel after notifying trackers."));
|
||||
if (finalShutdown) {
|
||||
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
|
||||
|
||||
@@ -32,16 +32,21 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.i2p.crypto.SHA1;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFile;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
* Maintains pieces on disk. Can be used to store and retrieve pieces.
|
||||
*/
|
||||
public class Storage
|
||||
{
|
||||
private MetaInfo metainfo;
|
||||
private final MetaInfo metainfo;
|
||||
private long[] lengths;
|
||||
private RandomAccessFile[] rafs;
|
||||
private String[] names;
|
||||
@@ -55,6 +60,7 @@ public class Storage
|
||||
|
||||
private final StorageListener listener;
|
||||
private final I2PSnarkUtil _util;
|
||||
private final Log _log;
|
||||
|
||||
private /* FIXME final FIXME */ BitField bitfield; // BitField to represent the pieces
|
||||
private int needed; // Number of pieces needed
|
||||
@@ -64,6 +70,8 @@ public class Storage
|
||||
private final int pieces;
|
||||
private final long total_length;
|
||||
private boolean changed;
|
||||
private volatile boolean _isChecking;
|
||||
private final AtomicInteger _allocateCount = new AtomicInteger();
|
||||
|
||||
/** The default piece size. */
|
||||
private static final int MIN_PIECE_SIZE = 256*1024;
|
||||
@@ -75,22 +83,24 @@ public class Storage
|
||||
|
||||
private static final Map<String, String> _filterNameCache = new ConcurrentHashMap();
|
||||
|
||||
private static final boolean _isWindows = System.getProperty("os.name").startsWith("Win");
|
||||
private static final boolean _isWindows = SystemVersion.isWindows();
|
||||
|
||||
private static final int BUFSIZE = PeerState.PARTSIZE;
|
||||
private static final ByteCache _cache = ByteCache.getInstance(16, BUFSIZE);
|
||||
|
||||
/**
|
||||
* Creates a new storage based on the supplied MetaInfo. This will
|
||||
* try to create and/or check all needed files in the MetaInfo.
|
||||
*
|
||||
* @exception IOException when creating and/or checking files fails.
|
||||
* Does not check storage. Caller MUST call check()
|
||||
*/
|
||||
public Storage(I2PSnarkUtil util, MetaInfo metainfo, StorageListener listener)
|
||||
throws IOException
|
||||
{
|
||||
_util = util;
|
||||
_log = util.getContext().logManager().getLog(Storage.class);
|
||||
this.metainfo = metainfo;
|
||||
this.listener = listener;
|
||||
needed = metainfo.getPieces();
|
||||
_probablyComplete = false;
|
||||
bitfield = new BitField(needed);
|
||||
piece_size = metainfo.getPieceLength(0);
|
||||
pieces = needed;
|
||||
@@ -98,18 +108,22 @@ public class Storage
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a storage from the existing file or directory together
|
||||
* with an appropriate MetaInfo file as can be announced on the
|
||||
* given announce String location.
|
||||
* Creates a storage from the existing file or directory.
|
||||
* Creates an in-memory metainfo but does not save it to
|
||||
* a file, caller must do that.
|
||||
*
|
||||
* Creates the metainfo, this may take a LONG time. BLOCKING.
|
||||
*
|
||||
* @param announce may be null
|
||||
* @param listener may be null
|
||||
* @throws IOException when creating and/or checking files fails.
|
||||
*/
|
||||
public Storage(I2PSnarkUtil util, File baseFile, String announce,
|
||||
boolean privateTorrent, StorageListener listener)
|
||||
throws IOException
|
||||
{
|
||||
_util = util;
|
||||
_log = util.getContext().logManager().getLog(Storage.class);
|
||||
this.listener = listener;
|
||||
// Create names, rafs and lengths arrays.
|
||||
getFiles(baseFile);
|
||||
@@ -125,6 +139,8 @@ public class Storage
|
||||
|
||||
if (total <= 0)
|
||||
throw new IOException("Torrent contains no data");
|
||||
if (total > MAX_TOTAL_SIZE)
|
||||
throw new IOException("Torrent too big (" + total + " bytes), max is " + MAX_TOTAL_SIZE);
|
||||
|
||||
int pc_size = MIN_PIECE_SIZE;
|
||||
int pcs = (int) ((total - 1)/pc_size) + 1;
|
||||
@@ -160,6 +176,7 @@ public class Storage
|
||||
lengthsList = null;
|
||||
}
|
||||
|
||||
// TODO thread this so we can return and show something on the UI
|
||||
byte[] piece_hashes = fast_digestCreate();
|
||||
metainfo = new MetaInfo(announce, baseFile.getName(), null, files,
|
||||
lengthsList, piece_size, piece_hashes, total, privateTorrent);
|
||||
@@ -195,6 +212,8 @@ public class Storage
|
||||
|
||||
private void getFiles(File base) throws IOException
|
||||
{
|
||||
if (base.getAbsolutePath().equals("/"))
|
||||
throw new IOException("Don't seed root");
|
||||
ArrayList files = new ArrayList();
|
||||
addFiles(files, base);
|
||||
|
||||
@@ -223,17 +242,21 @@ public class Storage
|
||||
}
|
||||
}
|
||||
|
||||
private void addFiles(List l, File f)
|
||||
{
|
||||
if (!f.isDirectory())
|
||||
l.add(f);
|
||||
else
|
||||
{
|
||||
/**
|
||||
* @throws IOException if too many total files
|
||||
*/
|
||||
private void addFiles(List l, File f) throws IOException {
|
||||
if (!f.isDirectory()) {
|
||||
if (l.size() >= SnarkManager.MAX_FILES_PER_TORRENT)
|
||||
throw new IOException("Too many files, limit is " + SnarkManager.MAX_FILES_PER_TORRENT + ", zip them?");
|
||||
l.add(f);
|
||||
} else {
|
||||
File[] files = f.listFiles();
|
||||
if (files == null)
|
||||
{
|
||||
_util.debug("WARNING: Skipping '" + f
|
||||
+ "' not a normal file.", Snark.WARNING);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("WARNING: Skipping '" + f
|
||||
+ "' not a normal file.");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < files.length; i++)
|
||||
@@ -273,6 +296,23 @@ public class Storage
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* File checking in progress.
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public boolean isChecking() {
|
||||
return _isChecking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disk allocation (ballooning) in progress.
|
||||
* Always false on Windows.
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public boolean isAllocating() {
|
||||
return _allocateCount.get() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file canonical path (non-directory)
|
||||
* @return number of bytes remaining; -1 if unknown file
|
||||
@@ -457,7 +497,8 @@ public class Storage
|
||||
if (files == null)
|
||||
{
|
||||
// Create base as file.
|
||||
_util.debug("Creating/Checking file: " + base, Snark.NOTICE);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Creating/Checking file: " + base);
|
||||
if (!base.createNewFile() && !base.exists())
|
||||
throw new IOException("Could not create file " + base);
|
||||
|
||||
@@ -481,7 +522,8 @@ public class Storage
|
||||
else
|
||||
{
|
||||
// Create base as dir.
|
||||
_util.debug("Creating/Checking directory: " + base, Snark.NOTICE);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Creating/Checking directory: " + base);
|
||||
if (!base.mkdir() && !base.isDirectory())
|
||||
throw new IOException("Could not create directory " + base);
|
||||
|
||||
@@ -540,19 +582,22 @@ public class Storage
|
||||
bitfield = savedBitField;
|
||||
needed = metainfo.getPieces() - bitfield.count();
|
||||
_probablyComplete = complete();
|
||||
_util.debug("Found saved state and files unchanged, skipping check", Snark.NOTICE);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Found saved state and files unchanged, skipping check");
|
||||
} else {
|
||||
// the following sets the needed variable
|
||||
changed = true;
|
||||
checkCreateFiles(false);
|
||||
}
|
||||
if (complete()) {
|
||||
_util.debug("Torrent is complete", Snark.NOTICE);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Torrent is complete");
|
||||
} else {
|
||||
// fixme saved priorities
|
||||
if (files != null)
|
||||
priorities = new int[files.size()];
|
||||
_util.debug("Still need " + needed + " out of " + metainfo.getPieces() + " pieces", Snark.NOTICE);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Still need " + needed + " out of " + metainfo.getPieces() + " pieces");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,11 +732,25 @@ public class Storage
|
||||
* This is called at the beginning, and at presumed completion,
|
||||
* so we have to be careful about locking.
|
||||
*
|
||||
* TODO thread the checking so we can return and display
|
||||
* something on the UI
|
||||
*
|
||||
* @param recheck if true, this is a check after we downloaded the
|
||||
* last piece, and we don't modify the global bitfield unless
|
||||
* the check fails.
|
||||
*/
|
||||
private void checkCreateFiles(boolean recheck) throws IOException
|
||||
private void checkCreateFiles(boolean recheck) throws IOException {
|
||||
synchronized(this) {
|
||||
_isChecking = true;
|
||||
try {
|
||||
locked_checkCreateFiles(recheck);
|
||||
} finally {
|
||||
_isChecking = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void locked_checkCreateFiles(boolean recheck) throws IOException
|
||||
{
|
||||
// Whether we are resuming or not,
|
||||
// if any of the files already exists we assume we are resuming.
|
||||
@@ -730,8 +789,9 @@ public class Storage
|
||||
} else {
|
||||
String msg = "File '" + names[i] + "' exists, but has wrong length (expected " +
|
||||
lengths[i] + " but found " + length + ") - repairing corruption";
|
||||
SnarkManager.instance().addMessage(msg);
|
||||
_util.debug(msg, Snark.ERROR);
|
||||
if (listener != null)
|
||||
listener.addMessage(msg);
|
||||
_log.error(msg);
|
||||
changed = true;
|
||||
resume = true;
|
||||
_probablyComplete = false; // to force RW
|
||||
@@ -844,15 +904,25 @@ public class Storage
|
||||
*/
|
||||
private void balloonFile(int nr) throws IOException
|
||||
{
|
||||
_util.debug("Ballooning " + nr + ": " + RAFfile[nr], Snark.INFO);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Ballooning " + nr + ": " + RAFfile[nr]);
|
||||
long remaining = lengths[nr];
|
||||
final int ZEROBLOCKSIZE = (int) Math.min(remaining, 32*1024);
|
||||
byte[] zeros = new byte[ZEROBLOCKSIZE];
|
||||
rafs[nr].seek(0);
|
||||
while (remaining > 0) {
|
||||
int size = (int) Math.min(remaining, ZEROBLOCKSIZE);
|
||||
rafs[nr].write(zeros, 0, size);
|
||||
remaining -= size;
|
||||
// don't bother setting flag for small files
|
||||
if (remaining > 20*1024*1024)
|
||||
_allocateCount.incrementAndGet();
|
||||
try {
|
||||
while (remaining > 0) {
|
||||
int size = (int) Math.min(remaining, ZEROBLOCKSIZE);
|
||||
rafs[nr].write(zeros, 0, size);
|
||||
remaining -= size;
|
||||
}
|
||||
} finally {
|
||||
remaining = lengths[nr];
|
||||
if (remaining > 20*1024*1024)
|
||||
_allocateCount.decrementAndGet();
|
||||
}
|
||||
isSparse[nr] = false;
|
||||
}
|
||||
@@ -875,7 +945,7 @@ public class Storage
|
||||
closeRAF(i);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_util.debug("Error closing " + RAFfile[i], Snark.ERROR, ioe);
|
||||
_log.error("Error closing " + RAFfile[i], ioe);
|
||||
// gobble gobble
|
||||
}
|
||||
}
|
||||
@@ -886,21 +956,28 @@ public class Storage
|
||||
* Returns a byte array containing a portion of the requested piece or null if
|
||||
* the storage doesn't contain the piece yet.
|
||||
*/
|
||||
public byte[] getPiece(int piece, int off, int len) throws IOException
|
||||
public ByteArray getPiece(int piece, int off, int len) throws IOException
|
||||
{
|
||||
if (!bitfield.get(piece))
|
||||
return null;
|
||||
|
||||
//Catch a common place for OOMs esp. on 1MB pieces
|
||||
ByteArray rv;
|
||||
byte[] bs;
|
||||
try {
|
||||
bs = new byte[len];
|
||||
// Will be restored to cache in Message.sendMessage()
|
||||
if (len == BUFSIZE)
|
||||
rv = _cache.acquire();
|
||||
else
|
||||
rv = new ByteArray(new byte[len]);
|
||||
} catch (OutOfMemoryError oom) {
|
||||
_util.debug("Out of memory, can't honor request for piece " + piece, Snark.WARNING, oom);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Out of memory, can't honor request for piece " + piece, oom);
|
||||
return null;
|
||||
}
|
||||
bs = rv.getData();
|
||||
getUncheckedPiece(piece, bs, off, len);
|
||||
return bs;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1000,8 +1077,9 @@ public class Storage
|
||||
if (needed > 0) {
|
||||
if (listener != null)
|
||||
listener.setWantedPieces(this);
|
||||
_util.debug("WARNING: Not really done, missing " + needed
|
||||
+ " pieces", Snark.WARNING);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("WARNING: Not really done, missing " + needed
|
||||
+ " pieces");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,4 +61,6 @@ interface StorageListener
|
||||
*
|
||||
*/
|
||||
void setWantedPieces(Storage storage);
|
||||
|
||||
void addMessage(String message);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
@@ -38,11 +39,12 @@ import java.util.Set;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
import org.klomp.snark.dht.DHT;
|
||||
|
||||
/**
|
||||
* Informs metainfo tracker of events and gets new peers for peer
|
||||
* coordinator.
|
||||
@@ -62,7 +64,7 @@ import net.i2p.util.SimpleTimer2;
|
||||
* @author Mark Wielaard (mark@klomp.org)
|
||||
*/
|
||||
public class TrackerClient implements Runnable {
|
||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(TrackerClient.class);
|
||||
private final Log _log;
|
||||
private static final String NO_EVENT = "";
|
||||
private static final String STARTED_EVENT = "started";
|
||||
private static final String COMPLETED_EVENT = "completed";
|
||||
@@ -76,6 +78,8 @@ public class TrackerClient implements Runnable {
|
||||
private final static int INITIAL_SLEEP = 90*1000;
|
||||
private final static int MAX_CONSEC_FAILS = 5; // slow down after this
|
||||
private final static int LONG_SLEEP = 30*60*1000; // sleep a while after lots of fails
|
||||
private final static long MIN_TRACKER_ANNOUNCE_INTERVAL = 10*60*1000;
|
||||
private final static long MIN_DHT_ANNOUNCE_INTERVAL = 10*60*1000;
|
||||
|
||||
private final I2PSnarkUtil _util;
|
||||
private final MetaInfo meta;
|
||||
@@ -98,8 +102,9 @@ public class TrackerClient implements Runnable {
|
||||
// these 2 used in loop()
|
||||
private volatile boolean runStarted;
|
||||
private volatile int consecutiveFails;
|
||||
private boolean completed;
|
||||
private volatile boolean _fastUnannounce;
|
||||
|
||||
private long lastDHTAnnounce;
|
||||
private final List<Tracker> trackers;
|
||||
|
||||
/**
|
||||
@@ -116,6 +121,7 @@ public class TrackerClient implements Runnable {
|
||||
String id = urlencode(snark.getID());
|
||||
_threadName = "TrackerClient " + id.substring(id.length() - 12);
|
||||
_util = util;
|
||||
_log = util.getContext().logManager().getLog(TrackerClient.class);
|
||||
this.meta = meta;
|
||||
this.additionalTrackerURL = additionalTrackerURL;
|
||||
this.coordinator = coordinator;
|
||||
@@ -183,7 +189,7 @@ public class TrackerClient implements Runnable {
|
||||
|
||||
private class Runner extends SimpleTimer2.TimedEvent {
|
||||
public Runner(long delay) {
|
||||
super(SimpleTimer2.getInstance(), delay);
|
||||
super(_util.getContext().simpleTimer2(), delay);
|
||||
}
|
||||
|
||||
public void timeReached() {
|
||||
@@ -209,17 +215,22 @@ public class TrackerClient implements Runnable {
|
||||
* This will take several seconds to several minutes.
|
||||
*/
|
||||
public void run() {
|
||||
long begin = Clock.getInstance().now();
|
||||
long begin = _util.getContext().clock().now();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Start " + Thread.currentThread().getName());
|
||||
try {
|
||||
if (!_initialized) {
|
||||
setup();
|
||||
// FIXME dht
|
||||
if (trackers.isEmpty()) {
|
||||
stop = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (trackers.isEmpty() && _util.getDHT() == null) {
|
||||
stop = true;
|
||||
this.snark.addMessage(_util.getString("No valid trackers for {0} - enable opentrackers or DHT?",
|
||||
this.snark.getBaseName()));
|
||||
_log.error("No valid trackers for " + this.snark.getBaseName());
|
||||
this.snark.stopTorrent();
|
||||
return;
|
||||
}
|
||||
if (!_initialized) {
|
||||
_initialized = true;
|
||||
// FIXME only when starting everybody at once, not for a single torrent
|
||||
long delay = I2PAppContext.getGlobalContext().random().nextInt(30*1000);
|
||||
@@ -233,7 +244,7 @@ public class TrackerClient implements Runnable {
|
||||
_thread = null;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Finish " + Thread.currentThread().getName() +
|
||||
" after " + DataHelper.formatDuration(Clock.getInstance().now() - begin));
|
||||
" after " + DataHelper.formatDuration(_util.getContext().clock().now() - begin));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,16 +304,7 @@ public class TrackerClient implements Runnable {
|
||||
_log.debug("Additional announce: [" + url + "] for infoHash: " + infoHash);
|
||||
}
|
||||
}
|
||||
|
||||
if (trackers.isEmpty()) {
|
||||
stop = true;
|
||||
// FIXME translate
|
||||
SnarkManager.instance().addMessage("No valid trackers for " + this.snark.getBaseName() + " - enable opentrackers?");
|
||||
_log.error("No valid trackers for " + this.snark.getBaseName());
|
||||
// FIXME keep going if DHT enabled
|
||||
this.snark.stopTorrent();
|
||||
return;
|
||||
}
|
||||
this.completed = coordinator.getLeft() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -322,13 +324,13 @@ public class TrackerClient implements Runnable {
|
||||
}
|
||||
|
||||
// Local DHT tracker announce
|
||||
if (_util.getDHT() != null)
|
||||
_util.getDHT().announce(snark.getInfoHash());
|
||||
DHT dht = _util.getDHT();
|
||||
if (dht != null)
|
||||
dht.announce(snark.getInfoHash());
|
||||
|
||||
long uploaded = coordinator.getUploaded();
|
||||
long downloaded = coordinator.getDownloaded();
|
||||
long left = coordinator.getLeft(); // -1 in magnet mode
|
||||
boolean completed = (left == 0);
|
||||
|
||||
// First time we got a complete download?
|
||||
String event;
|
||||
@@ -342,8 +344,7 @@ public class TrackerClient implements Runnable {
|
||||
|
||||
// *** loop once for each tracker
|
||||
int maxSeenPeers = 0;
|
||||
for (Iterator iter = trackers.iterator(); iter.hasNext(); ) {
|
||||
Tracker tr = (Tracker)iter.next();
|
||||
for (Tracker tr : trackers) {
|
||||
if ((!stop) && (!tr.stop) &&
|
||||
(completed || coordinator.needOutboundPeers() || !tr.started) &&
|
||||
(event.equals(COMPLETED_EVENT) || System.currentTimeMillis() > tr.lastRequestTime + tr.interval))
|
||||
@@ -371,9 +372,10 @@ public class TrackerClient implements Runnable {
|
||||
snark.setTrackerSeenPeers(tr.seenPeers);
|
||||
|
||||
// pass everybody over to our tracker
|
||||
if (_util.getDHT() != null) {
|
||||
dht = _util.getDHT();
|
||||
if (dht != null) {
|
||||
for (Peer peer : peers) {
|
||||
_util.getDHT().announce(snark.getInfoHash(), peer.getPeerID().getDestHash());
|
||||
dht.announce(snark.getInfoHash(), peer.getPeerID().getDestHash());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,9 +399,10 @@ public class TrackerClient implements Runnable {
|
||||
catch (IOException ioe)
|
||||
{
|
||||
// Probably not fatal (if it doesn't last to long...)
|
||||
_util.debug
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn
|
||||
("WARNING: Could not contact tracker at '"
|
||||
+ tr.announce + "': " + ioe, Snark.WARNING);
|
||||
+ tr.announce + "': " + ioe);
|
||||
tr.trackerProblems = ioe.getMessage();
|
||||
// don't show secondary tracker problems to the user
|
||||
if (tr.isPrimary)
|
||||
@@ -421,8 +424,9 @@ public class TrackerClient implements Runnable {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_util.debug("Not announcing to " + tr.announce + " last announce was " +
|
||||
new Date(tr.lastRequestTime) + " interval is " + DataHelper.formatDuration(tr.interval), Snark.INFO);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Not announcing to " + tr.announce + " last announce was " +
|
||||
new Date(tr.lastRequestTime) + " interval is " + DataHelper.formatDuration(tr.interval));
|
||||
}
|
||||
if ((!tr.stop) && maxSeenPeers < tr.seenPeers)
|
||||
maxSeenPeers = tr.seenPeers;
|
||||
@@ -432,7 +436,8 @@ public class TrackerClient implements Runnable {
|
||||
if (coordinator.needOutboundPeers() && (meta == null || !meta.isPrivate()) && !stop) {
|
||||
Set<PeerID> pids = coordinator.getPEXPeers();
|
||||
if (!pids.isEmpty()) {
|
||||
_util.debug("Got " + pids.size() + " from PEX", Snark.INFO);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Got " + pids.size() + " from PEX");
|
||||
List<Peer> peers = new ArrayList(pids.size());
|
||||
for (PeerID pID : pids) {
|
||||
peers.add(new Peer(pID, snark.getID(), snark.getInfoHash(), snark.getMetaInfo()));
|
||||
@@ -448,31 +453,41 @@ public class TrackerClient implements Runnable {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_util.debug("Not getting PEX peers", Snark.INFO);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Not getting PEX peers");
|
||||
}
|
||||
|
||||
// Get peers from DHT
|
||||
// FIXME this needs to be in its own thread
|
||||
if (_util.getDHT() != null && (meta == null || !meta.isPrivate()) && !stop) {
|
||||
dht = _util.getDHT();
|
||||
if (dht != null && (meta == null || !meta.isPrivate()) && (!stop) &&
|
||||
_util.getContext().clock().now() > lastDHTAnnounce + MIN_DHT_ANNOUNCE_INTERVAL) {
|
||||
int numwant;
|
||||
if (event.equals(STOPPED_EVENT) || !coordinator.needOutboundPeers())
|
||||
numwant = 1;
|
||||
else
|
||||
numwant = _util.getMaxConnections();
|
||||
List<Hash> hashes = _util.getDHT().getPeers(snark.getInfoHash(), numwant, 2*60*1000);
|
||||
_util.debug("Got " + hashes + " from DHT", Snark.INFO);
|
||||
Collection<Hash> hashes = dht.getPeers(snark.getInfoHash(), numwant, 2*60*1000);
|
||||
if (!hashes.isEmpty()) {
|
||||
runStarted = true;
|
||||
lastDHTAnnounce = _util.getContext().clock().now();
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Got " + hashes + " from DHT");
|
||||
// announce ourselves while the token is still good
|
||||
// FIXME this needs to be in its own thread
|
||||
if (!stop) {
|
||||
int good = _util.getDHT().announce(snark.getInfoHash(), 8, 5*60*1000);
|
||||
_util.debug("Sent " + good + " good announces to DHT", Snark.INFO);
|
||||
// announce only to the 1 closest
|
||||
int good = dht.announce(snark.getInfoHash(), 1, 5*60*1000);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sent " + good + " good announces to DHT");
|
||||
}
|
||||
|
||||
// now try these peers
|
||||
if ((!stop) && !hashes.isEmpty()) {
|
||||
List<Peer> peers = new ArrayList(hashes.size());
|
||||
for (Hash h : hashes) {
|
||||
PeerID pID = new PeerID(h.getData());
|
||||
PeerID pID = new PeerID(h.getData(), _util);
|
||||
peers.add(new Peer(pID, snark.getID(), snark.getInfoHash(), snark.getMetaInfo()));
|
||||
}
|
||||
Collections.shuffle(peers, r);
|
||||
@@ -486,7 +501,8 @@ public class TrackerClient implements Runnable {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_util.debug("Not getting DHT peers", Snark.INFO);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Not getting DHT peers");
|
||||
}
|
||||
|
||||
|
||||
@@ -496,9 +512,6 @@ public class TrackerClient implements Runnable {
|
||||
if (stop)
|
||||
return;
|
||||
|
||||
if (!runStarted)
|
||||
_util.debug(" Retrying in one minute...", Snark.DEBUG);
|
||||
|
||||
try {
|
||||
// Sleep some minutes...
|
||||
// Sleep the minimum interval for all the trackers, but 60s minimum
|
||||
@@ -508,6 +521,8 @@ public class TrackerClient implements Runnable {
|
||||
delay = 3*SLEEP*60*1000 + random;
|
||||
else if (snark.getTrackerProblems() != null && ++consecutiveFails < MAX_CONSEC_FAILS)
|
||||
delay = INITIAL_SLEEP;
|
||||
else if ((!runStarted) && _runCount < MAX_CONSEC_FAILS)
|
||||
delay = INITIAL_SLEEP;
|
||||
else
|
||||
// sleep a while, when we wake up we will contact only the trackers whose intervals have passed
|
||||
delay = SLEEP*60*1000 + random;
|
||||
@@ -526,7 +541,7 @@ public class TrackerClient implements Runnable {
|
||||
} // try
|
||||
catch (Throwable t)
|
||||
{
|
||||
_util.debug("TrackerClient: " + t, Snark.ERROR, t);
|
||||
_log.error("TrackerClient: " + t, t);
|
||||
if (t instanceof OutOfMemoryError)
|
||||
throw (OutOfMemoryError)t;
|
||||
}
|
||||
@@ -538,8 +553,9 @@ public class TrackerClient implements Runnable {
|
||||
*/
|
||||
private void unannounce() {
|
||||
// Local DHT tracker unannounce
|
||||
if (_util.getDHT() != null)
|
||||
_util.getDHT().unannounce(snark.getInfoHash());
|
||||
DHT dht = _util.getDHT();
|
||||
if (dht != null)
|
||||
dht.unannounce(snark.getInfoHash());
|
||||
int i = 0;
|
||||
for (Tracker tr : trackers) {
|
||||
if (_util.connected() &&
|
||||
@@ -619,7 +635,8 @@ public class TrackerClient implements Runnable {
|
||||
else
|
||||
buf.append(_util.getMaxConnections());
|
||||
String s = buf.toString();
|
||||
_util.debug("Sending TrackerClient request: " + s, Snark.INFO);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending TrackerClient request: " + s);
|
||||
|
||||
tr.lastRequestTime = System.currentTimeMillis();
|
||||
// Don't wait for a response to stopped when shutting down
|
||||
@@ -634,14 +651,15 @@ public class TrackerClient implements Runnable {
|
||||
in = new FileInputStream(fetched);
|
||||
|
||||
TrackerInfo info = new TrackerInfo(in, snark.getID(),
|
||||
snark.getInfoHash(), snark.getMetaInfo());
|
||||
_util.debug("TrackerClient response: " + info, Snark.INFO);
|
||||
snark.getInfoHash(), snark.getMetaInfo(), _util);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("TrackerClient response: " + info);
|
||||
|
||||
String failure = info.getFailureReason();
|
||||
if (failure != null)
|
||||
throw new IOException(failure);
|
||||
|
||||
tr.interval = info.getInterval() * 1000;
|
||||
tr.interval = Math.max(MIN_TRACKER_ANNOUNCE_INTERVAL, info.getInterval() * 1000l);
|
||||
return info;
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
|
||||
@@ -38,7 +38,7 @@ import org.klomp.snark.bencode.InvalidBEncodingException;
|
||||
* Compact format 1 - a list of hashes - early format for testing
|
||||
* Compact format 2 - One big string of concatenated hashes - official format
|
||||
*/
|
||||
public class TrackerInfo
|
||||
class TrackerInfo
|
||||
{
|
||||
private final String failure_reason;
|
||||
private final int interval;
|
||||
@@ -47,19 +47,19 @@ public class TrackerInfo
|
||||
private int incomplete;
|
||||
|
||||
/** @param metainfo may be null */
|
||||
public TrackerInfo(InputStream in, byte[] my_id, byte[] infohash, MetaInfo metainfo)
|
||||
public TrackerInfo(InputStream in, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
|
||||
throws IOException
|
||||
{
|
||||
this(new BDecoder(in), my_id, infohash, metainfo);
|
||||
this(new BDecoder(in), my_id, infohash, metainfo, util);
|
||||
}
|
||||
|
||||
private TrackerInfo(BDecoder be, byte[] my_id, byte[] infohash, MetaInfo metainfo)
|
||||
private TrackerInfo(BDecoder be, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
|
||||
throws IOException
|
||||
{
|
||||
this(be.bdecodeMap().getMap(), my_id, infohash, metainfo);
|
||||
this(be.bdecodeMap().getMap(), my_id, infohash, metainfo, util);
|
||||
}
|
||||
|
||||
private TrackerInfo(Map m, byte[] my_id, byte[] infohash, MetaInfo metainfo)
|
||||
private TrackerInfo(Map m, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
|
||||
throws IOException
|
||||
{
|
||||
BEValue reason = (BEValue)m.get("failure reason");
|
||||
@@ -85,10 +85,10 @@ public class TrackerInfo
|
||||
Set<Peer> p;
|
||||
try {
|
||||
// One big string (the official compact format)
|
||||
p = getPeers(bePeers.getBytes(), my_id, infohash, metainfo);
|
||||
p = getPeers(bePeers.getBytes(), my_id, infohash, metainfo, util);
|
||||
} catch (InvalidBEncodingException ibe) {
|
||||
// List of Dictionaries or List of Strings
|
||||
p = getPeers(bePeers.getList(), my_id, infohash, metainfo);
|
||||
p = getPeers(bePeers.getList(), my_id, infohash, metainfo, util);
|
||||
}
|
||||
peers = p;
|
||||
}
|
||||
@@ -124,7 +124,7 @@ public class TrackerInfo
|
||||
******/
|
||||
|
||||
/** List of Dictionaries or List of Strings */
|
||||
private static Set<Peer> getPeers(List<BEValue> l, byte[] my_id, byte[] infohash, MetaInfo metainfo)
|
||||
private static Set<Peer> getPeers(List<BEValue> l, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
|
||||
throws IOException
|
||||
{
|
||||
Set<Peer> peers = new HashSet(l.size());
|
||||
@@ -138,7 +138,7 @@ public class TrackerInfo
|
||||
try {
|
||||
// Case 2 - compact - A list of 32-byte binary strings (hashes)
|
||||
// This was just for testing and is not the official format
|
||||
peerID = new PeerID(bev.getBytes());
|
||||
peerID = new PeerID(bev.getBytes(), util);
|
||||
} catch (InvalidBEncodingException ibe2) {
|
||||
// don't let one bad entry spoil the whole list
|
||||
//Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR);
|
||||
@@ -157,7 +157,7 @@ public class TrackerInfo
|
||||
* One big string of concatenated 32-byte hashes
|
||||
* @since 0.8.1
|
||||
*/
|
||||
private static Set<Peer> getPeers(byte[] l, byte[] my_id, byte[] infohash, MetaInfo metainfo)
|
||||
private static Set<Peer> getPeers(byte[] l, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
|
||||
throws IOException
|
||||
{
|
||||
int count = l.length / HASH_LENGTH;
|
||||
@@ -168,7 +168,7 @@ public class TrackerInfo
|
||||
byte[] hash = new byte[HASH_LENGTH];
|
||||
System.arraycopy(l, i * HASH_LENGTH, hash, 0, HASH_LENGTH);
|
||||
try {
|
||||
peerID = new PeerID(hash);
|
||||
peerID = new PeerID(hash, util);
|
||||
} catch (InvalidBEncodingException ibe) {
|
||||
// won't happen
|
||||
continue;
|
||||
@@ -195,6 +195,7 @@ public class TrackerInfo
|
||||
return failure_reason;
|
||||
}
|
||||
|
||||
/** in seconds */
|
||||
public int getInterval()
|
||||
{
|
||||
return interval;
|
||||
|
||||
@@ -4,7 +4,7 @@ package org.klomp.snark.dht;
|
||||
* GPLv2
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
@@ -12,15 +12,21 @@ import net.i2p.data.Hash;
|
||||
|
||||
/**
|
||||
* Stub for KRPC
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public interface DHT {
|
||||
|
||||
|
||||
/**
|
||||
* @return The UDP port that should be included in a PORT message.
|
||||
* @return The UDP query port
|
||||
*/
|
||||
public int getPort();
|
||||
|
||||
/**
|
||||
* @return The UDP response port
|
||||
*/
|
||||
public int getRPort();
|
||||
|
||||
/**
|
||||
* Ping. We don't have a NID yet so the node is presumed
|
||||
* to be absent from our DHT.
|
||||
@@ -37,9 +43,9 @@ public interface DHT {
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param max maximum number of peers to return
|
||||
* @param maxWait the maximum time to wait (ms) must be > 0
|
||||
* @return list or empty list (never null)
|
||||
* @return possibly empty (never null)
|
||||
*/
|
||||
public List<Hash> getPeers(byte[] ih, int max, long maxWait);
|
||||
public Collection<Hash> getPeers(byte[] ih, int max, long maxWait);
|
||||
|
||||
/**
|
||||
* Announce to ourselves.
|
||||
@@ -79,4 +85,19 @@ public interface DHT {
|
||||
* @return the number of successful announces, not counting ourselves.
|
||||
*/
|
||||
public int announce(byte[] ih, int max, long maxWait);
|
||||
|
||||
/**
|
||||
* Stop everything.
|
||||
*/
|
||||
public void stop();
|
||||
|
||||
/**
|
||||
* Known nodes, not estimated total network size.
|
||||
*/
|
||||
public int size();
|
||||
|
||||
/**
|
||||
* Debug info, HTML formatted
|
||||
*/
|
||||
public String renderStatusHTML();
|
||||
}
|
||||
|
||||
166
apps/i2psnark/java/src/org/klomp/snark/dht/DHTNodes.java
Normal file
166
apps/i2psnark/java/src/org/klomp/snark/dht/DHTNodes.java
Normal file
@@ -0,0 +1,166 @@
|
||||
package org.klomp.snark.dht;
|
||||
/*
|
||||
* From zzzot, modded and relicensed to GPLv2
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.SHA1Hash;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.kademlia.KBucketSet;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
* All the nodes we know about, stored as a mapping from
|
||||
* node ID to a Destination and Port.
|
||||
*
|
||||
* And a real Kademlia routing table, which stores node IDs only.
|
||||
*
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
class DHTNodes {
|
||||
|
||||
private final I2PAppContext _context;
|
||||
private long _expireTime;
|
||||
private final Log _log;
|
||||
private final ConcurrentHashMap<NID, NodeInfo> _nodeMap;
|
||||
private final KBucketSet<NID> _kad;
|
||||
private volatile boolean _isRunning;
|
||||
|
||||
/** stagger with other cleaners */
|
||||
private static final long CLEAN_TIME = 187*1000;
|
||||
/** how long since last heard from do we delete - BEP 5 says 15 minutes */
|
||||
private static final long MAX_EXPIRE_TIME = 30*60*1000;
|
||||
private static final long MIN_EXPIRE_TIME = 10*60*1000;
|
||||
private static final long DELTA_EXPIRE_TIME = 3*60*1000;
|
||||
private static final int MAX_PEERS = 799;
|
||||
/** Buckets older than this are refreshed - BEP 5 says 15 minutes */
|
||||
private static final long MAX_BUCKET_AGE = 15*60*1000;
|
||||
private static final int KAD_K = 8;
|
||||
private static final int KAD_B = 1;
|
||||
|
||||
public DHTNodes(I2PAppContext ctx, NID me) {
|
||||
_context = ctx;
|
||||
_expireTime = MAX_EXPIRE_TIME;
|
||||
_log = _context.logManager().getLog(DHTNodes.class);
|
||||
_nodeMap = new ConcurrentHashMap();
|
||||
_kad = new KBucketSet(ctx, me, KAD_K, KAD_B, new KBTrimmer(ctx, KAD_K));
|
||||
}
|
||||
|
||||
public void start() {
|
||||
_isRunning = true;
|
||||
new Cleaner();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
clear();
|
||||
_isRunning = false;
|
||||
}
|
||||
|
||||
// begin ConcurrentHashMap methods
|
||||
|
||||
public int size() {
|
||||
return _nodeMap.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
_kad.clear();
|
||||
_nodeMap.clear();
|
||||
}
|
||||
|
||||
public NodeInfo get(NID nid) {
|
||||
return _nodeMap.get(nid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the old value if present, else null
|
||||
*/
|
||||
public NodeInfo putIfAbsent(NodeInfo nInfo) {
|
||||
_kad.add(nInfo.getNID());
|
||||
return _nodeMap.putIfAbsent(nInfo.getNID(), nInfo);
|
||||
}
|
||||
|
||||
public NodeInfo remove(NID nid) {
|
||||
_kad.remove(nid);
|
||||
return _nodeMap.remove(nid);
|
||||
}
|
||||
|
||||
public Collection<NodeInfo> values() {
|
||||
return _nodeMap.values();
|
||||
}
|
||||
|
||||
// end ConcurrentHashMap methods
|
||||
|
||||
/**
|
||||
* DHT
|
||||
* @param h either a InfoHash or a NID
|
||||
*/
|
||||
public List<NodeInfo> findClosest(SHA1Hash h, int numWant) {
|
||||
NID key;
|
||||
if (h instanceof NID)
|
||||
key = (NID) h;
|
||||
else
|
||||
key = new NID(h.getData());
|
||||
List<NID> keys = _kad.getClosest(key, numWant);
|
||||
List<NodeInfo> rv = new ArrayList(keys.size());
|
||||
for (NID nid : keys) {
|
||||
NodeInfo ninfo = _nodeMap.get(nid);
|
||||
if (ninfo != null)
|
||||
rv.add(ninfo);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* DHT - get random keys to explore
|
||||
*/
|
||||
public List<NID> getExploreKeys() {
|
||||
return _kad.getExploreKeys(MAX_BUCKET_AGE);
|
||||
}
|
||||
|
||||
/** */
|
||||
private class Cleaner extends SimpleTimer2.TimedEvent {
|
||||
|
||||
public Cleaner() {
|
||||
super(SimpleTimer2.getInstance(), CLEAN_TIME);
|
||||
}
|
||||
|
||||
public void timeReached() {
|
||||
if (!_isRunning)
|
||||
return;
|
||||
long now = _context.clock().now();
|
||||
int peerCount = 0;
|
||||
for (Iterator<NodeInfo> iter = DHTNodes.this.values().iterator(); iter.hasNext(); ) {
|
||||
NodeInfo peer = iter.next();
|
||||
if (peer.lastSeen() < now - _expireTime) {
|
||||
iter.remove();
|
||||
_kad.remove(peer.getNID());
|
||||
} else {
|
||||
peerCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (peerCount > MAX_PEERS)
|
||||
_expireTime = Math.max(_expireTime - DELTA_EXPIRE_TIME, MIN_EXPIRE_TIME);
|
||||
else
|
||||
_expireTime = Math.min(_expireTime + DELTA_EXPIRE_TIME, MAX_EXPIRE_TIME);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("DHT storage cleaner done, now with " +
|
||||
peerCount + " peers, " +
|
||||
DataHelper.formatDuration(_expireTime) + " expiration");
|
||||
|
||||
schedule(CLEAN_TIME);
|
||||
}
|
||||
}
|
||||
}
|
||||
168
apps/i2psnark/java/src/org/klomp/snark/dht/DHTTracker.java
Normal file
168
apps/i2psnark/java/src/org/klomp/snark/dht/DHTTracker.java
Normal file
@@ -0,0 +1,168 @@
|
||||
package org.klomp.snark.dht;
|
||||
/*
|
||||
* From zzzot, relicensed to GPLv2
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
* The tracker stores peers, i.e. Dest hashes (not nodes).
|
||||
*
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
class DHTTracker {
|
||||
|
||||
private final I2PAppContext _context;
|
||||
private final Torrents _torrents;
|
||||
private long _expireTime;
|
||||
private final Log _log;
|
||||
private volatile boolean _isRunning;
|
||||
/** not current, updated by cleaner */
|
||||
private int _peerCount;
|
||||
/** not current, updated by cleaner */
|
||||
private int _torrentCount;
|
||||
|
||||
/** stagger with other cleaners */
|
||||
private static final long CLEAN_TIME = 199*1000;
|
||||
private static final long MAX_EXPIRE_TIME = 45*60*1000;
|
||||
private static final long MIN_EXPIRE_TIME = 15*60*1000;
|
||||
private static final long DELTA_EXPIRE_TIME = 3*60*1000;
|
||||
private static final int MAX_PEERS = 2000;
|
||||
private static final int MAX_PEERS_PER_TORRENT = 150;
|
||||
|
||||
DHTTracker(I2PAppContext ctx) {
|
||||
_context = ctx;
|
||||
_torrents = new Torrents();
|
||||
_expireTime = MAX_EXPIRE_TIME;
|
||||
_log = _context.logManager().getLog(DHTTracker.class);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
_isRunning = true;
|
||||
new Cleaner();
|
||||
}
|
||||
|
||||
void stop() {
|
||||
_torrents.clear();
|
||||
_isRunning = false;
|
||||
}
|
||||
|
||||
void announce(InfoHash ih, Hash hash) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Announce " + hash + " for " + ih);
|
||||
Peers peers = _torrents.get(ih);
|
||||
if (peers == null) {
|
||||
peers = new Peers();
|
||||
Peers peers2 = _torrents.putIfAbsent(ih, peers);
|
||||
if (peers2 != null)
|
||||
peers = peers2;
|
||||
}
|
||||
|
||||
Peer peer = new Peer(hash.getData());
|
||||
Peer peer2 = peers.putIfAbsent(peer, peer);
|
||||
if (peer2 != null)
|
||||
peer = peer2;
|
||||
peer.setLastSeen(_context.clock().now());
|
||||
}
|
||||
|
||||
void unannounce(InfoHash ih, Hash hash) {
|
||||
Peers peers = _torrents.get(ih);
|
||||
if (peers == null)
|
||||
return;
|
||||
Peer peer = new Peer(hash.getData());
|
||||
peers.remove(peer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Caller's responsibility to remove himself from the list
|
||||
* @return list or empty list (never null)
|
||||
*/
|
||||
List<Hash> getPeers(InfoHash ih, int max) {
|
||||
Peers peers = _torrents.get(ih);
|
||||
if (peers == null)
|
||||
return Collections.EMPTY_LIST;
|
||||
|
||||
int size = peers.size();
|
||||
List<Hash> rv = new ArrayList(peers.values());
|
||||
if (max < size) {
|
||||
Collections.shuffle(rv, _context.random());
|
||||
rv = rv.subList(0, max);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug info, HTML formatted
|
||||
*/
|
||||
public void renderStatusHTML(StringBuilder buf) {
|
||||
buf.append("DHT tracker: ").append(_torrentCount).append(" torrents ")
|
||||
.append(_peerCount).append(" peers ")
|
||||
.append(DataHelper.formatDuration(_expireTime)).append(" expiration<br>");
|
||||
}
|
||||
|
||||
private class Cleaner extends SimpleTimer2.TimedEvent {
|
||||
|
||||
public Cleaner() {
|
||||
super(SimpleTimer2.getInstance(), CLEAN_TIME);
|
||||
}
|
||||
|
||||
public void timeReached() {
|
||||
if (!_isRunning)
|
||||
return;
|
||||
long now = _context.clock().now();
|
||||
int torrentCount = 0;
|
||||
int peerCount = 0;
|
||||
for (Iterator<Peers> iter = _torrents.values().iterator(); iter.hasNext(); ) {
|
||||
Peers p = iter.next();
|
||||
int recent = 0;
|
||||
for (Iterator<Peer> iterp = p.values().iterator(); iterp.hasNext(); ) {
|
||||
Peer peer = iterp.next();
|
||||
if (peer.lastSeen() < now - _expireTime)
|
||||
iterp.remove();
|
||||
else {
|
||||
recent++;
|
||||
peerCount++;
|
||||
}
|
||||
}
|
||||
if (recent > MAX_PEERS_PER_TORRENT) {
|
||||
// too many, delete at random
|
||||
// TODO per-torrent adjustable expiration?
|
||||
for (Iterator<Peer> iterp = p.values().iterator(); iterp.hasNext() && p.size() > MAX_PEERS_PER_TORRENT; ) {
|
||||
iterp.next();
|
||||
iterp.remove();
|
||||
peerCount--;
|
||||
}
|
||||
torrentCount++;
|
||||
} else if (recent <= 0) {
|
||||
iter.remove();
|
||||
} else {
|
||||
torrentCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (peerCount > MAX_PEERS)
|
||||
_expireTime = Math.max(_expireTime - DELTA_EXPIRE_TIME, MIN_EXPIRE_TIME);
|
||||
else
|
||||
_expireTime = Math.min(_expireTime + DELTA_EXPIRE_TIME, MAX_EXPIRE_TIME);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("DHT tracker cleaner done, now with " +
|
||||
torrentCount + " torrents, " +
|
||||
peerCount + " peers, " +
|
||||
DataHelper.formatDuration(_expireTime) + " expiration");
|
||||
_peerCount = peerCount;
|
||||
_torrentCount = torrentCount;
|
||||
schedule(CLEAN_TIME);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
apps/i2psnark/java/src/org/klomp/snark/dht/InfoHash.java
Normal file
29
apps/i2psnark/java/src/org/klomp/snark/dht/InfoHash.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package org.klomp.snark.dht;
|
||||
/*
|
||||
* From zzzot, modded and relicensed to GPLv2
|
||||
*/
|
||||
|
||||
import net.i2p.crypto.SHA1Hash;
|
||||
import org.klomp.snark.I2PSnarkUtil;
|
||||
|
||||
/**
|
||||
* A 20-byte SHA1 info hash
|
||||
*
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
class InfoHash extends SHA1Hash {
|
||||
|
||||
public InfoHash(byte[] data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (_data == null) {
|
||||
return super.toString();
|
||||
} else {
|
||||
return "[InfoHash: " + I2PSnarkUtil.toHex(_data) + ']';
|
||||
}
|
||||
}
|
||||
}
|
||||
38
apps/i2psnark/java/src/org/klomp/snark/dht/KBTrimmer.java
Normal file
38
apps/i2psnark/java/src/org/klomp/snark/dht/KBTrimmer.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package org.klomp.snark.dht;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.kademlia.KBucket;
|
||||
import net.i2p.kademlia.KBucketTrimmer;
|
||||
|
||||
/**
|
||||
* Removes an element older than 15 minutes, but only if the bucket hasn't changed in 5 minutes.
|
||||
* @since 0.9.2
|
||||
*/
|
||||
class KBTrimmer implements KBucketTrimmer<NID> {
|
||||
private final I2PAppContext _ctx;
|
||||
private final int _max;
|
||||
|
||||
private static final long MIN_BUCKET_AGE = 5*60*1000;
|
||||
private static final long MAX_NODE_AGE = 15*60*1000;
|
||||
|
||||
public KBTrimmer(I2PAppContext ctx, int max) {
|
||||
_ctx = ctx;
|
||||
_max = max;
|
||||
}
|
||||
|
||||
public boolean trim(KBucket<NID> kbucket, NID toAdd) {
|
||||
long now = _ctx.clock().now();
|
||||
if (kbucket.getLastChanged() > now - MIN_BUCKET_AGE)
|
||||
return false;
|
||||
Set<NID> entries = kbucket.getEntries();
|
||||
for (NID nid : entries) {
|
||||
if (nid.lastSeen() < now - MAX_NODE_AGE) {
|
||||
if (kbucket.remove(nid))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return entries.size() < _max;
|
||||
}
|
||||
}
|
||||
1611
apps/i2psnark/java/src/org/klomp/snark/dht/KRPC.java
Normal file
1611
apps/i2psnark/java/src/org/klomp/snark/dht/KRPC.java
Normal file
File diff suppressed because it is too large
Load Diff
37
apps/i2psnark/java/src/org/klomp/snark/dht/MsgID.java
Normal file
37
apps/i2psnark/java/src/org/klomp/snark/dht/MsgID.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package org.klomp.snark.dht;
|
||||
/*
|
||||
* GPLv2
|
||||
*/
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
|
||||
/**
|
||||
* Used for both incoming and outgoing message IDs
|
||||
*
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
class MsgID extends ByteArray {
|
||||
|
||||
/** BEP 5: 2 bytes, incremented */
|
||||
private static final int MY_TOK_LEN = 8;
|
||||
private static final int MAX_TOK_LEN = 16;
|
||||
|
||||
/** outgoing - generate a random ID */
|
||||
public MsgID(I2PAppContext ctx) {
|
||||
super(null);
|
||||
byte[] data = new byte[MY_TOK_LEN];
|
||||
ctx.random().nextBytes(data);
|
||||
setData(data);
|
||||
setValid(MY_TOK_LEN);
|
||||
}
|
||||
|
||||
/** incoming - save the ID (arbitrary length) */
|
||||
public MsgID(byte[] data) {
|
||||
super(data);
|
||||
// lets not get carried away
|
||||
if (data.length > MAX_TOK_LEN)
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
46
apps/i2psnark/java/src/org/klomp/snark/dht/NID.java
Normal file
46
apps/i2psnark/java/src/org/klomp/snark/dht/NID.java
Normal file
@@ -0,0 +1,46 @@
|
||||
package org.klomp.snark.dht;
|
||||
/*
|
||||
* From zzzot, modded and relicensed to GPLv2
|
||||
*/
|
||||
|
||||
import net.i2p.crypto.SHA1Hash;
|
||||
import net.i2p.util.Clock;
|
||||
|
||||
/**
|
||||
* A 20-byte peer ID, used as a Map key in lots of places.
|
||||
* Must be public for constructor in KBucketSet.generateRandomKey()
|
||||
*
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
public class NID extends SHA1Hash {
|
||||
|
||||
private long lastSeen;
|
||||
private int fails;
|
||||
|
||||
private static final int MAX_FAILS = 2;
|
||||
|
||||
public NID() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
public NID(byte[] data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
public long lastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
public void setLastSeen() {
|
||||
lastSeen = Clock.getInstance().now();
|
||||
fails = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if more than max timeouts
|
||||
*/
|
||||
public boolean timeout() {
|
||||
return ++fails > MAX_FAILS;
|
||||
}
|
||||
}
|
||||
253
apps/i2psnark/java/src/org/klomp/snark/dht/NodeInfo.java
Normal file
253
apps/i2psnark/java/src/org/klomp/snark/dht/NodeInfo.java
Normal file
@@ -0,0 +1,253 @@
|
||||
package org.klomp.snark.dht;
|
||||
/*
|
||||
* From zzzot, modded and relicensed to GPLv2
|
||||
*/
|
||||
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
import net.i2p.util.RandomSource;
|
||||
|
||||
/*
|
||||
* A Node ID, Hash, and port, and an optional Destination.
|
||||
* This is what DHTNodes remembers. The DHT tracker just stores Hashes.
|
||||
* getData() returns the 54 byte compact info (NID, Hash, port).
|
||||
*
|
||||
* Things are a little tricky in KRPC since we exchange Hashes and don't
|
||||
* always have the Destination.
|
||||
* The conpact info is immutable. The Destination may be added later.
|
||||
*
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
|
||||
class NodeInfo extends SimpleDataStructure {
|
||||
|
||||
private final NID nID;
|
||||
private final Hash hash;
|
||||
private Destination dest;
|
||||
private final int port;
|
||||
|
||||
public static final int LENGTH = NID.HASH_LENGTH + Hash.HASH_LENGTH + 2;
|
||||
|
||||
/**
|
||||
* With a fake NID used for pings
|
||||
*/
|
||||
public NodeInfo(Destination dest, int port) {
|
||||
super();
|
||||
this.nID = KRPC.FAKE_NID;
|
||||
this.dest = dest;
|
||||
this.hash = dest.calculateHash();
|
||||
this.port = port;
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this if we have the full destination
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
public NodeInfo(NID nID, Destination dest, int port) {
|
||||
super();
|
||||
this.nID = nID;
|
||||
this.dest = dest;
|
||||
this.hash = dest.calculateHash();
|
||||
this.port = port;
|
||||
initialize();
|
||||
verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* No Destination yet available
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
public NodeInfo(NID nID, Hash hash, int port) {
|
||||
super();
|
||||
this.nID = nID;
|
||||
this.hash = hash;
|
||||
this.port = port;
|
||||
initialize();
|
||||
verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* No Destination yet available
|
||||
* @param compactInfo 20 byte node ID, 32 byte destHash, 2 byte port
|
||||
* @param offset starting at this offset in compactInfo
|
||||
* @throws IllegalArgumentException
|
||||
* @throws AIOOBE
|
||||
*/
|
||||
public NodeInfo(byte[] compactInfo, int offset) {
|
||||
super();
|
||||
byte[] d = new byte[LENGTH];
|
||||
System.arraycopy(compactInfo, offset, d, 0, LENGTH);
|
||||
setData(d);
|
||||
byte[] ndata = new byte[NID.HASH_LENGTH];
|
||||
System.arraycopy(d, 0, ndata, 0, NID.HASH_LENGTH);
|
||||
this.nID = new NID(ndata);
|
||||
this.hash = Hash.create(d, NID.HASH_LENGTH);
|
||||
this.port = (int) DataHelper.fromLong(d, NID.HASH_LENGTH + Hash.HASH_LENGTH, 2);
|
||||
if (port <= 0 || port >= 65535)
|
||||
throw new IllegalArgumentException("Bad port");
|
||||
verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from persistent storage string.
|
||||
* Format: NID:Hash:Destination:port
|
||||
* First 3 in base 64; Destination may be empty string
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
public NodeInfo(String s) throws DataFormatException {
|
||||
super();
|
||||
String[] parts = s.split(":", 4);
|
||||
if (parts.length != 4)
|
||||
throw new DataFormatException("Bad format");
|
||||
byte[] nid = Base64.decode(parts[0]);
|
||||
if (nid == null)
|
||||
throw new DataFormatException("Bad NID");
|
||||
nID = new NID(nid);
|
||||
byte[] h = Base64.decode(parts[1]);
|
||||
if (h == null)
|
||||
throw new DataFormatException("Bad hash");
|
||||
//hash = new Hash(h);
|
||||
hash = Hash.create(h);
|
||||
if (parts[2].length() > 0)
|
||||
dest = new Destination(parts[2]);
|
||||
try {
|
||||
port = Integer.parseInt(parts[3]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new DataFormatException("Bad port", nfe);
|
||||
}
|
||||
initialize();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates 54-byte compact info
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
private void initialize() {
|
||||
if (port <= 0 || port >= 65535)
|
||||
throw new IllegalArgumentException("Bad port");
|
||||
byte[] compactInfo = new byte[LENGTH];
|
||||
System.arraycopy(nID.getData(), 0, compactInfo, 0, NID.HASH_LENGTH);
|
||||
System.arraycopy(hash.getData(), 0, compactInfo, NID.HASH_LENGTH, Hash.HASH_LENGTH);
|
||||
DataHelper.toLong(compactInfo, NID.HASH_LENGTH + Hash.HASH_LENGTH, 2, port);
|
||||
setData(compactInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a secure NID that matches the Hash and port.
|
||||
* Rules: First 4 bytes must match Hash.
|
||||
* Next 2 bytes must match Hash ^ port.
|
||||
* Remaining bytes may be random.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
public static NID generateNID(Hash h, int p, RandomSource random) {
|
||||
byte[] n = new byte[NID.HASH_LENGTH];
|
||||
System.arraycopy(h.getData(), 0, n, 0, 6);
|
||||
n[4] ^= (byte) (p >> 8);
|
||||
n[5] ^= (byte) p;
|
||||
random.nextBytes(n, 6, NID.HASH_LENGTH - 6);
|
||||
return new NID(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the NID matches the Hash.
|
||||
* See generateNID() for requirements.
|
||||
* @throws IllegalArgumentException on mismatch
|
||||
*/
|
||||
private void verify() {
|
||||
if (!KRPC.SECURE_NID)
|
||||
return;
|
||||
byte[] nb = nID.getData();
|
||||
byte[] hb = hash.getData();
|
||||
if ((!DataHelper.eq(nb, 0, hb, 0, 4)) ||
|
||||
((nb[4] ^ (port >> 8)) & 0xff) != (hb[4] & 0xff) ||
|
||||
((nb[5] ^ port) & 0xff) != (hb[5] & 0xff))
|
||||
throw new IllegalArgumentException("NID/Hash mismatch");
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return LENGTH;
|
||||
}
|
||||
|
||||
public NID getNID() {
|
||||
return this.nID;
|
||||
}
|
||||
|
||||
/** @return may be null if we don't have it */
|
||||
public Destination getDestination() {
|
||||
return this.dest;
|
||||
}
|
||||
|
||||
public Hash getHash() {
|
||||
return this.hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hash calculateHash() {
|
||||
return this.hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* This can come in later but the hash must match.
|
||||
* @throws IllegalArgumentException if hash of dest doesn't match previous hash
|
||||
*/
|
||||
public void setDestination(Destination dest) throws IllegalArgumentException {
|
||||
if (this.dest != null)
|
||||
return;
|
||||
if (!dest.calculateHash().equals(this.hash))
|
||||
throw new IllegalArgumentException("Hash mismatch, was: " + this.hash + " new: " + dest.calculateHash());
|
||||
this.dest = dest;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
public long lastSeen() {
|
||||
return nID.lastSeen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ nID.hashCode() ^ port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
try {
|
||||
NodeInfo ni = (NodeInfo) o;
|
||||
// assume dest matches, ignore it
|
||||
return this.hash.equals(ni.hash) && nID.equals(ni.nID) && port == ni.port;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NodeInfo: " + nID + ' ' + hash + " port: " + port + (dest != null ? " known dest" : " null dest");
|
||||
}
|
||||
|
||||
/**
|
||||
* To persistent storage string.
|
||||
* Format: NID:Hash:Destination:port
|
||||
* First 3 in base 64; Destination may be empty string
|
||||
*/
|
||||
public String toPersistentString() {
|
||||
StringBuilder buf = new StringBuilder(650);
|
||||
buf.append(nID.toBase64()).append(':');
|
||||
buf.append(hash.toBase64()).append(':');
|
||||
if (dest != null)
|
||||
buf.append(dest.toBase64());
|
||||
buf.append(':').append(port);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.klomp.snark.dht;
|
||||
/*
|
||||
* From zzzot, modded and relicensed to GPLv2
|
||||
*/
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import net.i2p.crypto.SHA1Hash;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* Closest to a InfoHash or NID key.
|
||||
* Use for NodeInfos.
|
||||
*
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
class NodeInfoComparator implements Comparator<NodeInfo> {
|
||||
private final byte[] _base;
|
||||
|
||||
public NodeInfoComparator(SHA1Hash h) {
|
||||
_base = h.getData();
|
||||
}
|
||||
|
||||
public int compare(NodeInfo lhs, NodeInfo rhs) {
|
||||
byte lhsDelta[] = DataHelper.xor(lhs.getNID().getData(), _base);
|
||||
byte rhsDelta[] = DataHelper.xor(rhs.getNID().getData(), _base);
|
||||
return DataHelper.compareTo(lhsDelta, rhsDelta);
|
||||
}
|
||||
|
||||
}
|
||||
30
apps/i2psnark/java/src/org/klomp/snark/dht/Peer.java
Normal file
30
apps/i2psnark/java/src/org/klomp/snark/dht/Peer.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package org.klomp.snark.dht;
|
||||
/*
|
||||
* From zzzot, modded and relicensed to GPLv2
|
||||
*/
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
/**
|
||||
* A single peer for a single torrent.
|
||||
* This is what the DHT tracker remembers.
|
||||
*
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
class Peer extends Hash {
|
||||
|
||||
private long lastSeen;
|
||||
|
||||
public Peer(byte[] data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
public long lastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
public void setLastSeen(long now) {
|
||||
lastSeen = now;
|
||||
}
|
||||
}
|
||||
21
apps/i2psnark/java/src/org/klomp/snark/dht/Peers.java
Normal file
21
apps/i2psnark/java/src/org/klomp/snark/dht/Peers.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package org.klomp.snark.dht;
|
||||
/*
|
||||
* From zzzot, modded and relicensed to GPLv2
|
||||
*/
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
/**
|
||||
* All the peers for a single torrent
|
||||
*
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
class Peers extends ConcurrentHashMap<Hash, Peer> {
|
||||
|
||||
public Peers() {
|
||||
super(8);
|
||||
}
|
||||
}
|
||||
88
apps/i2psnark/java/src/org/klomp/snark/dht/PersistDHT.java
Normal file
88
apps/i2psnark/java/src/org/klomp/snark/dht/PersistDHT.java
Normal file
@@ -0,0 +1,88 @@
|
||||
package org.klomp.snark.dht;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
|
||||
/**
|
||||
* Retrieve / Store the local DHT in a file
|
||||
*
|
||||
* @since 0.9.2
|
||||
*/
|
||||
abstract class PersistDHT {
|
||||
|
||||
private static final long MAX_AGE = 60*60*1000;
|
||||
|
||||
public static synchronized void loadDHT(KRPC krpc, File file) {
|
||||
Log log = I2PAppContext.getGlobalContext().logManager().getLog(PersistDHT.class);
|
||||
int count = 0;
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(file);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(in, "ISO-8859-1"));
|
||||
String line = null;
|
||||
while ( (line = br.readLine()) != null) {
|
||||
if (line.startsWith("#"))
|
||||
continue;
|
||||
try {
|
||||
krpc.heardAbout(new NodeInfo(line));
|
||||
count++;
|
||||
// TODO limit number? this will flush the router's SDS caches
|
||||
} catch (IllegalArgumentException iae) {
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("Error reading DHT entry", iae);
|
||||
} catch (DataFormatException dfe) {
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("Error reading DHT entry", dfe);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
if (log.shouldLog(Log.WARN) && file.exists())
|
||||
log.warn("Error reading the DHT File", ioe);
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("Loaded " + count + " nodes from " + file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param saveAll if true, don't check last seen time
|
||||
*/
|
||||
public static synchronized void saveDHT(DHTNodes nodes, boolean saveAll, File file) {
|
||||
if (nodes.size() <= 0)
|
||||
return;
|
||||
Log log = I2PAppContext.getGlobalContext().logManager().getLog(PersistDHT.class);
|
||||
int count = 0;
|
||||
long maxAge = saveAll ? 0 : I2PAppContext.getGlobalContext().clock().now() - MAX_AGE;
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "ISO-8859-1")));
|
||||
out.println("# DHT nodes, format is NID:Hash:Destination:port");
|
||||
for (NodeInfo ni : nodes.values()) {
|
||||
if (ni.lastSeen() < maxAge)
|
||||
continue;
|
||||
// DHTNodes shouldn't contain us, if that changes check here
|
||||
out.println(ni.toPersistentString());
|
||||
count++;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("Error writing the DHT File", ioe);
|
||||
} finally {
|
||||
if (out != null) out.close();
|
||||
}
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("Stored " + count + " nodes to " + file);
|
||||
}
|
||||
}
|
||||
75
apps/i2psnark/java/src/org/klomp/snark/dht/Token.java
Normal file
75
apps/i2psnark/java/src/org/klomp/snark/dht/Token.java
Normal file
@@ -0,0 +1,75 @@
|
||||
package org.klomp.snark.dht;
|
||||
/*
|
||||
* GPLv2
|
||||
*/
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* Used for Both outgoing and incoming tokens
|
||||
*
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
class Token extends ByteArray {
|
||||
|
||||
private static final int MY_TOK_LEN = 8;
|
||||
private static final int MAX_TOK_LEN = 64;
|
||||
private final long lastSeen;
|
||||
|
||||
/** outgoing - generate a random token */
|
||||
public Token(I2PAppContext ctx) {
|
||||
super(null);
|
||||
byte[] data = new byte[MY_TOK_LEN];
|
||||
ctx.random().nextBytes(data);
|
||||
setData(data);
|
||||
setValid(MY_TOK_LEN);
|
||||
lastSeen = ctx.clock().now();
|
||||
}
|
||||
|
||||
/** incoming - save the token (arbitrary length) */
|
||||
public Token(I2PAppContext ctx, byte[] data) {
|
||||
super(data);
|
||||
// lets not get carried away
|
||||
if (data.length > MAX_TOK_LEN)
|
||||
throw new IllegalArgumentException();
|
||||
lastSeen = ctx.clock().now();
|
||||
}
|
||||
|
||||
/** incoming - for lookup only, not storage, lastSeen is 0 */
|
||||
public Token(byte[] data) {
|
||||
super(data);
|
||||
lastSeen = 0;
|
||||
}
|
||||
|
||||
public long lastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(64);
|
||||
buf.append("[Token: ");
|
||||
byte[] bs = getData();
|
||||
if (bs.length == 0) {
|
||||
buf.append("0 bytes");
|
||||
} else {
|
||||
buf.append(bs.length).append(" bytes: 0x");
|
||||
// backwards, but the same way BEValue does it
|
||||
for (int i = 0; i < bs.length; i++) {
|
||||
int b = bs[i] & 0xff;
|
||||
if (b < 16)
|
||||
buf.append('0');
|
||||
buf.append(Integer.toHexString(b));
|
||||
}
|
||||
}
|
||||
if (lastSeen > 0)
|
||||
buf.append(" created ").append((new Date(lastSeen)).toString());
|
||||
buf.append(']');
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
20
apps/i2psnark/java/src/org/klomp/snark/dht/TokenKey.java
Normal file
20
apps/i2psnark/java/src/org/klomp/snark/dht/TokenKey.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package org.klomp.snark.dht;
|
||||
/*
|
||||
* GPLv2
|
||||
*/
|
||||
|
||||
import net.i2p.crypto.SHA1Hash;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* Used to index incoming Tokens
|
||||
*
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
class TokenKey extends SHA1Hash {
|
||||
|
||||
public TokenKey(NID nID, InfoHash ih) {
|
||||
super(DataHelper.xor(nID.getData(), ih.getData()));
|
||||
}
|
||||
}
|
||||
19
apps/i2psnark/java/src/org/klomp/snark/dht/Torrents.java
Normal file
19
apps/i2psnark/java/src/org/klomp/snark/dht/Torrents.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package org.klomp.snark.dht;
|
||||
/*
|
||||
* From zzzot, relicensed to GPLv2
|
||||
*/
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* All the torrents
|
||||
*
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
class Torrents extends ConcurrentHashMap<InfoHash, Peers> {
|
||||
|
||||
public Torrents() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
|
||||
_log = ctx.logManager().getLog(FetchAndAdd.class);
|
||||
_mgr = mgr;
|
||||
_url = url;
|
||||
_name = "* " + _("Download torrent file from {0}", url);
|
||||
_name = _("Download torrent file from {0}", url);
|
||||
byte[] fake = null;
|
||||
try {
|
||||
fake = SHA1.getInstance().digest(url.getBytes("ISO-8859-1"));
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.klomp.snark.SnarkManager;
|
||||
import org.klomp.snark.Storage;
|
||||
import org.klomp.snark.Tracker;
|
||||
import org.klomp.snark.TrackerClient;
|
||||
import org.klomp.snark.dht.DHT;
|
||||
|
||||
import org.mortbay.jetty.servlet.DefaultServlet;
|
||||
import org.mortbay.resource.Resource;
|
||||
@@ -74,8 +75,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_log = _context.logManager().getLog(I2PSnarkServlet.class);
|
||||
_nonce = _context.random().nextLong();
|
||||
// FIXME instantiate new one every time
|
||||
_manager = SnarkManager.instance();
|
||||
_manager = new SnarkManager(_context);
|
||||
String configFile = _context.getProperty(PROP_CONFIG_FILE);
|
||||
if ( (configFile == null) || (configFile.trim().length() <= 0) )
|
||||
configFile = "i2psnark.config";
|
||||
@@ -89,7 +89,8 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
_manager.stop();
|
||||
if (_manager != null)
|
||||
_manager.stop();
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@@ -448,9 +449,9 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
}
|
||||
out.write("</th></tr></thead>\n");
|
||||
String uri = "/i2psnark/";
|
||||
boolean showDebug = "2".equals(peerParam);
|
||||
for (int i = 0; i < snarks.size(); i++) {
|
||||
Snark snark = (Snark)snarks.get(i);
|
||||
boolean showDebug = "2".equals(peerParam);
|
||||
boolean showPeers = showDebug || "1".equals(peerParam) || Base64.encode(snark.getInfoHash()).equals(peerParam);
|
||||
displaySnark(out, snark, uri, i, stats, showPeers, isDegraded, noThinsp, showDebug);
|
||||
}
|
||||
@@ -461,7 +462,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
" colspan=\"11\"><i>");
|
||||
out.write(_("No torrents loaded."));
|
||||
out.write("</i></td></tr>\n");
|
||||
} else if (snarks.size() > 1) {
|
||||
} else /** if (snarks.size() > 1) */ {
|
||||
out.write("<tfoot><tr>\n" +
|
||||
" <th align=\"left\" colspan=\"6\">");
|
||||
out.write(_("Totals"));
|
||||
@@ -470,6 +471,16 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write(", ");
|
||||
out.write(DataHelper.formatSize2(stats[5]) + "B, ");
|
||||
out.write(ngettext("1 connected peer", "{0} connected peers", (int) stats[4]));
|
||||
DHT dht = _manager.util().getDHT();
|
||||
if (dht != null) {
|
||||
int dhts = dht.size();
|
||||
if (dhts > 0) {
|
||||
out.write(", ");
|
||||
out.write(ngettext("1 DHT peer", "{0} DHT peers", dhts));
|
||||
}
|
||||
if (showDebug)
|
||||
out.write(dht.renderStatusHTML());
|
||||
}
|
||||
out.write("</th>\n");
|
||||
if (_manager.util().connected()) {
|
||||
out.write(" <th align=\"right\">" + formatSize(stats[0]) + "</th>\n" +
|
||||
@@ -598,6 +609,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
if (meta == null) {
|
||||
// magnet - remove and delete are the same thing
|
||||
// Remove not shown on UI so we shouldn't get here
|
||||
_manager.deleteMagnet(snark);
|
||||
_manager.addMessage(_("Magnet deleted: {0}", name));
|
||||
return;
|
||||
@@ -626,7 +638,10 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
if (meta == null) {
|
||||
// magnet - remove and delete are the same thing
|
||||
_manager.deleteMagnet(snark);
|
||||
_manager.addMessage(_("Magnet deleted: {0}", name));
|
||||
if (snark instanceof FetchAndAdd)
|
||||
_manager.addMessage(_("Download deleted: {0}", name));
|
||||
else
|
||||
_manager.addMessage(_("Magnet deleted: {0}", name));
|
||||
return;
|
||||
}
|
||||
_manager.stopTorrent(snark, true);
|
||||
@@ -699,11 +714,12 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
String refreshDel = req.getParameter("refreshDelay");
|
||||
String startupDel = req.getParameter("startupDelay");
|
||||
boolean useOpenTrackers = req.getParameter("useOpenTrackers") != null;
|
||||
boolean useDHT = req.getParameter("useDHT") != null;
|
||||
//String openTrackers = req.getParameter("openTrackers");
|
||||
String theme = req.getParameter("theme");
|
||||
_manager.updateConfig(dataDir, filesPublic, autoStart, refreshDel, startupDel,
|
||||
seedPct, eepHost, eepPort, i2cpHost, i2cpPort, i2cpOpts,
|
||||
upLimit, upBW, useOpenTrackers, theme);
|
||||
upLimit, upBW, useOpenTrackers, useDHT, theme);
|
||||
} else if ("Save2".equals(action)) {
|
||||
String taction = req.getParameter("taction");
|
||||
if (taction != null)
|
||||
@@ -727,6 +743,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
try {
|
||||
// This may take a long time to check the storage, but since it already exists,
|
||||
// it shouldn't be THAT bad, so keep it in this thread.
|
||||
// TODO thread it for big torrents, perhaps a la FetchAndAdd
|
||||
boolean isPrivate = _manager.getPrivateTrackers().contains(announceURL);
|
||||
Storage s = new Storage(_manager.util(), baseFile, announceURL, isPrivate, null);
|
||||
s.close(); // close the files... maybe need a way to pass this Storage to addTorrent rather than starting over
|
||||
@@ -900,11 +917,17 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
TreeSet<String> fileNames = new TreeSet(new TorrentNameComparator());
|
||||
fileNames.addAll(files);
|
||||
ArrayList<Snark> rv = new ArrayList(fileNames.size());
|
||||
int magnet = 0;
|
||||
for (Iterator iter = fileNames.iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
Snark snark = _manager.getTorrent(name);
|
||||
if (snark != null)
|
||||
rv.add(snark);
|
||||
if (snark != null) {
|
||||
// put downloads and magnets first
|
||||
if (snark.getStorage() == null)
|
||||
rv.add(magnet++, snark);
|
||||
else
|
||||
rv.add(snark);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@@ -970,7 +993,13 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
|
||||
String rowClass = (row % 2 == 0 ? "snarkTorrentEven" : "snarkTorrentOdd");
|
||||
String statusString;
|
||||
if (err != null) {
|
||||
if (snark.isChecking()) {
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Checking") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Checking");
|
||||
} else if (snark.isAllocating()) {
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Allocating") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Allocating");
|
||||
} else if (err != null) {
|
||||
if (isRunning && curPeers > 0 && !showPeers)
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "trackererror.png\" title=\"" + err + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Tracker Error") +
|
||||
@@ -1152,7 +1181,9 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
String b64 = Base64.encode(snark.getInfoHash());
|
||||
if (showPeers)
|
||||
parameters = parameters + "&p=1";
|
||||
if (isRunning) {
|
||||
if (snark.isChecking()) {
|
||||
// show no buttons
|
||||
} else if (isRunning) {
|
||||
// Stop Button
|
||||
if (isDegraded)
|
||||
out.write("<a href=\"/i2psnark/?action=Stop_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
@@ -1492,6 +1523,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
boolean autoStart = _manager.shouldAutoStart();
|
||||
boolean useOpenTrackers = _manager.util().shouldUseOpenTrackers();
|
||||
//String openTrackers = _manager.util().getOpenTrackerString();
|
||||
boolean useDHT = _manager.util().shouldUseDHT();
|
||||
//int seedPct = 0;
|
||||
|
||||
out.write("<form action=\"/i2psnark/configure\" method=\"POST\">\n" +
|
||||
@@ -1605,6 +1637,14 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
+ (useOpenTrackers ? "checked " : "")
|
||||
+ "title=\"");
|
||||
out.write(_("If checked, announce torrents to open trackers as well as the tracker listed in the torrent file"));
|
||||
out.write("\" ></td></tr>\n" +
|
||||
|
||||
"<tr><td>");
|
||||
out.write(_("Enable DHT") + " (**BETA**)");
|
||||
out.write(": <td><input type=\"checkbox\" class=\"optbox\" name=\"useDHT\" value=\"true\" "
|
||||
+ (useDHT ? "checked " : "")
|
||||
+ "title=\"");
|
||||
out.write(_("If checked, use DHT"));
|
||||
out.write("\" ></td></tr>\n");
|
||||
|
||||
// "<tr><td>");
|
||||
@@ -1762,7 +1802,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
}
|
||||
ihash = xt.substring("urn:btih:".length());
|
||||
trackerURL = getTrackerParam(url);
|
||||
name = "* " + _("Magnet") + ' ' + ihash;
|
||||
name = _("Magnet") + ' ' + ihash;
|
||||
String dn = getParam("dn", url);
|
||||
if (dn != null)
|
||||
name += " (" + Storage.filterName(dn) + ')';
|
||||
@@ -1772,7 +1812,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
int col = ihash.indexOf(':');
|
||||
if (col >= 0)
|
||||
ihash = ihash.substring(0, col);
|
||||
name = "* " + _("Magnet") + ' ' + ihash;
|
||||
name = _("Magnet") + ' ' + ihash;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@@ -2093,12 +2133,14 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
.append(snark.getBaseName())
|
||||
.append("</th></tr>\n");
|
||||
|
||||
String fullPath = snark.getName();
|
||||
String baseName = urlEncode((new File(fullPath)).getName());
|
||||
buf.append("<tr><td>")
|
||||
.append("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "file.png\" > <b>")
|
||||
.append(_("Torrent file"))
|
||||
.append(":</b> ")
|
||||
.append(snark.getName())
|
||||
.append("</td></tr>\n");
|
||||
.append(":</b> <a href=\"/i2psnark/").append(baseName).append("\">")
|
||||
.append(fullPath)
|
||||
.append("</a></td></tr>\n");
|
||||
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
if (meta != null) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -694,7 +694,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
public void runClient(String args[], Logging l) {
|
||||
boolean isShared = true;
|
||||
if (args.length >= 3)
|
||||
isShared = Boolean.valueOf(args[2].trim()).booleanValue();
|
||||
isShared = Boolean.parseBoolean(args[2].trim());
|
||||
if (args.length >= 2) {
|
||||
int portNum = -1;
|
||||
try {
|
||||
@@ -717,7 +717,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
addtask(task);
|
||||
notifyEvent("clientTaskId", Integer.valueOf(task.getId()));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
String msg = "Invalid I2PTunnel configuration to create an HTTP Proxy connecting to the router at " + host + ':'+ port +
|
||||
String msg = "Invalid I2PTunnel configuration to create a standard client tunnel connecting to the router at " + host + ':'+ port +
|
||||
" and listening on " + listenHost + ':' + portNum;
|
||||
_log.error(getPrefix() + msg, iae);
|
||||
l.log(msg);
|
||||
@@ -766,7 +766,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
String proxy = "";
|
||||
boolean isShared = true;
|
||||
if (args.length > 1) {
|
||||
if (Boolean.valueOf(args[1].trim()).booleanValue()) {
|
||||
if (Boolean.parseBoolean(args[1].trim())) {
|
||||
isShared = true;
|
||||
if (args.length == 3)
|
||||
proxy = args[2];
|
||||
@@ -835,7 +835,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
String proxy = "";
|
||||
boolean isShared = true;
|
||||
if (args.length > 1) {
|
||||
if (Boolean.valueOf(args[1].trim()).booleanValue()) {
|
||||
if (Boolean.parseBoolean(args[1].trim())) {
|
||||
isShared = true;
|
||||
if (args.length == 3)
|
||||
proxy = args[2];
|
||||
@@ -906,7 +906,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
|
||||
boolean isShared = true;
|
||||
if (args.length > 2) {
|
||||
if (Boolean.valueOf(args[2].trim()).booleanValue()) {
|
||||
if (Boolean.parseBoolean(args[2].trim())) {
|
||||
isShared = true;
|
||||
} else if ("false".equalsIgnoreCase(args[2].trim())) {
|
||||
_log.warn("args[2] == [" + args[2] + "] and rejected explicitly");
|
||||
@@ -973,7 +973,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
|
||||
boolean isShared = false;
|
||||
if (args.length > 1)
|
||||
isShared = Boolean.valueOf(args[1].trim()).booleanValue();
|
||||
isShared = Boolean.parseBoolean(args[1].trim());
|
||||
|
||||
ownDest = !isShared;
|
||||
try {
|
||||
@@ -1017,7 +1017,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
|
||||
boolean isShared = false;
|
||||
if (args.length == 2)
|
||||
isShared = Boolean.valueOf(args[1].trim()).booleanValue();
|
||||
isShared = Boolean.parseBoolean(args[1].trim());
|
||||
|
||||
ownDest = !isShared;
|
||||
String privateKeyFile = null;
|
||||
|
||||
@@ -190,11 +190,11 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
// no need to load the netDb with leaseSets for destinations that will never
|
||||
// be looked up
|
||||
boolean dccEnabled = (this instanceof I2PTunnelIRCClient) &&
|
||||
Boolean.valueOf(tunnel.getClientOptions().getProperty(I2PTunnelIRCClient.PROP_DCC)).booleanValue();
|
||||
Boolean.parseBoolean(tunnel.getClientOptions().getProperty(I2PTunnelIRCClient.PROP_DCC));
|
||||
if (!dccEnabled)
|
||||
tunnel.getClientOptions().setProperty("i2cp.dontPublishLeaseSet", "true");
|
||||
|
||||
boolean openNow = !Boolean.valueOf(tunnel.getClientOptions().getProperty("i2cp.delayOpen")).booleanValue();
|
||||
boolean openNow = !Boolean.parseBoolean(tunnel.getClientOptions().getProperty("i2cp.delayOpen"));
|
||||
if (openNow) {
|
||||
while (sockMgr == null) {
|
||||
verifySocketManager();
|
||||
@@ -258,8 +258,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
if (sess == null) {
|
||||
newManager = true;
|
||||
} else if (sess.isClosed() &&
|
||||
Boolean.valueOf(getTunnel().getClientOptions().getProperty("i2cp.closeOnIdle")).booleanValue() &&
|
||||
Boolean.valueOf(getTunnel().getClientOptions().getProperty("i2cp.newDestOnResume")).booleanValue()) {
|
||||
Boolean.parseBoolean(getTunnel().getClientOptions().getProperty("i2cp.closeOnIdle")) &&
|
||||
Boolean.parseBoolean(getTunnel().getClientOptions().getProperty("i2cp.newDestOnResume"))) {
|
||||
// build a new socket manager and a new dest if the session is closed.
|
||||
getTunnel().removeSession(sess);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
|
||||
@@ -253,7 +253,7 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
|
||||
line = null;
|
||||
} else {
|
||||
// Add Proxy-Authentication header for next hop (outproxy)
|
||||
if (usingWWWProxy && Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_AUTH)).booleanValue()) {
|
||||
if (usingWWWProxy && Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_AUTH))) {
|
||||
// specific for this proxy
|
||||
String user = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_USER_PREFIX + currentProxy);
|
||||
String pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW_PREFIX + currentProxy);
|
||||
|
||||
@@ -521,7 +521,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
// Try to find an address helper in the query
|
||||
String[] helperStrings = removeHelper(query);
|
||||
if(helperStrings != null &&
|
||||
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
|
||||
!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER))) {
|
||||
query = helperStrings[0];
|
||||
if(query.equals("")) {
|
||||
query = null;
|
||||
@@ -736,7 +736,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
} else if(lowercaseLine.startsWith("user-agent: ")) {
|
||||
// save for deciding whether to offer address book form
|
||||
userAgent = lowercaseLine.substring(12);
|
||||
if(!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT)).booleanValue()) {
|
||||
if(!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT))) {
|
||||
line = null;
|
||||
continue;
|
||||
}
|
||||
@@ -746,13 +746,13 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
line = null;
|
||||
continue;
|
||||
} else if(lowercaseLine.startsWith("referer: ") &&
|
||||
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_REFERER)).booleanValue()) {
|
||||
!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_REFERER))) {
|
||||
// Shouldn't we be more specific, like accepting in-site referers ?
|
||||
//line = "Referer: i2p";
|
||||
line = null;
|
||||
continue; // completely strip the line
|
||||
} else if(lowercaseLine.startsWith("via: ") &&
|
||||
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_VIA)).booleanValue()) {
|
||||
!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_VIA))) {
|
||||
//line = "Via: i2p";
|
||||
line = null;
|
||||
continue; // completely strip the line
|
||||
@@ -786,7 +786,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
String ok = getTunnel().getClientOptions().getProperty("i2ptunnel.gzip");
|
||||
boolean gzip = DEFAULT_GZIP;
|
||||
if(ok != null) {
|
||||
gzip = Boolean.valueOf(ok).booleanValue();
|
||||
gzip = Boolean.parseBoolean(ok);
|
||||
}
|
||||
if(gzip && !usingInternalServer) {
|
||||
// according to rfc2616 s14.3, this *should* force identity, even if
|
||||
@@ -796,7 +796,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
newRequest.append("X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n");
|
||||
}
|
||||
if(!shout) {
|
||||
if(!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT)).booleanValue()) {
|
||||
if(!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT))) {
|
||||
// let's not advertise to external sites that we are from I2P
|
||||
if(usingWWWProxy) {
|
||||
newRequest.append("User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6\r\n");
|
||||
@@ -806,7 +806,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
}
|
||||
}
|
||||
// Add Proxy-Authentication header for next hop (outproxy)
|
||||
if(usingWWWProxy && Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_AUTH)).booleanValue()) {
|
||||
if(usingWWWProxy && Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_AUTH))) {
|
||||
// specific for this proxy
|
||||
String user = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_USER_PREFIX + currentProxy);
|
||||
String pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW_PREFIX + currentProxy);
|
||||
@@ -869,7 +869,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
if(usingInternalServer) {
|
||||
// disable the add form if address helper is disabled
|
||||
if(internalPath.equals("/add") &&
|
||||
Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
|
||||
Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER))) {
|
||||
out.write(ERR_HELPER_DISABLED);
|
||||
} else {
|
||||
LocalHTTPServer.serveLocalFile(out, method, internalPath, internalRawQuery, _proxyNonce);
|
||||
@@ -949,7 +949,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
// Don't do this for eepget, which uses a user-agent of "Wget"
|
||||
if(ahelperNew && "GET".equals(method) &&
|
||||
(userAgent == null || !userAgent.startsWith("Wget")) &&
|
||||
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
|
||||
!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER))) {
|
||||
writeHelperSaveForm(out, destination, ahelperKey, targetRequest);
|
||||
s.close();
|
||||
return;
|
||||
|
||||
@@ -100,7 +100,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
|
||||
// Ref: RFC 2617
|
||||
// If the socket is an InternalSocket, no auth required.
|
||||
String authRequired = getTunnel().getClientOptions().getProperty(PROP_AUTH);
|
||||
if (Boolean.valueOf(authRequired).booleanValue() ||
|
||||
if (Boolean.parseBoolean(authRequired) ||
|
||||
(authRequired != null && "basic".equals(authRequired.toLowerCase(Locale.US)))) {
|
||||
if (s instanceof InternalSocket) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -44,7 +45,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
private static final String[] CLIENT_SKIPHEADERS = {HASH_HEADER, DEST64_HEADER, DEST32_HEADER};
|
||||
private static final String SERVER_HEADER = "Server";
|
||||
private static final String[] SERVER_SKIPHEADERS = {SERVER_HEADER};
|
||||
private static final long HEADER_TIMEOUT = 60*1000;
|
||||
private static final long HEADER_TIMEOUT = 15*1000;
|
||||
private static final long TOTAL_HEADER_TIMEOUT = 2 * HEADER_TIMEOUT;
|
||||
private static final long START_INTERVAL = (60 * 1000) * 3;
|
||||
private long _startedOn = 0L;
|
||||
|
||||
@@ -153,7 +155,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
boolean allowGZIP = true;
|
||||
if (opts != null) {
|
||||
String val = opts.getProperty("i2ptunnel.gzip");
|
||||
if ( (val != null) && (!Boolean.valueOf(val).booleanValue()) )
|
||||
if ( (val != null) && (!Boolean.parseBoolean(val)) )
|
||||
allowGZIP = false;
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
@@ -219,6 +221,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
// shadows _log in super()
|
||||
private final Log _log;
|
||||
|
||||
private static final int BUF_SIZE = 16*1024;
|
||||
|
||||
public CompressedRequestor(Socket webserver, I2PSocket browser, String headers, I2PAppContext ctx, Log log) {
|
||||
_webserver = webserver;
|
||||
_browser = browser;
|
||||
@@ -259,7 +263,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
// at java.lang.Thread.run(Thread.java:619)
|
||||
// at net.i2p.util.I2PThread.run(I2PThread.java:71)
|
||||
try {
|
||||
serverin = _webserver.getInputStream();
|
||||
serverin = new BufferedInputStream(_webserver.getInputStream(), BUF_SIZE);
|
||||
} catch (NullPointerException npe) {
|
||||
throw new IOException("getInputStream NPE");
|
||||
}
|
||||
@@ -489,7 +493,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
}
|
||||
}
|
||||
|
||||
protected static Map<String, List<String>> readHeaders(InputStream in, StringBuilder command, String[] skipHeaders, I2PAppContext ctx) throws IOException {
|
||||
protected static Map<String, List<String>> readHeaders(InputStream in, StringBuilder command,
|
||||
String[] skipHeaders, I2PAppContext ctx) throws IOException {
|
||||
HashMap<String, List<String>> headers = new HashMap<String, List<String>>();
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
|
||||
@@ -513,6 +518,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
if (trimmed > 0)
|
||||
ctx.statManager().addRateData("i2ptunnel.httpNullWorkaround", trimmed, 0);
|
||||
|
||||
// slowloris / darkloris
|
||||
long expire = ctx.clock().now() + TOTAL_HEADER_TIMEOUT;
|
||||
int i = 0;
|
||||
while (true) {
|
||||
if (++i > MAX_HEADERS)
|
||||
@@ -525,6 +532,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
// end of headers reached
|
||||
return headers;
|
||||
} else {
|
||||
if (ctx.clock().now() > expire)
|
||||
throw new IOException("Headers took too long [" + buf.toString() + "]");
|
||||
int split = buf.indexOf(":");
|
||||
if (split <= 0) throw new IOException("Invalid HTTP header, missing colon [" + buf.toString() + "]");
|
||||
String name = buf.substring(0, split).trim();
|
||||
|
||||
@@ -87,7 +87,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase {
|
||||
|
||||
setName("IRC Client on " + tunnel.listenHost + ':' + localPort);
|
||||
|
||||
_dccEnabled = Boolean.valueOf(tunnel.getClientOptions().getProperty(PROP_DCC)).booleanValue();
|
||||
_dccEnabled = Boolean.parseBoolean(tunnel.getClientOptions().getProperty(PROP_DCC));
|
||||
// TODO add some prudent tunnel options (or is it too late?)
|
||||
|
||||
startRunning();
|
||||
|
||||
@@ -62,7 +62,8 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
|
||||
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";
|
||||
private static final long HEADER_TIMEOUT = 60*1000;
|
||||
private static final long HEADER_TIMEOUT = 15*1000;
|
||||
private static final long TOTAL_HEADER_TIMEOUT = 2 * HEADER_TIMEOUT;
|
||||
|
||||
private final static byte[] ERR_UNAVAILABLE =
|
||||
(":ircserver.i2p 499 you :" +
|
||||
@@ -188,12 +189,16 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
int lineCount = 0;
|
||||
|
||||
// slowloris / darkloris
|
||||
long expire = System.currentTimeMillis() + TOTAL_HEADER_TIMEOUT;
|
||||
while (true) {
|
||||
String s = DataHelper.readLine(in);
|
||||
if (s == null)
|
||||
throw new IOException("EOF reached before the end of the headers [" + buf.toString() + "]");
|
||||
if (++lineCount > 10)
|
||||
throw new IOException("Too many lines before USER or SERVER, giving up");
|
||||
if (System.currentTimeMillis() > expire)
|
||||
throw new IOException("Headers took too long [" + buf.toString() + "]");
|
||||
s = s.trim();
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Got line: " + s);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
@@ -17,6 +18,7 @@ import net.i2p.data.ByteArray;
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.InternalSocket;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErrorListener {
|
||||
@@ -143,7 +145,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
||||
OutputStream i2pout = i2ps.getOutputStream(); //new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
|
||||
if (initialI2PData != null) {
|
||||
// why synchronize this? we could be in here a LONG time for large initial data
|
||||
synchronized (slock) {
|
||||
//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.
|
||||
@@ -157,7 +159,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
||||
// only flush if it fits in one message.
|
||||
if (initialI2PData.length <= 1730) // ConnectionOptions.DEFAULT_MAX_MESSAGE_SIZE
|
||||
i2pout.flush();
|
||||
}
|
||||
//}
|
||||
}
|
||||
if (initialSocketData != null) {
|
||||
// this does not increment totalReceived
|
||||
@@ -167,6 +169,8 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
||||
_log.debug("Initial data " + (initialI2PData != null ? initialI2PData.length : 0)
|
||||
+ " written to I2P, " + (initialSocketData != null ? initialSocketData.length : 0)
|
||||
+ " written to the socket, starting forwarders");
|
||||
if (!(s instanceof InternalSocket))
|
||||
in = new BufferedInputStream(in, 2*NETWORK_BUFFER_SIZE);
|
||||
Thread t1 = new StreamForwarder(in, i2pout, true);
|
||||
Thread t2 = new StreamForwarder(i2pin, out, false);
|
||||
synchronized (finishLock) {
|
||||
|
||||
@@ -49,8 +49,8 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
|
||||
protected Logging l;
|
||||
|
||||
private static final long DEFAULT_READ_TIMEOUT = -1; // 3*60*1000;
|
||||
/** default timeout to 3 minutes - override if desired */
|
||||
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000;
|
||||
/** default timeout to 5 minutes - override if desired */
|
||||
protected long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
|
||||
/** do we use threads? default true (ignored for standard servers, always false) */
|
||||
@@ -74,6 +74,9 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
protected int localPort = DEFAULT_LOCALPORT;
|
||||
|
||||
/**
|
||||
* Warning, blocks in constructor while connecting to router and building tunnels;
|
||||
* TODO move that to startRunning()
|
||||
*
|
||||
* @param privData Base64-encoded private key data,
|
||||
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
||||
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
|
||||
@@ -87,6 +90,9 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning, blocks in constructor while connecting to router and building tunnels;
|
||||
* TODO move that to startRunning()
|
||||
*
|
||||
* @param privkey file containing the private key data,
|
||||
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
||||
* @param privkeyname the name of the privKey file, not clear why we need this too
|
||||
@@ -111,6 +117,9 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning, blocks in constructor while connecting to router and building tunnels;
|
||||
* TODO move that to startRunning()
|
||||
*
|
||||
* @param privData stream containing the private key data,
|
||||
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
||||
* @param privkeyname the name of the privKey file, not clear why we need this too
|
||||
@@ -124,6 +133,8 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-blocking
|
||||
*
|
||||
* @param sktMgr the existing socket manager
|
||||
* @since 0.8.9
|
||||
*/
|
||||
@@ -142,6 +153,9 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
private static final int MAX_RETRIES = 4;
|
||||
|
||||
/**
|
||||
* Warning, blocks while connecting to router and building tunnels;
|
||||
* TODO move that to startRunning()
|
||||
*
|
||||
* @param privData stream containing the private key data,
|
||||
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
||||
* @param privkeyname the name of the privKey file, not clear why we need this too
|
||||
@@ -177,7 +191,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
if (_usePool) {
|
||||
String usePool = getTunnel().getClientOptions().getProperty(PROP_USE_POOL);
|
||||
if (usePool != null)
|
||||
_usePool = Boolean.valueOf(usePool).booleanValue();
|
||||
_usePool = Boolean.parseBoolean(usePool);
|
||||
else
|
||||
_usePool = DEFAULT_USE_POOL;
|
||||
}
|
||||
@@ -193,7 +207,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
if (sockMgr == null) {
|
||||
// try to make this error sensible as it will happen...
|
||||
String msg = "Unable to connect to the router at " + getTunnel().host + ':' + portNum +
|
||||
" and build tunnels for the server at " + getTunnel().listenHost + ':' + port;
|
||||
" and build tunnels for the server at " + host.getHostAddress() + ':' + port;
|
||||
if (++retries < MAX_RETRIES) {
|
||||
this.l.log(msg + ", retrying in " + (RETRY_DELAY / 1000) + " seconds");
|
||||
_log.error(msg + ", retrying in " + (RETRY_DELAY / 1000) + " seconds");
|
||||
@@ -209,7 +223,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
|
||||
sockMgr.setName("Server");
|
||||
getTunnel().addSession(sockMgr.getSession());
|
||||
l.log("Tunnels ready for server at " + getTunnel().listenHost + ':' + port);
|
||||
l.log("Tunnels ready for server at " + host.getHostAddress() + ':' + port);
|
||||
notifyEvent("openServerResult", "ok");
|
||||
open = true;
|
||||
}
|
||||
@@ -236,6 +250,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
/**
|
||||
* Start running the I2PTunnelServer.
|
||||
*
|
||||
* TODO: Wait to connect to router until here.
|
||||
*/
|
||||
public void startRunning() {
|
||||
// prevent JVM exit when running outside the router
|
||||
@@ -360,7 +375,9 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
} catch (I2PException ipe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error accepting - KILLING THE TUNNEL SERVER", ipe);
|
||||
return;
|
||||
// TODO delay and loop if internal router is soft restarting?
|
||||
open = false;
|
||||
break;
|
||||
} catch (ConnectException ce) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error accepting", ce);
|
||||
|
||||
@@ -15,7 +15,7 @@ public abstract class I2PTunnelTask extends EventDispatcherImpl {
|
||||
|
||||
private int id;
|
||||
private String name;
|
||||
protected boolean open;
|
||||
protected volatile boolean open;
|
||||
private I2PTunnel tunnel;
|
||||
|
||||
//protected I2PTunnelTask(String name) {
|
||||
|
||||
@@ -156,8 +156,8 @@ public class TunnelController implements Logging {
|
||||
}
|
||||
String type = getType();
|
||||
if ( (type == null) || (type.length() <= 0) ) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Cannot start the tunnel - no type specified");
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Cannot start the tunnel - no type specified");
|
||||
return;
|
||||
}
|
||||
// Config options may have changed since instantiation, so do this again.
|
||||
@@ -455,6 +455,25 @@ public class TunnelController implements Logging {
|
||||
}
|
||||
}
|
||||
_config = props;
|
||||
|
||||
// Set up some per-type defaults
|
||||
// This really isn't the best spot to do this but for servers in particular,
|
||||
// it's hard to override settings in the subclass since the session connect
|
||||
// is done in the I2PTunnelServer constructor.
|
||||
String type = getType();
|
||||
if (type != null) {
|
||||
if (type.equals("httpserver") || type.equals("streamrserver")) {
|
||||
if (!_config.containsKey("option.shouldBundleReplyInfo"))
|
||||
_config.setProperty("option.shouldBundleReplyInfo", "false");
|
||||
} else if (type.contains("irc") || type.equals("streamrclient")) {
|
||||
// maybe a bad idea for ircclient if DCC is enabled
|
||||
if (!_config.containsKey("option.crypto.tagsToSend"))
|
||||
_config.setProperty("option.crypto.tagsToSend", "20");
|
||||
if (!_config.containsKey("option.crypto.lowTagThreshold"))
|
||||
_config.setProperty("option.crypto.lowTagThreshold", "14");
|
||||
}
|
||||
}
|
||||
|
||||
// tell i2ptunnel, who will tell the TunnelTask, who will tell the SocketManager
|
||||
setSessionOptions();
|
||||
if (_running && _sessions != null) {
|
||||
@@ -467,6 +486,9 @@ public class TunnelController implements Logging {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a copy
|
||||
*/
|
||||
public Properties getConfig(String prefix) {
|
||||
Properties rv = new Properties();
|
||||
for (Map.Entry e : _config.entrySet()) {
|
||||
@@ -514,8 +536,8 @@ public class TunnelController implements Logging {
|
||||
/** default true */
|
||||
public String getSharedClient() { return _config.getProperty("sharedClient", "true"); }
|
||||
/** default true */
|
||||
public boolean getStartOnLoad() { return Boolean.valueOf(_config.getProperty("startOnLoad", "true")).booleanValue(); }
|
||||
public boolean getPersistentClientKey() { return Boolean.valueOf(_config.getProperty("option.persistentClientKey")).booleanValue(); }
|
||||
public boolean getStartOnLoad() { return Boolean.parseBoolean(_config.getProperty("startOnLoad", "true")); }
|
||||
public boolean getPersistentClientKey() { return Boolean.parseBoolean(_config.getProperty("option.persistentClientKey")); }
|
||||
|
||||
public String getMyDestination() {
|
||||
if (_tunnel != null) {
|
||||
|
||||
@@ -61,7 +61,7 @@ public class SOCKS5Server extends SOCKSServer {
|
||||
this.clientSock = clientSock;
|
||||
this.props = props;
|
||||
this.authRequired =
|
||||
Boolean.valueOf(props.getProperty(I2PTunnelHTTPClientBase.PROP_AUTH)).booleanValue() &&
|
||||
Boolean.parseBoolean(props.getProperty(I2PTunnelHTTPClientBase.PROP_AUTH)) &&
|
||||
props.containsKey(I2PTunnelHTTPClientBase.PROP_USER) &&
|
||||
props.containsKey(I2PTunnelHTTPClientBase.PROP_PW);
|
||||
}
|
||||
@@ -181,7 +181,7 @@ public class SOCKS5Server extends SOCKSServer {
|
||||
sendRequestReply(Reply.COMMAND_NOT_SUPPORTED, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
|
||||
throw new SOCKSException("BIND command not supported");
|
||||
case Command.UDP_ASSOCIATE:
|
||||
/*** if(!Boolean.valueOf(tunnel.getOptions().getProperty("i2ptunnel.socks.allowUDP")).booleanValue()) {
|
||||
/*** if(!Boolean.parseBoolean(tunnel.getOptions().getProperty("i2ptunnel.socks.allowUDP"))) {
|
||||
_log.debug("UDP ASSOCIATE command is not supported!");
|
||||
sendRequestReply(Reply.COMMAND_NOT_SUPPORTED, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
|
||||
throw new SOCKSException("UDP ASSOCIATE command not supported");
|
||||
@@ -463,7 +463,7 @@ public class SOCKS5Server extends SOCKSServer {
|
||||
I2PSocket destSock = tun.createI2PSocket(I2PAppContext.getGlobalContext().namingService().lookup(proxy), proxyOpts);
|
||||
try {
|
||||
DataOutputStream out = new DataOutputStream(destSock.getOutputStream());
|
||||
boolean authAvail = Boolean.valueOf(props.getProperty(I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH)).booleanValue();
|
||||
boolean authAvail = Boolean.parseBoolean(props.getProperty(I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH));
|
||||
String configUser = null;
|
||||
String configPW = null;
|
||||
if (authAvail) {
|
||||
|
||||
@@ -49,7 +49,7 @@ public class SOCKSServerFactory {
|
||||
switch (socksVer) {
|
||||
case 0x04:
|
||||
// SOCKS version 4/4a
|
||||
if (Boolean.valueOf(props.getProperty(I2PTunnelHTTPClientBase.PROP_AUTH)).booleanValue() &&
|
||||
if (Boolean.parseBoolean(props.getProperty(I2PTunnelHTTPClientBase.PROP_AUTH)) &&
|
||||
props.containsKey(I2PTunnelHTTPClientBase.PROP_USER) &&
|
||||
props.containsKey(I2PTunnelHTTPClientBase.PROP_PW)) {
|
||||
throw new SOCKSException("SOCKS 4/4a not supported when authorization is required");
|
||||
|
||||
@@ -114,7 +114,7 @@ public class EditBean extends IndexBean {
|
||||
public boolean isSharedClient(int tunnel) {
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun != null)
|
||||
return Boolean.valueOf(tun.getSharedClient()).booleanValue();
|
||||
return Boolean.parseBoolean(tun.getSharedClient());
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@@ -312,7 +312,7 @@ public class EditBean extends IndexBean {
|
||||
if (tun != null) {
|
||||
Properties opts = getOptions(tun);
|
||||
if (opts != null)
|
||||
return Boolean.valueOf(opts.getProperty(prop)).booleanValue();
|
||||
return Boolean.parseBoolean(opts.getProperty(prop));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase;
|
||||
import net.i2p.i2ptunnel.I2PTunnelIRCClient;
|
||||
import net.i2p.i2ptunnel.TunnelController;
|
||||
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
||||
import net.i2p.util.Addresses;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.Log;
|
||||
@@ -265,7 +266,7 @@ public class IndexBean {
|
||||
}
|
||||
// Only modify other shared tunnels
|
||||
// if the current tunnel is shared, and of supported type
|
||||
if (Boolean.valueOf(cur.getSharedClient()).booleanValue() && isClient(cur.getType())) {
|
||||
if (Boolean.parseBoolean(cur.getSharedClient()) && isClient(cur.getType())) {
|
||||
// all clients use the same I2CP session, and as such, use the same I2CP options
|
||||
List controllers = _group.getControllers();
|
||||
|
||||
@@ -277,7 +278,7 @@ public class IndexBean {
|
||||
|
||||
// Only modify this non-current tunnel
|
||||
// if it belongs to a shared destination, and is of supported type
|
||||
if (Boolean.valueOf(c.getSharedClient()).booleanValue() && isClient(c.getType())) {
|
||||
if (Boolean.parseBoolean(c.getSharedClient()) && isClient(c.getType())) {
|
||||
Properties cOpt = c.getConfig("");
|
||||
if (_tunnelQuantity != null) {
|
||||
cOpt.setProperty("option.inbound.quantity", _tunnelQuantity);
|
||||
@@ -393,8 +394,8 @@ public class IndexBean {
|
||||
////
|
||||
|
||||
public String getTheme() {
|
||||
String theme = _context.getProperty(PROP_THEME_NAME, DEFAULT_THEME);
|
||||
return "/themes/console/" + theme + "/";
|
||||
String theme = _context.getProperty(PROP_THEME_NAME, DEFAULT_THEME);
|
||||
return "/themes/console/" + theme + "/";
|
||||
}
|
||||
|
||||
public boolean allowCSS() {
|
||||
@@ -436,6 +437,9 @@ public class IndexBean {
|
||||
return _("New Tunnel");
|
||||
}
|
||||
|
||||
/**
|
||||
* No validation
|
||||
*/
|
||||
public String getClientPort(int tunnel) {
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun != null && tun.getListenPort() != null)
|
||||
@@ -444,6 +448,28 @@ public class IndexBean {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns error message if blank or invalid
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public String getClientPort2(int tunnel) {
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun != null && tun.getListenPort() != null) {
|
||||
String port = tun.getListenPort();
|
||||
if (port.length() == 0)
|
||||
return "<font color=\"red\">" + _("Port not set") + "</font>";
|
||||
int iport = Addresses.getPort(port);
|
||||
if (iport == 0)
|
||||
return "<font color=\"red\">" + _("Invalid port") + ' ' + port + "</font>";
|
||||
if (iport < 1024)
|
||||
return "<font color=\"red\">" +
|
||||
_("Warning - ports less than 1024 are not recommended") +
|
||||
": " + port + "</font>";
|
||||
return port;
|
||||
}
|
||||
return "<font color=\"red\">" + _("Port not set") + "</font>";
|
||||
}
|
||||
|
||||
public String getTunnelType(int tunnel) {
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun != null)
|
||||
@@ -551,12 +577,16 @@ public class IndexBean {
|
||||
else
|
||||
host = tun.getTargetHost();
|
||||
String port = tun.getTargetPort();
|
||||
if (host == null)
|
||||
if (host == null || host.length() == 0)
|
||||
host = "<font color=\"red\">" + _("Host not set") + "</font>";
|
||||
else if (Addresses.getIP(host) == null)
|
||||
host = "<font color=\"red\">" + _("Invalid address") + ' ' + host + "</font>";
|
||||
else if (host.indexOf(':') >= 0)
|
||||
host = '[' + host + ']';
|
||||
if (port == null)
|
||||
if (port == null || port.length() == 0)
|
||||
port = "<font color=\"red\">" + _("Port not set") + "</font>";
|
||||
else if (Addresses.getPort(port) == 0)
|
||||
port = "<font color=\"red\">" + _("Invalid port") + ' ' + port + "</font>";
|
||||
return host + ':' + port;
|
||||
} else
|
||||
return "";
|
||||
|
||||
@@ -222,14 +222,8 @@
|
||||
<label><%=intl._("Port")%>:</label>
|
||||
<span class="text">
|
||||
<%
|
||||
String cPort= indexBean.getClientPort(curClient);
|
||||
if ("".equals(cPort)) {
|
||||
out.write("<font color=\"red\">");
|
||||
out.write(intl._("Port not set"));
|
||||
out.write("</font>");
|
||||
} else {
|
||||
out.write(cPort);
|
||||
}
|
||||
String cPort= indexBean.getClientPort2(curClient);
|
||||
out.write(cPort);
|
||||
%>
|
||||
</span>
|
||||
</div>
|
||||
@@ -291,7 +285,9 @@
|
||||
</label>
|
||||
<div class="text">
|
||||
<% String cdest = indexBean.getClientDestination(curClient);
|
||||
if (cdest.length() > 0) {
|
||||
if (cdest.length() > 70) { // Probably a B64 (a B32 is 60 chars) so truncate
|
||||
%><%=cdest.substring(0, 45)%>…<%=cdest.substring(cdest.length() - 15, cdest.length())%><%
|
||||
} else if (cdest.length() > 0) {
|
||||
%><%=cdest%><%
|
||||
} else {
|
||||
%><i><%=intl._("none")%></i><%
|
||||
|
||||
@@ -500,8 +500,7 @@
|
||||
}
|
||||
if ("httpclient".equals(tunnelType)) {
|
||||
%><input type="hidden" name="jumpList" value="http://i2host.i2p/cgi-bin/i2hostjump?
|
||||
http://stats.i2p/cgi-bin/jump.cgi?a=
|
||||
http://i2jump.i2p/" /><%
|
||||
http://stats.i2p/cgi-bin/jump.cgi?a=" /><%
|
||||
} /* httpclient */
|
||||
} else { /* Server-only defaults */
|
||||
%><input type="hidden" name="privKeyFile" value="<%=editBean.getPrivateKeyFile(-1)%>" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P i2ptunnel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2012-07-16 23:58+0000\n"
|
||||
"POT-Creation-Date: 2012-10-12 00:38+0000\n"
|
||||
"PO-Revision-Date: 2010-06-15 14:09+0100\n"
|
||||
"Last-Translator: duck <duck@mail.i2p>\n"
|
||||
"Language-Team: duck <duck@mail.i2p>\n"
|
||||
@@ -47,7 +47,7 @@ msgid "Base 32"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1031
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:380
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:374
|
||||
msgid "Destination"
|
||||
msgstr ""
|
||||
|
||||
@@ -131,94 +131,108 @@ msgstr ""
|
||||
msgid "internal"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:174
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:175
|
||||
msgid ""
|
||||
"Invalid form submission, probably because you used the 'back' or 'reload' "
|
||||
"button on your browser. Please resubmit."
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:221
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:222
|
||||
msgid "Configuration reloaded for all tunnels"
|
||||
msgstr ""
|
||||
|
||||
#. and give them something to look at in any case
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:233
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:234
|
||||
msgid "Starting tunnel"
|
||||
msgstr ""
|
||||
|
||||
#. and give them something to look at in any case
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:246
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:247
|
||||
msgid "Stopping tunnel"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:314
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:315
|
||||
msgid "Configuration changes saved"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:317
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:318
|
||||
msgid "Failed to save configuration"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:436
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:437
|
||||
msgid "New Tunnel"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:456
|
||||
msgid "Standard client"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:457
|
||||
msgid "HTTP client"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:458
|
||||
msgid "IRC client"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:459
|
||||
msgid "Standard server"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:460
|
||||
msgid "HTTP server"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:461
|
||||
msgid "SOCKS 4/4a/5 proxy"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:462
|
||||
msgid "SOCKS IRC proxy"
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:470
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:587
|
||||
msgid "Port not set"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:463
|
||||
msgid "CONNECT/SSL/HTTPS proxy"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:464
|
||||
msgid "IRC server"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:465
|
||||
msgid "Streamr client"
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:589
|
||||
msgid "Invalid port"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:466
|
||||
msgid "Warning - ports less than 1024 are not recommended"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:482
|
||||
msgid "Standard client"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:483
|
||||
msgid "HTTP client"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:484
|
||||
msgid "IRC client"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:485
|
||||
msgid "Standard server"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:486
|
||||
msgid "HTTP server"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:487
|
||||
msgid "SOCKS 4/4a/5 proxy"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:488
|
||||
msgid "SOCKS IRC proxy"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:489
|
||||
msgid "CONNECT/SSL/HTTPS proxy"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:490
|
||||
msgid "IRC server"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:491
|
||||
msgid "Streamr client"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:492
|
||||
msgid "Streamr server"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:467
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:493
|
||||
msgid "HTTP bidir"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:555
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:305
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:581
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:299
|
||||
msgid "Host not set"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:559
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:287
|
||||
msgid "Port not set"
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:583
|
||||
msgid "Invalid address"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:82
|
||||
@@ -246,14 +260,14 @@ msgstr ""
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:127
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:127
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:261
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:294
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:288
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:131
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:131
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:241
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:399
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:393
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:330
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
@@ -798,7 +812,7 @@ msgstr ""
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:129
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:192
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:265
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:312
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:306
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
@@ -811,30 +825,30 @@ msgid "No Preview"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:199
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:319
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:313
|
||||
msgid "Starting..."
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:206
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:220
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:326
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:340
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:354
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:320
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:334
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:348
|
||||
msgid "Stop"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:213
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:347
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:341
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:227
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:361
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:355
|
||||
msgid "Stopped"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:234
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:368
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:362
|
||||
msgid "Start"
|
||||
msgstr ""
|
||||
|
||||
@@ -843,7 +857,7 @@ msgid "New server tunnel"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:251
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:409
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:403
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:223
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:265
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:295
|
||||
@@ -853,7 +867,7 @@ msgid "Standard"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:253
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:411
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:405
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
|
||||
@@ -862,23 +876,23 @@ msgid "I2P Client Tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:263
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:298
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:292
|
||||
msgid "Interface"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:333
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:327
|
||||
msgid "Standby"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:377
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:371
|
||||
msgid "Outproxy"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:394
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:388
|
||||
msgid "none"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:407
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:401
|
||||
msgid "New client tunnel"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
# Copyright (C) 2009 The I2P Project
|
||||
# This file is distributed under the same license as the i2ptunnel package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
# "blabla", 2011.
|
||||
# <blabla@trash-mail.com>, 2011, 2012.
|
||||
# Daniel Mustieles <daniel.mustieles@gmail.com>, 2012.
|
||||
# ducki2p <ducki2p@gmail.com>, 2011.
|
||||
# foo <foo@bar>, 2009.
|
||||
# <punkibastardo@gmail.com>, 2011, 2012.
|
||||
@@ -13,17 +14,16 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2012-07-26 19:45+0000\n"
|
||||
"PO-Revision-Date: 2012-07-21 19:52+0000\n"
|
||||
"Last-Translator: blabla <blabla@trash-mail.com>\n"
|
||||
"Language-Team: Spanish (http://www.transifex.com/projects/p/I2P/language/"
|
||||
"es/)\n"
|
||||
"Language: es\n"
|
||||
"Report-Msgid-Bugs-To: https://trac.i2p2.de/\n"
|
||||
"POT-Creation-Date: 2012-10-12 00:38+0000\n"
|
||||
"PO-Revision-Date: 2012-10-18 08:55+0000\n"
|
||||
"Last-Translator: Daniel Mustieles <daniel.mustieles@gmail.com>\n"
|
||||
"Language-Team: Spanish (http://www.transifex.com/projects/p/I2P/language/es/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"Language: es\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:554
|
||||
msgid "This seems to be a bad destination:"
|
||||
@@ -36,13 +36,10 @@ msgstr "El ayudante de direcciones no te puede ayudar con un destino así."
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:621
|
||||
#, java-format
|
||||
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 ""
|
||||
"Para visitar el destino en la base de datos de hosts, ¡pincha <a href="
|
||||
"\"{0}\">aquí</a>! Para visitar el destino del ayudante de direcciones en "
|
||||
"conflicto, ¡pincha <a href=\"{1}\">aquí</a>!"
|
||||
"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 "Para visitar el destino en la base de datos de hosts, ¡pincha <a href=\"{0}\">aquí</a>! Para visitar el destino del ayudante de direcciones en conflicto, ¡pincha <a href=\"{1}\">aquí</a>!"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1023
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:403
|
||||
@@ -57,7 +54,7 @@ msgid "Base 32"
|
||||
msgstr "Base 32"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1031
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:380
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:374
|
||||
msgid "Destination"
|
||||
msgstr "Destino"
|
||||
|
||||
@@ -69,21 +66,18 @@ msgstr "Acceder a {0} sin guardar"
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1042
|
||||
#, java-format
|
||||
msgid "Save {0} to router address book and continue to eepsite"
|
||||
msgstr ""
|
||||
"Guardar {0} a la libreta de direcciones del router y acceder al sitio i2p."
|
||||
msgstr "Guardar {0} a la libreta de direcciones del router y acceder al sitio i2p."
|
||||
|
||||
#. only blockfile supports multiple books
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1045
|
||||
#, java-format
|
||||
msgid "Save {0} to master address book and continue to eepsite"
|
||||
msgstr ""
|
||||
"Guardar {0} a la libreta de direcciones principal y acceder al sitio i2p."
|
||||
msgstr "Guardar {0} a la libreta de direcciones principal y acceder al sitio i2p."
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1046
|
||||
#, java-format
|
||||
msgid "Save {0} to private address book and continue to eepsite"
|
||||
msgstr ""
|
||||
"Guardar {0} a la libreta de direcciones privada y acceder al sitio i2p."
|
||||
msgstr "Guardar {0} a la libreta de direcciones privada y acceder al sitio i2p."
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1211
|
||||
msgid "HTTP Outproxy"
|
||||
@@ -91,10 +85,9 @@ msgstr "Puerta de salida HTTP"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1216
|
||||
msgid ""
|
||||
"Click a link below to look for an address helper by using a \"jump\" service:"
|
||||
msgstr ""
|
||||
"Pincha en un enlace de debajo para buscar un ayudante de direcciones "
|
||||
"mediante el uso de un servicio de \"salto\":"
|
||||
"Click a link below to look for an address helper by using a \"jump\" "
|
||||
"service:"
|
||||
msgstr "Pincha en un enlace de debajo para buscar un ayudante de direcciones mediante el uso de un servicio de \"salto\":"
|
||||
|
||||
#. Translators: parameter is a host name
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1252
|
||||
@@ -146,97 +139,109 @@ msgstr "¡Haz click aquí si no estás siendo enviado automáticamente!"
|
||||
msgid "internal"
|
||||
msgstr "interno"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:174
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:175
|
||||
msgid ""
|
||||
"Invalid form submission, probably because you used the 'back' or 'reload' "
|
||||
"button on your browser. Please resubmit."
|
||||
msgstr ""
|
||||
"El formulario presentado es inválido, probablemente porque has utilizado el "
|
||||
"botón 'atrás' o 'recargar' de tu navegador. Por favor, ¡vuelve a enviarlo!"
|
||||
msgstr "El formulario presentado es inválido, probablemente porque has utilizado el botón 'atrás' o 'recargar' de tu navegador. Por favor, ¡vuelve a enviarlo!"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:221
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:222
|
||||
msgid "Configuration reloaded for all tunnels"
|
||||
msgstr "Configuración recargada para todos los túneles"
|
||||
|
||||
#. and give them something to look at in any case
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:233
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:234
|
||||
msgid "Starting tunnel"
|
||||
msgstr "Inicializando el túnel"
|
||||
|
||||
#. and give them something to look at in any case
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:246
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:247
|
||||
msgid "Stopping tunnel"
|
||||
msgstr "Deteniendo el túnel"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:314
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:315
|
||||
msgid "Configuration changes saved"
|
||||
msgstr "Cambios en la configuración guardados"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:317
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:318
|
||||
msgid "Failed to save configuration"
|
||||
msgstr "No se pudo guardar la configuración"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:436
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:437
|
||||
msgid "New Tunnel"
|
||||
msgstr "Nuevo túnel"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:456
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:460
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:470
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:587
|
||||
msgid "Port not set"
|
||||
msgstr "Puerto no establecido"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:463
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:589
|
||||
msgid "Invalid port"
|
||||
msgstr "Puerto no válido"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:466
|
||||
msgid "Warning - ports less than 1024 are not recommended"
|
||||
msgstr "Advertencia: no se recomienda usar puertos inferiores al 1024"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:482
|
||||
msgid "Standard client"
|
||||
msgstr "Cliente estándar"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:457
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:483
|
||||
msgid "HTTP client"
|
||||
msgstr "Cliente HTTP"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:458
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:484
|
||||
msgid "IRC client"
|
||||
msgstr "Cliente IRC"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:459
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:485
|
||||
msgid "Standard server"
|
||||
msgstr "Servidor estándar"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:460
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:486
|
||||
msgid "HTTP server"
|
||||
msgstr "Servidor HTTP"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:461
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:487
|
||||
msgid "SOCKS 4/4a/5 proxy"
|
||||
msgstr "Proxy SOCKS 4/4a/5"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:462
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:488
|
||||
msgid "SOCKS IRC proxy"
|
||||
msgstr "Proxy IRC SOCKS"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:463
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:489
|
||||
msgid "CONNECT/SSL/HTTPS proxy"
|
||||
msgstr "Proxy CONNECT/SSL/HTTPS"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:464
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:490
|
||||
msgid "IRC server"
|
||||
msgstr "Servidor de IRC"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:465
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:491
|
||||
msgid "Streamr client"
|
||||
msgstr "Cliente Streamr"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:466
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:492
|
||||
msgid "Streamr server"
|
||||
msgstr "Servidor Streamr"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:467
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:493
|
||||
msgid "HTTP bidir"
|
||||
msgstr "HTTP bidir"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:555
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:305
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:581
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:299
|
||||
msgid "Host not set"
|
||||
msgstr "Host no establecido"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:559
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:287
|
||||
msgid "Port not set"
|
||||
msgstr "Puerto no establecido"
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:583
|
||||
msgid "Invalid address"
|
||||
msgstr "Dirección no válida"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:82
|
||||
msgid "I2P Tunnel Manager - Edit Client Tunnel"
|
||||
@@ -263,14 +268,14 @@ msgstr "Nombre"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:127
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:127
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:261
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:294
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:288
|
||||
msgid "Type"
|
||||
msgstr "Tipo"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:131
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:131
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:241
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:399
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:393
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:330
|
||||
msgid "Description"
|
||||
msgstr "Descripción"
|
||||
@@ -329,9 +334,7 @@ msgstr "Túnel Compartido"
|
||||
msgid ""
|
||||
"(Share tunnels with other clients and irc/httpclients? Change requires "
|
||||
"restart of client proxy)"
|
||||
msgstr ""
|
||||
"(¿Compartir túneles con otros clientes y clientes de IRC/http? Cambiar esto "
|
||||
"requiere reiniciar el proxy de cliente)"
|
||||
msgstr "(¿Compartir túneles con otros clientes y clientes de IRC/http? Cambiar esto requiere reiniciar el proxy de cliente)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:225
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:135
|
||||
@@ -359,9 +362,7 @@ msgstr "Opciones de red avanzadas"
|
||||
msgid ""
|
||||
"(NOTE: when this client proxy is configured to share tunnels, then these "
|
||||
"options are for all the shared proxy clients!)"
|
||||
msgstr ""
|
||||
"(NOTA: Si este proxy de cliente está configurado para compartir túneles, "
|
||||
"estas opciones se aplicarán a todos los proxys de cliente compartidos.)"
|
||||
msgstr "(NOTA: Si este proxy de cliente está configurado para compartir túneles, estas opciones se aplicarán a todos los proxys de cliente compartidos.)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:245
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:255
|
||||
@@ -412,23 +413,18 @@ msgstr "Variación de 0 saltos (sin aleatoriedad, rendimiento constante)"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:300
|
||||
msgid ""
|
||||
"+ 0-1 hop variance (medium additive randomisation, subtractive performance)"
|
||||
msgstr ""
|
||||
"Variación de + 0-1 salto (aleatoriedad media aditiva, rendimiento "
|
||||
"substractivo)"
|
||||
msgstr "Variación de + 0-1 salto (aleatoriedad media aditiva, rendimiento substractivo)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:294
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:304
|
||||
msgid ""
|
||||
"+ 0-2 hop variance (high additive randomisation, subtractive performance)"
|
||||
msgstr ""
|
||||
"Variación de + 0-2 saltos (aleatoriedad alta aditiva, rendimiento "
|
||||
"substractivo)"
|
||||
msgstr "Variación de + 0-2 saltos (aleatoriedad alta aditiva, rendimiento substractivo)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:298
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:308
|
||||
msgid "+/- 0-1 hop variance (standard randomisation, standard performance)"
|
||||
msgstr ""
|
||||
"Variación de +/- 0-1 salto (aleatoriedad estándar, rendimiento estándar)"
|
||||
msgstr "Variación de +/- 0-1 salto (aleatoriedad estándar, rendimiento estándar)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:302
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:312
|
||||
@@ -448,25 +444,20 @@ msgstr "Cantidad"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:325
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:335
|
||||
msgid "1 inbound, 1 outbound tunnel (low bandwidth usage, less reliability)"
|
||||
msgstr ""
|
||||
"1 túnel entrante, 1 de salida (bajo uso de ancho de banda, menos fiabilidad)"
|
||||
msgstr "1 túnel entrante, 1 de salida (bajo uso de ancho de banda, menos fiabilidad)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:329
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:339
|
||||
msgid ""
|
||||
"2 inbound, 2 outbound tunnels (standard bandwidth usage, standard "
|
||||
"reliability)"
|
||||
msgstr ""
|
||||
"2 túneles entrantes, 2 de salida (uso de ancho de banda estándar, fiabilidad "
|
||||
"estándar)"
|
||||
msgstr "2 túneles entrantes, 2 de salida (uso de ancho de banda estándar, fiabilidad estándar)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:333
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:343
|
||||
msgid ""
|
||||
"3 inbound, 3 outbound tunnels (higher bandwidth usage, higher reliability)"
|
||||
msgstr ""
|
||||
"3 túneles entrantes, 3 de salida (mayor uso de ancho de banda, mayor "
|
||||
"fiabilidad)"
|
||||
msgstr "3 túneles entrantes, 3 de salida (mayor uso de ancho de banda, mayor fiabilidad)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:341
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:351
|
||||
@@ -486,24 +477,18 @@ msgstr "0 túneles de respaldo (redundancia 0, no aumenta el uso de recursos)"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:357
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:367
|
||||
msgid "1 backup tunnel each direction (low redundancy, low resource usage)"
|
||||
msgstr ""
|
||||
"1 túnel de respaldo en cada dirección (redundancia baja, uso bajo de "
|
||||
"recursos)"
|
||||
msgstr "1 túnel de respaldo en cada dirección (redundancia baja, uso bajo de recursos)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:361
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:371
|
||||
msgid ""
|
||||
"2 backup tunnels each direction (medium redundancy, medium resource usage)"
|
||||
msgstr ""
|
||||
"2 túneles de respaldo en cada dirección (redundancia media, uso de recursos "
|
||||
"medio)"
|
||||
msgstr "2 túneles de respaldo en cada dirección (redundancia media, uso de recursos medio)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:365
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:375
|
||||
msgid "3 backup tunnels each direction (high redundancy, high resource usage)"
|
||||
msgstr ""
|
||||
"3 túneles de respaldo en cada dirección (alta redundancia, uso de recursos "
|
||||
"alto)"
|
||||
msgstr "3 túneles de respaldo en cada dirección (alta redundancia, uso de recursos alto)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:373
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:383
|
||||
@@ -835,7 +820,7 @@ msgstr "Vista previa"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:129
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:192
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:265
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:312
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:306
|
||||
msgid "Status"
|
||||
msgstr "Estado"
|
||||
|
||||
@@ -848,30 +833,30 @@ msgid "No Preview"
|
||||
msgstr "Sin vista previa"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:199
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:319
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:313
|
||||
msgid "Starting..."
|
||||
msgstr "Iniciando..."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:206
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:220
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:326
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:340
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:354
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:320
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:334
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:348
|
||||
msgid "Stop"
|
||||
msgstr "Detener"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:213
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:347
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:341
|
||||
msgid "Running"
|
||||
msgstr "Ejecutándose"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:227
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:361
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:355
|
||||
msgid "Stopped"
|
||||
msgstr "Detenido"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:234
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:368
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:362
|
||||
msgid "Start"
|
||||
msgstr "Iniciar"
|
||||
|
||||
@@ -880,7 +865,7 @@ msgid "New server tunnel"
|
||||
msgstr "Nuevo servidor de túnel"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:251
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:409
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:403
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:223
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:265
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:295
|
||||
@@ -890,7 +875,7 @@ msgid "Standard"
|
||||
msgstr "Estándar"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:253
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:411
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:405
|
||||
msgid "Create"
|
||||
msgstr "Crear"
|
||||
|
||||
@@ -899,23 +884,23 @@ msgid "I2P Client Tunnels"
|
||||
msgstr "Túneles de cliente I2P"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:263
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:298
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:292
|
||||
msgid "Interface"
|
||||
msgstr "Interfaz"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:333
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:327
|
||||
msgid "Standby"
|
||||
msgstr "En espera"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:377
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:371
|
||||
msgid "Outproxy"
|
||||
msgstr "Puerta de salida"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:394
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:388
|
||||
msgid "none"
|
||||
msgstr "ninguno"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:407
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:401
|
||||
msgid "New client tunnel"
|
||||
msgstr "Nuevo túnel cliente"
|
||||
|
||||
@@ -959,35 +944,27 @@ msgstr "Asistente completado"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:189
|
||||
msgid ""
|
||||
"This wizard will take you through the various options available for creating "
|
||||
"tunnels in I2P."
|
||||
msgstr ""
|
||||
"Este asistente le guiará a través de las distintas opciones disponibles para "
|
||||
"la creación de túneles en I2P."
|
||||
"This wizard will take you through the various options available for creating"
|
||||
" tunnels in I2P."
|
||||
msgstr "Este asistente le guiará a través de las distintas opciones disponibles para la creación de túneles en I2P."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:191
|
||||
msgid ""
|
||||
"The first thing to decide is whether you want to create a server or a client "
|
||||
"tunnel."
|
||||
msgstr ""
|
||||
"Lo primero que debe decidir es si se desea crear un túnel de servidor o de "
|
||||
"cliente."
|
||||
"The first thing to decide is whether you want to create a server or a client"
|
||||
" tunnel."
|
||||
msgstr "Lo primero que debe decidir es si se desea crear un túnel de servidor o de cliente."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:193
|
||||
msgid ""
|
||||
"If you need to connect to a remote service, such as an IRC server inside I2P "
|
||||
"or a code repository, then you will require a CLIENT tunnel."
|
||||
msgstr ""
|
||||
"Si necesita conectarse a un servicio remoto, como un servidor de IRC dentro "
|
||||
"de I2P o un repositorio de código, va a requerir un túnel CLIENTE."
|
||||
"If you need to connect to a remote service, such as an IRC server inside I2P"
|
||||
" or a code repository, then you will require a CLIENT tunnel."
|
||||
msgstr "Si necesita conectarse a un servicio remoto, como un servidor de IRC dentro de I2P o un repositorio de código, va a requerir un túnel CLIENTE."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:195
|
||||
msgid ""
|
||||
"On the other hand, if you wish to host a service for others to connect to "
|
||||
"you'll need to create a SERVER tunnel."
|
||||
msgstr ""
|
||||
"Por otro lado, si desea hospedar un servicio para que otros puedan "
|
||||
"conectarse a usted, necesitará crear un túnel SERVIDOR."
|
||||
msgstr "Por otro lado, si desea hospedar un servicio para que otros puedan conectarse a usted, necesitará crear un túnel SERVIDOR."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:197
|
||||
msgid "Server Tunnel"
|
||||
@@ -1010,9 +987,7 @@ msgstr "Túnel básico para la conexión a un servicio dentro de I2P."
|
||||
msgid ""
|
||||
"Try this if none of the tunnel types below fit your requirements, or you "
|
||||
"don't know what type of tunnel you need."
|
||||
msgstr ""
|
||||
"Pruebe esto si ninguno de los tipos de túneles a continuación se ajustan a "
|
||||
"sus requerimientos, o si no sabe qué tipo de túnel necesita."
|
||||
msgstr "Pruebe esto si ninguno de los tipos de túneles a continuación se ajustan a sus requerimientos, o si no sabe qué tipo de túnel necesita."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:229
|
||||
msgid "Tunnel that acts as an HTTP proxy for reaching eepsites inside I2P."
|
||||
@@ -1021,20 +996,15 @@ msgstr "Túnel que actúa como un proxy HTTP para llegar a eepsites dentro I2P."
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:231
|
||||
msgid ""
|
||||
"Set your browser to use this tunnel as an http proxy, or set your "
|
||||
"\"http_proxy\" environment variable for command-line applications in GNU/"
|
||||
"Linux."
|
||||
msgstr ""
|
||||
"Configure su navegador para usar este túnel como un proxy HTTP, o configure "
|
||||
"su variable de entorno \"http_proxy\" para aplicaciones de línea de comandos "
|
||||
"en GNU / Linux."
|
||||
"\"http_proxy\" environment variable for command-line applications in "
|
||||
"GNU/Linux."
|
||||
msgstr "Configure su navegador para usar este túnel como un proxy HTTP, o configure su variable de entorno \"http_proxy\" para aplicaciones de línea de comandos en GNU / Linux."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:233
|
||||
msgid ""
|
||||
"Websites outside I2P can also be reached if an HTTP proxy within I2P is "
|
||||
"known."
|
||||
msgstr ""
|
||||
"También es posible llegar a sitios web de fuera de I2P si se conoce algún "
|
||||
"proxy HTTP dentro de I2P."
|
||||
msgstr "También es posible llegar a sitios web de fuera de I2P si se conoce algún proxy HTTP dentro de I2P."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:235
|
||||
msgid "Customised client tunnel specific for IRC connections."
|
||||
@@ -1044,17 +1014,13 @@ msgstr "Túnel de cliente personalizado específicamente para conexiones de IRC.
|
||||
msgid ""
|
||||
"With this tunnel type, your IRC client will be able to connect to an IRC "
|
||||
"network inside I2P."
|
||||
msgstr ""
|
||||
"Con este tipo de túnel, su cliente de IRC será capaz de conectarse a una red "
|
||||
"de IRC dentro de I2P."
|
||||
msgstr "Con este tipo de túnel, su cliente de IRC será capaz de conectarse a una red de IRC dentro de I2P."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:239
|
||||
msgid ""
|
||||
"Each IRC network in I2P that you wish to connect to will require its own "
|
||||
"tunnel. (See Also, SOCKS IRC)"
|
||||
msgstr ""
|
||||
"Cada red IRC en I2P a la que desee conectarse requerirá su propio túnel. "
|
||||
"(Véase también, SOCKS IRC)"
|
||||
msgstr "Cada red IRC en I2P a la que desee conectarse requerirá su propio túnel. (Véase también, SOCKS IRC)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:241
|
||||
msgid "A tunnel that implements the SOCKS protocol."
|
||||
@@ -1064,45 +1030,33 @@ msgstr "Un túnel que implementa el protocolo SOCKS."
|
||||
msgid ""
|
||||
"This enables both TCP and UDP connections to be made through a SOCKS "
|
||||
"outproxy within I2P."
|
||||
msgstr ""
|
||||
"Esto permite que las conexiones TCP y UDP se hagan a través de un outproxy "
|
||||
"SOCKS que esté dentro de I2P."
|
||||
msgstr "Esto permite que las conexiones TCP y UDP se hagan a través de un outproxy SOCKS que esté dentro de I2P."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:245
|
||||
msgid ""
|
||||
"A client tunnel implementing the SOCKS protocol, which is customised for "
|
||||
"connecting to IRC networks."
|
||||
msgstr ""
|
||||
"Un túnel de cliente que implementa el protocolo SOCKS, personalizado para la "
|
||||
"conexión con redes IRC."
|
||||
msgstr "Un túnel de cliente que implementa el protocolo SOCKS, personalizado para la conexión con redes IRC."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:247
|
||||
msgid ""
|
||||
"With this tunnel type, IRC networks in I2P can be reached by typing the I2P "
|
||||
"address into your IRC client, and configuring the IRC client to use this "
|
||||
"SOCKS tunnel."
|
||||
msgstr ""
|
||||
"Con este tipo de túnel, las redes IRC de I2P pueden ser alcanzadas "
|
||||
"escribiendo directamente la dirección I2P en el cliente de IRC, y "
|
||||
"configurando el cliente de IRC para utilizar este túnel SOCKS."
|
||||
msgstr "Con este tipo de túnel, las redes IRC de I2P pueden ser alcanzadas escribiendo directamente la dirección I2P en el cliente de IRC, y configurando el cliente de IRC para utilizar este túnel SOCKS."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:249
|
||||
msgid ""
|
||||
"This means that only one I2P tunnel is required rather than a separate "
|
||||
"tunnel per IRC network."
|
||||
msgstr ""
|
||||
"Esto significa que sólo es necesario un único túnel I2P en lugar de un túnel "
|
||||
"distinto por cada red IRC."
|
||||
msgstr "Esto significa que sólo es necesario un único túnel I2P en lugar de un túnel distinto por cada red IRC."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:251
|
||||
msgid ""
|
||||
"IRC networks outside I2P can also be reached if a SOCKS outproxy within I2P "
|
||||
"is known, though it depends on whether or not the outproxy has been blocked "
|
||||
"by the IRC network."
|
||||
msgstr ""
|
||||
"También se puede llegar a redes IRC de fuera de I2P si se conoce un outproxy "
|
||||
"SOCKS en I2P, aunque depende de si el outproxy ha sido bloqueado por la red "
|
||||
"IRC."
|
||||
msgstr "También se puede llegar a redes IRC de fuera de I2P si se conoce un outproxy SOCKS en I2P, aunque depende de si el outproxy ha sido bloqueado por la red IRC."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:253
|
||||
msgid "A client tunnel that implements the HTTP CONNECT command."
|
||||
@@ -1112,9 +1066,7 @@ msgstr "Un túnel de cliente que implementa el comando HTTP CONNECT."
|
||||
msgid ""
|
||||
"This enables TCP connections to be made through an HTTP outproxy, assuming "
|
||||
"the proxy supports the CONNECT command."
|
||||
msgstr ""
|
||||
"Esto permite hacer conexiones TCP a través de un outproxy HTTP, suponiendo "
|
||||
"que el servidor proxy admita el comando CONNECT."
|
||||
msgstr "Esto permite hacer conexiones TCP a través de un outproxy HTTP, suponiendo que el servidor proxy admita el comando CONNECT."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:257
|
||||
msgid "A customised client tunnel for Streamr."
|
||||
@@ -1122,8 +1074,7 @@ msgstr "Un túnel de cliente personalizado para Streamr."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:267
|
||||
msgid "A basic server tunnel for hosting a generic service inside I2P."
|
||||
msgstr ""
|
||||
"Un túnel básico de servidor para alojar un servicio genérico dentro de I2P."
|
||||
msgstr "Un túnel básico de servidor para alojar un servicio genérico dentro de I2P."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:271
|
||||
msgid "A server tunnel that is customised for HTTP connections."
|
||||
@@ -1137,28 +1088,21 @@ msgstr "Utilice este tipo de túnel si desea alojar una eepsite."
|
||||
msgid ""
|
||||
"A customised server tunnel that can both serve HTTP data and connect to "
|
||||
"other server tunnels."
|
||||
msgstr ""
|
||||
"Un túnel de servidor personalizado que puede servir tanto datos HTTP como "
|
||||
"conectar a otros túneles de servidor."
|
||||
msgstr "Un túnel de servidor personalizado que puede servir tanto datos HTTP como conectar a otros túneles de servidor."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:277
|
||||
msgid "This tunnel type is predominantly used when running a Seedless server."
|
||||
msgstr ""
|
||||
"Este tipo de túnel se utiliza principalmente cuando se ejecuta un servidor "
|
||||
"sin semillas (Seedless)."
|
||||
msgstr "Este tipo de túnel se utiliza principalmente cuando se ejecuta un servidor sin semillas (Seedless)."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:279
|
||||
msgid "A customised server tunnel for hosting IRC networks inside I2P."
|
||||
msgstr ""
|
||||
"Un túnel de servidor personalizado para alojar redes IRC dentro de I2P."
|
||||
msgstr "Un túnel de servidor personalizado para alojar redes IRC dentro de I2P."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:281
|
||||
msgid ""
|
||||
"Usually, a separate tunnel needs to be created for each IRC server that is "
|
||||
"to be accessible inside I2P."
|
||||
msgstr ""
|
||||
"Normalmente, se debe crear un túnel por separado para cada servidor IRC, que "
|
||||
"será accesible dentro de I2P."
|
||||
msgstr "Normalmente, se debe crear un túnel por separado para cada servidor IRC, que será accesible dentro de I2P."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:283
|
||||
msgid "A customised server tunnel for Streamr."
|
||||
@@ -1172,17 +1116,13 @@ msgstr "Elegir un nombre y una descripción para su túnel."
|
||||
msgid ""
|
||||
"These can be anything you want - they are just for ease of identifying the "
|
||||
"tunnel in the routerconsole."
|
||||
msgstr ""
|
||||
"Estos pueden ser lo que se quiera - son sólo para facilitar la "
|
||||
"identificación del túnel en la consola del router."
|
||||
msgstr "Estos pueden ser lo que se quiera - son sólo para facilitar la identificación del túnel en la consola del router."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:354
|
||||
msgid ""
|
||||
"If you know of any outproxies for this type of tunnel (either HTTP or "
|
||||
"SOCKS), fill them in below."
|
||||
msgstr ""
|
||||
"Si conoce algún outproxie para este tipo de túnel (HTTP o SOCKS), rellénelo "
|
||||
"a continuación."
|
||||
msgstr "Si conoce algún outproxie para este tipo de túnel (HTTP o SOCKS), rellénelo a continuación."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:356
|
||||
msgid "Separate multiple proxies with commas."
|
||||
@@ -1192,35 +1132,27 @@ msgstr "Separe múltiples servidores proxy con comas."
|
||||
msgid ""
|
||||
"Type in the I2P destination of the service that this client tunnel should "
|
||||
"connect to."
|
||||
msgstr ""
|
||||
"Introduzca el destino de I2P del servicio al que este túnel de cliente debe "
|
||||
"conectarse."
|
||||
msgstr "Introduzca el destino de I2P del servicio al que este túnel de cliente debe conectarse."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:376
|
||||
msgid ""
|
||||
"This could be the full base 64 destination key, or an I2P URL from your "
|
||||
"address book."
|
||||
msgstr ""
|
||||
"Este puede ser la clave de destino en base 64 o una dirección URL I2P de su "
|
||||
"libreta de direcciones."
|
||||
msgstr "Este puede ser la clave de destino en base 64 o una dirección URL I2P de su libreta de direcciones."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:406
|
||||
msgid ""
|
||||
"This is the IP that your service is running on, this is usually on the same "
|
||||
"machine so 127.0.0.1 is autofilled."
|
||||
msgstr ""
|
||||
"Esta es la IP en la que el servicio se está ejecutando, esto suele ser en la "
|
||||
"misma máquina, por lo que se ha auto-rellenado con 127.0.0.1 "
|
||||
msgstr "Esta es la IP en la que el servicio se está ejecutando, esto suele ser en la misma máquina, por lo que se ha auto-rellenado con 127.0.0.1 "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:429
|
||||
msgid "This is the port that the service is accepting connections on."
|
||||
msgstr ""
|
||||
"Este es el puerto por el que el servicio está aceptando conexiones entrantes."
|
||||
msgstr "Este es el puerto por el que el servicio está aceptando conexiones entrantes."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:450
|
||||
msgid "This is the port that the client tunnel will be accessed from locally."
|
||||
msgstr ""
|
||||
"Este es el puerto por el que se accederá al túnel de cliente localmente."
|
||||
msgstr "Este es el puerto por el que se accederá al túnel de cliente localmente."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:452
|
||||
msgid "This is also the client port for the HTTPBidir server tunnel."
|
||||
@@ -1230,9 +1162,7 @@ msgstr "También es el puerto de cliente para el túnel de servidor HTTPBidir."
|
||||
msgid ""
|
||||
"How do you want this tunnel to be accessed? By just this machine, your "
|
||||
"entire subnet, or external internet?"
|
||||
msgstr ""
|
||||
"¿Cómo quiere que se acceda a este túnel? ¿Sólo esta máquina, su subred "
|
||||
"entera, o todo el internet externo?"
|
||||
msgstr "¿Cómo quiere que se acceda a este túnel? ¿Sólo esta máquina, su subred entera, o todo el internet externo?"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:473
|
||||
msgid "You will most likely want to just allow 127.0.0.1"
|
||||
@@ -1242,41 +1172,30 @@ msgstr "Lo más probable es que desee permitir sólo 127.0.0.1"
|
||||
msgid ""
|
||||
"The I2P router can automatically start this tunnel for you when the router "
|
||||
"is started."
|
||||
msgstr ""
|
||||
"El router I2P puede activar automáticamente este túnel cuando el router se "
|
||||
"inicie"
|
||||
msgstr "El router I2P puede activar automáticamente este túnel cuando el router se inicie"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:517
|
||||
msgid ""
|
||||
"This can be useful for frequently-used tunnels (especially server tunnels), "
|
||||
"but for tunnels that are only used occassionally it would mean that the I2P "
|
||||
"router is creating and maintaining unnecessary tunnels."
|
||||
msgstr ""
|
||||
"Esto puede ser útil para los túneles de uso frecuente (especialmente en los "
|
||||
"túneles de servidor), pero para los túneles que sólo se utilizan "
|
||||
"ocasionalmente, significaría que el router I2P está creando y manteniendo "
|
||||
"túneles innecesarios."
|
||||
msgstr "Esto puede ser útil para los túneles de uso frecuente (especialmente en los túneles de servidor), pero para los túneles que sólo se utilizan ocasionalmente, significaría que el router I2P está creando y manteniendo túneles innecesarios."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:543
|
||||
msgid "The wizard has now collected enough information to create your tunnel."
|
||||
msgstr ""
|
||||
"El asistente ya ha recogido suficiente información para crear el túnel."
|
||||
msgstr "El asistente ya ha recogido suficiente información para crear el túnel."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:545
|
||||
msgid ""
|
||||
"Upon clicking the Save button below, the wizard will set up the tunnel, and "
|
||||
"take you back to the main I2PTunnel page."
|
||||
msgstr ""
|
||||
"Al hacer clic en el botón Guardar a continuación, el asistente creará el "
|
||||
"túnel, y le llevará de vuelta a la página principal de túneles I2P."
|
||||
msgstr "Al hacer clic en el botón Guardar a continuación, el asistente creará el túnel, y le llevará de vuelta a la página principal de túneles I2P."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:550
|
||||
msgid ""
|
||||
"Because you chose to automatically start the tunnel when the router starts, "
|
||||
"you don't have to do anything further."
|
||||
msgstr ""
|
||||
"Como ha decidido iniciar automáticamente el túnel cuando el router se "
|
||||
"inicie, no tiene que hacer nada más."
|
||||
msgstr "Como ha decidido iniciar automáticamente el túnel cuando el router se inicie, no tiene que hacer nada más."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:552
|
||||
msgid "The router will start the tunnel once it has been set up."
|
||||
@@ -1286,17 +1205,13 @@ msgstr "El router iniciará el túnel una vez haya sido establecido."
|
||||
msgid ""
|
||||
"Because you chose not to automatically start the tunnel, you will have to "
|
||||
"manually start it."
|
||||
msgstr ""
|
||||
"Como usted ha decidido no iniciar automáticamente el túnel, tendrá que "
|
||||
"iniciarlo de forma manual."
|
||||
msgstr "Como usted ha decidido no iniciar automáticamente el túnel, tendrá que iniciarlo de forma manual."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:558
|
||||
msgid ""
|
||||
"You can do this by clicking the Start button on the main page which "
|
||||
"corresponds to the new tunnel."
|
||||
msgstr ""
|
||||
"Esto se puede hacer haciendo clic en el botón Iniciar en la página principal "
|
||||
"que corresponde al nuevo túnel."
|
||||
msgstr "Esto se puede hacer haciendo clic en el botón Iniciar en la página principal que corresponde al nuevo túnel."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:562
|
||||
msgid "Below is a summary of the options you chose:"
|
||||
@@ -1306,19 +1221,14 @@ msgstr "A continuación se muestra un resumen de las opciones que ha elegido:"
|
||||
msgid ""
|
||||
"Alongside these basic settings, there are a number of advanced options for "
|
||||
"tunnel configuration."
|
||||
msgstr ""
|
||||
"Junto a estos valores básicos, hay una serie de opciones avanzadas para la "
|
||||
"configuración de túneles."
|
||||
msgstr "Junto a estos valores básicos, hay una serie de opciones avanzadas para la configuración de túneles."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:662
|
||||
msgid ""
|
||||
"The wizard will set reasonably sensible default values for these, but you "
|
||||
"can view and/or edit these by clicking on the tunnel's name in the main "
|
||||
"I2PTunnel page."
|
||||
msgstr ""
|
||||
"El asistente establecerá valores razonablemente sensibles para ellos por "
|
||||
"defecto, pero se pueden ver y/o editar haciendo clic en el nombre del túnel "
|
||||
"en la página de túneles I2P principal."
|
||||
msgstr "El asistente establecerá valores razonablemente sensibles para ellos por defecto, pero se pueden ver y/o editar haciendo clic en el nombre del túnel en la página de túneles I2P principal."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:704
|
||||
msgid "Previous"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1259
apps/i2ptunnel/locale/messages_pt.po
Normal file
1259
apps/i2ptunnel/locale/messages_pt.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,23 +2,22 @@
|
||||
# Copyright (C) 2009 The I2P Project
|
||||
# This file is distributed under the same license as the i2ptunnel package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
# Martin Svensson <digitalmannen@gmail.com>, 2011, 2012.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2012-07-26 19:10+0000\n"
|
||||
"PO-Revision-Date: 2012-07-23 16:31+0000\n"
|
||||
"Last-Translator: Martin Svensson <digitalmannen@gmail.com>\n"
|
||||
"Language-Team: Swedish (Sweden) (http://www.transifex.com/projects/p/I2P/"
|
||||
"language/sv_SE/)\n"
|
||||
"Language: sv_SE\n"
|
||||
"Report-Msgid-Bugs-To: https://trac.i2p2.de/\n"
|
||||
"POT-Creation-Date: 2012-10-12 00:38+0000\n"
|
||||
"PO-Revision-Date: 2012-10-12 00:40+0000\n"
|
||||
"Last-Translator: kytv <killyourtv@i2pmail.org>\n"
|
||||
"Language-Team: Swedish (Sweden) (http://www.transifex.com/projects/p/I2P/language/sv_SE/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"Language: sv_SE\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:554
|
||||
msgid "This seems to be a bad destination:"
|
||||
@@ -31,13 +30,10 @@ msgstr "i2padresshjälp kan inte hjälpa dig med ett sådant mål!"
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:621
|
||||
#, java-format
|
||||
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 ""
|
||||
"För att besöka målet i din värd databas href=\"{0}\"> klicka <a här </ a>. "
|
||||
"För att besöka de motstridiga hjälpaddresserna,<a href=\"{1}\"> klicka <a "
|
||||
"här </ a>."
|
||||
"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 "För att besöka målet i din värd databas href=\"{0}\"> klicka <a här </ a>. För att besöka de motstridiga hjälpaddresserna,<a href=\"{1}\"> klicka <a här </ a>."
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1023
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:403
|
||||
@@ -52,7 +48,7 @@ msgid "Base 32"
|
||||
msgstr "Bas 32"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1031
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:380
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:374
|
||||
msgid "Destination"
|
||||
msgstr "Mål"
|
||||
|
||||
@@ -83,10 +79,9 @@ msgstr "HTTP Utproxy"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1216
|
||||
msgid ""
|
||||
"Click a link below to look for an address helper by using a \"jump\" service:"
|
||||
msgstr ""
|
||||
"Klicka på en länk nedan för att söka efter en hjälpaddress genom att använda "
|
||||
"en \"hopp\" tjänst"
|
||||
"Click a link below to look for an address helper by using a \"jump\" "
|
||||
"service:"
|
||||
msgstr "Klicka på en länk nedan för att söka efter en hjälpaddress genom att använda en \"hopp\" tjänst"
|
||||
|
||||
#. Translators: parameter is a host name
|
||||
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1252
|
||||
@@ -138,97 +133,109 @@ msgstr "Klicka här om du inte omdirigeras automatiskt "
|
||||
msgid "internal"
|
||||
msgstr "Intern "
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:174
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:175
|
||||
msgid ""
|
||||
"Invalid form submission, probably because you used the 'back' or 'reload' "
|
||||
"button on your browser. Please resubmit."
|
||||
msgstr ""
|
||||
"Ogiltigt formulärbegäran, beror troligtvis på attt du använde 'tillbaka' "
|
||||
"eller 'uppdatera' knappen. Försök att skicka igen"
|
||||
msgstr "Ogiltigt formulärbegäran, beror troligtvis på attt du använde 'tillbaka' eller 'uppdatera' knappen. Försök att skicka igen"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:221
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:222
|
||||
msgid "Configuration reloaded for all tunnels"
|
||||
msgstr "Konfigurationen uppdateras för alla tunnlar"
|
||||
|
||||
#. and give them something to look at in any case
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:233
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:234
|
||||
msgid "Starting tunnel"
|
||||
msgstr "Startar tunnel"
|
||||
|
||||
#. and give them something to look at in any case
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:246
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:247
|
||||
msgid "Stopping tunnel"
|
||||
msgstr "Stannar tunnel"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:314
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:315
|
||||
msgid "Configuration changes saved"
|
||||
msgstr "Konfigurationsändringar sparas"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:317
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:318
|
||||
msgid "Failed to save configuration"
|
||||
msgstr "Det gick inte att spara konfigurationen"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:436
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:437
|
||||
msgid "New Tunnel"
|
||||
msgstr "Ny tunnel"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:456
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:460
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:470
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:587
|
||||
msgid "Port not set"
|
||||
msgstr "Ingen port angiven"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:463
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:589
|
||||
msgid "Invalid port"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:466
|
||||
msgid "Warning - ports less than 1024 are not recommended"
|
||||
msgstr ""
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:482
|
||||
msgid "Standard client"
|
||||
msgstr "Standard klient"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:457
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:483
|
||||
msgid "HTTP client"
|
||||
msgstr "HTTP-klient"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:458
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:484
|
||||
msgid "IRC client"
|
||||
msgstr "IRC-klient"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:459
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:485
|
||||
msgid "Standard server"
|
||||
msgstr "Standard server"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:460
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:486
|
||||
msgid "HTTP server"
|
||||
msgstr "HTTP server"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:461
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:487
|
||||
msgid "SOCKS 4/4a/5 proxy"
|
||||
msgstr "SOCKS 4/4a/5 proxy"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:462
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:488
|
||||
msgid "SOCKS IRC proxy"
|
||||
msgstr "SOCKS IRC proxy"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:463
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:489
|
||||
msgid "CONNECT/SSL/HTTPS proxy"
|
||||
msgstr "CONNECT/SSL/HTTPS proxy"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:464
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:490
|
||||
msgid "IRC server"
|
||||
msgstr "IRC-server"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:465
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:491
|
||||
msgid "Streamr client"
|
||||
msgstr "Klient för Streamr "
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:466
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:492
|
||||
msgid "Streamr server"
|
||||
msgstr "Server för Streamr"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:467
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:493
|
||||
msgid "HTTP bidir"
|
||||
msgstr "HTTP bidir"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:555
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:305
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:581
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:299
|
||||
msgid "Host not set"
|
||||
msgstr "Ingen värd angiven"
|
||||
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:559
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:287
|
||||
msgid "Port not set"
|
||||
msgstr "Ingen port angiven"
|
||||
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:583
|
||||
msgid "Invalid address"
|
||||
msgstr ""
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:82
|
||||
msgid "I2P Tunnel Manager - Edit Client Tunnel"
|
||||
@@ -255,14 +262,14 @@ msgstr "Namn"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:127
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:127
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:261
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:294
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:288
|
||||
msgid "Type"
|
||||
msgstr "Typ"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:131
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:131
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:241
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:399
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:393
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:330
|
||||
msgid "Description"
|
||||
msgstr "Beskrivning"
|
||||
@@ -321,9 +328,7 @@ msgstr "Delad klient"
|
||||
msgid ""
|
||||
"(Share tunnels with other clients and irc/httpclients? Change requires "
|
||||
"restart of client proxy)"
|
||||
msgstr ""
|
||||
"(Dela tunnlarna med andra klienter och irc/HTTP-klienter? Ändring kräver "
|
||||
"omstart av klientproxyn)"
|
||||
msgstr "(Dela tunnlarna med andra klienter och irc/HTTP-klienter? Ändring kräver omstart av klientproxyn)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:225
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:135
|
||||
@@ -351,9 +356,7 @@ msgstr "Avancerade nätverks instälningar"
|
||||
msgid ""
|
||||
"(NOTE: when this client proxy is configured to share tunnels, then these "
|
||||
"options are for all the shared proxy clients!)"
|
||||
msgstr ""
|
||||
"(OBS: när denna klientproxyn är konfigurerad för att dela tunnlar, då gäller "
|
||||
"dessa alternativ för alla delade proxyklienter!)"
|
||||
msgstr "(OBS: när denna klientproxyn är konfigurerad för att dela tunnlar, då gäller dessa alternativ för alla delade proxyklienter!)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:245
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:255
|
||||
@@ -435,26 +438,20 @@ msgstr "Antal"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:325
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:335
|
||||
msgid "1 inbound, 1 outbound tunnel (low bandwidth usage, less reliability)"
|
||||
msgstr ""
|
||||
"1 inkommande, 1 utgående tunnlar (låg bandbreddsanvändning, låg "
|
||||
"tillförlitlighet)"
|
||||
msgstr "1 inkommande, 1 utgående tunnlar (låg bandbreddsanvändning, låg tillförlitlighet)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:329
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:339
|
||||
msgid ""
|
||||
"2 inbound, 2 outbound tunnels (standard bandwidth usage, standard "
|
||||
"reliability)"
|
||||
msgstr ""
|
||||
"2 inkommande, 2 utgående tunnlar (normal bandbreddsanvändning, normal "
|
||||
"tillförlitlighet)"
|
||||
msgstr "2 inkommande, 2 utgående tunnlar (normal bandbreddsanvändning, normal tillförlitlighet)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:333
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:343
|
||||
msgid ""
|
||||
"3 inbound, 3 outbound tunnels (higher bandwidth usage, higher reliability)"
|
||||
msgstr ""
|
||||
"3 inkommande, 3 utgående tunnlar (Högre bandbreddsanvändning, högre "
|
||||
"tillförlitlighet)"
|
||||
msgstr "3 inkommande, 3 utgående tunnlar (Högre bandbreddsanvändning, högre tillförlitlighet)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:341
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:351
|
||||
@@ -469,8 +466,7 @@ msgstr "Antal reserver"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:353
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:363
|
||||
msgid "0 backup tunnels (0 redundancy, no added resource usage)"
|
||||
msgstr ""
|
||||
"0 reserv tunnlar i varje riktning (ingen redundans, ingen resursanvändning)"
|
||||
msgstr "0 reserv tunnlar i varje riktning (ingen redundans, ingen resursanvändning)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:357
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:367
|
||||
@@ -481,15 +477,12 @@ msgstr "1 reserv tunnel i varje riktning (låg redundans, låg resursanvändning
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:371
|
||||
msgid ""
|
||||
"2 backup tunnels each direction (medium redundancy, medium resource usage)"
|
||||
msgstr ""
|
||||
"2 reserv tunnlar i varje riktning (medel hög redundans, medel hög "
|
||||
"resursanvändning)"
|
||||
msgstr "2 reserv tunnlar i varje riktning (medel hög redundans, medel hög resursanvändning)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:365
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:375
|
||||
msgid "3 backup tunnels each direction (high redundancy, high resource usage)"
|
||||
msgstr ""
|
||||
"3 reserv tunnlar i varje riktning (hög redundans, hög resursanvändning)"
|
||||
msgstr "3 reserv tunnlar i varje riktning (hög redundans, hög resursanvändning)"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:373
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:383
|
||||
@@ -821,7 +814,7 @@ msgstr "förhandsvisning"
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:129
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:192
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:265
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:312
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:306
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
@@ -834,30 +827,30 @@ msgid "No Preview"
|
||||
msgstr "Ingen förhandsvisning"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:199
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:319
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:313
|
||||
msgid "Starting..."
|
||||
msgstr "Startar..."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:206
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:220
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:326
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:340
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:354
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:320
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:334
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:348
|
||||
msgid "Stop"
|
||||
msgstr "Stopp"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:213
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:347
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:341
|
||||
msgid "Running"
|
||||
msgstr "Kör"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:227
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:361
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:355
|
||||
msgid "Stopped"
|
||||
msgstr "Stoppad"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:234
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:368
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:362
|
||||
msgid "Start"
|
||||
msgstr "Start"
|
||||
|
||||
@@ -866,7 +859,7 @@ msgid "New server tunnel"
|
||||
msgstr "Ny severtunnel "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:251
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:409
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:403
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:223
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:265
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:295
|
||||
@@ -876,7 +869,7 @@ msgid "Standard"
|
||||
msgstr "Standard"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:253
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:411
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:405
|
||||
msgid "Create"
|
||||
msgstr "Skapa"
|
||||
|
||||
@@ -885,23 +878,23 @@ msgid "I2P Client Tunnels"
|
||||
msgstr "I2P Klienttunnel"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:263
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:298
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:292
|
||||
msgid "Interface"
|
||||
msgstr "Gränssnitt "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:333
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:327
|
||||
msgid "Standby"
|
||||
msgstr "Standby"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:377
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:371
|
||||
msgid "Outproxy"
|
||||
msgstr "Utproxy"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:394
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:388
|
||||
msgid "none"
|
||||
msgstr "Ingen"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:407
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:401
|
||||
msgid "New client tunnel"
|
||||
msgstr "Ny klienttunnel"
|
||||
|
||||
@@ -945,33 +938,27 @@ msgstr "Guiden färdig "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:189
|
||||
msgid ""
|
||||
"This wizard will take you through the various options available for creating "
|
||||
"tunnels in I2P."
|
||||
msgstr ""
|
||||
"Guiden tar dig igenom de olika inställningsmöjligheterna för att skapa "
|
||||
"tunnlar."
|
||||
"This wizard will take you through the various options available for creating"
|
||||
" tunnels in I2P."
|
||||
msgstr "Guiden tar dig igenom de olika inställningsmöjligheterna för att skapa tunnlar."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:191
|
||||
msgid ""
|
||||
"The first thing to decide is whether you want to create a server or a client "
|
||||
"tunnel."
|
||||
msgstr ""
|
||||
"Det första är att bestämma om det skall vara en server- eller klient-tunnel. "
|
||||
"The first thing to decide is whether you want to create a server or a client"
|
||||
" tunnel."
|
||||
msgstr "Det första är att bestämma om det skall vara en server- eller klient-tunnel. "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:193
|
||||
msgid ""
|
||||
"If you need to connect to a remote service, such as an IRC server inside I2P "
|
||||
"or a code repository, then you will require a CLIENT tunnel."
|
||||
msgstr ""
|
||||
"Om du ansluter till en fjärrtjänst så som tex en IRC-server inom I2P, behövs "
|
||||
"en KLIENT-tunnel. "
|
||||
"If you need to connect to a remote service, such as an IRC server inside I2P"
|
||||
" or a code repository, then you will require a CLIENT tunnel."
|
||||
msgstr "Om du ansluter till en fjärrtjänst så som tex en IRC-server inom I2P, behövs en KLIENT-tunnel. "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:195
|
||||
msgid ""
|
||||
"On the other hand, if you wish to host a service for others to connect to "
|
||||
"you'll need to create a SERVER tunnel."
|
||||
msgstr ""
|
||||
"Men om du vill göra en tjänst tillgänglig för andra behövs en SERVER-tunnel."
|
||||
msgstr "Men om du vill göra en tjänst tillgänglig för andra behövs en SERVER-tunnel."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:197
|
||||
msgid "Server Tunnel"
|
||||
@@ -987,32 +974,25 @@ msgstr "Det finns flera typer av tunnlar att välja på:"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:225
|
||||
msgid "Basic tunnel for connecting to a single service inside I2P."
|
||||
msgstr ""
|
||||
"Grundläggande tunnel för anslutning till en snigel tjänst innanför I2P. "
|
||||
msgstr "Grundläggande tunnel för anslutning till en snigel tjänst innanför I2P. "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:227
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:269
|
||||
msgid ""
|
||||
"Try this if none of the tunnel types below fit your requirements, or you "
|
||||
"don't know what type of tunnel you need."
|
||||
msgstr ""
|
||||
"Prova detta om ingen av valen passar eller du inte vet vilken typ av tunnel "
|
||||
"som behövs."
|
||||
msgstr "Prova detta om ingen av valen passar eller du inte vet vilken typ av tunnel som behövs."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:229
|
||||
msgid "Tunnel that acts as an HTTP proxy for reaching eepsites inside I2P."
|
||||
msgstr ""
|
||||
"Tunneln agerar som en HTTP-proxy för att komma åt eepsites innanför I2P. "
|
||||
msgstr "Tunneln agerar som en HTTP-proxy för att komma åt eepsites innanför I2P. "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:231
|
||||
msgid ""
|
||||
"Set your browser to use this tunnel as an http proxy, or set your "
|
||||
"\"http_proxy\" environment variable for command-line applications in GNU/"
|
||||
"Linux."
|
||||
msgstr ""
|
||||
"Peka din webbläsare på denna tunnel som en http-proxy eller ställ in "
|
||||
"miljövariabeln \"http_proxy\" för terminal baserade applikationer i GNU/"
|
||||
"Linux."
|
||||
"\"http_proxy\" environment variable for command-line applications in "
|
||||
"GNU/Linux."
|
||||
msgstr "Peka din webbläsare på denna tunnel som en http-proxy eller ställ in miljövariabeln \"http_proxy\" för terminal baserade applikationer i GNU/Linux."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:233
|
||||
msgid ""
|
||||
@@ -1034,9 +1014,7 @@ msgstr "Med denna tunneltyp kan IRC-klienter ansluta till IRC-nät inom I2P "
|
||||
msgid ""
|
||||
"Each IRC network in I2P that you wish to connect to will require its own "
|
||||
"tunnel. (See Also, SOCKS IRC)"
|
||||
msgstr ""
|
||||
"Varje IRC-nät inom I2P som du vill ansluta till kräver en egen tunnel (Se "
|
||||
"även: SOCKS IRC) "
|
||||
msgstr "Varje IRC-nät inom I2P som du vill ansluta till kräver en egen tunnel (Se även: SOCKS IRC) "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:241
|
||||
msgid "A tunnel that implements the SOCKS protocol."
|
||||
@@ -1046,9 +1024,7 @@ msgstr "En tunnel som implementerar SOCKS protokollet."
|
||||
msgid ""
|
||||
"This enables both TCP and UDP connections to be made through a SOCKS "
|
||||
"outproxy within I2P."
|
||||
msgstr ""
|
||||
"Detta möjliggör både TCP och UDP anslutningar genom SOCKS utgående-proxy "
|
||||
"innanför I2P "
|
||||
msgstr "Detta möjliggör både TCP och UDP anslutningar genom SOCKS utgående-proxy innanför I2P "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:245
|
||||
msgid ""
|
||||
@@ -1061,27 +1037,20 @@ msgid ""
|
||||
"With this tunnel type, IRC networks in I2P can be reached by typing the I2P "
|
||||
"address into your IRC client, and configuring the IRC client to use this "
|
||||
"SOCKS tunnel."
|
||||
msgstr ""
|
||||
"Med denna tunnel typen kan IRC-nät inom I2P nås genom att skriva in I2P "
|
||||
"adressen i IRC-klienten och konfigurera IRC-klienten att använda denna SOCKS-"
|
||||
"tunneln."
|
||||
msgstr "Med denna tunnel typen kan IRC-nät inom I2P nås genom att skriva in I2P adressen i IRC-klienten och konfigurera IRC-klienten att använda denna SOCKS-tunneln."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:249
|
||||
msgid ""
|
||||
"This means that only one I2P tunnel is required rather than a separate "
|
||||
"tunnel per IRC network."
|
||||
msgstr ""
|
||||
"Detta innebär att enbart en I2P-tunnel behövs istället för en tunnel per IRC-"
|
||||
"nät."
|
||||
msgstr "Detta innebär att enbart en I2P-tunnel behövs istället för en tunnel per IRC-nät."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:251
|
||||
msgid ""
|
||||
"IRC networks outside I2P can also be reached if a SOCKS outproxy within I2P "
|
||||
"is known, though it depends on whether or not the outproxy has been blocked "
|
||||
"by the IRC network."
|
||||
msgstr ""
|
||||
"IRC-nät utanför I2P kan nås om en SOCKS-proxy innanför I2P är känd, men det "
|
||||
"bror på om utgående-proxy har blockerats av IRC-nätet."
|
||||
msgstr "IRC-nät utanför I2P kan nås om en SOCKS-proxy innanför I2P är känd, men det bror på om utgående-proxy har blockerats av IRC-nätet."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:253
|
||||
msgid "A client tunnel that implements the HTTP CONNECT command."
|
||||
@@ -1091,9 +1060,7 @@ msgstr "En klient-tunnel som implementerar HTTP CONNECT kommandot. "
|
||||
msgid ""
|
||||
"This enables TCP connections to be made through an HTTP outproxy, assuming "
|
||||
"the proxy supports the CONNECT command."
|
||||
msgstr ""
|
||||
"Detta möjliggör TCP anslutningar genom en HTTP utgående-proxy om proxyn "
|
||||
"stödjer CONNECT kommandot"
|
||||
msgstr "Detta möjliggör TCP anslutningar genom en HTTP utgående-proxy om proxyn stödjer CONNECT kommandot"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:257
|
||||
msgid "A customised client tunnel for Streamr."
|
||||
@@ -1115,9 +1082,7 @@ msgstr "Använd denna denna typ av tunnel om du vill köra en eepsite."
|
||||
msgid ""
|
||||
"A customised server tunnel that can both serve HTTP data and connect to "
|
||||
"other server tunnels."
|
||||
msgstr ""
|
||||
"En anpassad server tunnel som hanterar både HTTP data och anslutningar till "
|
||||
"andra server-tunnlar."
|
||||
msgstr "En anpassad server tunnel som hanterar både HTTP data och anslutningar till andra server-tunnlar."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:277
|
||||
msgid "This tunnel type is predominantly used when running a Seedless server."
|
||||
@@ -1131,9 +1096,7 @@ msgstr "En server-tunnel för IRC-Nät innanför I2P."
|
||||
msgid ""
|
||||
"Usually, a separate tunnel needs to be created for each IRC server that is "
|
||||
"to be accessible inside I2P."
|
||||
msgstr ""
|
||||
"Vanligtvis behövs en separat tunnel för varje IRC-server som skall anslutas "
|
||||
"till innanför I2P."
|
||||
msgstr "Vanligtvis behövs en separat tunnel för varje IRC-server som skall anslutas till innanför I2P."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:283
|
||||
msgid "A customised server tunnel for Streamr."
|
||||
@@ -1147,17 +1110,13 @@ msgstr "Välj namn och beskrivning av tunneln."
|
||||
msgid ""
|
||||
"These can be anything you want - they are just for ease of identifying the "
|
||||
"tunnel in the routerconsole."
|
||||
msgstr ""
|
||||
"Kan vad som helst, används enbart för att enkelt identifiera tunneln i "
|
||||
"routerkonsolen "
|
||||
msgstr "Kan vad som helst, används enbart för att enkelt identifiera tunneln i routerkonsolen "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:354
|
||||
msgid ""
|
||||
"If you know of any outproxies for this type of tunnel (either HTTP or "
|
||||
"SOCKS), fill them in below."
|
||||
msgstr ""
|
||||
"Om du känner till några utgående proxies för denna typen av tunnlar (HTTP "
|
||||
"eller SOCKS), fyll i dem nedan. "
|
||||
msgstr "Om du känner till några utgående proxies för denna typen av tunnlar (HTTP eller SOCKS), fyll i dem nedan. "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:356
|
||||
msgid "Separate multiple proxies with commas."
|
||||
@@ -1179,9 +1138,7 @@ msgstr "Kan vara hela base 64 målsnyckeln eller en I2P URL från adressboken."
|
||||
msgid ""
|
||||
"This is the IP that your service is running on, this is usually on the same "
|
||||
"machine so 127.0.0.1 is autofilled."
|
||||
msgstr ""
|
||||
"Detta är IP-adressen som tjänsten körs på, detta är vanligtvis på samma "
|
||||
"maskin så 127.0.0.1 fylls i automatiskt. "
|
||||
msgstr "Detta är IP-adressen som tjänsten körs på, detta är vanligtvis på samma maskin så 127.0.0.1 fylls i automatiskt. "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:429
|
||||
msgid "This is the port that the service is accepting connections on."
|
||||
@@ -1199,9 +1156,7 @@ msgstr "Detta är också en klient-port för HTTPBidir server-tunneln."
|
||||
msgid ""
|
||||
"How do you want this tunnel to be accessed? By just this machine, your "
|
||||
"entire subnet, or external internet?"
|
||||
msgstr ""
|
||||
"Hur du vill att tunneln ska nås? Enbart denna maskinen, ditt lokala nät "
|
||||
"eller hela internet? "
|
||||
msgstr "Hur du vill att tunneln ska nås? Enbart denna maskinen, ditt lokala nät eller hela internet? "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:473
|
||||
msgid "You will most likely want to just allow 127.0.0.1"
|
||||
@@ -1218,10 +1173,7 @@ msgid ""
|
||||
"This can be useful for frequently-used tunnels (especially server tunnels), "
|
||||
"but for tunnels that are only used occassionally it would mean that the I2P "
|
||||
"router is creating and maintaining unnecessary tunnels."
|
||||
msgstr ""
|
||||
"Detta kan vara användbart för tunnlar som används ofta (tex server-tunnlar) "
|
||||
"men för tunnlar som används sällan innebär det att onödiga tunnlar "
|
||||
"upprättas. "
|
||||
msgstr "Detta kan vara användbart för tunnlar som används ofta (tex server-tunnlar) men för tunnlar som används sällan innebär det att onödiga tunnlar upprättas. "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:543
|
||||
msgid "The wizard has now collected enough information to create your tunnel."
|
||||
@@ -1231,17 +1183,13 @@ msgstr "Guiden hat samlat tillräckligt med information för att skapa tunneln.
|
||||
msgid ""
|
||||
"Upon clicking the Save button below, the wizard will set up the tunnel, and "
|
||||
"take you back to the main I2PTunnel page."
|
||||
msgstr ""
|
||||
"När du klickar på \"spara\" kommer guiden att skapa tunneln och sedan ta dig "
|
||||
"till sidan för tunnlar."
|
||||
msgstr "När du klickar på \"spara\" kommer guiden att skapa tunneln och sedan ta dig till sidan för tunnlar."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:550
|
||||
msgid ""
|
||||
"Because you chose to automatically start the tunnel when the router starts, "
|
||||
"you don't have to do anything further."
|
||||
msgstr ""
|
||||
"Efter som du valt att starta tunneln samtidigt som routern startas behövs "
|
||||
"inget mer göras. "
|
||||
msgstr "Efter som du valt att starta tunneln samtidigt som routern startas behövs inget mer göras. "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:552
|
||||
msgid "The router will start the tunnel once it has been set up."
|
||||
@@ -1251,9 +1199,7 @@ msgstr "Routern starta tunneln när den skapats. "
|
||||
msgid ""
|
||||
"Because you chose not to automatically start the tunnel, you will have to "
|
||||
"manually start it."
|
||||
msgstr ""
|
||||
"Efter som du valt att inte starta tunneln samtidigt som routern startas, "
|
||||
"behövs den startas manuellt."
|
||||
msgstr "Efter som du valt att inte starta tunneln samtidigt som routern startas, behövs den startas manuellt."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:558
|
||||
msgid ""
|
||||
@@ -1269,18 +1215,14 @@ msgstr "Nedan är en sammanfattning över valen du kan göra:"
|
||||
msgid ""
|
||||
"Alongside these basic settings, there are a number of advanced options for "
|
||||
"tunnel configuration."
|
||||
msgstr ""
|
||||
"Vid sidan om dessa grundägande inställningar finns att par avancerade val "
|
||||
"för tunneln. "
|
||||
msgstr "Vid sidan om dessa grundägande inställningar finns att par avancerade val för tunneln. "
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:662
|
||||
msgid ""
|
||||
"The wizard will set reasonably sensible default values for these, but you "
|
||||
"can view and/or edit these by clicking on the tunnel's name in the main "
|
||||
"I2PTunnel page."
|
||||
msgstr ""
|
||||
"Guiden väljer lämpliga värden för dessa. Men du kan ändra/se värdena på "
|
||||
"huvudsidan för I2P-tunnlar."
|
||||
msgstr "Guiden väljer lämpliga värden för dessa. Men du kan ändra/se värdena på huvudsidan för I2P-tunnlar."
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/wizard_jsp.java:704
|
||||
msgid "Previous"
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@@ -36,7 +37,7 @@ public class I2PSocketManagerFactory {
|
||||
* @return the newly created socket manager, or null if there were errors
|
||||
*/
|
||||
public static I2PSocketManager createManager() {
|
||||
return createManager(getHost(), getPort(), System.getProperties());
|
||||
return createManager(getHost(), getPort(), (Properties) System.getProperties().clone());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,7 +60,7 @@ public class I2PSocketManagerFactory {
|
||||
* @return the newly created socket manager, or null if there were errors
|
||||
*/
|
||||
public static I2PSocketManager createManager(String host, int port) {
|
||||
return createManager(host, port, System.getProperties());
|
||||
return createManager(host, port, (Properties) System.getProperties().clone());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +96,7 @@ public class I2PSocketManagerFactory {
|
||||
* @return the newly created socket manager, or null if there were errors
|
||||
*/
|
||||
public static I2PSocketManager createManager(InputStream myPrivateKeyStream) {
|
||||
return createManager(myPrivateKeyStream, getHost(), getPort(), System.getProperties());
|
||||
return createManager(myPrivateKeyStream, getHost(), getPort(), (Properties) System.getProperties().clone());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,10 +127,11 @@ public class I2PSocketManagerFactory {
|
||||
I2PClient client = I2PClientFactory.createClient();
|
||||
if (opts == null)
|
||||
opts = new Properties();
|
||||
for (Iterator iter = System.getProperties().keySet().iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
Properties syscopy = (Properties) System.getProperties().clone();
|
||||
for (Map.Entry e : syscopy.entrySet()) {
|
||||
String name = (String) e.getKey();
|
||||
if (!opts.containsKey(name))
|
||||
opts.setProperty(name, System.getProperty(name));
|
||||
opts.setProperty(name, (String) e.getValue());
|
||||
}
|
||||
//boolean oldLib = DEFAULT_MANAGER.equals(opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER));
|
||||
//if (oldLib && false) {
|
||||
|
||||
@@ -17,6 +17,9 @@ public interface I2PSocketOptions {
|
||||
/**
|
||||
* How long we will wait for the ACK from a SYN, in milliseconds.
|
||||
*
|
||||
* Default 60 seconds. Max of 2 minutes enforced in Connection.java,
|
||||
* and it also interprets <= 0 as default.
|
||||
*
|
||||
* @return milliseconds to wait, or -1 if we will wait indefinitely
|
||||
*/
|
||||
public long getConnectTimeout();
|
||||
@@ -24,6 +27,9 @@ public interface I2PSocketOptions {
|
||||
/**
|
||||
* Define how long we will wait for the ACK from a SYN, in milliseconds.
|
||||
*
|
||||
* Default 60 seconds. Max of 2 minutes enforced in Connection.java,
|
||||
* and it also interprets <= 0 as default.
|
||||
*
|
||||
* @param ms timeout in ms
|
||||
*/
|
||||
public void setConnectTimeout(long ms);
|
||||
@@ -32,6 +38,9 @@ public interface I2PSocketOptions {
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*
|
||||
* WARNING: Default -1 (unlimited), which is probably not what you want.
|
||||
*
|
||||
* @return timeout in ms
|
||||
*/
|
||||
public long getReadTimeout();
|
||||
@@ -40,6 +49,9 @@ public interface I2PSocketOptions {
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*
|
||||
* WARNING: Default -1 (unlimited), which is probably not what you want.
|
||||
*
|
||||
* @param ms timeout in ms
|
||||
*/
|
||||
public void setReadTimeout(long ms);
|
||||
@@ -50,6 +62,8 @@ public interface I2PSocketOptions {
|
||||
* either some data is removed or the connection is closed. If this is
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
* Default 64 KB
|
||||
*
|
||||
* @return buffer size limit, in bytes
|
||||
*/
|
||||
public int getMaxBufferSize();
|
||||
@@ -60,6 +74,8 @@ public interface I2PSocketOptions {
|
||||
* either some data is removed or the connection is closed. If this is
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
* Default 64 KB
|
||||
*
|
||||
* @param numBytes How much data will we accept that hasn't been written out yet.
|
||||
*/
|
||||
public void setMaxBufferSize(int numBytes);
|
||||
@@ -69,6 +85,9 @@ public interface I2PSocketOptions {
|
||||
* for the data to flush. If this value is exceeded, the write() throws
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*
|
||||
* Default -1 (unlimited)
|
||||
*
|
||||
* @return wait time to block on the output stream while waiting for the data to flush.
|
||||
*/
|
||||
public long getWriteTimeout();
|
||||
@@ -78,6 +97,9 @@ public interface I2PSocketOptions {
|
||||
* for the data to flush. If this value is exceeded, the write() throws
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*
|
||||
* Default -1 (unlimited)
|
||||
*
|
||||
* @param ms wait time to block on the output stream while waiting for the data to flush.
|
||||
*/
|
||||
public void setWriteTimeout(long ms);
|
||||
|
||||
@@ -96,6 +96,9 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
|
||||
/**
|
||||
* How long we will wait for the ACK from a SYN, in milliseconds.
|
||||
*
|
||||
* Default 60 seconds. Max of 2 minutes enforced in Connection.java,
|
||||
* and it also interprets <= 0 as default.
|
||||
*
|
||||
* @return milliseconds to wait, or -1 if we will wait indefinitely
|
||||
*/
|
||||
public long getConnectTimeout() {
|
||||
@@ -105,6 +108,9 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
|
||||
/**
|
||||
* Define how long we will wait for the ACK from a SYN, in milliseconds.
|
||||
*
|
||||
* Default 60 seconds. Max of 2 minutes enforced in Connection.java,
|
||||
* and it also interprets <= 0 as default.
|
||||
*
|
||||
*/
|
||||
public void setConnectTimeout(long ms) {
|
||||
_connectTimeout = ms;
|
||||
@@ -114,6 +120,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*
|
||||
* WARNING: Default -1 (unlimited), which is probably not what you want.
|
||||
*/
|
||||
public long getReadTimeout() {
|
||||
return _readTimeout;
|
||||
@@ -123,6 +131,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*
|
||||
* WARNING: Default -1 (unlimited), which is probably not what you want.
|
||||
*/
|
||||
public void setReadTimeout(long ms) {
|
||||
_readTimeout = ms;
|
||||
@@ -134,6 +144,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
|
||||
* either some data is removed or the connection is closed. If this is
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
* Default 64 KB
|
||||
*
|
||||
* @return buffer size limit, in bytes
|
||||
*/
|
||||
public int getMaxBufferSize() {
|
||||
@@ -146,6 +158,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
|
||||
* either some data is removed or the connection is closed. If this is
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
* Default 64 KB
|
||||
*
|
||||
*/
|
||||
public void setMaxBufferSize(int numBytes) {
|
||||
_maxBufferSize = numBytes;
|
||||
@@ -156,6 +170,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
|
||||
* for the data to flush. If this value is exceeded, the write() throws
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*
|
||||
* Default -1 (unlimited)
|
||||
*/
|
||||
public long getWriteTimeout() {
|
||||
return _writeTimeout;
|
||||
@@ -166,6 +182,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
|
||||
* for the data to flush. If this value is exceeded, the write() throws
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*
|
||||
* Default -1 (unlimited)
|
||||
*/
|
||||
public void setWriteTimeout(long ms) {
|
||||
_writeTimeout = ms;
|
||||
|
||||
@@ -44,6 +44,7 @@ fi
|
||||
# list specific files in core/ and router/ here, so we don't scan the whole tree
|
||||
ROUTERFILES="\
|
||||
../../../core/java/src/net/i2p/data/DataHelper.java \
|
||||
../../../core/java/src/net/i2p/util/LogWriter.java \
|
||||
../../../router/java/src/net/i2p/router/tasks/CoalesceStatsEvent.java \
|
||||
../../../router/java/src/net/i2p/router/RouterThrottleImpl.java \
|
||||
../../../router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java \
|
||||
|
||||
@@ -12,7 +12,8 @@ public class CSSHelper extends HelperBase {
|
||||
private static final Map<String, Boolean> _UACache = new ConcurrentHashMap();
|
||||
|
||||
public CSSHelper() {}
|
||||
|
||||
|
||||
public static final String PROP_UNIVERSAL_THEMING = "routerconsole.universal.theme";
|
||||
public static final String PROP_THEME_NAME = "routerconsole.theme";
|
||||
public static final String DEFAULT_THEME = "light";
|
||||
public static final String BASE_THEME_PATH = "/themes/console/";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user