forked from I2P_Developers/i2p.i2p
Compare commits
605 Commits
i2p-0.9.7
...
i2p-0.9.10
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b6ed48ec0 | |||
| 96f6865835 | |||
|
|
fa50f9f246 | ||
|
|
538b4b10d7 | ||
|
|
bdb3e26d07 | ||
|
|
ec87600e80 | ||
| 0624f46e67 | |||
|
|
ece1198dd4 | ||
| 8bbab31872 | |||
| 8c6922ac5f | |||
| 6b67f399f6 | |||
|
|
a9598633b3 | ||
|
|
1fb2672b67 | ||
|
|
4308ce6347 | ||
|
|
32b095efbd | ||
| eb4bdfcefb | |||
|
|
fc6554cabc | ||
|
|
058590f69b | ||
|
|
9825fcf97b | ||
|
|
1ed96d72b2 | ||
|
|
a29935abb3 | ||
|
|
e92a5da5a5 | ||
|
|
f08e0299ef | ||
| 9757435b09 | |||
| 80fadb4580 | |||
|
|
8658c23974 | ||
|
|
0264cc9030 | ||
|
|
bd5b6b32b5 | ||
|
|
dc0a1281bf | ||
| 44e7110c9a | |||
| c860c49c6b | |||
|
|
3b06f0b83c | ||
|
|
dc60c2b478 | ||
| b59aa1fb69 | |||
|
|
54a21bfa7b | ||
|
|
50f55877f8 | ||
|
|
f9ff262318 | ||
|
|
91ba76f2a6 | ||
|
|
ec97bc2f81 | ||
| b91b242a1a | |||
| 38186c8f75 | |||
| 1b3aefbbce | |||
| c03511b971 | |||
| 78e7599a8a | |||
| dc871cf1eb | |||
|
|
e98b9d0af5 | ||
|
|
2a09d5baa6 | ||
|
|
1feb317f8b | ||
|
|
09668453d0 | ||
| bf485d8bce | |||
| 38c02b44b9 | |||
| 04a596899a | |||
| ee1ed1bb82 | |||
| 2b39d28e99 | |||
| 8cb503d8bb | |||
| efff25a87c | |||
| 6e2583ad92 | |||
| af84bcf945 | |||
| 3dc429415b | |||
| ec9dd25631 | |||
| 2bda87d5a7 | |||
|
|
a7a816e0a7 | ||
| 3d9d722cee | |||
| 289a8e7b40 | |||
|
|
7d3aa33c25 | ||
| 0db1314595 | |||
| 68641626aa | |||
| 5b9fb403c9 | |||
|
|
a4114b96fd | ||
|
|
de184ed139 | ||
| 04c342ec6a | |||
| 27ce28027d | |||
| f8a54bde19 | |||
| 3acfdbe8f7 | |||
| d9fed57c89 | |||
| 46e7e9be82 | |||
| d87d4eb232 | |||
| 88ea451f81 | |||
|
|
822ec4aa53 | ||
| 7b0b07933f | |||
| 7fe8573df4 | |||
|
|
c180292358 | ||
|
|
a3fa48dcbe | ||
| 445e4301d5 | |||
|
|
736da22bba | ||
|
|
f29c64cd70 | ||
|
|
aa4b4b9d2b | ||
|
|
1112fc8544 | ||
|
|
9b361ac445 | ||
|
|
0ff423fc57 | ||
|
|
efe3bd2c05 | ||
|
|
f112baac48 | ||
|
|
707f616498 | ||
|
|
ed2feb3ff7 | ||
|
|
a17b1b99c0 | ||
|
|
27bc32f2f3 | ||
|
|
b535054e13 | ||
|
|
97a9a6090a | ||
|
|
8b8e2c88c1 | ||
|
|
9d7ee30c15 | ||
|
|
4ee144533a | ||
|
|
0f2a983bb7 | ||
|
|
8fd2f9090e | ||
|
|
8770d7eae0 | ||
|
|
c59ef24acf | ||
|
|
85aa2fb083 | ||
| 434b9fa0d1 | |||
| 56116ad8c2 | |||
| c0ef19a281 | |||
|
|
9804e5b7d9 | ||
|
|
2f33186e58 | ||
|
|
0347c56c96 | ||
|
|
e77409e57a | ||
|
|
615ba94559 | ||
|
|
ce0596d5b1 | ||
|
|
35b6926e4f | ||
| 76925fa3bd | |||
| dbdf36d85c | |||
| 60aa8c57a4 | |||
| 001070f677 | |||
|
|
c6f2d4948b | ||
| 8699c82614 | |||
| 1d7eedd463 | |||
| 796a231f54 | |||
|
|
e1fcad686c | ||
| ffa03f2b83 | |||
| 54fb91ba8e | |||
| e498e2113f | |||
| f42ac71fe0 | |||
| 74f2fd06cc | |||
| 143a0dfc47 | |||
|
|
fdb0097934 | ||
|
|
0dde4162e6 | ||
| 844bae18ba | |||
| 9e4d5c0e61 | |||
| 9b8d3eb688 | |||
|
|
8478bfbddc | ||
|
|
d6bb5f6a4d | ||
|
|
e9fec9354b | ||
|
|
f9f0e6d0a2 | ||
|
|
3bc0be1cbe | ||
|
|
8d826cee93 | ||
|
|
e853d9a40b | ||
|
|
15bf94b479 | ||
|
|
6314f33d8f | ||
|
|
5fa0376f58 | ||
|
|
552dd189a5 | ||
|
|
57144f3e6a | ||
| 0454639db8 | |||
|
|
c32b451733 | ||
|
|
2f4765665d | ||
|
|
bff79cdae8 | ||
|
|
4bddf8ae0b | ||
|
|
ae79deff39 | ||
|
|
e3aeb267f8 | ||
|
|
c5c26c440d | ||
|
|
77971624b4 | ||
|
|
f5621c5082 | ||
|
|
3fa7fe9733 | ||
|
|
c97d07e10a | ||
|
|
567c328331 | ||
|
|
3aa982529e | ||
|
|
0c07f9ff96 | ||
|
|
f0055ccbfe | ||
|
|
693cc828c2 | ||
|
|
688dd23111 | ||
|
|
e38db5eb44 | ||
|
|
817f531619 | ||
|
|
53623da2eb | ||
|
|
4910266d9b | ||
|
|
24ae66df6d | ||
|
|
228bd980db | ||
|
|
f161a2dfc9 | ||
|
|
abe1dc676e | ||
| 9de57a5d5a | |||
|
|
2cc742c3ed | ||
|
|
5bcfe025d5 | ||
|
|
6dc6ca7713 | ||
|
|
eabcc96a99 | ||
|
|
28b6675979 | ||
|
|
413ad6b0e6 | ||
|
|
a7a7e96188 | ||
|
|
796dbc5d2e | ||
|
|
c86845078c | ||
|
|
89dcceefee | ||
|
|
bacce17990 | ||
|
|
e61e950713 | ||
|
|
244209d3b7 | ||
| dbe0a8240e | |||
| 7e3e08532f | |||
| 1d4190734d | |||
| 96cf1d60c2 | |||
|
|
3aa33378c1 | ||
|
|
747bd0c5a3 | ||
| ea7b42810f | |||
| 19022baa27 | |||
|
|
e8248f5005 | ||
|
|
f8178b7165 | ||
|
|
79b5d9748d | ||
| b53ed94e8f | |||
| df84a2fcd0 | |||
|
|
25e7dea370 | ||
|
|
90919ebf6b | ||
| 76078deb3f | |||
|
|
1b95a03d2e | ||
|
|
108039de08 | ||
|
|
addd2e6d6a | ||
|
|
5c38d5a6c9 | ||
| 69489dd19e | |||
| 3fce0e8e45 | |||
|
|
35fb332c2c | ||
| 1b5309be05 | |||
| d2a1025b3f | |||
|
|
0a8f79f0e3 | ||
| 18e4c2ac63 | |||
| 90c2e08489 | |||
| 598ef67c4e | |||
| 1b9d870b91 | |||
| 68f67b7c8e | |||
|
|
d2f0c251c0 | ||
| 2b2f34b3f1 | |||
| 4e680479da | |||
| d1b93e0705 | |||
| 4382def62f | |||
| 952a56c537 | |||
| 67aead214b | |||
| 50f45a50a7 | |||
|
|
6b326c3705 | ||
|
|
919ec3af01 | ||
|
|
ca5a301a4f | ||
| ae76a6ee1a | |||
| 5cbecb3599 | |||
| 5a34e1de4f | |||
| c810694e07 | |||
| ca866d48e6 | |||
|
|
f1e77499e2 | ||
| 9007db1485 | |||
| 85c998e500 | |||
| 8296f8229e | |||
| 059ae3a80e | |||
|
|
67e242c441 | ||
| e23f3b4875 | |||
| 06ea9af733 | |||
| 884818f518 | |||
| 3f39bd0f7b | |||
|
|
48cce6435b | ||
| 777e08c8b6 | |||
| 8c4b0b7c00 | |||
| dae8b25374 | |||
|
|
2ae293444e | ||
| 0f11d3566a | |||
| fa70d439c3 | |||
| 0010581405 | |||
| 1d659e4f8a | |||
|
|
509f00c5e2 | ||
|
|
aeb3241abb | ||
| 8909df3c88 | |||
| 1cffcae36b | |||
| d2ee5b96ad | |||
| 0506a5915b | |||
| 91ef3fd0bc | |||
|
|
79f5484f87 | ||
|
|
6afd2c4b97 | ||
| 06b09f89de | |||
| bd0eee6aa9 | |||
|
|
5bc13c16dc | ||
|
|
626daeb86e | ||
| a92913da4c | |||
| f0f363e8c3 | |||
| 7839c0fec3 | |||
| 4d24d65c1f | |||
| ddf761b1f8 | |||
| 2814fe75b1 | |||
| 7316c82ef3 | |||
| e04646bd37 | |||
| 8f8022347d | |||
|
|
acc0ab66a3 | ||
| 5a6acf1d85 | |||
| ca45194c30 | |||
| 102506ebe8 | |||
| d06f1c4a30 | |||
| c732c1c038 | |||
| 4aa1bba575 | |||
| 9d3925eb20 | |||
| 80fdf4e917 | |||
| 35a86e603b | |||
| 8f7b31aed3 | |||
| 0f5a0b6b1b | |||
| 4cf3906ed2 | |||
| 57875586cf | |||
| ad8ec011d0 | |||
| 0d93b86a56 | |||
| 63712002e2 | |||
| 67af1a17c1 | |||
| 9cac546547 | |||
| 3ffb321f46 | |||
| 14ea6d8d0a | |||
| 8e0dbf31ba | |||
| 5187bf1eae | |||
| 99471d8e1b | |||
| 012e999354 | |||
| bdd9900d0d | |||
| c71b485083 | |||
| a78d34ab4b | |||
| 255ebe7efb | |||
| 5f7a761e42 | |||
| 09548358fa | |||
| df381c37ff | |||
|
|
31e96b416d | ||
|
|
53b0f7b579 | ||
| 45deaa3a87 | |||
| 0c8eabcdf6 | |||
| f9571740ae | |||
| eb2af2b5fd | |||
| ded00300b4 | |||
| 811819af69 | |||
|
|
1804c852bb | ||
| 3ec602865d | |||
| 0c0a25b038 | |||
| 208192f445 | |||
|
|
d0f635e30c | ||
| 20b2f7dcb1 | |||
| cf66951818 | |||
| c6f41cc8fa | |||
| 45a579403a | |||
| 74a57abfb4 | |||
| 380783c1ba | |||
|
|
c8843a736d | ||
|
|
e69fefda62 | ||
|
|
513da3b743 | ||
| 7513d42e9e | |||
| 8872437caf | |||
|
|
712c77a4b6 | ||
| 38cef14cf4 | |||
| 05c3b0d391 | |||
| 854090e9d8 | |||
| f035815f7a | |||
| df4302dda0 | |||
| 31f117e74c | |||
| 890f40b2ac | |||
| 3ac8083faf | |||
| 249319f76f | |||
| efe87060b4 | |||
| afe3ff57cf | |||
| 6bb1505d3b | |||
| a1c8e3eae3 | |||
| aa171bbaa6 | |||
| 845b70fe0c | |||
| 82b1eb7c18 | |||
| 4bd27ea1d3 | |||
| d0f6be3161 | |||
| 7764257e41 | |||
| af0e72ac4d | |||
| 0534440695 | |||
| c2fa2d0c5b | |||
| 887017b54c | |||
| 3a4f5a2f1b | |||
| 3fb4643742 | |||
| a5e3bc9b85 | |||
| 8a0c3f10f4 | |||
| e1d808a284 | |||
| e755051ebe | |||
| d7c3ffa4de | |||
| cba3b249dd | |||
| 32f250003e | |||
| e004b0e6e9 | |||
| a5c5917a5f | |||
|
|
cbd24946b6 | ||
| 9b4842931a | |||
| e04cf132cc | |||
| 7d237b4cf6 | |||
| 3cbfd09722 | |||
| 0ae774dd68 | |||
| 2884df873e | |||
|
|
d4d1424c4f | ||
|
|
33827f9aaf | ||
| 30a666c833 | |||
| 9a00621fa4 | |||
|
|
46bc479884 | ||
|
|
6ab6abf4dd | ||
| 0c6a9ff2a0 | |||
| aefc5b5317 | |||
| 25682fdea7 | |||
| 9318099845 | |||
| fdf38a952d | |||
| fb40ab1f00 | |||
| 3499ed7bb0 | |||
| b05906a3c2 | |||
| 61d5f46295 | |||
| 9ebfccd8f6 | |||
| 4fb3e86e4d | |||
| 837517e94e | |||
| f47ec65b8f | |||
| 6fede7f524 | |||
| bd0c18b2e3 | |||
| fba596c78c | |||
|
|
61f2b49022 | ||
|
|
e71a1a5c4d | ||
|
|
683ce3254f | ||
|
|
df555731c4 | ||
|
|
641fc0cae9 | ||
|
|
5ab1d6896a | ||
| 0ae2d92fcd | |||
| 26c8201e03 | |||
|
|
37521c69a2 | ||
|
|
43383a5b3c | ||
|
|
bfea3e4dd6 | ||
|
|
35b02a52e1 | ||
|
|
8e3e566915 | ||
|
|
968b9a0304 | ||
|
|
c97f0f3d22 | ||
|
|
65b1124d81 | ||
| 89034e1f9d | |||
| 9f2fa6a8be | |||
| 19cf8787d8 | |||
| a80c34c1df | |||
| ab8900f910 | |||
| ce2d0b0e12 | |||
| 87d98781a9 | |||
| 79dc95dd66 | |||
| c6533202f7 | |||
| b5dc9bc0ba | |||
| 79891c6677 | |||
| 68aa1aea8e | |||
| 4ffaf4128e | |||
| 801ca47a0c | |||
| 43f5062169 | |||
| 7ab4dd7f4b | |||
| 71c0104236 | |||
| a608d21571 | |||
| 935ddaa0b2 | |||
| 945e7b75fd | |||
|
|
5e90780590 | ||
| a8a21ea7ce | |||
| 23444e4b81 | |||
| a3ea1f9429 | |||
| 78d4b6d8a7 | |||
| 3e3399adc6 | |||
| 1e554dd0fe | |||
|
|
388e7088e1 | ||
|
|
e65289cd0d | ||
|
|
c4d68a8352 | ||
|
|
7be0a93251 | ||
|
|
175f47293a | ||
| 27936fce04 | |||
|
|
592680302f | ||
| 55318cf14b | |||
| 83ead0c304 | |||
| 38ec55bc72 | |||
| c4f97ed65e | |||
| 78a426e9ac | |||
| 928b4bbbe5 | |||
| d27c465371 | |||
| 4d62f63c71 | |||
| f4039b085a | |||
|
|
53ed10cfc8 | ||
| 0859dbe57f | |||
| 42bc4bb1f4 | |||
| caead8a3a4 | |||
| 7394c7997b | |||
| 0298e4ab4c | |||
| e3a5cdbbc2 | |||
|
|
6ae46abac0 | ||
|
|
615a5f3c39 | ||
|
|
6812dc1db8 | ||
| 41595cafce | |||
| d6c4e411be | |||
| 6ca797ec1f | |||
| 8655988c66 | |||
| de5f2940ce | |||
| 1933e6239b | |||
| 8aec1e2eb6 | |||
|
|
def30c5903 | ||
|
|
193f0bbc42 | ||
|
|
b7a3b7bf05 | ||
|
|
a2bd45fa9b | ||
|
|
fd297118f9 | ||
| 7171edad24 | |||
| d8466333f3 | |||
| a5e4d586eb | |||
|
|
28a1c22438 | ||
|
|
74e238322d | ||
|
|
1f3227409b | ||
|
|
afda1da9c3 | ||
| f2857e8f97 | |||
| 4802b1e2cd | |||
| 0328304f04 | |||
| 06d2db0046 | |||
|
|
0539610219 | ||
| 170be8f033 | |||
| ca0bb1ab76 | |||
| cdccb51456 | |||
|
|
870ecb847f | ||
|
|
8ba493c60e | ||
|
|
f3affff5be | ||
| 5941a52a0d | |||
| 04e6beb43c | |||
| 1284c7ace0 | |||
|
|
63414f0348 | ||
|
|
c8f22fdfd0 | ||
| 7737bf5212 | |||
| 4340f70d72 | |||
| 6dbd8a6d1a | |||
| 076871fe44 | |||
| be753d7a1a | |||
| e3f02553fd | |||
| 767ef8c489 | |||
| 482787fbc3 | |||
|
|
b2d72f90ce | ||
|
|
dd181a90e1 | ||
| 19faa352e3 | |||
| ffda7f6326 | |||
| 8ebacf4c10 | |||
| a02cc25844 | |||
| 8aeca5b433 | |||
| 7b4855d7cf | |||
| 803d7ff282 | |||
|
|
a1c724f866 | ||
|
|
96609e9173 | ||
| f5518739e2 | |||
| e7c8d28b99 | |||
| dff357a658 | |||
| cc271de7df | |||
| a7485ab5a3 | |||
|
|
7133736702 | ||
|
|
2313d82369 | ||
| 1b42d99e66 | |||
|
|
d709f46183 | ||
|
|
97c1676bcb | ||
| 02b92ac3fe | |||
| 29eb1d5dc5 | |||
| a87fc68cfd | |||
| bc1cf64df4 | |||
| b607d7b223 | |||
| 4e00eaf9a3 | |||
| 90cc71d14d | |||
| 554a3a6b0e | |||
| 8505e8a1ca | |||
|
|
54ec878698 | ||
|
|
ea4606fe79 | ||
|
|
96de87fdde | ||
|
|
55d571ffec | ||
|
|
ae347c4fa1 | ||
|
|
e93beb7c63 | ||
|
|
018098b8ef | ||
|
|
1e2fb4bea5 | ||
|
|
171f0d2671 | ||
|
|
175cb0817e | ||
| 3b46acc285 | |||
| d31ce49e77 | |||
|
|
8937c4bf2a | ||
|
|
2902a708f9 | ||
| c1210b1c04 | |||
| 71038c311f | |||
| 70a8ab1d1a | |||
| f3c4a26483 | |||
| 9a1e1a92ca | |||
| 732eddd1b9 | |||
| 2caa6ad975 | |||
| d3e0161a6b | |||
| aabbdc1c1b | |||
| 3aafea0d98 | |||
| 79f8e88e5f | |||
| 34b7081303 | |||
| e0cd71069f | |||
| 378c5a0d4e | |||
| 6c62c1f362 | |||
| 3daf287de8 | |||
| 9a4cd11748 | |||
| c0350702fd | |||
| 55880844a5 | |||
| 729282c0c4 | |||
| d603c3b5cd | |||
| 5cda1ec703 | |||
| ec3756a69f | |||
| 0b49fa98f9 | |||
| 226c7eb8e3 | |||
| be262c6a70 | |||
| a374f00613 | |||
| fcdf837f33 | |||
| febc0a5237 | |||
| a19140e186 | |||
| e0b25cdcf9 | |||
| e332c8bc27 | |||
| 7318632db9 | |||
| 1b38a6478b | |||
| 6ceea60c92 | |||
| fcaebb4416 | |||
| 0be3beb30e | |||
| 5e51c6abef | |||
| 5e953b0857 | |||
| c76c80043f | |||
| 3a49d6d28f | |||
| 94e34ff366 | |||
| af27c76b2c | |||
| 60336c9555 | |||
| a85b7aa9f8 | |||
| 228e6d7d03 | |||
| 31531ee882 | |||
| 368c2073b2 | |||
| 757df8c726 | |||
| c6121cb31e | |||
| eecab472eb | |||
| b71631d2ec | |||
| 3ec78e27b4 |
22
.mtn-ignore
22
.mtn-ignore
@@ -1,5 +1,7 @@
|
||||
# Just to try and prevent some noob disasters.
|
||||
# Use mtn add --no-respect-ignore foo.jar to ignore this ignore list
|
||||
|
||||
# Temporary/build files
|
||||
_jsp\.java$
|
||||
\.bz2$
|
||||
\.tar$
|
||||
@@ -19,13 +21,27 @@ _jsp\.java$
|
||||
.\deb$
|
||||
\.zip$
|
||||
^\.
|
||||
^build
|
||||
^pkg-temp/
|
||||
~$
|
||||
web-fragment.xml
|
||||
web-out.xml
|
||||
|
||||
# Temporary/build dirs
|
||||
^build
|
||||
^pkg-temp
|
||||
/build
|
||||
/classes/
|
||||
/classes
|
||||
/dist
|
||||
/mo
|
||||
/tmp
|
||||
^apps/jetty/jettylib
|
||||
|
||||
# Debian-related
|
||||
^debian/copyright
|
||||
^debian/changelog
|
||||
|
||||
# Build property overrides
|
||||
override.properties
|
||||
|
||||
# Reporting
|
||||
sloccount.sc
|
||||
^reports/
|
||||
|
||||
88
.tx/config
88
.tx/config
@@ -9,16 +9,40 @@ trans.es = apps/i2ptunnel/locale/messages_es.po
|
||||
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.ja = apps/i2ptunnel/locale/messages_ja.po
|
||||
trans.nb = apps/i2ptunnel/locale/messages_nb.po
|
||||
trans.nl = apps/i2ptunnel/locale/messages_nl.po
|
||||
trans.pl = apps/i2ptunnel/locale/messages_pl.po
|
||||
trans.pt = apps/i2ptunnel/locale/messages_pt.po
|
||||
trans.pt_BR = apps/i2ptunnel/locale/messages_pt_BR.po
|
||||
trans.ru_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
|
||||
trans.vi = apps/i2ptunnel/locale/messages_vi.po
|
||||
trans.zh_CN = apps/i2ptunnel/locale/messages_zh.po
|
||||
|
||||
[I2P.proxy]
|
||||
source_file = apps/i2ptunnel/locale-proxy/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/i2ptunnel/locale-proxy/messages_ar.po
|
||||
trans.cs = apps/i2ptunnel/locale-proxy/messages_cs.po
|
||||
trans.de = apps/i2ptunnel/locale-proxy/messages_de.po
|
||||
trans.es = apps/i2ptunnel/locale-proxy/messages_es.po
|
||||
trans.fr = apps/i2ptunnel/locale-proxy/messages_fr.po
|
||||
trans.hu = apps/i2ptunnel/locale-proxy/messages_hu.po
|
||||
trans.it = apps/i2ptunnel/locale-proxy/messages_it.po
|
||||
trans.nb = apps/i2ptunnel/locale-proxy/messages_nb.po
|
||||
trans.nl = apps/i2ptunnel/locale-proxy/messages_nl.po
|
||||
trans.pl = apps/i2ptunnel/locale-proxy/messages_pl.po
|
||||
trans.pt = apps/i2ptunnel/locale-proxy/messages_pt.po
|
||||
trans.pt_BR = apps/i2ptunnel/locale-proxy/messages_pt_BR.po
|
||||
trans.ro = apps/i2ptunnel/locale-proxy/messages_ro.po
|
||||
trans.ru_RU = apps/i2ptunnel/locale-proxy/messages_ru.po
|
||||
trans.sv_SE = apps/i2ptunnel/locale-proxy/messages_sv.po
|
||||
trans.uk_UA = apps/i2ptunnel/locale-proxy/messages_uk.po
|
||||
trans.vi = apps/i2ptunnel/locale-proxy/messages_vi.po
|
||||
trans.zh_CN = apps/i2ptunnel/locale-proxy/messages_zh.po
|
||||
|
||||
[I2P.routerconsole]
|
||||
source_file = apps/routerconsole/locale/messages_en.po
|
||||
source_lang = en
|
||||
@@ -33,10 +57,13 @@ trans.fi = apps/routerconsole/locale/messages_fi.po
|
||||
trans.fr = apps/routerconsole/locale/messages_fr.po
|
||||
trans.hu = apps/routerconsole/locale/messages_hu.po
|
||||
trans.it = apps/routerconsole/locale/messages_it.po
|
||||
trans.ja = apps/routerconsole/locale/messages_ja.po
|
||||
trans.nb = apps/routerconsole/locale/messages_nb.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.pt_BR = apps/routerconsole/locale/messages_pt_BR.po
|
||||
trans.ro = apps/routerconsole/locale/messages_ro.po
|
||||
trans.ru_RU = apps/routerconsole/locale/messages_ru.po
|
||||
trans.sv_SE = apps/routerconsole/locale/messages_sv.po
|
||||
trans.tr_TR = apps/routerconsole/locale/messages_tr.po
|
||||
@@ -44,6 +71,51 @@ trans.uk_UA = apps/routerconsole/locale/messages_uk.po
|
||||
trans.vi = apps/routerconsole/locale/messages_vi.po
|
||||
trans.zh_CN = apps/routerconsole/locale/messages_zh.po
|
||||
|
||||
[I2P.welcome]
|
||||
source_file = apps/routerconsole/locale-news/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/routerconsole/locale-news/messages_ar.po
|
||||
trans.de = apps/routerconsole/locale-news/messages_de.po
|
||||
trans.es = apps/routerconsole/locale-news/messages_es.po
|
||||
trans.fr = apps/routerconsole/locale-news/messages_fr.po
|
||||
trans.ja = apps/routerconsole/locale-news/messages_ja.po
|
||||
trans.it = apps/routerconsole/locale-news/messages_it.po
|
||||
trans.nl = apps/routerconsole/locale-news/messages_nl.po
|
||||
trans.pl = apps/routerconsole/locale-news/messages_pl.po
|
||||
trans.pt = apps/routerconsole/locale-news/messages_pt.po
|
||||
trans.pt_BR = apps/routerconsole/locale-news/messages_pt_BR.po
|
||||
trans.ro = apps/routerconsole/locale-news/messages_ro.po
|
||||
trans.ru_RU = apps/routerconsole/locale-news/messages_ru.po
|
||||
trans.sv_SE = apps/routerconsole/locale-news/messages_sv.po
|
||||
trans.tr_TR = apps/routerconsole/locale-news/messages_tr.po
|
||||
trans.zh_CN = apps/routerconsole/locale-news/messages_zh.po
|
||||
|
||||
[I2P.countries]
|
||||
type = PO
|
||||
source_file = apps/routerconsole/locale-countries/messages_en.po
|
||||
source_lang = en
|
||||
trans.da = apps/routerconsole/locale-countries/messages_da.po
|
||||
trans.de = apps/routerconsole/locale-countries/messages_de.po
|
||||
trans.el = apps/routerconsole/locale-countries/messages_el.po
|
||||
trans.es = apps/routerconsole/locale-countries/messages_es.po
|
||||
trans.et_EE = apps/routerconsole/locale-countries/messages_et.po
|
||||
trans.fi = apps/routerconsole/locale-countries/messages_fi.po
|
||||
trans.fr = apps/routerconsole/locale-countries/messages_fr.po
|
||||
trans.hu = apps/routerconsole/locale-countries/messages_hu.po
|
||||
trans.it = apps/routerconsole/locale-countries/messages_it.po
|
||||
trans.ja = apps/routerconsole/locale-countries/messages_ja.po
|
||||
trans.nb = apps/routerconsole/locale-countries/messages_nb.po
|
||||
trans.nl = apps/routerconsole/locale-countries/messages_nl.po
|
||||
trans.pl = apps/routerconsole/locale-countries/messages_pl.po
|
||||
trans.pt = apps/routerconsole/locale-countries/messages_pt.po
|
||||
trans.pt_BR = apps/routerconsole/locale-countries/messages_pt_BR.po
|
||||
trans.ro = apps/routerconsole/locale-countries/messages_ro.po
|
||||
trans.ru_RU = apps/routerconsole/locale-countries/messages_ru.po
|
||||
trans.sv_SE = apps/routerconsole/locale-countries/messages_sv.po
|
||||
trans.tr_TR = apps/routerconsole/locale-countries/messages_tr.po
|
||||
trans.vi = apps/routerconsole/locale-countries/messages_vi.po
|
||||
trans.zh_CN = apps/routerconsole/locale-countries/messages_zh.po
|
||||
|
||||
[I2P.i2psnark]
|
||||
source_file = apps/i2psnark/locale/messages_en.po
|
||||
source_lang = en
|
||||
@@ -58,6 +130,7 @@ trans.nb = apps/i2psnark/locale/messages_nb.po
|
||||
trans.nl = apps/i2psnark/locale/messages_nl.po
|
||||
trans.pl = apps/i2psnark/locale/messages_pl.po
|
||||
trans.pt = apps/i2psnark/locale/messages_pt.po
|
||||
trans.ro = apps/i2psnark/locale/messages_ro.po
|
||||
trans.ru_RU = apps/i2psnark/locale/messages_ru.po
|
||||
trans.sv_SE = apps/i2psnark/locale/messages_sv.po
|
||||
trans.vi = apps/i2psnark/locale/messages_vi.po
|
||||
@@ -75,9 +148,12 @@ trans.es = apps/susidns/locale/messages_es.po
|
||||
trans.fr = apps/susidns/locale/messages_fr.po
|
||||
trans.hu = apps/susidns/locale/messages_hu.po
|
||||
trans.it = apps/susidns/locale/messages_it.po
|
||||
trans.ja = apps/susidns/locale/messages_ja.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.pt_BR = apps/susidns/locale/messages_pt_BR.po
|
||||
trans.ro = apps/susidns/locale/messages_ro.po
|
||||
trans.ru_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
|
||||
@@ -96,8 +172,11 @@ trans.es = apps/desktopgui/locale/messages_es.po
|
||||
trans.fr = apps/desktopgui/locale/messages_fr.po
|
||||
trans.hu = apps/desktopgui/locale/messages_hu.po
|
||||
trans.it = apps/desktopgui/locale/messages_it.po
|
||||
trans.ja = apps/desktopgui/locale/messages_ja.po
|
||||
trans.nl = apps/desktopgui/locale/messages_nl.po
|
||||
trans.pl = apps/desktopgui/locale/messages_pl.po
|
||||
trans.pt_BR = apps/desktopgui/locale/messages_pt_BR.po
|
||||
trans.ro = apps/desktopgui/locale/messages_ro.po
|
||||
trans.ru_RU = apps/desktopgui/locale/messages_ru.po
|
||||
trans.sv_SE = apps/desktopgui/locale/messages_sv.po
|
||||
trans.uk_UA = apps/desktopgui/locale/messages_uk.po
|
||||
@@ -119,6 +198,8 @@ trans.ru_RU = apps/susimail/locale/messages_ru.po
|
||||
trans.sv_SE = apps/susimail/locale/messages_sv.po
|
||||
trans.pl = apps/susimail/locale/messages_pl.po
|
||||
trans.pt = apps/susimail/locale/messages_pt.po
|
||||
trans.pt_BR = apps/susimail/locale/messages_pt_BR.po
|
||||
trans.ro = apps/susimail/locale/messages_ro.po
|
||||
trans.uk_UA = apps/susimail/locale/messages_uk.po
|
||||
trans.vi = apps/susimail/locale/messages_vi.po
|
||||
trans.zh_CN = apps/susimail/locale/messages_zh.po
|
||||
@@ -135,6 +216,7 @@ trans.it = debian/po/it.po
|
||||
trans.hu = debian/po/hu.po
|
||||
trans.pl = debian/po/pl.po
|
||||
trans.pt = debian/po/pt.po
|
||||
trans.ro = debian/po/ro.po
|
||||
trans.ru_RU = debian/po/ru.po
|
||||
trans.sv_SE = debian/po/sv.po
|
||||
trans.uk_UA = debian/po/uk.po
|
||||
@@ -145,13 +227,17 @@ trans.zh_CN = debian/po/zh.po
|
||||
source_file = installer/resources/locale/po/messages_en.po
|
||||
source_lang = en
|
||||
trans.de = installer/resources/locale/po/messages_de.po
|
||||
trans.es = installer/resources/locale/po/messages_es.po
|
||||
trans.fr = installer/resources/locale/po/messages_fr.po
|
||||
trans.it = installer/resources/locale/po/messages_it.po
|
||||
trans.pt = installer/resources/locale/po/messages_pt.po
|
||||
trans.pt_BR = installer/resources/locale/po/messages_pt_BR.po
|
||||
trans.ro = installer/resources/locale/po/messages_ro.po
|
||||
trans.sv_SE = installer/resources/locale/po/messages_sv.po
|
||||
trans.ru_RU = installer/resources/locale/po/messages_ru.po
|
||||
trans.tr_TR = installer/resources/locale/po/messages_tr.po
|
||||
trans.zh_CN = installer/resources/locale/po/messages_zh.po
|
||||
|
||||
[main]
|
||||
host = http://www.transifex.net
|
||||
host = https://www.transifex.com
|
||||
|
||||
|
||||
@@ -42,4 +42,4 @@ Supported JVMs:
|
||||
Windows: Latest available from http://java.com/download (1.5+ supported)
|
||||
Linux: Latest available from http://java.com/download (1.5+ supported)
|
||||
FreeBSD: 1.5-compatible (NIO required)
|
||||
Other operating systems and JVMs: See http://trac.i2p2.de/wiki/java
|
||||
Other operating systems and JVMs: See https://trac.i2p2.de/wiki/java
|
||||
|
||||
@@ -2,7 +2,7 @@ I2P source installation instructions
|
||||
|
||||
Prerequisites to build from source:
|
||||
Java SDK (preferably Oracle/Sun or OpenJDK) 1.6.0 or higher
|
||||
Non-linux operating systems and JVMs: See http://trac.i2p2.de/wiki/java
|
||||
Non-linux operating systems and JVMs: See https://trac.i2p2.de/wiki/java
|
||||
Apache Ant 1.7.0 or higher
|
||||
The xgettext, msgfmt, and msgmerge tools installed
|
||||
from the GNU gettext package http://www.gnu.org/software/gettext/
|
||||
|
||||
@@ -177,7 +177,7 @@ Applications:
|
||||
By welterde.
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
Jetty 7.6.11.v20130520:
|
||||
Jetty 7.6.14.v20131031:
|
||||
See licenses/ABOUT-Jetty.html
|
||||
See licenses/NOTICE-Jetty.html
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
@@ -207,7 +207,7 @@ Applications:
|
||||
FatCow icons: See licenses/LICENSE-FatCowIcons.txt
|
||||
|
||||
GeoIP Data:
|
||||
Copyright (c) 2008 MaxMind, Inc. All Rights Reserved.
|
||||
This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com/
|
||||
See licenses/LICENSE-GeoIP.txt
|
||||
|
||||
Router Console and I2PSnark themes:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Prerequisites to build from source:
|
||||
Java SDK (preferably Oracle/Sun or OpenJDK) 1.6.0 or higher
|
||||
Non-linux operating systems and JVMs: See http://trac.i2p2.de/wiki/java
|
||||
Non-linux operating systems and JVMs: See https://trac.i2p2.de/wiki/java
|
||||
Apache Ant 1.7.0 or higher
|
||||
The xgettext, msgfmt, and msgmerge tools installed
|
||||
from the GNU gettext package http://www.gnu.org/software/gettext/
|
||||
@@ -22,13 +22,13 @@ Documentation:
|
||||
API: run 'ant javadoc' then start at build/javadoc/index.html
|
||||
|
||||
Latest release:
|
||||
http://www.i2p2.de/download.html
|
||||
http://www.i2p2.de/download
|
||||
|
||||
To get development branch from source control:
|
||||
http://www.i2p2.de/newdevelopers.html
|
||||
http://www.i2p2.de/newdevelopers
|
||||
|
||||
FAQ:
|
||||
http://www.i2p2.de/faq.html
|
||||
http://www.i2p2.de/faq
|
||||
|
||||
Need help?
|
||||
IRC irc.freenode.net #i2p
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
INST_DIR=directory
|
||||
|
||||
|
||||
8
apps/BOB/.classpath
Normal file
8
apps/BOB/.classpath
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/i2p_sdk"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/ministreaming"/>
|
||||
<classpathentry kind="output" path="build"/>
|
||||
</classpath>
|
||||
17
apps/BOB/.project
Normal file
17
apps/BOB/.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>BOB</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import static net.i2p.app.ClientAppState.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -25,12 +27,17 @@ import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.*;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
@@ -105,57 +112,76 @@ import net.i2p.util.SimpleTimer2;
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class BOB {
|
||||
public class BOB implements Runnable, ClientApp {
|
||||
|
||||
public final static String PROP_CONFIG_LOCATION = "BOB.config";
|
||||
public final static String PROP_BOB_PORT = "BOB.port";
|
||||
public final static String PROP_BOB_HOST = "BOB.host";
|
||||
public final static String PROP_CFG_VER = "BOB.CFG.VER";
|
||||
private static NamedDB database;
|
||||
private static Properties props = new Properties();
|
||||
private static AtomicBoolean spin = new AtomicBoolean(true);
|
||||
|
||||
private static BOB _bob;
|
||||
|
||||
private NamedDB database;
|
||||
private Properties props = new Properties();
|
||||
private AtomicBoolean spin = new AtomicBoolean(true);
|
||||
private static final String P_RUNNING = "RUNNING";
|
||||
private static final String P_STARTING = "STARTING";
|
||||
private static final String P_STOPPING = "STOPPING";
|
||||
private static AtomicBoolean lock = new AtomicBoolean(false);
|
||||
private AtomicBoolean lock = new AtomicBoolean(false);
|
||||
// no longer used.
|
||||
// private static int maxConnections = 0;
|
||||
|
||||
/**
|
||||
* Log a warning
|
||||
*
|
||||
* @param arg
|
||||
*/
|
||||
public static void info(String arg) {
|
||||
System.out.println("INFO:" + arg);
|
||||
(new Log(BOB.class)).info(arg);
|
||||
}
|
||||
private final Logger _log;
|
||||
private final ClientAppManager _mgr;
|
||||
private final String[] _args;
|
||||
private volatile ClientAppState _state = UNINITIALIZED;
|
||||
|
||||
/**
|
||||
* Log a warning
|
||||
*
|
||||
* @param arg
|
||||
*/
|
||||
public static void warn(String arg) {
|
||||
System.out.println("WARNING:" + arg);
|
||||
(new Log(BOB.class)).warn(arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an error
|
||||
*
|
||||
* @param arg
|
||||
*/
|
||||
public static void error(String arg) {
|
||||
System.out.println("ERROR: " + arg);
|
||||
(new Log(BOB.class)).error(arg);
|
||||
}
|
||||
private volatile ServerSocket listener;
|
||||
private volatile Thread _runner;
|
||||
|
||||
/**
|
||||
* Stop BOB gracefully
|
||||
* @deprecated unused
|
||||
*/
|
||||
public static void stop() {
|
||||
spin.set(false);
|
||||
_bob.shutdown(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* For ClientApp interface.
|
||||
* Does NOT open the listener socket or start threads; caller must call startup()
|
||||
*
|
||||
* @param mgr may be null
|
||||
* @param args non-null
|
||||
* @throws Exception on bad args
|
||||
* @since 0.9.10
|
||||
*/
|
||||
public BOB(I2PAppContext context, ClientAppManager mgr, String[] args) {
|
||||
// If we were run from command line, log to stdout
|
||||
boolean logToStdout = false;
|
||||
URL classResource = BOB.class.getResource("BOB.class");
|
||||
if (classResource != null) {
|
||||
String classPath = classResource.toString();
|
||||
if (classPath.startsWith("jar")) {
|
||||
String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) +
|
||||
"/META-INF/MANIFEST.MF";
|
||||
try {
|
||||
Manifest manifest = new Manifest(new URL(manifestPath).openStream());
|
||||
Attributes attrs = manifest.getMainAttributes();
|
||||
String mainClass = attrs.getValue("Main-Class");
|
||||
if ("net.i2p.BOB.Main".equals(mainClass))
|
||||
logToStdout = true;
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
_log = new Logger(context.logManager().getLog(BOB.class), logToStdout);
|
||||
|
||||
_mgr = mgr;
|
||||
_args = args;
|
||||
_state = INITIALIZED;
|
||||
database = new NamedDB();
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,8 +190,22 @@ public class BOB {
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
database = new NamedDB();
|
||||
ServerSocket listener = null;
|
||||
try {
|
||||
_bob = new BOB(I2PAppContext.getGlobalContext(), null, args);
|
||||
_bob.startup();
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void loadConfig() {
|
||||
int i = 0;
|
||||
boolean save = false;
|
||||
// Set up all defaults to be passed forward to other threads.
|
||||
@@ -176,116 +216,140 @@ public class BOB {
|
||||
SimpleTimer2 Y2 = SimpleTimer2.getInstance();
|
||||
i = Y1.hashCode();
|
||||
i = Y2.hashCode();
|
||||
Log _log = new Log(BOB.class);
|
||||
try {
|
||||
{
|
||||
File cfg = new File(configLocation);
|
||||
if (!cfg.isAbsolute()) {
|
||||
cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), configLocation);
|
||||
}
|
||||
try {
|
||||
FileInputStream fi = new FileInputStream(cfg);
|
||||
props.load(fi);
|
||||
fi.close();
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
warn("Unable to load up the BOB config file " + cfg.getAbsolutePath() + ", Using defaults.");
|
||||
warn(fnfe.toString());
|
||||
save = true;
|
||||
} catch (IOException ioe) {
|
||||
warn("IOException on BOB config file " + cfg.getAbsolutePath() + ", using defaults.");
|
||||
warn(ioe.toString());
|
||||
}
|
||||
{
|
||||
File cfg = new File(configLocation);
|
||||
if (!cfg.isAbsolute()) {
|
||||
cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), configLocation);
|
||||
}
|
||||
// Global router and client API configurations that are missing are set to defaults here.
|
||||
if (!props.containsKey(I2PClient.PROP_TCP_HOST)) {
|
||||
props.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(I2PClient.PROP_TCP_PORT)) {
|
||||
props.setProperty(I2PClient.PROP_TCP_PORT, "7654");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(PROP_BOB_PORT)) {
|
||||
props.setProperty(PROP_BOB_PORT, "2827"); // 0xB0B
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("inbound.length")) {
|
||||
props.setProperty("inbound.length", "1");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("outbound.length")) {
|
||||
props.setProperty("outbound.length", "1");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("inbound.lengthVariance")) {
|
||||
props.setProperty("inbound.lengthVariance", "0");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("outbound.lengthVariance")) {
|
||||
props.setProperty("outbound.lengthVariance", "0");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(PROP_BOB_HOST)) {
|
||||
props.setProperty(PROP_BOB_HOST, "localhost");
|
||||
save = true;
|
||||
}
|
||||
// PROP_RELIABILITY_NONE, PROP_RELIABILITY_BEST_EFFORT, PROP_RELIABILITY_GUARANTEED
|
||||
if (!props.containsKey(PROP_CFG_VER)) {
|
||||
props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_NONE);
|
||||
props.setProperty(PROP_CFG_VER,"1");
|
||||
save = true;
|
||||
}
|
||||
if (save) {
|
||||
File cfg = new File(configLocation);
|
||||
if (!cfg.isAbsolute()) {
|
||||
cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), configLocation);
|
||||
}
|
||||
try {
|
||||
warn("Writing new defaults file " + cfg.getAbsolutePath());
|
||||
FileOutputStream fo = new FileOutputStream(cfg);
|
||||
props.store(fo, cfg.getAbsolutePath());
|
||||
fo.close();
|
||||
} catch (IOException ioe) {
|
||||
error("IOException on BOB config file " + cfg.getAbsolutePath() + ", " + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
boolean g = false;
|
||||
spin.set(true);
|
||||
try {
|
||||
info("BOB is now running.");
|
||||
listener = new ServerSocket(Integer.parseInt(props.getProperty(PROP_BOB_PORT)), 10, InetAddress.getByName(props.getProperty(PROP_BOB_HOST)));
|
||||
Socket server = null;
|
||||
listener.setSoTimeout(500); // .5 sec
|
||||
|
||||
while (spin.get()) {
|
||||
//DoCMDS connection;
|
||||
|
||||
try {
|
||||
server = listener.accept();
|
||||
server.setKeepAlive(true);
|
||||
g = true;
|
||||
} catch (ConnectException ce) {
|
||||
g = false;
|
||||
} catch (SocketTimeoutException ste) {
|
||||
g = false;
|
||||
}
|
||||
|
||||
if (g) {
|
||||
DoCMDS conn_c = new DoCMDS(spin, lock, server, props, database, _log);
|
||||
Thread t = new Thread(conn_c);
|
||||
t.setName("BOB.DoCMDS " + i);
|
||||
t.start();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
FileInputStream fi = new FileInputStream(cfg);
|
||||
props.load(fi);
|
||||
fi.close();
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
_log.warn("Unable to load up the BOB config file " + cfg.getAbsolutePath() + ", Using defaults.", fnfe);
|
||||
save = true;
|
||||
} catch (IOException ioe) {
|
||||
error("IOException on socket listen: " + ioe);
|
||||
ioe.printStackTrace();
|
||||
_log.warn("IOException on BOB config file " + cfg.getAbsolutePath() + ", using defaults.", ioe);
|
||||
}
|
||||
}
|
||||
// Global router and client API configurations that are missing are set to defaults here.
|
||||
if (!props.containsKey(I2PClient.PROP_TCP_HOST)) {
|
||||
props.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(I2PClient.PROP_TCP_PORT)) {
|
||||
props.setProperty(I2PClient.PROP_TCP_PORT, "7654");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(PROP_BOB_PORT)) {
|
||||
props.setProperty(PROP_BOB_PORT, "2827"); // 0xB0B
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("inbound.length")) {
|
||||
props.setProperty("inbound.length", "1");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("outbound.length")) {
|
||||
props.setProperty("outbound.length", "1");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("inbound.lengthVariance")) {
|
||||
props.setProperty("inbound.lengthVariance", "0");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("outbound.lengthVariance")) {
|
||||
props.setProperty("outbound.lengthVariance", "0");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(PROP_BOB_HOST)) {
|
||||
props.setProperty(PROP_BOB_HOST, "localhost");
|
||||
save = true;
|
||||
}
|
||||
// PROP_RELIABILITY_NONE, PROP_RELIABILITY_BEST_EFFORT, PROP_RELIABILITY_GUARANTEED
|
||||
if (!props.containsKey(PROP_CFG_VER)) {
|
||||
props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_NONE);
|
||||
props.setProperty(PROP_CFG_VER,"1");
|
||||
save = true;
|
||||
}
|
||||
if (save) {
|
||||
File cfg = new File(configLocation);
|
||||
if (!cfg.isAbsolute()) {
|
||||
cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), configLocation);
|
||||
}
|
||||
try {
|
||||
_log.warn("Writing new defaults file " + cfg.getAbsolutePath());
|
||||
FileOutputStream fo = new FileOutputStream(cfg);
|
||||
props.store(fo, cfg.getAbsolutePath());
|
||||
fo.close();
|
||||
} catch (IOException ioe) {
|
||||
_log.error("IOException on BOB config file " + cfg.getAbsolutePath(), ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void startListener() throws IOException {
|
||||
listener = new ServerSocket(Integer.parseInt(props.getProperty(PROP_BOB_PORT)), 10, InetAddress.getByName(props.getProperty(PROP_BOB_HOST)));
|
||||
listener.setSoTimeout(500); // .5 sec
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void startThread() {
|
||||
I2PAppThread t = new I2PAppThread(this, "BOBListener");
|
||||
t.start();
|
||||
_runner = t;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
public void run() {
|
||||
if (listener == null) return;
|
||||
changeState(RUNNING);
|
||||
_log.info("BOB is now running.");
|
||||
if (_mgr != null)
|
||||
_mgr.register(this);
|
||||
|
||||
int i = 0;
|
||||
boolean g = false;
|
||||
spin.set(true);
|
||||
try {
|
||||
Socket server = null;
|
||||
|
||||
while (spin.get()) {
|
||||
//DoCMDS connection;
|
||||
|
||||
try {
|
||||
server = listener.accept();
|
||||
server.setKeepAlive(true);
|
||||
g = true;
|
||||
} catch (ConnectException ce) {
|
||||
g = false;
|
||||
} catch (SocketTimeoutException ste) {
|
||||
g = false;
|
||||
}
|
||||
|
||||
if (g) {
|
||||
DoCMDS conn_c = new DoCMDS(spin, lock, server, props, database, _log);
|
||||
Thread t = new Thread(conn_c);
|
||||
t.setName("BOB.DoCMDS " + i);
|
||||
t.start();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
changeState(STOPPING);
|
||||
} catch (Exception e) {
|
||||
if (spin.get())
|
||||
_log.error("Unexpected error while listening for connections", e);
|
||||
else
|
||||
e = null;
|
||||
changeState(STOPPING, e);
|
||||
} finally {
|
||||
info("BOB is now shutting down...");
|
||||
_log.info("BOB is now shutting down...");
|
||||
// Clean up everything.
|
||||
try {
|
||||
listener.close();
|
||||
@@ -308,7 +372,7 @@ public class BOB {
|
||||
database.releaseReadLock();
|
||||
database.getWriteLock();
|
||||
nickinfo.getWriteLock();
|
||||
nickinfo.add(P_STOPPING, new Boolean(true));
|
||||
nickinfo.add(P_STOPPING, Boolean.valueOf(true));
|
||||
nickinfo.releaseWriteLock();
|
||||
database.releaseWriteLock();
|
||||
} else {
|
||||
@@ -316,8 +380,8 @@ public class BOB {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
}
|
||||
info("BOB is now stopped.");
|
||||
|
||||
changeState(STOPPED);
|
||||
_log.info("BOB is now stopped.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,4 +433,86 @@ public class BOB {
|
||||
waitjoin(groups[i], level + 1, groups[i].getName());
|
||||
}
|
||||
}
|
||||
|
||||
////// begin ClientApp interface
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public void startup() throws IOException {
|
||||
if (_state != INITIALIZED)
|
||||
return;
|
||||
changeState(STARTING);
|
||||
try {
|
||||
startListener();
|
||||
} catch (IOException e) {
|
||||
_log.error("Error starting BOB on"
|
||||
+ props.getProperty(PROP_BOB_HOST)
|
||||
+ ":" + props.getProperty(PROP_BOB_PORT), e);
|
||||
changeState(START_FAILED, e);
|
||||
throw e;
|
||||
}
|
||||
startThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public void shutdown(String[] args) {
|
||||
if (_state != RUNNING)
|
||||
return;
|
||||
changeState(STOPPING);
|
||||
spin.set(false);
|
||||
if (_runner != null)
|
||||
_runner.interrupt();
|
||||
else
|
||||
changeState(STOPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public ClientAppState getState() {
|
||||
return _state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return "BOB";
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "BOB " + Arrays.toString(_args);
|
||||
}
|
||||
|
||||
////// end ClientApp interface
|
||||
////// begin ClientApp helpers
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void changeState(ClientAppState state) {
|
||||
changeState(state, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private synchronized void changeState(ClientAppState state, Exception e) {
|
||||
_state = state;
|
||||
if (_mgr != null)
|
||||
_mgr.notify(this, state, null, e);
|
||||
}
|
||||
|
||||
////// end ClientApp helpers
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import net.i2p.client.I2PClientFactory;
|
||||
//import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
//import net.i2p.i2ptunnel.I2PTunnel;
|
||||
import net.i2p.util.Log;
|
||||
// needed only for debugging.
|
||||
// import java.util.logging.Level;
|
||||
// import java.util.logging.Logger;
|
||||
@@ -56,7 +55,7 @@ public class DoCMDS implements Runnable {
|
||||
private ByteArrayOutputStream prikey;
|
||||
private boolean dk, ns, ip, op;
|
||||
private NamedDB nickinfo;
|
||||
private Log _log;
|
||||
private Logger _log;
|
||||
private AtomicBoolean LIVE;
|
||||
private AtomicBoolean lock;
|
||||
/* database strings */
|
||||
@@ -164,7 +163,7 @@ public class DoCMDS implements Runnable {
|
||||
* @param database
|
||||
* @param _log
|
||||
*/
|
||||
DoCMDS(AtomicBoolean LIVE, AtomicBoolean lock, Socket server, Properties props, NamedDB database, Log _log) {
|
||||
DoCMDS(AtomicBoolean LIVE, AtomicBoolean lock, Socket server, Properties props, NamedDB database, Logger _log) {
|
||||
this.lock = lock;
|
||||
this.LIVE = LIVE;
|
||||
this.server = server;
|
||||
@@ -606,7 +605,7 @@ public class DoCMDS implements Runnable {
|
||||
break die;
|
||||
}
|
||||
} catch (I2PException ipe) {
|
||||
BOB.error("Error generating keys" + ipe);
|
||||
_log.error("Error generating keys", ipe);
|
||||
out.println("ERROR generating keys");
|
||||
}
|
||||
|
||||
@@ -665,7 +664,7 @@ public class DoCMDS implements Runnable {
|
||||
break die;
|
||||
}
|
||||
try {
|
||||
nickinfo.add(P_QUIET, new Boolean(Boolean.parseBoolean(Arg) == true));
|
||||
nickinfo.add(P_QUIET, Boolean.valueOf(Arg));
|
||||
} catch (Exception ex) {
|
||||
try {
|
||||
wunlock();
|
||||
@@ -817,10 +816,10 @@ public class DoCMDS implements Runnable {
|
||||
try {
|
||||
database.add(Arg, nickinfo);
|
||||
nickinfo.add(P_NICKNAME, Arg);
|
||||
nickinfo.add(P_STARTING, new Boolean(false));
|
||||
nickinfo.add(P_RUNNING, new Boolean(false));
|
||||
nickinfo.add(P_STOPPING, new Boolean(false));
|
||||
nickinfo.add(P_QUIET, new Boolean(false));
|
||||
nickinfo.add(P_STARTING, Boolean.valueOf(false));
|
||||
nickinfo.add(P_RUNNING, Boolean.valueOf(false));
|
||||
nickinfo.add(P_STOPPING, Boolean.valueOf(false));
|
||||
nickinfo.add(P_QUIET, Boolean.valueOf(false));
|
||||
nickinfo.add(P_INHOST, "localhost");
|
||||
nickinfo.add(P_OUTHOST, "localhost");
|
||||
Properties Q = new Properties();
|
||||
@@ -989,7 +988,7 @@ public class DoCMDS implements Runnable {
|
||||
prt = Integer.parseInt(Arg);
|
||||
if (prt > 1 && prt < 65536) {
|
||||
try {
|
||||
nickinfo.add(P_INPORT, new Integer(prt));
|
||||
nickinfo.add(P_INPORT, Integer.valueOf(prt));
|
||||
} catch (Exception ex) {
|
||||
try {
|
||||
wunlock();
|
||||
@@ -1076,7 +1075,7 @@ public class DoCMDS implements Runnable {
|
||||
prt = Integer.parseInt(Arg);
|
||||
if (prt > 1 && prt < 65536) {
|
||||
try {
|
||||
nickinfo.add(P_OUTPORT, new Integer(prt));
|
||||
nickinfo.add(P_OUTPORT, Integer.valueOf(prt));
|
||||
} catch (Exception ex) {
|
||||
try {
|
||||
wunlock();
|
||||
@@ -1355,7 +1354,7 @@ public class DoCMDS implements Runnable {
|
||||
break die;
|
||||
}
|
||||
|
||||
nickinfo.add(P_STOPPING, new Boolean(true));
|
||||
nickinfo.add(P_STOPPING, Boolean.valueOf(true));
|
||||
try {
|
||||
wunlock();
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Listen on I2P and connect to TCP
|
||||
@@ -32,7 +31,7 @@ import net.i2p.util.Log;
|
||||
public class I2Plistener implements Runnable {
|
||||
|
||||
private NamedDB info, database;
|
||||
private Log _log;
|
||||
private Logger _log;
|
||||
public I2PSocketManager socketManager;
|
||||
public I2PServerSocket serverSocket;
|
||||
private AtomicBoolean lives;
|
||||
@@ -45,7 +44,7 @@ public class I2Plistener implements Runnable {
|
||||
* @param database
|
||||
* @param _log
|
||||
*/
|
||||
I2Plistener(I2PServerSocket SS, I2PSocketManager S, NamedDB info, NamedDB database, Log _log, AtomicBoolean lives) {
|
||||
I2Plistener(I2PServerSocket SS, I2PSocketManager S, NamedDB info, NamedDB database, Logger _log, AtomicBoolean lives) {
|
||||
this.database = database;
|
||||
this.info = info;
|
||||
this._log = _log;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
@@ -39,10 +39,8 @@ public class Lifted {
|
||||
*
|
||||
**/
|
||||
public static void copyProperties(Properties src_prop, Properties dest_prop) {
|
||||
for (Enumeration propertyNames = src_prop.propertyNames();
|
||||
propertyNames.hasMoreElements();) {
|
||||
Object key = propertyNames.nextElement();
|
||||
dest_prop.put(key, src_prop.get(key));
|
||||
for (Map.Entry<Object, Object> e : src_prop.entrySet()) {
|
||||
dest_prop.put((String)e.getKey(), (String)e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
44
apps/BOB/src/net/i2p/BOB/Logger.java
Normal file
44
apps/BOB/src/net/i2p/BOB/Logger.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package net.i2p.BOB;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class Logger {
|
||||
public Log log;
|
||||
private boolean logToStdout;
|
||||
|
||||
public Logger(Log log, boolean logToStdout) {
|
||||
this.log = log;
|
||||
this.logToStdout = logToStdout;
|
||||
}
|
||||
|
||||
public void info(String msg) {
|
||||
if (logToStdout)
|
||||
System.out.println("INFO: " + msg);
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info(msg);
|
||||
}
|
||||
|
||||
public void warn(String msg) {
|
||||
warn(msg, null);
|
||||
}
|
||||
|
||||
public void warn(String msg, Throwable e) {
|
||||
if (logToStdout) {
|
||||
System.out.println("WARNING: " + msg);
|
||||
if (e != null)
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn(msg, e);
|
||||
}
|
||||
|
||||
public void error(String msg, Throwable e) {
|
||||
if (logToStdout) {
|
||||
System.out.println("ERROR: " + msg);
|
||||
if (e != null)
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (log.shouldLog(Log.ERROR))
|
||||
log.error(msg, e);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import java.net.ServerSocket;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.client.streaming.I2PSocketManagerFactory;
|
||||
@@ -36,7 +37,7 @@ import net.i2p.util.Log;
|
||||
public class MUXlisten implements Runnable {
|
||||
|
||||
private NamedDB database, info;
|
||||
private Log _log;
|
||||
private Logger _log;
|
||||
private I2PSocketManager socketManager;
|
||||
private ByteArrayInputStream prikey;
|
||||
private ThreadGroup tg;
|
||||
@@ -57,7 +58,7 @@ public class MUXlisten implements Runnable {
|
||||
* @throws net.i2p.I2PException
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
MUXlisten(AtomicBoolean lock, NamedDB database, NamedDB info, Log _log) throws I2PException, IOException, RuntimeException {
|
||||
MUXlisten(AtomicBoolean lock, NamedDB database, NamedDB info, Logger _log) throws I2PException, IOException, RuntimeException {
|
||||
try {
|
||||
int port = 0;
|
||||
InetAddress host = null;
|
||||
@@ -70,7 +71,7 @@ public class MUXlisten implements Runnable {
|
||||
|
||||
this.database.getWriteLock();
|
||||
this.info.getWriteLock();
|
||||
this.info.add("STARTING", new Boolean(true));
|
||||
this.info.add("STARTING", Boolean.valueOf(true));
|
||||
this.info.releaseWriteLock();
|
||||
this.database.releaseWriteLock();
|
||||
this.database.getReadLock();
|
||||
@@ -96,15 +97,25 @@ public class MUXlisten implements Runnable {
|
||||
this.database.releaseReadLock();
|
||||
this.info.releaseReadLock();
|
||||
|
||||
String i2cpHost = Q.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
|
||||
int i2cpPort = 7654;
|
||||
String i2cpPortStr = Q.getProperty(I2PClient.PROP_TCP_PORT, "7654");
|
||||
try {
|
||||
i2cpPort = Integer.parseInt(i2cpPortStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new IllegalArgumentException("Invalid I2CP port specified [" + i2cpPortStr + "]");
|
||||
}
|
||||
|
||||
if (this.come_in) {
|
||||
this.listener = new ServerSocket(port, backlog, host);
|
||||
}
|
||||
socketManager = I2PSocketManagerFactory.createManager(prikey, Q);
|
||||
socketManager = I2PSocketManagerFactory.createManager(
|
||||
prikey, i2cpHost, i2cpPort, Q);
|
||||
} catch (IOException e) {
|
||||
// Something went bad.
|
||||
this.database.getWriteLock();
|
||||
this.info.getWriteLock();
|
||||
this.info.add("STARTING", new Boolean(false));
|
||||
this.info.add("STARTING", Boolean.valueOf(false));
|
||||
this.info.releaseWriteLock();
|
||||
this.database.releaseWriteLock();
|
||||
throw new IOException(e.toString());
|
||||
@@ -112,7 +123,7 @@ public class MUXlisten implements Runnable {
|
||||
// Something went bad.
|
||||
this.database.getWriteLock();
|
||||
this.info.getWriteLock();
|
||||
this.info.add("STARTING", new Boolean(false));
|
||||
this.info.add("STARTING", Boolean.valueOf(false));
|
||||
this.info.releaseWriteLock();
|
||||
this.database.releaseWriteLock();
|
||||
throw new RuntimeException(e);
|
||||
@@ -120,7 +131,7 @@ public class MUXlisten implements Runnable {
|
||||
// Something else went bad.
|
||||
this.database.getWriteLock();
|
||||
this.info.getWriteLock();
|
||||
this.info.add("STARTING", new Boolean(false));
|
||||
this.info.add("STARTING", Boolean.valueOf(false));
|
||||
this.info.releaseWriteLock();
|
||||
this.database.releaseWriteLock();
|
||||
e.printStackTrace();
|
||||
@@ -160,7 +171,7 @@ public class MUXlisten implements Runnable {
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
info.add("RUNNING", new Boolean(true));
|
||||
info.add("RUNNING", Boolean.valueOf(true));
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
wunlock();
|
||||
@@ -204,7 +215,7 @@ public class MUXlisten implements Runnable {
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
info.add("STARTING", new Boolean(false));
|
||||
info.add("STARTING", Boolean.valueOf(false));
|
||||
} catch (Exception e) {
|
||||
wunlock();
|
||||
break quit;
|
||||
@@ -258,9 +269,9 @@ public class MUXlisten implements Runnable {
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
info.add("STARTING", new Boolean(false));
|
||||
info.add("STOPPING", new Boolean(true));
|
||||
info.add("RUNNING", new Boolean(false));
|
||||
info.add("STARTING", Boolean.valueOf(false));
|
||||
info.add("STOPPING", Boolean.valueOf(true));
|
||||
info.add("RUNNING", Boolean.valueOf(false));
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
wunlock();
|
||||
@@ -309,9 +320,9 @@ public class MUXlisten implements Runnable {
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
info.add("STARTING", new Boolean(false));
|
||||
info.add("STOPPING", new Boolean(false));
|
||||
info.add("RUNNING", new Boolean(false));
|
||||
info.add("STARTING", Boolean.valueOf(false));
|
||||
info.add("STOPPING", Boolean.valueOf(false));
|
||||
info.add("RUNNING", Boolean.valueOf(false));
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
wunlock();
|
||||
|
||||
@@ -23,7 +23,7 @@ package net.i2p.BOB;
|
||||
public class NamedDB {
|
||||
|
||||
private volatile Object[][] data;
|
||||
private volatile int index, writersWaiting, readers;
|
||||
private int index, writersWaiting, readers;
|
||||
|
||||
/**
|
||||
* make initial NULL object
|
||||
@@ -31,7 +31,6 @@ public class NamedDB {
|
||||
*/
|
||||
public NamedDB() {
|
||||
this.data = new Object[1][2];
|
||||
this.index = this.writersWaiting = this.readers = 0;
|
||||
}
|
||||
|
||||
synchronized public void getReadLock() {
|
||||
|
||||
@@ -116,7 +116,6 @@ public class TCPio implements Runnable {
|
||||
Aout.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.net.SocketTimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Listen on TCP port and connect to I2P
|
||||
@@ -32,7 +31,7 @@ import net.i2p.util.Log;
|
||||
public class TCPlistener implements Runnable {
|
||||
|
||||
private NamedDB info, database;
|
||||
private Log _log;
|
||||
private Logger _log;
|
||||
public I2PSocketManager socketManager;
|
||||
public I2PServerSocket serverSocket;
|
||||
private ServerSocket listener;
|
||||
@@ -45,7 +44,7 @@ public class TCPlistener implements Runnable {
|
||||
* @param database
|
||||
* @param _log
|
||||
*/
|
||||
TCPlistener(ServerSocket listener, I2PSocketManager S, NamedDB info, NamedDB database, Log _log, AtomicBoolean lives) {
|
||||
TCPlistener(ServerSocket listener, I2PSocketManager S, NamedDB info, NamedDB database, Logger _log, AtomicBoolean lives) {
|
||||
this.database = database;
|
||||
this.info = info;
|
||||
this._log = _log;
|
||||
|
||||
@@ -70,12 +70,10 @@ public class TCPtoI2P implements Runnable {
|
||||
* @throws IOException
|
||||
*/
|
||||
private static String lnRead(InputStream in) throws IOException {
|
||||
String S;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int b;
|
||||
char c;
|
||||
|
||||
S = new String();
|
||||
|
||||
while (true) {
|
||||
b = in.read();
|
||||
if (b == 13) {
|
||||
@@ -87,9 +85,9 @@ public class TCPtoI2P implements Runnable {
|
||||
break;
|
||||
}
|
||||
c = (char) (b & 0x7f); // We only care about ASCII
|
||||
S = new String(S + c);
|
||||
builder.append(c);
|
||||
}
|
||||
return S;
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +99,7 @@ public class TCPtoI2P implements Runnable {
|
||||
*/
|
||||
private void Emsg(String e, OutputStream out) throws IOException {
|
||||
// Debugging System.out.println("ERROR TCPtoI2P: " + e);
|
||||
out.write("ERROR ".concat(e).getBytes());
|
||||
out.write("ERROR ".concat(e).getBytes("UTF-8"));
|
||||
out.write(13);
|
||||
out.write(10);
|
||||
out.flush();
|
||||
|
||||
8
apps/addressbook/.classpath
Normal file
8
apps/addressbook/.classpath
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="java/src"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/i2p_sdk"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="lib" path="/jetty/jettylib/javax.servlet.jar"/>
|
||||
<classpathentry kind="output" path="build"/>
|
||||
</classpath>
|
||||
17
apps/addressbook/.project
Normal file
17
apps/addressbook/.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>addressbook</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -120,11 +120,11 @@ class AddressBook {
|
||||
subscription.setLastFetched(I2PAppContext.getGlobalContext().clock().now());
|
||||
subf = tmp;
|
||||
} else {
|
||||
a = Collections.EMPTY_MAP;
|
||||
a = Collections.emptyMap();
|
||||
tmp.delete();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
a = Collections.EMPTY_MAP;
|
||||
a = Collections.emptyMap();
|
||||
}
|
||||
this.addresses = a;
|
||||
this.subFile = subf;
|
||||
@@ -148,7 +148,7 @@ class AddressBook {
|
||||
try {
|
||||
a = ConfigParser.parse(file);
|
||||
} catch (IOException exp) {
|
||||
a = new HashMap();
|
||||
a = new HashMap<String, String>();
|
||||
}
|
||||
this.addresses = a;
|
||||
this.subFile = null;
|
||||
@@ -260,7 +260,7 @@ class AddressBook {
|
||||
* An AddressBook to merge with.
|
||||
* @param overwrite True to overwrite
|
||||
* @param log
|
||||
* The log to write messages about new addresses or conflicts to.
|
||||
* The log to write messages about new addresses or conflicts to. May be null.
|
||||
*
|
||||
* @throws IllegalStateException if this was created with the Subscription constructor.
|
||||
*/
|
||||
|
||||
@@ -138,7 +138,8 @@ class ConfigIterator implements Iterator<Map.Entry<String, String>> {
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Map.Entry))
|
||||
return false;
|
||||
Map.Entry e = (Map.Entry) o;
|
||||
@SuppressWarnings("unchecked")
|
||||
Map.Entry<Object, Object> e = (Map.Entry<Object, Object>) o;
|
||||
return key.equals(e.getKey()) && value.equals(e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ class ConfigParser {
|
||||
*
|
||||
*/
|
||||
public static Map<String, String> parse(BufferedReader input) throws IOException {
|
||||
Map<String, String> result = new HashMap();
|
||||
Map<String, String> result = new HashMap<String, String>();
|
||||
String inputLine;
|
||||
inputLine = input.readLine();
|
||||
while (inputLine != null) {
|
||||
@@ -179,7 +179,7 @@ class ConfigParser {
|
||||
*/
|
||||
public static List<String> parseSubscriptions(BufferedReader input)
|
||||
throws IOException {
|
||||
List<String> result = new LinkedList();
|
||||
List<String> result = new LinkedList<String>();
|
||||
String inputLine = input.readLine();
|
||||
while (inputLine != null) {
|
||||
inputLine = ConfigParser.stripComments(inputLine).trim();
|
||||
|
||||
@@ -37,6 +37,7 @@ import net.i2p.client.naming.SingleFileNamingService;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
* Main class of addressbook. Performs updates, and runs the main loop.
|
||||
@@ -168,7 +169,7 @@ public class Daemon {
|
||||
if (publishedNS == null)
|
||||
publishedNS = new SingleFileNamingService(I2PAppContext.getGlobalContext(), published.getAbsolutePath());
|
||||
success = publishedNS.putIfAbsent(key, dest);
|
||||
if (!success) {
|
||||
if (log != null && !success) {
|
||||
try {
|
||||
log.append("Save to published address book " + published.getCanonicalPath() + " failed for new key " + key);
|
||||
} catch (IOException ioe) {}
|
||||
@@ -250,14 +251,14 @@ public class Daemon {
|
||||
}
|
||||
delay *= 60 * 60 * 1000;
|
||||
|
||||
List<String> defaultSubs = new LinkedList();
|
||||
List<String> defaultSubs = new LinkedList<String>();
|
||||
// defaultSubs.add("http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/i2p/hosts.txt");
|
||||
defaultSubs.add("http://www.i2p2.i2p/hosts.txt");
|
||||
|
||||
SubscriptionList subscriptions = new SubscriptionList(subscriptionFile,
|
||||
etagsFile, lastModifiedFile, lastFetchedFile, delay, defaultSubs, settings
|
||||
.get("proxy_host"), Integer.parseInt(settings.get("proxy_port")));
|
||||
Log log = new Log(logFile);
|
||||
Log log = SystemVersion.isAndroid() ? null : new Log(logFile);
|
||||
|
||||
// If false, add hosts via naming service; if true, write hosts.txt file directly
|
||||
// Default false
|
||||
@@ -330,7 +331,7 @@ public class Daemon {
|
||||
homeFile = new SecureDirectory(System.getProperty("user.dir"));
|
||||
}
|
||||
|
||||
Map<String, String> defaultSettings = new HashMap();
|
||||
Map<String, String> defaultSettings = new HashMap<String, String>();
|
||||
defaultSettings.put("proxy_host", "127.0.0.1");
|
||||
defaultSettings.put("proxy_port", "4444");
|
||||
defaultSettings.put("master_addressbook", "../userhosts.txt");
|
||||
|
||||
@@ -81,7 +81,7 @@ class SubscriptionIterator implements Iterator<AddressBook> {
|
||||
// DataHelper.formatDuration(I2PAppContext.getGlobalContext().clock().now() - sub.getLastFetched()) +
|
||||
// " ago but the minimum delay is " +
|
||||
// DataHelper.formatDuration(this.delay));
|
||||
return new AddressBook(Collections.EMPTY_MAP);
|
||||
return new AddressBook(Collections.<String, String> emptyMap());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ class SubscriptionList {
|
||||
public SubscriptionList(File locationsFile, File etagsFile,
|
||||
File lastModifiedFile, File lastFetchedFile, long delay, List<String> defaultSubs, String proxyHost,
|
||||
int proxyPort) {
|
||||
this.subscriptions = new LinkedList();
|
||||
this.subscriptions = new LinkedList<Subscription>();
|
||||
this.etagsFile = etagsFile;
|
||||
this.lastModifiedFile = lastModifiedFile;
|
||||
this.lastFetchedFile = lastFetchedFile;
|
||||
@@ -84,17 +84,17 @@ class SubscriptionList {
|
||||
try {
|
||||
etags = ConfigParser.parse(etagsFile);
|
||||
} catch (IOException exp) {
|
||||
etags = new HashMap();
|
||||
etags = new HashMap<String, String>();
|
||||
}
|
||||
try {
|
||||
lastModified = ConfigParser.parse(lastModifiedFile);
|
||||
} catch (IOException exp) {
|
||||
lastModified = new HashMap();
|
||||
lastModified = new HashMap<String, String>();
|
||||
}
|
||||
try {
|
||||
lastFetched = ConfigParser.parse(lastFetchedFile);
|
||||
} catch (IOException exp) {
|
||||
lastFetched = new HashMap();
|
||||
lastFetched = new HashMap<String, String>();
|
||||
}
|
||||
for (String location : locations) {
|
||||
this.subscriptions.add(new Subscription(location, etags.get(location),
|
||||
@@ -121,9 +121,9 @@ class SubscriptionList {
|
||||
* won't be read back correctly; the '=' should be escaped.
|
||||
*/
|
||||
public void write() {
|
||||
Map<String, String> etags = new HashMap();
|
||||
Map<String, String> lastModified = new HashMap();
|
||||
Map<String, String> lastFetched = new HashMap();
|
||||
Map<String, String> etags = new HashMap<String, String>();
|
||||
Map<String, String> lastModified = new HashMap<String, String>();
|
||||
Map<String, String> lastFetched = new HashMap<String, String>();
|
||||
for (Subscription sub : this.subscriptions) {
|
||||
if (sub.getEtag() != null) {
|
||||
etags.put(sub.getLocation(), sub.getEtag());
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
|
||||
<target name="bundle" unless="no.bundle">
|
||||
<exec executable="sh" osfamily="unix" failifexecutionfails="true" failonerror="${require.gettext}" >
|
||||
<env key="JAVA_HOME" value="${java.home}" />
|
||||
<arg value="./bundle-messages.sh" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="mac" failifexecutionfails="true" failonerror="${require.gettext}" >
|
||||
|
||||
@@ -16,6 +16,10 @@ TMPFILE=build/javafiles.txt
|
||||
export TZ=UTC
|
||||
RC=0
|
||||
|
||||
if ! $(which javac > /dev/null 2>&1); then
|
||||
export JAVAC=${JAVA_HOME}/../bin/javac
|
||||
fi
|
||||
|
||||
if [ "$1" = "-p" ]
|
||||
then
|
||||
POUPDATE=1
|
||||
|
||||
@@ -11,6 +11,7 @@ msgstr ""
|
||||
"POT-Creation-Date: 2011-02-20 11:53+0000\n"
|
||||
"PO-Revision-Date: 2011-02-26 19:46-0000\n"
|
||||
"Last-Translator: hamada <hamada@mail.i2p>\n"
|
||||
"Language: ar\n"
|
||||
"Language-Team: duck <duck@mail.i2p>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
# foo <foo@bar>, 2009.
|
||||
#
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: https://trac.i2p2.de/\n"
|
||||
"POT-Creation-Date: 2011-03-03 18:29+0000\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-01-09 18:07+0000\n"
|
||||
"PO-Revision-Date: 2011-03-22 15:49+0000\n"
|
||||
"Last-Translator: blabla <blabla@trash-mail.com>\n"
|
||||
"Language-Team: German <>\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: de\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:23
|
||||
@@ -46,10 +46,10 @@ msgstr "I2P neustarten"
|
||||
msgid "Stop I2P"
|
||||
msgstr "I2P beenden"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:43
|
||||
msgid "Tray icon configuration"
|
||||
msgstr "Systemleistensymbol konfigurieren"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:46
|
||||
msgid "Should tray icon be enabled?"
|
||||
msgstr "Systemleistensymbol aktivieren?"
|
||||
|
||||
@@ -8,11 +8,11 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P desktopgui\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2011-03-03 18:29+0000\n"
|
||||
"POT-Creation-Date: 2014-01-09 19:27+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"
|
||||
"Language: \n"
|
||||
"Language: en\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@@ -46,10 +46,10 @@ msgstr ""
|
||||
msgid "Stop I2P"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:43
|
||||
msgid "Tray icon configuration"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:46
|
||||
msgid "Should tray icon be enabled?"
|
||||
msgstr ""
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Copyright (C) 2009 The I2P Project
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
# blabla <blabla@trash-mail.com>, 2011
|
||||
# ducki2p <ducki2p@gmail.com>, 2011
|
||||
@@ -11,15 +11,16 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: https://trac.i2p2.de/\n"
|
||||
"POT-Creation-Date: 2011-03-03 18:29+0000\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-01-09 19:14+0000\n"
|
||||
"PO-Revision-Date: 2013-06-08 04:50+0000\n"
|
||||
"Last-Translator: Boxoa590\n"
|
||||
"Language-Team: French (http://www.transifex.com/projects/p/I2P/language/fr/)\n"
|
||||
"Language-Team: French (http://www.transifex.com/projects/p/I2P/language/"
|
||||
"fr/)\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fr\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:23
|
||||
@@ -50,10 +51,10 @@ msgstr "Redémarrer I2P"
|
||||
msgid "Stop I2P"
|
||||
msgstr "Arrêter I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:43
|
||||
msgid "Tray icon configuration"
|
||||
msgstr "Configuration de l'icône de notification"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:46
|
||||
msgid "Should tray icon be enabled?"
|
||||
msgstr "Activer l'icône de notification ?"
|
||||
|
||||
56
apps/desktopgui/locale/messages_ja.po
Normal file
56
apps/desktopgui/locale/messages_ja.po
Normal file
@@ -0,0 +1,56 @@
|
||||
# I2P
|
||||
# Copyright (C) 2009 The I2P Project
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# plazmism <gomidori@live.jp>, 2013
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2011-03-03 18:29+0000\n"
|
||||
"PO-Revision-Date: 2013-11-26 10:38+0000\n"
|
||||
"Last-Translator: plazmism <gomidori@live.jp>\n"
|
||||
"Language-Team: Japanese (http://www.transifex.com/projects/p/I2P/language/ja/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ja\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:23
|
||||
msgid "Start I2P"
|
||||
msgstr "I2P を開始"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P 起動中!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
|
||||
msgid "Starting"
|
||||
msgstr "起動中"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:26
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "I2P ブラウザを起動"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:50
|
||||
msgid "Configure desktopgui"
|
||||
msgstr "desktopgui を設定"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:67
|
||||
msgid "Restart I2P"
|
||||
msgstr "I2P を再起動"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:85
|
||||
msgid "Stop I2P"
|
||||
msgstr "I2P を停止"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
|
||||
msgid "Tray icon configuration"
|
||||
msgstr "トレイアイコン設定"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
|
||||
msgid "Should tray icon be enabled?"
|
||||
msgstr "トレイアイコンを有効にしますか?"
|
||||
56
apps/desktopgui/locale/messages_pt_BR.po
Normal file
56
apps/desktopgui/locale/messages_pt_BR.po
Normal file
@@ -0,0 +1,56 @@
|
||||
# I2P
|
||||
# Copyright (C) 2009 The I2P Project
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# blueboy, 2013
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2011-03-03 18:29+0000\n"
|
||||
"PO-Revision-Date: 2013-11-23 16:31+0000\n"
|
||||
"Last-Translator: blueboy\n"
|
||||
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/I2P/language/pt_BR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: pt_BR\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:23
|
||||
msgid "Start I2P"
|
||||
msgstr "Conectar-se à I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
|
||||
msgid "I2P is starting!"
|
||||
msgstr "Conectando-se a I2P!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
|
||||
msgid "Starting"
|
||||
msgstr "Conectando"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:26
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:50
|
||||
msgid "Configure desktopgui"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:67
|
||||
msgid "Restart I2P"
|
||||
msgstr "Reinicializar o roteador I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:85
|
||||
msgid "Stop I2P"
|
||||
msgstr "Interromper o roteador I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
|
||||
msgid "Tray icon configuration"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
|
||||
msgid "Should tray icon be enabled?"
|
||||
msgstr ""
|
||||
57
apps/desktopgui/locale/messages_ro.po
Normal file
57
apps/desktopgui/locale/messages_ro.po
Normal file
@@ -0,0 +1,57 @@
|
||||
# I2P
|
||||
# Copyright (C) 2009 The I2P Project
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-01-09 19:03+0000\n"
|
||||
"PO-Revision-Date: 2013-11-11 11:31+0000\n"
|
||||
"Last-Translator: polearnik <polearnik@mail.ru>\n"
|
||||
"Language-Team: Romanian (http://www.transifex.com/projects/p/I2P/language/"
|
||||
"ro/)\n"
|
||||
"Language: ro\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?"
|
||||
"2:1));\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:23
|
||||
msgid "Start I2P"
|
||||
msgstr "Start I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P se pornește!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
|
||||
msgid "Starting"
|
||||
msgstr "Începere"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:26
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Lansare I2P Browser"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:50
|
||||
msgid "Configure desktopgui"
|
||||
msgstr "Configurarea desktopgui"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:67
|
||||
msgid "Restart I2P"
|
||||
msgstr "Restart I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:85
|
||||
msgid "Stop I2P"
|
||||
msgstr "Stop I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:43
|
||||
msgid "Tray icon configuration"
|
||||
msgstr "Configurare pictogramei din bara de sistem"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:46
|
||||
msgid "Should tray icon be enabled?"
|
||||
msgstr "Ar trebui să fie activata pictograma din bara de sistem?"
|
||||
@@ -6,14 +6,14 @@
|
||||
# Translators:
|
||||
# ducki2p <ducki2p@gmail.com>, 2011
|
||||
# foo <foo@bar>, 2009
|
||||
# Roman Azarenko <x12ozmouse@ya.ru>, 2013
|
||||
# Роман Азаренко <transifex@basicxp.ru>, 2013
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: https://trac.i2p2.de/\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2011-03-03 18:29+0000\n"
|
||||
"PO-Revision-Date: 2013-07-07 11:44+0000\n"
|
||||
"Last-Translator: Roman Azarenko <x12ozmouse@ya.ru>\n"
|
||||
"PO-Revision-Date: 2013-12-04 11:46+0000\n"
|
||||
"Last-Translator: Bergitte <alvina_alexandrova@mail.ru>\n"
|
||||
"Language-Team: Russian (Russia) (http://www.transifex.com/projects/p/I2P/language/ru_RU/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
@@ -7,9 +7,6 @@ package net.i2p.desktopgui;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.desktopgui.router.RouterManager;
|
||||
import net.i2p.desktopgui.util.*;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@@ -1,28 +1,15 @@
|
||||
package net.i2p.desktopgui;
|
||||
|
||||
import java.awt.AWTException;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Image;
|
||||
import java.awt.MenuItem;
|
||||
import java.awt.PopupMenu;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.TrayIcon;
|
||||
import java.awt.Desktop.Action;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.swing.SwingWorker;
|
||||
|
||||
import net.i2p.desktopgui.i18n.DesktopguiTranslator;
|
||||
import net.i2p.desktopgui.router.RouterManager;
|
||||
import net.i2p.desktopgui.util.BrowseException;
|
||||
import net.i2p.desktopgui.util.ConfigurationManager;
|
||||
import net.i2p.desktopgui.util.I2PDesktop;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,7 +32,6 @@ public class DesktopguiConfigurationFrame extends javax.swing.JFrame {
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
@@ -115,7 +114,7 @@ public class DesktopguiConfigurationFrame extends javax.swing.JFrame {
|
||||
System.out.println("Enabling desktopgui");
|
||||
}
|
||||
try {
|
||||
RouterManager.getRouterContext().setProperty(property, value);
|
||||
RouterManager.getRouterContext().router().saveConfig(property, value);
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(DesktopguiConfigurationFrame.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import java.io.IOException;
|
||||
import org.tanukisoftware.wrapper.WrapperManager;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.desktopgui.i18n.DesktopguiTranslator;
|
||||
import net.i2p.desktopgui.util.ConfigurationManager;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
package net.i2p.desktopgui.util;
|
||||
|
||||
import java.awt.Desktop;
|
||||
import java.awt.TrayIcon;
|
||||
import java.awt.Desktop.Action;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import net.i2p.desktopgui.router.RouterManager;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class I2PDesktop {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="java/src"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/i2p_sdk"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/ministreaming"/>
|
||||
<classpathentry kind="lib" path="/jetty/jettylib/javax.servlet.jar"/>
|
||||
<classpathentry kind="lib" path="/jetty/jettylib/jetty-util.jar"/>
|
||||
<classpathentry kind="lib" path="/jetty/jettylib/org.mortbay.jetty.jar"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/i2p_sdk"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/jetty"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/ministreaming"/>
|
||||
<classpathentry kind="output" path="java/build/obj"/>
|
||||
</classpath>
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
<!-- Update the messages_*.po files.
|
||||
We need to supply the bat file for windows, and then change the fail property to true -->
|
||||
<exec executable="sh" osfamily="unix" failifexecutionfails="true" failonerror="${require.gettext}" >
|
||||
<env key="JAVA_HOME" value="${java.home}" />
|
||||
<arg value="./bundle-messages.sh" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="mac" failifexecutionfails="true" failonerror="${require.gettext}" >
|
||||
|
||||
@@ -15,6 +15,10 @@ TMPFILE=build/javafiles.txt
|
||||
export TZ=UTC
|
||||
RC=0
|
||||
|
||||
if ! $(which javac > /dev/null 2>&1); then
|
||||
export JAVAC=${JAVA_HOME}/../bin/javac
|
||||
fi
|
||||
|
||||
if [ "$1" = "-p" ]
|
||||
then
|
||||
POUPDATE=1
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
package org.klomp.snark;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.client.I2PSessionException;
|
||||
|
||||
@@ -41,51 +41,49 @@ import net.i2p.util.SimpleTimer2;
|
||||
class ConnectionAcceptor implements Runnable
|
||||
{
|
||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(ConnectionAcceptor.class);
|
||||
private I2PServerSocket serverSocket;
|
||||
private PeerAcceptor peeracceptor;
|
||||
private final PeerAcceptor peeracceptor;
|
||||
private Thread thread;
|
||||
private final I2PSnarkUtil _util;
|
||||
private final ObjectCounter<Hash> _badCounter = new ObjectCounter();
|
||||
private final ObjectCounter<Hash> _badCounter = new ObjectCounter<Hash>();
|
||||
private final SimpleTimer2.TimedEvent _cleaner;
|
||||
|
||||
private volatile boolean stop;
|
||||
private boolean socketChanged;
|
||||
|
||||
// protocol errors before blacklisting.
|
||||
private static final int MAX_BAD = 1;
|
||||
private static final long BAD_CLEAN_INTERVAL = 30*60*1000;
|
||||
|
||||
/**
|
||||
* Multitorrent
|
||||
* Multitorrent. Caller MUST call startAccepting()
|
||||
*/
|
||||
public ConnectionAcceptor(I2PSnarkUtil util) {
|
||||
public ConnectionAcceptor(I2PSnarkUtil util, PeerCoordinatorSet set) {
|
||||
_util = util;
|
||||
_cleaner = new Cleaner();
|
||||
peeracceptor = new PeerAcceptor(set);
|
||||
}
|
||||
|
||||
public synchronized void startAccepting(PeerCoordinatorSet set, I2PServerSocket socket) {
|
||||
if (serverSocket != socket) {
|
||||
if ( (peeracceptor == null) || (peeracceptor.coordinators != set) )
|
||||
peeracceptor = new PeerAcceptor(set);
|
||||
serverSocket = socket;
|
||||
/**
|
||||
* May be called even when already running. May be called to start up again after halt().
|
||||
*/
|
||||
public synchronized void startAccepting() {
|
||||
stop = false;
|
||||
socketChanged = true;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("ConnectionAcceptor startAccepting new thread? " + (thread == null));
|
||||
if (thread == null) {
|
||||
thread = new I2PAppThread(this, "I2PSnark acceptor");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
_cleaner.schedule(BAD_CLEAN_INTERVAL);
|
||||
_cleaner.reschedule(BAD_CLEAN_INTERVAL, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unused (single torrent)
|
||||
* Unused (single torrent).
|
||||
* Do NOT call startAccepting().
|
||||
*/
|
||||
public ConnectionAcceptor(I2PSnarkUtil util, I2PServerSocket serverSocket,
|
||||
public ConnectionAcceptor(I2PSnarkUtil util,
|
||||
PeerAcceptor peeracceptor)
|
||||
{
|
||||
this.serverSocket = serverSocket;
|
||||
this.peeracceptor = peeracceptor;
|
||||
_util = util;
|
||||
|
||||
@@ -95,55 +93,79 @@ class ConnectionAcceptor implements Runnable
|
||||
_cleaner = new Cleaner();
|
||||
}
|
||||
|
||||
public void halt()
|
||||
/**
|
||||
* May be restarted later with startAccepting().
|
||||
*/
|
||||
public synchronized void halt()
|
||||
{
|
||||
if (stop) return;
|
||||
stop = true;
|
||||
locked_halt();
|
||||
Thread t = thread;
|
||||
if (t != null) {
|
||||
t.interrupt();
|
||||
thread = null;
|
||||
}
|
||||
}
|
||||
|
||||
I2PServerSocket ss = serverSocket;
|
||||
if (ss != null)
|
||||
|
||||
/**
|
||||
* Caller must synch
|
||||
* @since 0.9.9
|
||||
*/
|
||||
private void locked_halt()
|
||||
{
|
||||
I2PServerSocket ss = _util.getServerSocket();
|
||||
if (ss != null) {
|
||||
try
|
||||
{
|
||||
ss.close();
|
||||
}
|
||||
catch(I2PException ioe) { }
|
||||
|
||||
Thread t = thread;
|
||||
if (t != null)
|
||||
t.interrupt();
|
||||
}
|
||||
_badCounter.clear();
|
||||
_cleaner.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Effectively unused, would only be called if we changed
|
||||
* I2CP host/port, which is hidden in the gui if in router context
|
||||
* FIXME this only works if already running
|
||||
*/
|
||||
public void restart() {
|
||||
serverSocket = _util.getServerSocket();
|
||||
socketChanged = true;
|
||||
public synchronized void restart() {
|
||||
Thread t = thread;
|
||||
if (t != null)
|
||||
t.interrupt();
|
||||
_cleaner.schedule(BAD_CLEAN_INTERVAL);
|
||||
}
|
||||
|
||||
public int getPort()
|
||||
{
|
||||
return 6881; // serverSocket.getLocalPort();
|
||||
return TrackerClient.PORT; // serverSocket.getLocalPort();
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
try {
|
||||
run2();
|
||||
} finally {
|
||||
synchronized(this) {
|
||||
thread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void run2()
|
||||
{
|
||||
while(!stop)
|
||||
{
|
||||
if (socketChanged) {
|
||||
// ok, already updated
|
||||
socketChanged = false;
|
||||
}
|
||||
I2PServerSocket serverSocket = _util.getServerSocket();
|
||||
while ( (serverSocket == null) && (!stop)) {
|
||||
if (!(_util.isConnecting() || _util.connected())) {
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
|
||||
serverSocket = _util.getServerSocket();
|
||||
if (serverSocket == null)
|
||||
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
|
||||
}
|
||||
if(stop)
|
||||
break;
|
||||
@@ -151,25 +173,25 @@ class ConnectionAcceptor implements Runnable
|
||||
{
|
||||
I2PSocket socket = serverSocket.accept();
|
||||
if (socket == null) {
|
||||
if (socketChanged) {
|
||||
continue;
|
||||
} else {
|
||||
I2PServerSocket ss = _util.getServerSocket();
|
||||
if (ss != serverSocket) {
|
||||
serverSocket = ss;
|
||||
socketChanged = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (socket.getPeerDestination().equals(_util.getMyDestination())) {
|
||||
_log.error("Incoming connection from myself");
|
||||
try { socket.close(); } catch (IOException ioe) {}
|
||||
continue;
|
||||
}
|
||||
int bad = _badCounter.count(socket.getPeerDestination().calculateHash());
|
||||
Hash h = socket.getPeerDestination().calculateHash();
|
||||
if (socket.getLocalPort() == 80) {
|
||||
_badCounter.increment(h);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.error("Dropping incoming HTTP from " + h);
|
||||
try { socket.close(); } catch (IOException ioe) {}
|
||||
continue;
|
||||
}
|
||||
int bad = _badCounter.count(h);
|
||||
if (bad >= MAX_BAD) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Rejecting connection from " + socket.getPeerDestination().calculateHash() +
|
||||
_log.warn("Rejecting connection from " + h +
|
||||
" after " + bad + " failures, max is " + MAX_BAD);
|
||||
try { socket.close(); } catch (IOException ioe) {}
|
||||
continue;
|
||||
@@ -180,26 +202,34 @@ class ConnectionAcceptor implements Runnable
|
||||
}
|
||||
catch (I2PException ioe)
|
||||
{
|
||||
if (!socketChanged) {
|
||||
_log.error("Error while accepting", ioe);
|
||||
stop = true;
|
||||
int level = stop ? Log.WARN : Log.ERROR;
|
||||
if (_log.shouldLog(level))
|
||||
_log.log(level, "Error while accepting", ioe);
|
||||
synchronized(this) {
|
||||
if (!stop) {
|
||||
locked_halt();
|
||||
thread = null;
|
||||
stop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
_log.error("Error while accepting", ioe);
|
||||
stop = true;
|
||||
int level = stop ? Log.WARN : Log.ERROR;
|
||||
if (_log.shouldLog(level))
|
||||
_log.log(level, "Error while accepting", ioe);
|
||||
synchronized(this) {
|
||||
if (!stop) {
|
||||
locked_halt();
|
||||
thread = null;
|
||||
stop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// catch oom?
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (serverSocket != null)
|
||||
serverSocket.close();
|
||||
}
|
||||
catch (I2PException ignored) { }
|
||||
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("ConnectionAcceptor closed");
|
||||
}
|
||||
|
||||
private class Handler implements Runnable {
|
||||
|
||||
@@ -43,8 +43,8 @@ abstract class ExtensionHandler {
|
||||
* @return bencoded outgoing handshake message
|
||||
*/
|
||||
public static byte[] getHandshake(int metasize, boolean pexAndMetadata, boolean dht) {
|
||||
Map<String, Object> handshake = new HashMap();
|
||||
Map<String, Integer> m = new HashMap();
|
||||
Map<String, Object> handshake = new HashMap<String, Object>();
|
||||
Map<String, Integer> m = new HashMap<String, Integer>();
|
||||
if (pexAndMetadata) {
|
||||
m.put(TYPE_METADATA, Integer.valueOf(ID_METADATA));
|
||||
m.put(TYPE_PEX, Integer.valueOf(ID_PEX));
|
||||
@@ -56,7 +56,7 @@ abstract class ExtensionHandler {
|
||||
}
|
||||
// include the map even if empty so the far-end doesn't NPE
|
||||
handshake.put("m", m);
|
||||
handshake.put("p", Integer.valueOf(6881));
|
||||
handshake.put("p", Integer.valueOf(TrackerClient.PORT));
|
||||
handshake.put("v", "I2PSnark");
|
||||
handshake.put("reqq", Integer.valueOf(5));
|
||||
return BEncoder.bencode(handshake);
|
||||
@@ -110,7 +110,8 @@ abstract class ExtensionHandler {
|
||||
// drop if we need metainfo and we haven't found anybody yet
|
||||
synchronized(state) {
|
||||
if (!state.isInitialized()) {
|
||||
log.debug("Dropping peer, we need metadata! " + peer);
|
||||
if (log.shouldLog(Log.DEBUG))
|
||||
log.debug("Dropping peer, we need metadata! " + peer);
|
||||
peer.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -124,7 +125,8 @@ abstract class ExtensionHandler {
|
||||
// drop if we need metainfo and we haven't found anybody yet
|
||||
synchronized(state) {
|
||||
if (!state.isInitialized()) {
|
||||
log.debug("Dropping peer, we need metadata! " + peer);
|
||||
if (log.shouldLog(Log.DEBUG))
|
||||
log.debug("Dropping peer, we need metadata! " + peer);
|
||||
peer.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -274,7 +276,7 @@ abstract class ExtensionHandler {
|
||||
|
||||
/** REQUEST and REJECT are the same except for message type */
|
||||
private static void sendMessage(Peer peer, int type, int piece) {
|
||||
Map<String, Object> map = new HashMap();
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("msg_type", Integer.valueOf(type));
|
||||
map.put("piece", Integer.valueOf(piece));
|
||||
byte[] payload = BEncoder.bencode(map);
|
||||
@@ -289,7 +291,7 @@ abstract class ExtensionHandler {
|
||||
}
|
||||
|
||||
private static void sendPiece(Peer peer, int piece, byte[] data) {
|
||||
Map<String, Object> map = new HashMap();
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("msg_type", Integer.valueOf(TYPE_DATA));
|
||||
map.put("piece", Integer.valueOf(piece));
|
||||
map.put("total_size", Integer.valueOf(data.length));
|
||||
@@ -332,7 +334,7 @@ abstract class ExtensionHandler {
|
||||
if (ids.length < HASH_LENGTH)
|
||||
return;
|
||||
int len = Math.min(ids.length, (I2PSnarkUtil.MAX_CONNECTIONS - 1) * HASH_LENGTH);
|
||||
List<PeerID> peers = new ArrayList(len / HASH_LENGTH);
|
||||
List<PeerID> peers = new ArrayList<PeerID>(len / HASH_LENGTH);
|
||||
for (int off = 0; off < len; off += HASH_LENGTH) {
|
||||
byte[] hash = new byte[HASH_LENGTH];
|
||||
System.arraycopy(ids, off, hash, 0, HASH_LENGTH);
|
||||
@@ -380,7 +382,7 @@ abstract class ExtensionHandler {
|
||||
public static void sendPEX(Peer peer, List<Peer> pList) {
|
||||
if (pList.isEmpty())
|
||||
return;
|
||||
Map<String, Object> map = new HashMap();
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
byte[] peers = new byte[HASH_LENGTH * pList.size()];
|
||||
int off = 0;
|
||||
for (Peer p : pList) {
|
||||
@@ -404,7 +406,7 @@ abstract class ExtensionHandler {
|
||||
* @since DHT
|
||||
*/
|
||||
public static void sendDHT(Peer peer, int qport, int rport) {
|
||||
Map<String, Object> map = new HashMap();
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("port", Integer.valueOf(qport));
|
||||
map.put("rport", Integer.valueOf(rport));
|
||||
byte[] payload = BEncoder.bencode(map);
|
||||
|
||||
@@ -3,16 +3,12 @@ package org.klomp.snark;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PSession;
|
||||
@@ -33,7 +29,6 @@ import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
import net.i2p.util.SecureFile;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
import net.i2p.util.Translate;
|
||||
|
||||
@@ -81,6 +76,7 @@ public class I2PSnarkUtil {
|
||||
public static final int MAX_CONNECTIONS = 16; // per torrent
|
||||
public static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond";
|
||||
public static final boolean DEFAULT_USE_DHT = true;
|
||||
public static final String EEPGET_USER_AGENT = "I2PSnark";
|
||||
|
||||
public I2PSnarkUtil(I2PAppContext ctx) {
|
||||
this(ctx, "i2psnark");
|
||||
@@ -94,10 +90,10 @@ public class I2PSnarkUtil {
|
||||
_context = ctx;
|
||||
_log = _context.logManager().getLog(Snark.class);
|
||||
_baseName = baseName;
|
||||
_opts = new HashMap();
|
||||
_opts = new HashMap<String, String>();
|
||||
//setProxy("127.0.0.1", 4444);
|
||||
setI2CPConfig("127.0.0.1", 7654, null);
|
||||
_banlist = new ConcurrentHashSet();
|
||||
_banlist = new ConcurrentHashSet<Hash>();
|
||||
_maxUploaders = Snark.MAX_TOTAL_UPLOADERS;
|
||||
_maxUpBW = DEFAULT_MAX_UP_BW;
|
||||
_maxConnections = MAX_CONNECTIONS;
|
||||
@@ -220,10 +216,8 @@ public class I2PSnarkUtil {
|
||||
_log.debug("Connecting to I2P", new Exception("I did it"));
|
||||
Properties opts = _context.getProperties();
|
||||
if (_opts != null) {
|
||||
for (Iterator iter = _opts.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
opts.setProperty(key, _opts.get(key).toString());
|
||||
}
|
||||
for (Map.Entry<String, String> entry : _opts.entrySet() )
|
||||
opts.setProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (opts.getProperty("inbound.nickname") == null)
|
||||
opts.setProperty("inbound.nickname", _baseName.replace("i2psnark", "I2PSnark"));
|
||||
@@ -259,6 +253,8 @@ public class I2PSnarkUtil {
|
||||
opts.setProperty("i2p.streaming.enforceProtocol", "true");
|
||||
if (opts.getProperty("i2p.streaming.disableRejectLogging") == null)
|
||||
opts.setProperty("i2p.streaming.disableRejectLogging", "true");
|
||||
if (opts.getProperty("i2p.streaming.answerPings") == null)
|
||||
opts.setProperty("i2p.streaming.answerPings", "false");
|
||||
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
|
||||
_connecting = false;
|
||||
}
|
||||
@@ -324,6 +320,8 @@ public class I2PSnarkUtil {
|
||||
if (_banlist.contains(dest))
|
||||
throw new IOException("Not trying to contact " + dest.toBase64() + ", as they are banlisted");
|
||||
try {
|
||||
// TODO opts.setPort(xxx); connect(addr, opts)
|
||||
// DHT moved above 6881 in 0.9.9
|
||||
I2PSocket rv = _manager.connect(addr);
|
||||
if (rv != null)
|
||||
_banlist.remove(dest);
|
||||
@@ -331,7 +329,9 @@ public class I2PSnarkUtil {
|
||||
} catch (I2PException ie) {
|
||||
_banlist.add(dest);
|
||||
_context.simpleScheduler().addEvent(new Unbanlist(dest), 10*60*1000);
|
||||
throw new IOException("Unable to reach the peer " + peer + ": " + ie.getMessage());
|
||||
IOException ioe = new IOException("Unable to reach the peer " + peer);
|
||||
ioe.initCause(ie);
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,6 +394,7 @@ public class I2PSnarkUtil {
|
||||
}
|
||||
}
|
||||
EepGet get = new I2PSocketEepGet(_context, _manager, retries, out.getAbsolutePath(), fetchURL);
|
||||
get.addHeader("User-Agent", EEPGET_USER_AGENT);
|
||||
if (get.fetch(timeout)) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Fetch successful [" + url + "]: size=" + out.length());
|
||||
@@ -435,6 +436,7 @@ public class I2PSnarkUtil {
|
||||
}
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(initialSize);
|
||||
EepGet get = new I2PSocketEepGet(_context, _manager, retries, -1, maxSize, null, out, fetchURL);
|
||||
get.addHeader("User-Agent", EEPGET_USER_AGENT);
|
||||
if (get.fetch(timeout)) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Fetch successful [" + url + "]: size=" + out.size());
|
||||
@@ -513,7 +515,7 @@ public class I2PSnarkUtil {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Using existing session for lookup of " + ip);
|
||||
try {
|
||||
return sess.lookupDest(h);
|
||||
return sess.lookupDest(h, 15*1000);
|
||||
} catch (I2PSessionException ise) {
|
||||
}
|
||||
}
|
||||
@@ -573,7 +575,7 @@ public class I2PSnarkUtil {
|
||||
*/
|
||||
public List<String> getOpenTrackers() {
|
||||
if (!shouldUseOpenTrackers())
|
||||
return Collections.EMPTY_LIST;
|
||||
return Collections.emptyList();
|
||||
return _openTrackers;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,34 +22,56 @@ import net.i2p.util.SimpleTimer2;
|
||||
*/
|
||||
class IdleChecker extends SimpleTimer2.TimedEvent {
|
||||
|
||||
private final SnarkManager _mgr;
|
||||
private final I2PSnarkUtil _util;
|
||||
private final PeerCoordinatorSet _pcs;
|
||||
private final Log _log;
|
||||
private int _consec;
|
||||
private int _consecNotRunning;
|
||||
private boolean _isIdle;
|
||||
|
||||
private static final long CHECK_TIME = 63*1000;
|
||||
private static final int MAX_CONSEC_IDLE = 4;
|
||||
private static final int MAX_CONSEC_NOT_RUNNING = 20;
|
||||
|
||||
/**
|
||||
* Caller must schedule
|
||||
*/
|
||||
public IdleChecker(I2PSnarkUtil util, PeerCoordinatorSet pcs) {
|
||||
super(util.getContext().simpleTimer2());
|
||||
_log = util.getContext().logManager().getLog(IdleChecker.class);
|
||||
_util = util;
|
||||
public IdleChecker(SnarkManager mgr, PeerCoordinatorSet pcs) {
|
||||
super(mgr.util().getContext().simpleTimer2());
|
||||
_util = mgr.util();
|
||||
_log = _util.getContext().logManager().getLog(IdleChecker.class);
|
||||
_mgr = mgr;
|
||||
_pcs = pcs;
|
||||
}
|
||||
|
||||
public void timeReached() {
|
||||
if (_util.connected()) {
|
||||
boolean torrentRunning = false;
|
||||
boolean hasPeers = false;
|
||||
for (PeerCoordinator pc : _pcs) {
|
||||
if (pc.getPeers() > 0) {
|
||||
hasPeers = true;
|
||||
break;
|
||||
if (!pc.halted()) {
|
||||
torrentRunning = true;
|
||||
if (pc.getPeers() > 0) {
|
||||
hasPeers = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (torrentRunning) {
|
||||
_consecNotRunning = 0;
|
||||
} else {
|
||||
if (_consecNotRunning++ >= MAX_CONSEC_NOT_RUNNING) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Closing tunnels on idle");
|
||||
_util.disconnect();
|
||||
_mgr.addMessage(_util.getString("I2P tunnel closed."));
|
||||
schedule(3 * CHECK_TIME);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasPeers) {
|
||||
if (_isIdle)
|
||||
restoreTunnels();
|
||||
@@ -62,6 +84,7 @@ class IdleChecker extends SimpleTimer2.TimedEvent {
|
||||
} else {
|
||||
_isIdle = false;
|
||||
_consec = 0;
|
||||
_consecNotRunning = 0;
|
||||
}
|
||||
schedule(CHECK_TIME);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.RandomSource;
|
||||
|
||||
@@ -190,7 +189,7 @@ class MagnetState {
|
||||
*/
|
||||
public MetaInfo buildMetaInfo() throws Exception {
|
||||
// top map has nothing in it but the info map (no announce)
|
||||
Map<String, BEValue> map = new HashMap();
|
||||
Map<String, BEValue> map = new HashMap<String, BEValue>();
|
||||
InputStream is = new ByteArrayInputStream(metainfoBytes);
|
||||
BDecoder dec = new BDecoder(is);
|
||||
BEValue bev = dec.bdecodeMap();
|
||||
|
||||
@@ -133,7 +133,7 @@ public class MagnetURI {
|
||||
}
|
||||
if (idx < 0 || idx > uri.length())
|
||||
return null;
|
||||
List<String> rv = new ArrayList();
|
||||
List<String> rv = new ArrayList<String>();
|
||||
while (true) {
|
||||
String p = uri.substring(idx);
|
||||
uri = p;
|
||||
|
||||
@@ -156,11 +156,11 @@ public class MetaInfo
|
||||
if (val == null) {
|
||||
this.announce_list = null;
|
||||
} else {
|
||||
this.announce_list = new ArrayList();
|
||||
this.announce_list = new ArrayList<List<String>>();
|
||||
List<BEValue> bl1 = val.getList();
|
||||
for (BEValue bev : bl1) {
|
||||
List<BEValue> bl2 = bev.getList();
|
||||
List<String> sl2 = new ArrayList();
|
||||
List<String> sl2 = new ArrayList<String>();
|
||||
for (BEValue bev2 : bl2) {
|
||||
sl2.add(bev2.getString());
|
||||
}
|
||||
@@ -216,7 +216,16 @@ public class MetaInfo
|
||||
|
||||
// BEP 27
|
||||
val = info.get("private");
|
||||
privateTorrent = val != null && val.getString().equals("1");
|
||||
if (val != null) {
|
||||
Object o = val.getValue();
|
||||
// Is it supposed to be a number or a string?
|
||||
// i2psnark does it as a string. BEP 27 doesn't say.
|
||||
// Transmission does numbers.
|
||||
privateTorrent = "1".equals(o) ||
|
||||
((o instanceof Number) && ((Number) o).intValue() == 1);
|
||||
} else {
|
||||
privateTorrent = false;
|
||||
}
|
||||
|
||||
val = info.get("piece length");
|
||||
if (val == null)
|
||||
@@ -250,9 +259,9 @@ public class MetaInfo
|
||||
if (size == 0)
|
||||
throw new InvalidBEncodingException("zero size files list");
|
||||
|
||||
List<List<String>> m_files = new ArrayList(size);
|
||||
List<List<String>> m_files_utf8 = new ArrayList(size);
|
||||
List<Long> m_lengths = new ArrayList(size);
|
||||
List<List<String>> m_files = new ArrayList<List<String>>(size);
|
||||
List<List<String>> m_files_utf8 = new ArrayList<List<String>>(size);
|
||||
List<Long> m_lengths = new ArrayList<Long>(size);
|
||||
long l = 0;
|
||||
for (int i = 0; i < list.size(); i++)
|
||||
{
|
||||
@@ -278,7 +287,7 @@ public class MetaInfo
|
||||
if (path_length == 0)
|
||||
throw new InvalidBEncodingException("zero size file path list");
|
||||
|
||||
List<String> file = new ArrayList(path_length);
|
||||
List<String> file = new ArrayList<String>(path_length);
|
||||
Iterator<BEValue> it = path_list.iterator();
|
||||
while (it.hasNext()) {
|
||||
String s = it.next().getString();
|
||||
@@ -301,7 +310,7 @@ public class MetaInfo
|
||||
path_list = val.getList();
|
||||
path_length = path_list.size();
|
||||
if (path_length > 0) {
|
||||
file = new ArrayList(path_length);
|
||||
file = new ArrayList<String>(path_length);
|
||||
it = path_list.iterator();
|
||||
while (it.hasNext())
|
||||
file.add(it.next().getString());
|
||||
@@ -517,7 +526,6 @@ public class MetaInfo
|
||||
* @since 0.9.1
|
||||
*/
|
||||
boolean checkPiece(PartialPiece pp) {
|
||||
MessageDigest sha1 = SHA1.getInstance();
|
||||
int piece = pp.getPiece();
|
||||
byte[] hash;
|
||||
try {
|
||||
@@ -565,10 +573,10 @@ public class MetaInfo
|
||||
*/
|
||||
public MetaInfo reannounce(String announce) throws InvalidBEncodingException
|
||||
{
|
||||
Map<String, BEValue> m = new HashMap();
|
||||
Map<String, BEValue> m = new HashMap<String, BEValue>();
|
||||
if (announce != null)
|
||||
m.put("announce", new BEValue(DataHelper.getUTF8(announce)));
|
||||
Map info = createInfoMap();
|
||||
Map<String, BEValue> info = createInfoMap();
|
||||
m.put("info", new BEValue(info));
|
||||
return new MetaInfo(m);
|
||||
}
|
||||
@@ -578,12 +586,12 @@ public class MetaInfo
|
||||
*/
|
||||
public synchronized byte[] getTorrentData()
|
||||
{
|
||||
Map m = new HashMap();
|
||||
Map<String, Object> m = new HashMap<String, Object>();
|
||||
if (announce != null)
|
||||
m.put("announce", announce);
|
||||
if (announce_list != null)
|
||||
m.put("announce-list", announce_list);
|
||||
Map info = createInfoMap();
|
||||
Map<String, BEValue> info = createInfoMap();
|
||||
m.put("info", info);
|
||||
// don't save this locally, we should only do this once
|
||||
return BEncoder.bencode(m);
|
||||
@@ -607,31 +615,42 @@ public class MetaInfo
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Creating new infomap", new Exception());
|
||||
// otherwise we must create it
|
||||
Map info = new HashMap();
|
||||
info.put("name", name);
|
||||
Map<String, BEValue> info = new HashMap<String, BEValue>();
|
||||
info.put("name", new BEValue(DataHelper.getUTF8(name)));
|
||||
if (name_utf8 != null)
|
||||
info.put("name.utf-8", name_utf8);
|
||||
info.put("name.utf-8", new BEValue(DataHelper.getUTF8(name_utf8)));
|
||||
// BEP 27
|
||||
if (privateTorrent)
|
||||
info.put("private", "1");
|
||||
info.put("private", new BEValue(DataHelper.getUTF8("1")));
|
||||
|
||||
info.put("piece length", Integer.valueOf(piece_length));
|
||||
info.put("pieces", piece_hashes);
|
||||
info.put("piece length", new BEValue(Integer.valueOf(piece_length)));
|
||||
info.put("pieces", new BEValue(piece_hashes));
|
||||
if (files == null)
|
||||
info.put("length", Long.valueOf(length));
|
||||
info.put("length", new BEValue(Long.valueOf(length)));
|
||||
else
|
||||
{
|
||||
List l = new ArrayList();
|
||||
List<BEValue> l = new ArrayList<BEValue>();
|
||||
for (int i = 0; i < files.size(); i++)
|
||||
{
|
||||
Map file = new HashMap();
|
||||
file.put("path", files.get(i));
|
||||
if ( (files_utf8 != null) && (files_utf8.size() > i) )
|
||||
file.put("path.utf-8", files_utf8.get(i));
|
||||
file.put("length", lengths.get(i));
|
||||
l.add(file);
|
||||
Map<String, BEValue> file = new HashMap<String, BEValue>();
|
||||
List<String> fi = files.get(i);
|
||||
List<BEValue> befiles = new ArrayList<BEValue>(fi.size());
|
||||
for (int j = 0; j < fi.size(); j++) {
|
||||
befiles.add(new BEValue(DataHelper.getUTF8(fi.get(j))));
|
||||
}
|
||||
file.put("path", new BEValue(befiles));
|
||||
if ( (files_utf8 != null) && (files_utf8.size() > i) ) {
|
||||
List<String> fiu = files_utf8.get(i);
|
||||
List<BEValue> beufiles = new ArrayList<BEValue>(fiu.size());
|
||||
for (int j = 0; j < fiu.size(); j++) {
|
||||
beufiles.add(new BEValue(DataHelper.getUTF8(fiu.get(j))));
|
||||
}
|
||||
file.put("path.utf-8", new BEValue(beufiles));
|
||||
}
|
||||
file.put("length", new BEValue(lengths.get(i)));
|
||||
l.add(new BEValue(file));
|
||||
}
|
||||
info.put("files", l);
|
||||
info.put("files", new BEValue(l));
|
||||
}
|
||||
|
||||
// TODO if we add the ability for other keys in the first constructor
|
||||
|
||||
@@ -31,7 +31,7 @@ import net.i2p.util.SecureFile;
|
||||
*
|
||||
* @since 0.8.2
|
||||
*/
|
||||
class PartialPiece implements Comparable {
|
||||
class PartialPiece implements Comparable<PartialPiece> {
|
||||
|
||||
// we store the piece so we can use it in compareTo()
|
||||
private final Piece piece;
|
||||
@@ -295,8 +295,7 @@ class PartialPiece implements Comparable {
|
||||
* then rarest first,
|
||||
* then highest downloaded first
|
||||
*/
|
||||
public int compareTo(Object o) throws ClassCastException {
|
||||
PartialPiece opp = (PartialPiece)o;
|
||||
public int compareTo(PartialPiece opp) {
|
||||
int d = this.piece.compareTo(opp.piece);
|
||||
if (d != 0)
|
||||
return d;
|
||||
|
||||
@@ -28,6 +28,8 @@ import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
@@ -37,7 +39,7 @@ import net.i2p.util.Log;
|
||||
|
||||
import org.klomp.snark.bencode.BEValue;
|
||||
|
||||
public class Peer implements Comparable
|
||||
public class Peer implements Comparable<Peer>
|
||||
{
|
||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(Peer.class);
|
||||
// Identifying property, the peer id of the other side.
|
||||
@@ -68,8 +70,10 @@ public class Peer implements Comparable
|
||||
private I2PSocket sock;
|
||||
|
||||
private boolean deregister = true;
|
||||
private static long __id;
|
||||
private long _id;
|
||||
private static final AtomicLong __id = new AtomicLong();
|
||||
private final long _id;
|
||||
private final AtomicBoolean _disconnected = new AtomicBoolean();
|
||||
|
||||
final static long CHECK_PERIOD = PeerCoordinator.CHECK_PERIOD; // 40 seconds
|
||||
final static int RATE_DEPTH = PeerCoordinator.RATE_DEPTH; // make following arrays RATE_DEPTH long
|
||||
private long uploaded_old[] = {-1,-1,-1};
|
||||
@@ -98,7 +102,7 @@ public class Peer implements Comparable
|
||||
this.my_id = my_id;
|
||||
this.infohash = infohash;
|
||||
this.metainfo = metainfo;
|
||||
_id = ++__id;
|
||||
_id = __id.incrementAndGet();
|
||||
//_log.debug("Creating a new peer with " + peerID.toString(), new Exception("creating"));
|
||||
}
|
||||
|
||||
@@ -123,7 +127,7 @@ public class Peer implements Comparable
|
||||
|
||||
byte[] id = handshake(in, out);
|
||||
this.peerID = new PeerID(id, sock.getPeerDestination());
|
||||
_id = ++__id;
|
||||
_id = __id.incrementAndGet();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Creating a new peer " + peerID.toString(), new Exception("creating " + _id));
|
||||
}
|
||||
@@ -190,7 +194,7 @@ public class Peer implements Comparable
|
||||
* Compares the PeerIDs.
|
||||
* @deprecated unused?
|
||||
*/
|
||||
public int compareTo(Object o)
|
||||
public int compareTo(Peer o)
|
||||
{
|
||||
Peer p = (Peer)o;
|
||||
int rv = peerID.compareTo(p.peerID);
|
||||
@@ -457,6 +461,8 @@ public class Peer implements Comparable
|
||||
|
||||
void disconnect()
|
||||
{
|
||||
if (!_disconnected.compareAndSet(false, true))
|
||||
return;
|
||||
PeerState s = state;
|
||||
if (s != null)
|
||||
{
|
||||
@@ -476,9 +482,11 @@ public class Peer implements Comparable
|
||||
PeerConnectionIn in = s.in;
|
||||
if (in != null)
|
||||
in.disconnect();
|
||||
PeerConnectionOut out = s.out;
|
||||
if (out != null)
|
||||
out.disconnect();
|
||||
// this is blocking in streaming, so do this after closing the socket
|
||||
// so it won't really block
|
||||
//PeerConnectionOut out = s.out;
|
||||
//if (out != null)
|
||||
// out.disconnect();
|
||||
PeerListener pl = s.listener;
|
||||
if (pl != null)
|
||||
pl.disconnected(this);
|
||||
@@ -492,6 +500,13 @@ public class Peer implements Comparable
|
||||
_log.warn("Error disconnecting " + toString(), ioe);
|
||||
}
|
||||
}
|
||||
if (s != null) {
|
||||
// this is blocking in streaming, so do this after closing the socket
|
||||
// so it won't really block
|
||||
PeerConnectionOut out = s.out;
|
||||
if (out != null)
|
||||
out.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,8 +26,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.SequenceInputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.Base64;
|
||||
@@ -170,8 +168,7 @@ class PeerAcceptor
|
||||
if (b != PROTO[i])
|
||||
throw new IOException("Bad protocol 0x" + Integer.toHexString(b) + " at byte " + i);
|
||||
}
|
||||
if (in.skip(8) != 8)
|
||||
throw new IOException("EOF before hash");
|
||||
DataHelper.skip(in, 8);
|
||||
byte buf[] = new byte[20];
|
||||
int read = DataHelper.read(in, buf);
|
||||
if (read != buf.length)
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@@ -73,7 +72,7 @@ class PeerCheckerTask implements Runnable
|
||||
|
||||
// Keep track of peers we remove now,
|
||||
// we will add them back to the end of the list.
|
||||
List<Peer> removed = new ArrayList();
|
||||
List<Peer> removed = new ArrayList<Peer>();
|
||||
int uploadLimit = coordinator.allowedUploaders();
|
||||
boolean overBWLimit = coordinator.overUpBWLimit();
|
||||
DHT dht = _util.getDHT();
|
||||
|
||||
@@ -65,7 +65,7 @@ class PeerConnectionIn implements Runnable
|
||||
try {
|
||||
din.close();
|
||||
} catch (IOException ioe) {
|
||||
_log.warn("Error closing the stream from " + peer, ioe);
|
||||
//_log.warn("Error closing the stream from " + peer, ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
@@ -42,10 +43,10 @@ class PeerConnectionOut implements Runnable
|
||||
private boolean quit;
|
||||
|
||||
// Contains Messages.
|
||||
private final List<Message> sendQueue = new ArrayList();
|
||||
private final List<Message> sendQueue = new ArrayList<Message>();
|
||||
|
||||
private static long __id = 0;
|
||||
private long _id;
|
||||
private static final AtomicLong __id = new AtomicLong();
|
||||
private final long _id;
|
||||
|
||||
long lastSent;
|
||||
|
||||
@@ -53,10 +54,9 @@ class PeerConnectionOut implements Runnable
|
||||
{
|
||||
this.peer = peer;
|
||||
this.dout = dout;
|
||||
_id = ++__id;
|
||||
_id = __id.incrementAndGet();
|
||||
|
||||
lastSent = System.currentTimeMillis();
|
||||
quit = false;
|
||||
}
|
||||
|
||||
public void startup() {
|
||||
@@ -66,7 +66,7 @@ class PeerConnectionOut implements Runnable
|
||||
|
||||
/**
|
||||
* Continuesly monitors for more outgoing messages that have to be send.
|
||||
* Stops if quit is true of an IOException occurs.
|
||||
* Stops if quit is true or an IOException occurs.
|
||||
*/
|
||||
public void run()
|
||||
{
|
||||
@@ -116,10 +116,10 @@ class PeerConnectionOut implements Runnable
|
||||
// And remove piece messages if we are choking.
|
||||
|
||||
// this should get fixed for starvation
|
||||
Iterator it = sendQueue.iterator();
|
||||
Iterator<Message> it = sendQueue.iterator();
|
||||
while (m == null && it.hasNext())
|
||||
{
|
||||
Message nm = (Message)it.next();
|
||||
Message nm = it.next();
|
||||
if (nm.type == Message.PIECE)
|
||||
{
|
||||
if (state.choking) {
|
||||
@@ -215,13 +215,13 @@ class PeerConnectionOut implements Runnable
|
||||
thread.interrupt();
|
||||
|
||||
sendQueue.clear();
|
||||
sendQueue.notify();
|
||||
sendQueue.notifyAll();
|
||||
}
|
||||
if (dout != null) {
|
||||
try {
|
||||
dout.close();
|
||||
} catch (IOException ioe) {
|
||||
_log.warn("Error closing the stream to " + peer, ioe);
|
||||
//_log.warn("Error closing the stream to " + peer, ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,10 +274,10 @@ class PeerConnectionOut implements Runnable
|
||||
boolean removed = false;
|
||||
synchronized(sendQueue)
|
||||
{
|
||||
Iterator it = sendQueue.iterator();
|
||||
Iterator<Message> it = sendQueue.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Message m = (Message)it.next();
|
||||
Message m = it.next();
|
||||
if (m.type == type)
|
||||
{
|
||||
it.remove();
|
||||
@@ -360,13 +360,13 @@ class PeerConnectionOut implements Runnable
|
||||
|
||||
/** reransmit requests not received in 7m */
|
||||
private static final int REQ_TIMEOUT = (2 * SEND_TIMEOUT) + (60 * 1000);
|
||||
void retransmitRequests(List requests)
|
||||
void retransmitRequests(List<Request> requests)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
Iterator it = requests.iterator();
|
||||
Iterator<Request> it = requests.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Request req = (Request)it.next();
|
||||
Request req = it.next();
|
||||
if(now > req.sendTime + REQ_TIMEOUT) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Retransmit request " + req + " to peer " + peer);
|
||||
@@ -375,12 +375,12 @@ class PeerConnectionOut implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
void sendRequests(List requests)
|
||||
void sendRequests(List<Request> requests)
|
||||
{
|
||||
Iterator it = requests.iterator();
|
||||
Iterator<Request> it = requests.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Request req = (Request)it.next();
|
||||
Request req = it.next();
|
||||
sendRequest(req);
|
||||
}
|
||||
}
|
||||
@@ -391,10 +391,10 @@ class PeerConnectionOut implements Runnable
|
||||
// (multiple choke/unchokes received cause duplicate requests in the queue)
|
||||
synchronized(sendQueue)
|
||||
{
|
||||
Iterator it = sendQueue.iterator();
|
||||
Iterator<Message> it = sendQueue.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Message m = (Message)it.next();
|
||||
Message m = it.next();
|
||||
if (m.type == Message.REQUEST && m.piece == req.getPiece() &&
|
||||
m.begin == req.off && m.length == req.len)
|
||||
{
|
||||
@@ -419,10 +419,10 @@ class PeerConnectionOut implements Runnable
|
||||
int total = 0;
|
||||
synchronized(sendQueue)
|
||||
{
|
||||
Iterator it = sendQueue.iterator();
|
||||
Iterator<Message> it = sendQueue.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Message m = (Message)it.next();
|
||||
Message m = it.next();
|
||||
if (m.type == Message.PIECE)
|
||||
total += m.length;
|
||||
}
|
||||
@@ -489,10 +489,10 @@ class PeerConnectionOut implements Runnable
|
||||
// See if it is still in our send queue
|
||||
synchronized(sendQueue)
|
||||
{
|
||||
Iterator it = sendQueue.iterator();
|
||||
Iterator<Message> it = sendQueue.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Message m = (Message)it.next();
|
||||
Message m = it.next();
|
||||
if (m.type == Message.REQUEST
|
||||
&& m.piece == req.getPiece()
|
||||
&& m.begin == req.off
|
||||
@@ -530,10 +530,10 @@ class PeerConnectionOut implements Runnable
|
||||
{
|
||||
synchronized (sendQueue)
|
||||
{
|
||||
Iterator it = sendQueue.iterator();
|
||||
Iterator<Message> it = sendQueue.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Message m = (Message)it.next();
|
||||
Message m = it.next();
|
||||
if (m.type == Message.PIECE
|
||||
&& m.piece == piece
|
||||
&& m.begin == begin
|
||||
|
||||
@@ -151,12 +151,12 @@ class PeerCoordinator implements PeerListener
|
||||
this.listener = listener;
|
||||
this.snark = torrent;
|
||||
|
||||
wantedPieces = new ArrayList();
|
||||
wantedPieces = new ArrayList<Piece>();
|
||||
setWantedPieces();
|
||||
partialPieces = new ArrayList(getMaxConnections() + 1);
|
||||
peers = new LinkedBlockingQueue();
|
||||
partialPieces = new ArrayList<PartialPiece>(getMaxConnections() + 1);
|
||||
peers = new LinkedBlockingQueue<Peer>();
|
||||
magnetState = new MagnetState(infohash, metainfo);
|
||||
pexPeers = new ConcurrentHashSet();
|
||||
pexPeers = new ConcurrentHashSet<PeerID>();
|
||||
|
||||
// Install a timer to check the uploaders.
|
||||
// Randomize the first start time so multiple tasks are spread out,
|
||||
@@ -218,7 +218,7 @@ class PeerCoordinator implements PeerListener
|
||||
/** for web page detailed stats */
|
||||
public List<Peer> peerList()
|
||||
{
|
||||
return new ArrayList(peers);
|
||||
return new ArrayList<Peer>(peers);
|
||||
}
|
||||
|
||||
public byte[] getID()
|
||||
@@ -412,7 +412,7 @@ class PeerCoordinator implements PeerListener
|
||||
public void halt()
|
||||
{
|
||||
halted = true;
|
||||
List<Peer> removed = new ArrayList();
|
||||
List<Peer> removed = new ArrayList<Peer>();
|
||||
synchronized(peers)
|
||||
{
|
||||
// Stop peer checker task.
|
||||
@@ -613,7 +613,7 @@ class PeerCoordinator implements PeerListener
|
||||
// linked list will contain all interested peers that we choke.
|
||||
// At the start are the peers that have us unchoked at the end the
|
||||
// other peer that are interested, but are choking us.
|
||||
List<Peer> interested = new LinkedList();
|
||||
List<Peer> interested = new LinkedList<Peer>();
|
||||
int count = 0;
|
||||
int unchokedCount = 0;
|
||||
int maxUploaders = allowedUploaders();
|
||||
@@ -729,7 +729,7 @@ class PeerCoordinator implements PeerListener
|
||||
}
|
||||
|
||||
Piece piece = null;
|
||||
List<Piece> requested = new ArrayList();
|
||||
List<Piece> requested = new ArrayList<Piece>();
|
||||
int wantedSize = END_GAME_THRESHOLD + 1;
|
||||
synchronized(wantedPieces)
|
||||
{
|
||||
@@ -833,7 +833,7 @@ class PeerCoordinator implements PeerListener
|
||||
_log.debug("Updated piece priorities called but no priorities to set?");
|
||||
return;
|
||||
}
|
||||
List<Piece> toCancel = new ArrayList();
|
||||
List<Piece> toCancel = new ArrayList<Piece>();
|
||||
synchronized(wantedPieces) {
|
||||
// Add incomplete and previously unwanted pieces to the list
|
||||
// Temp to avoid O(n**2)
|
||||
@@ -999,13 +999,13 @@ class PeerCoordinator implements PeerListener
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
snark.stopTorrent();
|
||||
String msg = "Error writing storage (piece " + piece + ") for " + metainfo.getName() + ": " + ioe;
|
||||
_log.error(msg, ioe);
|
||||
if (listener != null) {
|
||||
listener.addMessage(msg);
|
||||
listener.addMessage("Fatal storage error: Stopping torrent " + metainfo.getName());
|
||||
}
|
||||
snark.stopTorrent();
|
||||
throw new RuntimeException(msg, ioe);
|
||||
}
|
||||
wantedPieces.remove(p);
|
||||
@@ -1019,7 +1019,7 @@ class PeerCoordinator implements PeerListener
|
||||
|
||||
// Announce to the world we have it!
|
||||
// Disconnect from other seeders when we get the last piece
|
||||
List<Peer> toDisconnect = done ? new ArrayList() : null;
|
||||
List<Peer> toDisconnect = done ? new ArrayList<Peer>() : null;
|
||||
for (Peer p : peers) {
|
||||
if (p.isConnected())
|
||||
{
|
||||
@@ -1231,16 +1231,16 @@ class PeerCoordinator implements PeerListener
|
||||
return pp;
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
if (skipped)
|
||||
_log.warn("Partial piece " + pp + " with multiple peers skipped for seeder");
|
||||
_log.info("Partial piece " + pp + " with multiple peers skipped for seeder");
|
||||
else
|
||||
_log.warn("Partial piece " + pp + " NOT in wantedPieces??");
|
||||
_log.info("Partial piece " + pp + " NOT in wantedPieces??");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.WARN) && !partialPieces.isEmpty())
|
||||
_log.warn("Peer " + peer + " has none of our partials " + partialPieces);
|
||||
if (_log.shouldLog(Log.INFO) && !partialPieces.isEmpty())
|
||||
_log.info("Peer " + peer + " has none of our partials " + partialPieces);
|
||||
}
|
||||
// ...and this section turns this into the general move-requests-around code!
|
||||
// Temporary? So PeerState never calls wantPiece() directly for now...
|
||||
|
||||
@@ -16,7 +16,7 @@ class PeerCoordinatorSet implements Iterable<PeerCoordinator> {
|
||||
private final Map<SHA1Hash, PeerCoordinator> _coordinators;
|
||||
|
||||
public PeerCoordinatorSet() {
|
||||
_coordinators = new ConcurrentHashMap();
|
||||
_coordinators = new ConcurrentHashMap<SHA1Hash, PeerCoordinator>();
|
||||
}
|
||||
|
||||
public Iterator<PeerCoordinator> iterator() {
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
@@ -43,7 +42,7 @@ import org.klomp.snark.bencode.InvalidBEncodingException;
|
||||
* and the PeerID is not required.
|
||||
* Equality is now determined solely by the dest hash.
|
||||
*/
|
||||
class PeerID implements Comparable
|
||||
class PeerID implements Comparable<PeerID>
|
||||
{
|
||||
private byte[] id;
|
||||
private Destination address;
|
||||
@@ -58,7 +57,7 @@ class PeerID implements Comparable
|
||||
{
|
||||
this.id = id;
|
||||
this.address = address;
|
||||
this.port = 6881;
|
||||
this.port = TrackerClient.PORT;
|
||||
this.destHash = address.calculateHash().getData();
|
||||
hash = calculateHash();
|
||||
util = null;
|
||||
@@ -77,22 +76,22 @@ class PeerID implements Comparable
|
||||
* Creates a PeerID from a Map containing BEncoded peer id, ip and
|
||||
* port.
|
||||
*/
|
||||
public PeerID(Map m)
|
||||
public PeerID(Map<String, BEValue> m)
|
||||
throws InvalidBEncodingException, UnknownHostException
|
||||
{
|
||||
BEValue bevalue = (BEValue)m.get("peer id");
|
||||
BEValue bevalue = m.get("peer id");
|
||||
if (bevalue == null)
|
||||
throw new InvalidBEncodingException("peer id missing");
|
||||
id = bevalue.getBytes();
|
||||
|
||||
bevalue = (BEValue)m.get("ip");
|
||||
bevalue = m.get("ip");
|
||||
if (bevalue == null)
|
||||
throw new InvalidBEncodingException("ip missing");
|
||||
address = I2PSnarkUtil.getDestinationFromBase64(bevalue.getString());
|
||||
if (address == null)
|
||||
throw new InvalidBEncodingException("Invalid destination [" + bevalue.getString() + "]");
|
||||
|
||||
port = 6881;
|
||||
port = TrackerClient.PORT;
|
||||
this.destHash = address.calculateHash().getData();
|
||||
hash = calculateHash();
|
||||
util = null;
|
||||
@@ -106,7 +105,7 @@ class PeerID implements Comparable
|
||||
public PeerID(byte[] dest_hash, I2PSnarkUtil util) throws InvalidBEncodingException
|
||||
{
|
||||
// id and address remain null
|
||||
port = 6881;
|
||||
port = TrackerClient.PORT;
|
||||
if (dest_hash.length != 32)
|
||||
throw new InvalidBEncodingException("bad hash length");
|
||||
destHash = dest_hash;
|
||||
@@ -196,10 +195,8 @@ class PeerID implements Comparable
|
||||
* Compares port, address and id.
|
||||
* @deprecated unused? and will NPE now that address can be null?
|
||||
*/
|
||||
public int compareTo(Object o)
|
||||
public int compareTo(PeerID pid)
|
||||
{
|
||||
PeerID pid = (PeerID)o;
|
||||
|
||||
int result = port - pid.port;
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
@@ -56,7 +56,7 @@ class PeerState implements DataLoader
|
||||
final PeerConnectionOut out;
|
||||
|
||||
// Outstanding request
|
||||
private final List<Request> outstandingRequests = new ArrayList();
|
||||
private final List<Request> outstandingRequests = new ArrayList<Request>();
|
||||
/** the tail (NOT the head) of the request queue */
|
||||
private Request lastRequest = null;
|
||||
|
||||
@@ -451,7 +451,7 @@ class PeerState implements DataLoader
|
||||
synchronized List<Request> returnPartialPieces()
|
||||
{
|
||||
Set<Integer> pcs = getRequestedPieces();
|
||||
List<Request> rv = new ArrayList(pcs.size());
|
||||
List<Request> rv = new ArrayList<Request>(pcs.size());
|
||||
for (Integer p : pcs) {
|
||||
Request req = getLowestOutstandingRequest(p.intValue());
|
||||
if (req != null) {
|
||||
@@ -469,7 +469,7 @@ class PeerState implements DataLoader
|
||||
* @return all pieces we are currently requesting, or empty Set
|
||||
*/
|
||||
synchronized private Set<Integer> getRequestedPieces() {
|
||||
Set<Integer> rv = new HashSet(outstandingRequests.size() + 1);
|
||||
Set<Integer> rv = new HashSet<Integer>(outstandingRequests.size() + 1);
|
||||
for (Request req : outstandingRequests) {
|
||||
rv.add(Integer.valueOf(req.getPiece()));
|
||||
if (pendingRequest != null)
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.util.Set;
|
||||
* This class is used solely by PeerCoordinator.
|
||||
* Caller must synchronize on many of these methods.
|
||||
*/
|
||||
class Piece implements Comparable {
|
||||
class Piece implements Comparable<Piece> {
|
||||
|
||||
private final int id;
|
||||
private final Set<PeerID> peers;
|
||||
@@ -18,7 +18,7 @@ class Piece implements Comparable {
|
||||
|
||||
public Piece(int id) {
|
||||
this.id = id;
|
||||
this.peers = new HashSet(I2PSnarkUtil.MAX_CONNECTIONS / 2);
|
||||
this.peers = new HashSet<PeerID>(I2PSnarkUtil.MAX_CONNECTIONS / 2);
|
||||
// defer creating requests to save memory
|
||||
}
|
||||
|
||||
@@ -26,11 +26,11 @@ class Piece implements Comparable {
|
||||
* Highest priority first,
|
||||
* then rarest first
|
||||
*/
|
||||
public int compareTo(Object o) throws ClassCastException {
|
||||
int pdiff = ((Piece)o).priority - this.priority; // reverse
|
||||
public int compareTo(Piece op) {
|
||||
int pdiff = op.priority - this.priority; // reverse
|
||||
if (pdiff != 0)
|
||||
return pdiff;
|
||||
return this.peers.size() - ((Piece)o).peers.size();
|
||||
return this.peers.size() - op.peers.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,7 +82,7 @@ class Piece implements Comparable {
|
||||
public void setRequested(Peer peer, boolean requested) {
|
||||
if (requested) {
|
||||
if (this.requests == null)
|
||||
this.requests = new HashSet(2);
|
||||
this.requests = new HashSet<PeerID>(2);
|
||||
this.requests.add(peer.getPeerID());
|
||||
} else {
|
||||
if (this.requests != null)
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
@@ -34,7 +33,6 @@ import java.util.StringTokenizer;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -232,11 +230,12 @@ public class Snark
|
||||
private byte[] id;
|
||||
private final byte[] infoHash;
|
||||
private String additionalTrackerURL;
|
||||
private final I2PSnarkUtil _util;
|
||||
protected final I2PSnarkUtil _util;
|
||||
private final Log _log;
|
||||
private final PeerCoordinatorSet _peerCoordinatorSet;
|
||||
private String trackerProblems;
|
||||
private int trackerSeenPeers;
|
||||
private volatile String trackerProblems;
|
||||
private volatile int trackerSeenPeers;
|
||||
private volatile boolean _autoStoppable;
|
||||
|
||||
|
||||
/** from main() via parseArguments() single torrent */
|
||||
@@ -526,18 +525,17 @@ public class Snark
|
||||
if (_peerCoordinatorSet != null) {
|
||||
// multitorrent
|
||||
_peerCoordinatorSet.add(coordinator);
|
||||
if (acceptor != null) {
|
||||
acceptor.startAccepting(_peerCoordinatorSet, serversocket);
|
||||
} else {
|
||||
// error
|
||||
}
|
||||
} else {
|
||||
// single torrent
|
||||
acceptor = new ConnectionAcceptor(_util, serversocket, new PeerAcceptor(coordinator));
|
||||
acceptor = new ConnectionAcceptor(_util, new PeerAcceptor(coordinator));
|
||||
}
|
||||
// TODO pass saved closest DHT nodes to the tracker? or direct to the coordinator?
|
||||
trackerclient = new TrackerClient(_util, meta, additionalTrackerURL, coordinator, this);
|
||||
}
|
||||
// ensure acceptor is running when in multitorrent
|
||||
if (_peerCoordinatorSet != null && acceptor != null) {
|
||||
acceptor.startAccepting();
|
||||
}
|
||||
|
||||
stopped = false;
|
||||
if (coordinator.halted()) {
|
||||
@@ -761,7 +759,7 @@ public class Snark
|
||||
PeerCoordinator coord = coordinator;
|
||||
if (coord != null)
|
||||
return coord.peerList();
|
||||
return Collections.EMPTY_LIST;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -905,6 +903,16 @@ public class Snark
|
||||
return additionalTrackerURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.9
|
||||
*/
|
||||
public boolean isAutoStoppable() { return _autoStoppable; }
|
||||
|
||||
/**
|
||||
* @since 0.9.9
|
||||
*/
|
||||
public void setAutoStoppable(boolean yes) { _autoStoppable = yes; }
|
||||
|
||||
/**
|
||||
* Sets debug, ip and torrent variables then creates a Snark
|
||||
* instance. Calls usage(), which terminates the program, if
|
||||
|
||||
@@ -14,7 +14,6 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
@@ -35,7 +34,6 @@ import net.i2p.util.Log;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
@@ -114,7 +112,7 @@ public class SnarkManager implements CompleteListener {
|
||||
* "name", "announceURL=websiteURL" pairs
|
||||
* '=' in announceURL must be escaped as ,
|
||||
*/
|
||||
public static final String DEFAULT_TRACKERS[] = {
|
||||
private static final String DEFAULT_TRACKERS[] = {
|
||||
// "Postman", "http://YRgrgTLGnbTq2aZOZDJQ~o6Uk5k6TK-OZtx0St9pb0G-5EGYURZioxqYG8AQt~LgyyI~NCj6aYWpPO-150RcEvsfgXLR~CxkkZcVpgt6pns8SRc3Bi-QSAkXpJtloapRGcQfzTtwllokbdC-aMGpeDOjYLd8b5V9Im8wdCHYy7LRFxhEtGb~RL55DA8aYOgEXcTpr6RPPywbV~Qf3q5UK55el6Kex-6VCxreUnPEe4hmTAbqZNR7Fm0hpCiHKGoToRcygafpFqDw5frLXToYiqs9d4liyVB-BcOb0ihORbo0nS3CLmAwZGvdAP8BZ7cIYE3Z9IU9D1G8JCMxWarfKX1pix~6pIA-sp1gKlL1HhYhPMxwyxvuSqx34o3BqU7vdTYwWiLpGM~zU1~j9rHL7x60pVuYaXcFQDR4-QVy26b6Pt6BlAZoFmHhPcAuWfu-SFhjyZYsqzmEmHeYdAwa~HojSbofg0TMUgESRXMw6YThK1KXWeeJVeztGTz25sL8AAAA.i2p/announce.php=http://tracker.postman.i2p/"
|
||||
// , "eBook", "http://E71FRom6PZNEqTN2Lr8P-sr23b7HJVC32KoGnVQjaX6zJiXwhJy2HsXob36Qmj81TYFZdewFZa9mSJ533UZgGyQkXo2ahctg82JKYZfDe5uDxAn1E9YPjxZCWJaFJh0S~UwSs~9AZ7UcauSJIoNtpxrtbmRNVFLqnkEDdLZi26TeucfOmiFmIWnVblLniWv3tG1boE9Abd-6j3FmYVrRucYuepAILYt6katmVNOk6sXmno1Eynrp~~MBuFq0Ko6~jsc2E2CRVYXDhGHEMdt-j6JUz5D7S2RIVzDRqQyAZLKJ7OdQDmI31przzmne1vOqqqLC~1xUumZVIvF~yOeJUGNjJ1Vx0J8i2BQIusn1pQJ6UCB~ZtZZLQtEb8EPVCfpeRi2ri1M5CyOuxN0V5ekmPHrYIBNevuTCRC26NP7ZS5VDgx1~NaC3A-CzJAE6f1QXi0wMI9aywNG5KGzOPifcsih8eyGyytvgLtrZtV7ykzYpPCS-rDfITncpn5hliPUAAAA.i2p/pub/bt/announce.php=http://de-ebook-archiv.i2p/pub/bt/"
|
||||
// , "Gaytorrents", "http://uxPWHbK1OIj9HxquaXuhMiIvi21iK0~ZiG9d8G0840ZXIg0r6CbiV71xlsqmdnU6wm0T2LySriM0doW2gUigo-5BNkUquHwOjLROiETnB3ZR0Ml4IGa6QBPn1aAq2d9~g1r1nVjLE~pcFnXB~cNNS7kIhX1d6nLgYVZf0C2cZopEow2iWVUggGGnAA9mHjE86zLEnTvAyhbAMTqDQJhEuLa0ZYSORqzJDMkQt90MV4YMjX1ICY6RfUSFmxEqu0yWTrkHsTtRw48l~dz9wpIgc0a0T9C~eeWvmBFTqlJPtQZwntpNeH~jF7nlYzB58olgV2HHFYpVYD87DYNzTnmNWxCJ5AfDorm6AIUCV2qaE7tZtI1h6fbmGpGlPyW~Kw5GXrRfJwNvr6ajwAVi~bPVnrBwDZezHkfW4slOO8FACPR28EQvaTu9nwhAbqESxV2hCTq6vQSGjuxHeOuzBOEvRWkLKOHWTC09t2DbJ94FSqETmZopTB1ukEmaxRWbKSIaAAAA.i2p/announce.php=http://gaytorrents.i2p/"
|
||||
@@ -128,9 +126,20 @@ public class SnarkManager implements CompleteListener {
|
||||
,"Welterde", "http://tracker.welterde.i2p/a=http://tracker.welterde.i2p/stats?mode=top5"
|
||||
,"Diftracker", "http://diftracker.i2p/announce.php=http://diftracker.i2p/"
|
||||
// , "CRSTRACK", "http://b4G9sCdtfvccMAXh~SaZrPqVQNyGQbhbYMbw6supq2XGzbjU4NcOmjFI0vxQ8w1L05twmkOvg5QERcX6Mi8NQrWnR0stLExu2LucUXg1aYjnggxIR8TIOGygZVIMV3STKH4UQXD--wz0BUrqaLxPhrm2Eh9Hwc8TdB6Na4ShQUq5Xm8D4elzNUVdpM~RtChEyJWuQvoGAHY3ppX-EJJLkiSr1t77neS4Lc-KofMVmgI9a2tSSpNAagBiNI6Ak9L1T0F9uxeDfEG9bBSQPNMOSUbAoEcNxtt7xOW~cNOAyMyGydwPMnrQ5kIYPY8Pd3XudEko970vE0D6gO19yoBMJpKx6Dh50DGgybLQ9CpRaynh2zPULTHxm8rneOGRcQo8D3mE7FQ92m54~SvfjXjD2TwAVGI~ae~n9HDxt8uxOecAAvjjJ3TD4XM63Q9TmB38RmGNzNLDBQMEmJFpqQU8YeuhnS54IVdUoVQFqui5SfDeLXlSkh4vYoMU66pvBfWbAAAA.i2p/tracker/announce.php=http://crstrack.i2p/tracker/"
|
||||
,"Exotrack", "http://blbgywsjubw3d2zih2giokakhe3o2cko7jtte4risb3hohbcoyva.b32.i2p/announce.php=http://exotrack.i2p/"
|
||||
// ,"Exotrack", "http://blbgywsjubw3d2zih2giokakhe3o2cko7jtte4risb3hohbcoyva.b32.i2p/announce.php=http://exotrack.i2p/"
|
||||
};
|
||||
|
||||
public static final Set<String> DEFAULT_TRACKER_ANNOUNCES;
|
||||
|
||||
static {
|
||||
Set<String> ann = new HashSet();
|
||||
for (int i = 1; i < DEFAULT_TRACKERS.length; i += 2) {
|
||||
String urls[] = DEFAULT_TRACKERS[i].split("=", 2);
|
||||
ann.add(urls[0]);
|
||||
}
|
||||
DEFAULT_TRACKER_ANNOUNCES = Collections.unmodifiableSet(ann);
|
||||
}
|
||||
|
||||
/** comma delimited list of name=announceURL=baseURL for the trackers to be displayed */
|
||||
public static final String PROP_TRACKERS = "i2psnark.trackers";
|
||||
|
||||
@@ -148,20 +157,20 @@ public class SnarkManager implements CompleteListener {
|
||||
* @since 0.9.6
|
||||
*/
|
||||
public SnarkManager(I2PAppContext ctx, String ctxPath, String ctxName) {
|
||||
_snarks = new ConcurrentHashMap();
|
||||
_magnets = new ConcurrentHashSet();
|
||||
_snarks = new ConcurrentHashMap<String, Snark>();
|
||||
_magnets = new ConcurrentHashSet<String>();
|
||||
_addSnarkLock = new Object();
|
||||
_context = ctx;
|
||||
_contextPath = ctxPath;
|
||||
_contextName = ctxName;
|
||||
_log = _context.logManager().getLog(SnarkManager.class);
|
||||
_messages = new LinkedBlockingQueue();
|
||||
_messages = new LinkedBlockingQueue<String>();
|
||||
_util = new I2PSnarkUtil(_context, ctxName);
|
||||
String cfile = ctxName + CONFIG_FILE_SUFFIX;
|
||||
_configFile = new File(cfile);
|
||||
if (!_configFile.isAbsolute())
|
||||
_configFile = new File(_context.getConfigDir(), cfile);
|
||||
_trackerMap = new ConcurrentHashMap(4);
|
||||
_trackerMap = new ConcurrentHashMap<String, Tracker>(4);
|
||||
loadConfig(null);
|
||||
}
|
||||
|
||||
@@ -171,7 +180,7 @@ public class SnarkManager implements CompleteListener {
|
||||
public void start() {
|
||||
_running = true;
|
||||
_peerCoordinatorSet = new PeerCoordinatorSet();
|
||||
_connectionAcceptor = new ConnectionAcceptor(_util);
|
||||
_connectionAcceptor = new ConnectionAcceptor(_util, _peerCoordinatorSet);
|
||||
_monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor", true);
|
||||
_monitor.start();
|
||||
// only if default instance
|
||||
@@ -180,7 +189,7 @@ public class SnarkManager implements CompleteListener {
|
||||
_context.simpleScheduler().addEvent(new Register(), 4*60*1000);
|
||||
// Not required, Jetty has a shutdown hook
|
||||
//_context.addShutdownTask(new SnarkManagerShutdown());
|
||||
_idleChecker = new IdleChecker(_util, _peerCoordinatorSet);
|
||||
_idleChecker = new IdleChecker(this, _peerCoordinatorSet);
|
||||
_idleChecker.schedule(5*60*1000);
|
||||
}
|
||||
|
||||
@@ -193,6 +202,7 @@ public class SnarkManager implements CompleteListener {
|
||||
if (_umgr != null) {
|
||||
_uhandler = new UpdateHandler(_context, _umgr, SnarkManager.this);
|
||||
_umgr.register(_uhandler, UpdateType.ROUTER_SIGNED, UpdateMethod.TORRENT, 10);
|
||||
_umgr.register(_uhandler, UpdateType.ROUTER_SIGNED_SU3, UpdateMethod.TORRENT, 10);
|
||||
_log.warn("Registering with update manager");
|
||||
} else {
|
||||
_log.warn("No update manager to register with");
|
||||
@@ -210,6 +220,7 @@ public class SnarkManager implements CompleteListener {
|
||||
if (_umgr != null && _uhandler != null) {
|
||||
//_uhandler.shutdown();
|
||||
_umgr.unregister(_uhandler, UpdateType.ROUTER_SIGNED, UpdateMethod.TORRENT);
|
||||
_umgr.unregister(_uhandler, UpdateType.ROUTER_SIGNED_SU3, UpdateMethod.TORRENT);
|
||||
}
|
||||
_running = false;
|
||||
_monitor.interrupt();
|
||||
@@ -238,8 +249,8 @@ public class SnarkManager implements CompleteListener {
|
||||
/** newest last */
|
||||
public List<String> getMessages() {
|
||||
if (_messages.isEmpty())
|
||||
return Collections.EMPTY_LIST;
|
||||
return new ArrayList(_messages);
|
||||
return Collections.emptyList();
|
||||
return new ArrayList<String>(_messages);
|
||||
}
|
||||
|
||||
/** @since 0.9 */
|
||||
@@ -424,7 +435,7 @@ public class SnarkManager implements CompleteListener {
|
||||
String i2cpHost = _config.getProperty(PROP_I2CP_HOST);
|
||||
int i2cpPort = getInt(PROP_I2CP_PORT, 7654);
|
||||
String opts = _config.getProperty(PROP_I2CP_OPTS);
|
||||
Map i2cpOpts = new HashMap();
|
||||
Map<String, String> i2cpOpts = new HashMap<String, String>();
|
||||
if (opts != null) {
|
||||
StringTokenizer tok = new StringTokenizer(opts, " ");
|
||||
while (tok.hasMoreTokens()) {
|
||||
@@ -529,7 +540,7 @@ public class SnarkManager implements CompleteListener {
|
||||
_util.setStartupDelay(minutes);
|
||||
changed = true;
|
||||
_config.setProperty(PROP_STARTUP_DELAY, Integer.toString(minutes));
|
||||
addMessage(_("Startup delay changed to {0}", DataHelper.formatDuration2(minutes * 60 * 1000)));
|
||||
addMessage(_("Startup delay changed to {0}", DataHelper.formatDuration2(minutes * (60L * 1000))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,7 +604,7 @@ public class SnarkManager implements CompleteListener {
|
||||
try { port = Integer.parseInt(i2cpPort); } catch (NumberFormatException nfe) {}
|
||||
}
|
||||
|
||||
Map<String, String> opts = new HashMap();
|
||||
Map<String, String> opts = new HashMap<String, String>();
|
||||
if (i2cpOpts == null) i2cpOpts = "";
|
||||
StringTokenizer tok = new StringTokenizer(i2cpOpts, " \t\n");
|
||||
while (tok.hasMoreTokens()) {
|
||||
@@ -602,7 +613,7 @@ public class SnarkManager implements CompleteListener {
|
||||
if (split > 0)
|
||||
opts.put(pair.substring(0, split), pair.substring(split+1));
|
||||
}
|
||||
Map<String, String> oldOpts = new HashMap();
|
||||
Map<String, String> oldOpts = new HashMap<String, String>();
|
||||
String oldI2CPOpts = _config.getProperty(PROP_I2CP_OPTS);
|
||||
if (oldI2CPOpts == null) oldI2CPOpts = "";
|
||||
tok = new StringTokenizer(oldI2CPOpts, " \t\n");
|
||||
@@ -735,7 +746,7 @@ public class SnarkManager implements CompleteListener {
|
||||
*/
|
||||
private List<String> getOpenTrackers() {
|
||||
if (!_util.shouldUseOpenTrackers())
|
||||
return Collections.EMPTY_LIST;
|
||||
return Collections.emptyList();
|
||||
return getListConfig(PROP_OPENTRACKERS, I2PSnarkUtil.DEFAULT_OPENTRACKERS);
|
||||
}
|
||||
|
||||
@@ -752,7 +763,7 @@ public class SnarkManager implements CompleteListener {
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public void saveOpenTrackers(List<String> ot) {
|
||||
String val = setListConfig(PROP_OPENTRACKERS, ot);
|
||||
setListConfig(PROP_OPENTRACKERS, ot);
|
||||
if (ot == null)
|
||||
ot = Collections.singletonList(I2PSnarkUtil.DEFAULT_OPENTRACKERS);
|
||||
_util.setOpenTrackers(ot);
|
||||
@@ -780,7 +791,7 @@ public class SnarkManager implements CompleteListener {
|
||||
if (val == null)
|
||||
val = dflt;
|
||||
if (val == null)
|
||||
return Collections.EMPTY_LIST;
|
||||
return Collections.emptyList();
|
||||
return Arrays.asList(val.split(","));
|
||||
}
|
||||
|
||||
@@ -831,7 +842,7 @@ public class SnarkManager implements CompleteListener {
|
||||
* An unsynchronized copy.
|
||||
*/
|
||||
public Set<String> listTorrentFiles() {
|
||||
return new HashSet(_snarks.keySet());
|
||||
return new HashSet<String>(_snarks.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -902,7 +913,7 @@ public class SnarkManager implements CompleteListener {
|
||||
filename = sfile.getCanonicalPath();
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Unable to add the torrent " + filename, ioe);
|
||||
addMessage(_("Error: Could not add the torrent {0}", filename) + ": " + ioe.getMessage());
|
||||
addMessage(_("Error: Could not add the torrent {0}", filename) + ": " + ioe);
|
||||
return;
|
||||
}
|
||||
File dataDir = getDataDir();
|
||||
@@ -1016,7 +1027,8 @@ public class SnarkManager implements CompleteListener {
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public void addMagnet(String name, byte[] ih, String trackerURL, boolean updateStatus) {
|
||||
addMagnet(name, ih, trackerURL, updateStatus, shouldAutoStart(), this);
|
||||
// updateStatus is true from UI, false from config file bulk add
|
||||
addMagnet(name, ih, trackerURL, updateStatus, updateStatus, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1383,7 +1395,7 @@ public class SnarkManager implements CompleteListener {
|
||||
* @return failure message or null on success
|
||||
*/
|
||||
private String validateTorrent(MetaInfo info) {
|
||||
List files = info.getFiles();
|
||||
List<List<String>> files = info.getFiles();
|
||||
if ( (files != null) && (files.size() > MAX_FILES_PER_TORRENT) ) {
|
||||
return _("Too many files in \"{0}\" ({1}), deleting it!", info.getName(), files.size());
|
||||
} else if ( (files == null) && (info.getName().endsWith(".torrent")) ) {
|
||||
@@ -1399,7 +1411,7 @@ public class SnarkManager implements CompleteListener {
|
||||
return _("Torrent \"{0}\" has no data, deleting it!", info.getName());
|
||||
} else if (info.getTotalLength() > Storage.MAX_TOTAL_SIZE) {
|
||||
System.out.println("torrent info: " + info.toString());
|
||||
List lengths = info.getLengths();
|
||||
List<Long> lengths = info.getLengths();
|
||||
if (lengths != null)
|
||||
for (int i = 0; i < lengths.size(); i++)
|
||||
System.out.println("File " + i + " is " + lengths.get(i) + " long.");
|
||||
@@ -1486,7 +1498,7 @@ public class SnarkManager implements CompleteListener {
|
||||
private class DirMonitor implements Runnable {
|
||||
public void run() {
|
||||
// don't bother delaying if auto start is false
|
||||
long delay = 60 * 1000 * getStartupDelayMinutes();
|
||||
long delay = (60L * 1000) * getStartupDelayMinutes();
|
||||
if (delay > 0 && shouldAutoStart()) {
|
||||
addMessage(_("Adding torrents in {0}", DataHelper.formatDuration2(delay)));
|
||||
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
|
||||
@@ -1651,7 +1663,7 @@ public class SnarkManager implements CompleteListener {
|
||||
*/
|
||||
private void monitorTorrents(File dir) {
|
||||
String fileNames[] = dir.list(TorrentFilenameFilter.instance());
|
||||
List<String> foundNames = new ArrayList(0);
|
||||
List<String> foundNames = new ArrayList<String>(0);
|
||||
if (fileNames != null) {
|
||||
for (int i = 0; i < fileNames.length; i++) {
|
||||
try {
|
||||
@@ -1666,8 +1678,8 @@ public class SnarkManager implements CompleteListener {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("DirMon found: " + DataHelper.toString(foundNames) + " existing: " + DataHelper.toString(existingNames));
|
||||
// lets find new ones first...
|
||||
for (int i = 0; i < foundNames.size(); i++) {
|
||||
if (existingNames.contains(foundNames.get(i))) {
|
||||
for (String name : foundNames) {
|
||||
if (existingNames.contains(name)) {
|
||||
// already known. noop
|
||||
} else {
|
||||
if (shouldAutoStart() && !_util.connect())
|
||||
@@ -1675,17 +1687,17 @@ public class SnarkManager implements CompleteListener {
|
||||
try {
|
||||
// Snark.fatal() throws a RuntimeException
|
||||
// don't let one bad torrent kill the whole loop
|
||||
addTorrent(foundNames.get(i), !shouldAutoStart());
|
||||
addTorrent(name, !shouldAutoStart());
|
||||
} catch (Exception e) {
|
||||
addMessage(_("Unable to add {0}", foundNames.get(i)) + ": " + e);
|
||||
addMessage(_("Error: Could not add the torrent {0}", name) + ": " + e);
|
||||
_log.error("Unable to add the torrent " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't remove magnet torrents that don't have a torrent file yet
|
||||
existingNames.removeAll(_magnets);
|
||||
// now lets see which ones have been removed...
|
||||
for (Iterator iter = existingNames.iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
for (String name : existingNames) {
|
||||
if (foundNames.contains(name)) {
|
||||
// known and still there. noop
|
||||
} else {
|
||||
@@ -1737,7 +1749,7 @@ public class SnarkManager implements CompleteListener {
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public List<Tracker> getSortedTrackers() {
|
||||
List<Tracker> rv = new ArrayList(_trackerMap.values());
|
||||
List<Tracker> rv = new ArrayList<Tracker>(_trackerMap.values());
|
||||
Collections.sort(rv, new IgnoreCaseComparator());
|
||||
return rv;
|
||||
}
|
||||
@@ -1863,6 +1875,14 @@ public class SnarkManager implements CompleteListener {
|
||||
private final Snark snark;
|
||||
public ThreadedStarter(Snark s) { snark = s; }
|
||||
public void run() {
|
||||
try {
|
||||
run2();
|
||||
} catch (Exception e) {
|
||||
_log.error("Error starting", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void run2() {
|
||||
if (snark != null) {
|
||||
if (snark.isStopped())
|
||||
snark.startTorrent();
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -50,17 +51,7 @@ import net.i2p.util.SystemVersion;
|
||||
public class Storage
|
||||
{
|
||||
private final MetaInfo metainfo;
|
||||
private long[] lengths;
|
||||
private RandomAccessFile[] rafs;
|
||||
private String[] names;
|
||||
private Object[] RAFlock; // lock on RAF access
|
||||
private long[] RAFtime; // when was RAF last accessed, or 0 if closed
|
||||
private File[] RAFfile; // File to make it easier to reopen
|
||||
/** priorities by file; default 0; may be null. @since 0.8.1 */
|
||||
private int[] priorities;
|
||||
/** is the file empty and sparse? */
|
||||
private boolean[] isSparse;
|
||||
|
||||
private final List<TorrentFile> _torrentFiles;
|
||||
private final StorageListener listener;
|
||||
private final I2PSnarkUtil _util;
|
||||
private final Log _log;
|
||||
@@ -78,13 +69,13 @@ public class Storage
|
||||
|
||||
/** The default piece size. */
|
||||
private static final int DEFAULT_PIECE_SIZE = 256*1024;
|
||||
/** note that we start reducing max number of peer connections above 1MB */
|
||||
public static final int MAX_PIECE_SIZE = 2*1024*1024;
|
||||
/** bigger than this will be rejected */
|
||||
public static final int MAX_PIECE_SIZE = 4*1024*1024;
|
||||
/** The maximum number of pieces in a torrent. */
|
||||
public static final int MAX_PIECES = 10*1024;
|
||||
public static final long MAX_TOTAL_SIZE = MAX_PIECE_SIZE * (long) MAX_PIECES;
|
||||
|
||||
private static final Map<String, String> _filterNameCache = new ConcurrentHashMap();
|
||||
private static final Map<String, String> _filterNameCache = new ConcurrentHashMap<String, String>();
|
||||
|
||||
private static final boolean _isWindows = SystemVersion.isWindows();
|
||||
|
||||
@@ -108,6 +99,9 @@ public class Storage
|
||||
piece_size = metainfo.getPieceLength(0);
|
||||
pieces = needed;
|
||||
total_length = metainfo.getTotalLength();
|
||||
List<List<String>> files = metainfo.getFiles();
|
||||
int sz = files != null ? files.size() : 1;
|
||||
_torrentFiles = new ArrayList<TorrentFile>(sz);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,13 +124,13 @@ public class Storage
|
||||
_log = util.getContext().logManager().getLog(Storage.class);
|
||||
this.listener = listener;
|
||||
// Create names, rafs and lengths arrays.
|
||||
getFiles(baseFile);
|
||||
_torrentFiles = getFiles(baseFile);
|
||||
|
||||
long total = 0;
|
||||
ArrayList<Long> lengthsList = new ArrayList();
|
||||
for (int i = 0; i < lengths.length; i++)
|
||||
ArrayList<Long> lengthsList = new ArrayList<Long>();
|
||||
for (TorrentFile tf : _torrentFiles)
|
||||
{
|
||||
long length = lengths[i];
|
||||
long length = tf.length;
|
||||
total += length;
|
||||
lengthsList.add(Long.valueOf(length));
|
||||
}
|
||||
@@ -166,11 +160,11 @@ public class Storage
|
||||
bitfield = new BitField(pieces);
|
||||
needed = 0;
|
||||
|
||||
List<List<String>> files = new ArrayList();
|
||||
for (int i = 0; i < names.length; i++)
|
||||
List<List<String>> files = new ArrayList<List<String>>();
|
||||
for (TorrentFile tf : _torrentFiles)
|
||||
{
|
||||
List<String> file = new ArrayList();
|
||||
StringTokenizer st = new StringTokenizer(names[i], File.separator);
|
||||
List<String> file = new ArrayList<String>();
|
||||
StringTokenizer st = new StringTokenizer(tf.name, File.separator);
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
String part = st.nextToken();
|
||||
@@ -220,42 +214,29 @@ public class Storage
|
||||
return piece_hashes;
|
||||
}
|
||||
|
||||
private void getFiles(File base) throws IOException
|
||||
private List<TorrentFile> getFiles(File base) throws IOException
|
||||
{
|
||||
if (base.getAbsolutePath().equals("/"))
|
||||
throw new IOException("Don't seed root");
|
||||
ArrayList files = new ArrayList();
|
||||
List<File> files = new ArrayList<File>();
|
||||
addFiles(files, base);
|
||||
|
||||
int size = files.size();
|
||||
names = new String[size];
|
||||
lengths = new long[size];
|
||||
rafs = new RandomAccessFile[size];
|
||||
RAFlock = new Object[size];
|
||||
RAFtime = new long[size];
|
||||
RAFfile = new File[size];
|
||||
priorities = new int[size];
|
||||
isSparse = new boolean[size];
|
||||
List<TorrentFile> rv = new ArrayList<TorrentFile>(size);
|
||||
|
||||
int i = 0;
|
||||
Iterator it = files.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
File f = (File)it.next();
|
||||
names[i] = f.getPath();
|
||||
if (base.isDirectory() && names[i].startsWith(base.getPath()))
|
||||
names[i] = names[i].substring(base.getPath().length() + 1);
|
||||
lengths[i] = f.length();
|
||||
RAFlock[i] = new Object();
|
||||
RAFfile[i] = f;
|
||||
i++;
|
||||
}
|
||||
for (File f : files) {
|
||||
rv.add(new TorrentFile(base, f));
|
||||
}
|
||||
// Sort to prevent exposing OS type, and to make it more likely
|
||||
// the same torrent created twice will have the same infohash.
|
||||
Collections.sort(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IOException if too many total files
|
||||
*/
|
||||
private void addFiles(List l, File f) throws IOException {
|
||||
private void addFiles(List<File> 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?");
|
||||
@@ -330,8 +311,8 @@ public class Storage
|
||||
*/
|
||||
public long remaining(String file) {
|
||||
long bytes = 0;
|
||||
for (int i = 0; i < rafs.length; i++) {
|
||||
File f = RAFfile[i];
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
File f = tf.RAFfile;
|
||||
// use canonical in case snark dir or sub dirs are symlinked
|
||||
String canonical = null;
|
||||
if (f != null) {
|
||||
@@ -346,11 +327,11 @@ public class Storage
|
||||
return 0;
|
||||
int psz = piece_size;
|
||||
long start = bytes;
|
||||
long end = start + lengths[i];
|
||||
long end = start + tf.length;
|
||||
int pc = (int) (bytes / psz);
|
||||
long rv = 0;
|
||||
if (!bitfield.get(pc))
|
||||
rv = Math.min(psz - (start % psz), lengths[i]);
|
||||
rv = Math.min(psz - (start % psz), tf.length);
|
||||
for (int j = pc + 1; (((long)j) * psz) < end && j < pieces; j++) {
|
||||
if (!bitfield.get(j)) {
|
||||
if (((long)(j+1))*psz < end)
|
||||
@@ -361,7 +342,7 @@ public class Storage
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
bytes += lengths[i];
|
||||
bytes += tf.length;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -371,16 +352,16 @@ public class Storage
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public int getPriority(String file) {
|
||||
if (complete() || metainfo.getFiles() == null || priorities == null)
|
||||
if (complete() || metainfo.getFiles() == null)
|
||||
return 0;
|
||||
for (int i = 0; i < rafs.length; i++) {
|
||||
File f = RAFfile[i];
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
File f = tf.RAFfile;
|
||||
// use canonical in case snark dir or sub dirs are symlinked
|
||||
if (f != null) {
|
||||
try {
|
||||
String canonical = f.getCanonicalPath();
|
||||
if (canonical.equals(file))
|
||||
return priorities[i];
|
||||
return tf.priority;
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
@@ -395,16 +376,16 @@ public class Storage
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public void setPriority(String file, int pri) {
|
||||
if (complete() || metainfo.getFiles() == null || priorities == null)
|
||||
if (complete() || metainfo.getFiles() == null)
|
||||
return;
|
||||
for (int i = 0; i < rafs.length; i++) {
|
||||
File f = RAFfile[i];
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
File f = tf.RAFfile;
|
||||
// use canonical in case snark dir or sub dirs are symlinked
|
||||
if (f != null) {
|
||||
try {
|
||||
String canonical = f.getCanonicalPath();
|
||||
if (canonical.equals(file)) {
|
||||
priorities[i] = pri;
|
||||
tf.priority = pri;
|
||||
return;
|
||||
}
|
||||
} catch (IOException ioe) {}
|
||||
@@ -418,6 +399,15 @@ public class Storage
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public int[] getFilePriorities() {
|
||||
if (complete())
|
||||
return null;
|
||||
int sz = _torrentFiles.size();
|
||||
if (sz <= 1)
|
||||
return null;
|
||||
int[] priorities = new int[sz];
|
||||
for (int i = 0; i < sz; i++) {
|
||||
priorities[i] = _torrentFiles.get(i).priority;
|
||||
}
|
||||
return priorities;
|
||||
}
|
||||
|
||||
@@ -428,7 +418,18 @@ public class Storage
|
||||
* @since 0.8.1
|
||||
*/
|
||||
void setFilePriorities(int[] p) {
|
||||
priorities = p;
|
||||
if (p == null) {
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
tf.priority = 0;
|
||||
}
|
||||
} else {
|
||||
int sz = _torrentFiles.size();
|
||||
if (p.length != sz)
|
||||
throw new IllegalArgumentException();
|
||||
for (int i = 0; i < sz; i++) {
|
||||
_torrentFiles.get(i).priority = p[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -441,22 +442,23 @@ public class Storage
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public int[] getPiecePriorities() {
|
||||
if (complete() || metainfo.getFiles() == null || priorities == null)
|
||||
if (complete() || metainfo.getFiles() == null)
|
||||
return null;
|
||||
int[] rv = new int[metainfo.getPieces()];
|
||||
int file = 0;
|
||||
long pcEnd = -1;
|
||||
long fileEnd = lengths[0] - 1;
|
||||
long fileEnd = _torrentFiles.get(0).length - 1;
|
||||
int psz = piece_size;
|
||||
for (int i = 0; i < rv.length; i++) {
|
||||
pcEnd += psz;
|
||||
int pri = priorities[file];
|
||||
while (fileEnd <= pcEnd && file < lengths.length - 1) {
|
||||
int pri = _torrentFiles.get(file).priority;
|
||||
while (fileEnd <= pcEnd && file < _torrentFiles.size() - 1) {
|
||||
file++;
|
||||
TorrentFile tf = _torrentFiles.get(file);
|
||||
long oldFileEnd = fileEnd;
|
||||
fileEnd += lengths[file];
|
||||
if (priorities[file] > pri && oldFileEnd < pcEnd)
|
||||
pri = priorities[file];
|
||||
fileEnd += tf.length;
|
||||
if (tf.priority > pri && oldFileEnd < pcEnd)
|
||||
pri = tf.priority;
|
||||
}
|
||||
rv[i] = pri;
|
||||
}
|
||||
@@ -486,13 +488,18 @@ public class Storage
|
||||
|
||||
/**
|
||||
* Creates (and/or checks) all files from the metainfo file list.
|
||||
* Only call this once, and only after the constructor with the metainfo.
|
||||
*/
|
||||
public void check(String rootDir) throws IOException
|
||||
{
|
||||
check(rootDir, 0, null);
|
||||
}
|
||||
|
||||
/** use a saved bitfield and timestamp from a config file */
|
||||
/**
|
||||
* Creates (and/or checks) all files from the metainfo file list.
|
||||
* Use a saved bitfield and timestamp from a config file.
|
||||
* Only call this once, and only after the constructor with the metainfo.
|
||||
*/
|
||||
public void check(String rootDir, long savedTime, BitField savedBitField) throws IOException
|
||||
{
|
||||
File base;
|
||||
@@ -503,6 +510,8 @@ public class Storage
|
||||
base = new SecureFile(rootDir, filterName(metainfo.getName()));
|
||||
boolean useSavedBitField = savedTime > 0 && savedBitField != null;
|
||||
|
||||
if (!_torrentFiles.isEmpty())
|
||||
throw new IllegalStateException();
|
||||
List<List<String>> files = metainfo.getFiles();
|
||||
if (files == null)
|
||||
{
|
||||
@@ -512,22 +521,14 @@ public class Storage
|
||||
if (!base.createNewFile() && !base.exists())
|
||||
throw new IOException("Could not create file " + base);
|
||||
|
||||
lengths = new long[1];
|
||||
rafs = new RandomAccessFile[1];
|
||||
names = new String[1];
|
||||
RAFlock = new Object[1];
|
||||
RAFtime = new long[1];
|
||||
RAFfile = new File[1];
|
||||
isSparse = new boolean[1];
|
||||
lengths[0] = metainfo.getTotalLength();
|
||||
RAFlock[0] = new Object();
|
||||
RAFfile[0] = base;
|
||||
_torrentFiles.add(new TorrentFile(base, base, metainfo.getTotalLength()));
|
||||
if (useSavedBitField) {
|
||||
long lm = base.lastModified();
|
||||
if (lm <= 0 || lm > savedTime)
|
||||
useSavedBitField = false;
|
||||
else if (base.length() != metainfo.getTotalLength())
|
||||
useSavedBitField = false;
|
||||
}
|
||||
names[0] = base.getName();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -540,23 +541,16 @@ public class Storage
|
||||
List<Long> ls = metainfo.getLengths();
|
||||
int size = files.size();
|
||||
long total = 0;
|
||||
lengths = new long[size];
|
||||
rafs = new RandomAccessFile[size];
|
||||
names = new String[size];
|
||||
RAFlock = new Object[size];
|
||||
RAFtime = new long[size];
|
||||
RAFfile = new File[size];
|
||||
isSparse = new boolean[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
List<String> path = files.get(i);
|
||||
File f = createFileFromNames(base, path, areFilesPublic);
|
||||
// dup file name check after filtering
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (f.equals(RAFfile[j])) {
|
||||
if (f.equals(_torrentFiles.get(j).RAFfile)) {
|
||||
// Rename and start the check over again
|
||||
// Copy path since metainfo list is unmodifiable
|
||||
path = new ArrayList(path);
|
||||
path = new ArrayList<String>(path);
|
||||
int last = path.size() - 1;
|
||||
String lastPath = path.get(last);
|
||||
int dot = lastPath.lastIndexOf('.');
|
||||
@@ -570,16 +564,16 @@ public class Storage
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
lengths[i] = ls.get(i).longValue();
|
||||
RAFlock[i] = new Object();
|
||||
RAFfile[i] = f;
|
||||
total += lengths[i];
|
||||
long len = ls.get(i).longValue();
|
||||
_torrentFiles.add(new TorrentFile(base, f, len));
|
||||
total += len;
|
||||
if (useSavedBitField) {
|
||||
long lm = f.lastModified();
|
||||
if (lm <= 0 || lm > savedTime)
|
||||
useSavedBitField = false;
|
||||
else if (f.length() != len)
|
||||
useSavedBitField = false;
|
||||
}
|
||||
names[i] = f.getName();
|
||||
}
|
||||
|
||||
// Sanity check for metainfo file.
|
||||
@@ -597,6 +591,8 @@ public class Storage
|
||||
} else {
|
||||
// the following sets the needed variable
|
||||
changed = true;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Forcing check");
|
||||
checkCreateFiles(false);
|
||||
}
|
||||
if (complete()) {
|
||||
@@ -604,8 +600,6 @@ public class Storage
|
||||
_log.info("Torrent is complete");
|
||||
} else {
|
||||
// fixme saved priorities
|
||||
if (files != null)
|
||||
priorities = new int[files.size()];
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Still need " + needed + " out of " + metainfo.getPieces() + " pieces");
|
||||
}
|
||||
@@ -620,11 +614,11 @@ public class Storage
|
||||
*/
|
||||
public void reopen(String rootDir) throws IOException
|
||||
{
|
||||
if (RAFfile == null)
|
||||
if (_torrentFiles.isEmpty())
|
||||
throw new IOException("Storage not checked yet");
|
||||
for (int i = 0; i < RAFfile.length; i++) {
|
||||
if (!RAFfile[i].exists())
|
||||
throw new IOException("File does not exist: " + RAFfile[i]);
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
if (!tf.RAFfile.exists())
|
||||
throw new IOException("File does not exist: " + tf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -778,10 +772,10 @@ public class Storage
|
||||
|
||||
// Make sure all files are available and of correct length
|
||||
// The files should all exist as they have been created with zero length by createFilesFromNames()
|
||||
for (int i = 0; i < rafs.length; i++)
|
||||
for (TorrentFile tf : _torrentFiles)
|
||||
{
|
||||
long length = RAFfile[i].length();
|
||||
if(RAFfile[i].exists() && length == lengths[i])
|
||||
long length = tf.RAFfile.length();
|
||||
if(tf.RAFfile.exists() && length == tf.length)
|
||||
{
|
||||
if (listener != null)
|
||||
listener.storageAllocated(this, length);
|
||||
@@ -789,27 +783,27 @@ public class Storage
|
||||
}
|
||||
else if (length == 0) {
|
||||
changed = true;
|
||||
synchronized(RAFlock[i]) {
|
||||
allocateFile(i);
|
||||
synchronized(tf) {
|
||||
allocateFile(tf);
|
||||
// close as we go so we don't run out of file descriptors
|
||||
try {
|
||||
closeRAF(i);
|
||||
tf.closeRAF();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
} else {
|
||||
String msg = "File '" + names[i] + "' exists, but has wrong length (expected " +
|
||||
lengths[i] + " but found " + length + ") - repairing corruption";
|
||||
String msg = "File '" + tf.name + "' exists, but has wrong length (expected " +
|
||||
tf.length + " but found " + length + ") - repairing corruption";
|
||||
if (listener != null)
|
||||
listener.addMessage(msg);
|
||||
_log.error(msg);
|
||||
changed = true;
|
||||
resume = true;
|
||||
_probablyComplete = false; // to force RW
|
||||
synchronized(RAFlock[i]) {
|
||||
checkRAF(i);
|
||||
rafs[i].setLength(lengths[i]);
|
||||
synchronized(tf) {
|
||||
RandomAccessFile raf = tf.checkRAF();
|
||||
raf.setLength(tf.length);
|
||||
try {
|
||||
closeRAF(i);
|
||||
tf.closeRAF();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
@@ -820,7 +814,7 @@ public class Storage
|
||||
{
|
||||
byte[] piece = new byte[piece_size];
|
||||
int file = 0;
|
||||
long fileEnd = lengths[0];
|
||||
long fileEnd = _torrentFiles.get(0).length;
|
||||
long pieceEnd = 0;
|
||||
for (int i = 0; i < pieces; i++)
|
||||
{
|
||||
@@ -829,14 +823,15 @@ public class Storage
|
||||
// close as we go so we don't run out of file descriptors
|
||||
pieceEnd += length;
|
||||
while (fileEnd <= pieceEnd) {
|
||||
synchronized(RAFlock[file]) {
|
||||
TorrentFile tf = _torrentFiles.get(file);
|
||||
synchronized(tf) {
|
||||
try {
|
||||
closeRAF(file);
|
||||
tf.closeRAF();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
if (++file >= rafs.length)
|
||||
if (++file >= _torrentFiles.size())
|
||||
break;
|
||||
fileEnd += lengths[file];
|
||||
fileEnd += _torrentFiles.get(file).length;
|
||||
}
|
||||
if (correctHash)
|
||||
{
|
||||
@@ -882,59 +877,17 @@ public class Storage
|
||||
* Sets isSparse[nr] = true. balloonFile(nr) should be called later to
|
||||
* defrag the file.
|
||||
*
|
||||
* This calls openRAF(); caller must synchronize and call closeRAF().
|
||||
* This calls OpenRAF(); caller must synchronize and call closeRAF().
|
||||
*/
|
||||
private void allocateFile(int nr) throws IOException
|
||||
private void allocateFile(TorrentFile tf) throws IOException
|
||||
{
|
||||
// caller synchronized
|
||||
openRAF(nr, false); // RW
|
||||
long remaining = lengths[nr];
|
||||
if (listener != null)
|
||||
listener.storageCreateFile(this, names[nr], remaining);
|
||||
rafs[nr].setLength(remaining);
|
||||
// don't bother ballooning later on Windows since there is no sparse file support
|
||||
// until JDK7 using the JSR-203 interface.
|
||||
// RAF seeks/writes do not create sparse files.
|
||||
// Windows will zero-fill up to the point of the write, which
|
||||
// will make the file fairly unfragmented, on average, at least until
|
||||
// near the end where it will get exponentially more fragmented.
|
||||
if (!_isWindows)
|
||||
isSparse[nr] = true;
|
||||
// caller will close rafs[nr]
|
||||
if (listener != null)
|
||||
listener.storageAllocated(this, lengths[nr]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This "balloons" the file with zeros to eliminate disk fragmentation.,
|
||||
* Overwrites the entire file with zeros. Sets isSparse[nr] = false.
|
||||
*
|
||||
* Caller must synchronize and call checkRAF() or openRAF().
|
||||
* @since 0.9.1
|
||||
*/
|
||||
private void balloonFile(int nr) throws IOException
|
||||
{
|
||||
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);
|
||||
// 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();
|
||||
tf.allocateFile();
|
||||
if (listener != null) {
|
||||
listener.storageCreateFile(this, tf.name, tf.length);
|
||||
listener.storageAllocated(this, tf.length);
|
||||
}
|
||||
isSparse[nr] = false;
|
||||
// caller will close rafs[nr]
|
||||
}
|
||||
|
||||
|
||||
@@ -944,18 +897,14 @@ public class Storage
|
||||
*/
|
||||
public void close() throws IOException
|
||||
{
|
||||
if (rafs == null) return;
|
||||
for (int i = 0; i < rafs.length; i++)
|
||||
for (TorrentFile tf : _torrentFiles)
|
||||
{
|
||||
// if we had an IOE in check(), the RAFlock may be null
|
||||
if (RAFlock[i] == null)
|
||||
continue;
|
||||
try {
|
||||
synchronized(RAFlock[i]) {
|
||||
closeRAF(i);
|
||||
synchronized(tf) {
|
||||
tf.closeRAF();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error closing " + RAFfile[i], ioe);
|
||||
_log.error("Error closing " + tf, ioe);
|
||||
// gobble gobble
|
||||
}
|
||||
}
|
||||
@@ -1020,40 +969,50 @@ public class Storage
|
||||
// Early typecast, avoid possibly overflowing a temp integer
|
||||
long start = (long) piece * (long) piece_size;
|
||||
int i = 0;
|
||||
long raflen = lengths[i];
|
||||
long raflen = _torrentFiles.get(i).length;
|
||||
while (start > raflen) {
|
||||
i++;
|
||||
start -= raflen;
|
||||
raflen = lengths[i];
|
||||
raflen = _torrentFiles.get(i).length;
|
||||
}
|
||||
|
||||
int written = 0;
|
||||
int off = 0;
|
||||
int length = metainfo.getPieceLength(piece);
|
||||
while (written < length) {
|
||||
int need = length - written;
|
||||
int len = (start + need < raflen) ? need : (int)(raflen - start);
|
||||
synchronized(RAFlock[i]) {
|
||||
checkRAF(i);
|
||||
if (isSparse[i]) {
|
||||
// If the file is a newly created sparse file,
|
||||
// AND we aren't skipping it, balloon it with all
|
||||
// zeros to un-sparse it by allocating the space.
|
||||
// Obviously this could take a while.
|
||||
// Once we have written to it, it isn't empty/sparse any more.
|
||||
if (priorities == null || priorities[i] >= 0)
|
||||
balloonFile(i);
|
||||
else
|
||||
isSparse[i] = false;
|
||||
TorrentFile tf = _torrentFiles.get(i);
|
||||
synchronized(tf) {
|
||||
try {
|
||||
RandomAccessFile raf = tf.checkRAF();
|
||||
if (tf.isSparse) {
|
||||
// If the file is a newly created sparse file,
|
||||
// AND we aren't skipping it, balloon it with all
|
||||
// zeros to un-sparse it by allocating the space.
|
||||
// Obviously this could take a while.
|
||||
// Once we have written to it, it isn't empty/sparse any more.
|
||||
if (tf.priority >= 0) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Ballooning " + tf);
|
||||
tf.balloonFile();
|
||||
} else {
|
||||
tf.isSparse = false;
|
||||
}
|
||||
}
|
||||
raf.seek(start);
|
||||
//rafs[i].write(bs, off + written, len);
|
||||
pp.write(raf, written, len);
|
||||
} catch (IOException ioe) {
|
||||
// get the file name in the logs
|
||||
IOException ioe2 = new IOException("Error writing " + tf.RAFfile.getAbsolutePath());
|
||||
ioe2.initCause(ioe);
|
||||
throw ioe2;
|
||||
}
|
||||
rafs[i].seek(start);
|
||||
//rafs[i].write(bs, off + written, len);
|
||||
pp.write(rafs[i], off + written, len);
|
||||
}
|
||||
written += len;
|
||||
if (need - len > 0) {
|
||||
i++;
|
||||
raflen = lengths[i];
|
||||
raflen = _torrentFiles.get(i).length;
|
||||
start = 0;
|
||||
}
|
||||
}
|
||||
@@ -1130,12 +1089,12 @@ public class Storage
|
||||
long start = ((long) piece * (long) piece_size) + off;
|
||||
|
||||
int i = 0;
|
||||
long raflen = lengths[i];
|
||||
long raflen = _torrentFiles.get(i).length;
|
||||
while (start > raflen)
|
||||
{
|
||||
i++;
|
||||
start -= raflen;
|
||||
raflen = lengths[i];
|
||||
raflen = _torrentFiles.get(i).length;
|
||||
}
|
||||
|
||||
int read = 0;
|
||||
@@ -1143,17 +1102,24 @@ public class Storage
|
||||
{
|
||||
int need = length - read;
|
||||
int len = (start + need < raflen) ? need : (int)(raflen - start);
|
||||
synchronized(RAFlock[i])
|
||||
{
|
||||
checkRAF(i);
|
||||
rafs[i].seek(start);
|
||||
rafs[i].readFully(bs, read, len);
|
||||
}
|
||||
TorrentFile tf = _torrentFiles.get(i);
|
||||
synchronized(tf) {
|
||||
try {
|
||||
RandomAccessFile raf = tf.checkRAF();
|
||||
raf.seek(start);
|
||||
raf.readFully(bs, read, len);
|
||||
} catch (IOException ioe) {
|
||||
// get the file name in the logs
|
||||
IOException ioe2 = new IOException("Error reading " + tf.RAFfile.getAbsolutePath());
|
||||
ioe2.initCause(ioe);
|
||||
throw ioe2;
|
||||
}
|
||||
}
|
||||
read += len;
|
||||
if (need - len > 0)
|
||||
{
|
||||
i++;
|
||||
raflen = lengths[i];
|
||||
raflen = _torrentFiles.get(i).length;
|
||||
start = 0;
|
||||
}
|
||||
}
|
||||
@@ -1161,58 +1127,191 @@ public class Storage
|
||||
return length;
|
||||
}
|
||||
|
||||
private static final long RAFCloseDelay = 4*60*1000;
|
||||
|
||||
/**
|
||||
* Close unused RAFs - call periodically
|
||||
*/
|
||||
private static final long RAFCloseDelay = 4*60*1000;
|
||||
public void cleanRAFs() {
|
||||
long cutoff = System.currentTimeMillis() - RAFCloseDelay;
|
||||
for (int i = 0; i < RAFlock.length; i++) {
|
||||
synchronized(RAFlock[i]) {
|
||||
if (RAFtime[i] > 0 && RAFtime[i] < cutoff) {
|
||||
try {
|
||||
closeRAF(i);
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
synchronized(tf) {
|
||||
tf.closeRAF(cutoff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For each of the following,
|
||||
* caller must synchronize on RAFlock[i]
|
||||
* ... except at the beginning if you're careful
|
||||
*/
|
||||
|
||||
/**
|
||||
* This must be called before using the RAF to ensure it is open
|
||||
* A single file in a torrent.
|
||||
* @since 0.9.9
|
||||
*/
|
||||
private void checkRAF(int i) throws IOException {
|
||||
if (RAFtime[i] > 0) {
|
||||
RAFtime[i] = System.currentTimeMillis();
|
||||
return;
|
||||
}
|
||||
openRAF(i);
|
||||
}
|
||||
private class TorrentFile implements Comparable<TorrentFile> {
|
||||
public final long length;
|
||||
public final String name;
|
||||
public final File RAFfile;
|
||||
/**
|
||||
* when was RAF last accessed, or 0 if closed
|
||||
* locking: this
|
||||
*/
|
||||
private long RAFtime;
|
||||
/**
|
||||
* null when closed
|
||||
* locking: this
|
||||
*/
|
||||
private RandomAccessFile raf;
|
||||
/**
|
||||
* is the file empty and sparse?
|
||||
* locking: this
|
||||
*/
|
||||
public boolean isSparse;
|
||||
/** priority by file; default 0 */
|
||||
public volatile int priority;
|
||||
|
||||
private void openRAF(int i) throws IOException {
|
||||
openRAF(i, _probablyComplete);
|
||||
}
|
||||
/**
|
||||
* For new metainfo from files;
|
||||
* use base == f for single-file torrent
|
||||
*/
|
||||
public TorrentFile(File base, File f) {
|
||||
this(base, f, f.length());
|
||||
}
|
||||
|
||||
private void openRAF(int i, boolean readonly) throws IOException {
|
||||
rafs[i] = new RandomAccessFile(RAFfile[i], (readonly || !RAFfile[i].canWrite()) ? "r" : "rw");
|
||||
RAFtime[i] = System.currentTimeMillis();
|
||||
}
|
||||
/**
|
||||
* For existing metainfo with specified file length;
|
||||
* use base == f for single-file torrent
|
||||
*/
|
||||
public TorrentFile(File base, File f, long len) {
|
||||
String n = f.getPath();
|
||||
if (base.isDirectory() && n.startsWith(base.getPath()))
|
||||
n = n.substring(base.getPath().length() + 1);
|
||||
name = n;
|
||||
length = len;
|
||||
RAFfile = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be called even if not open
|
||||
*/
|
||||
private void closeRAF(int i) throws IOException {
|
||||
RAFtime[i] = 0;
|
||||
if (rafs[i] == null)
|
||||
return;
|
||||
rafs[i].close();
|
||||
rafs[i] = null;
|
||||
/*
|
||||
* For each of the following,
|
||||
* caller must synchronize on RAFlock[i]
|
||||
* ... except at the beginning if you're careful
|
||||
*/
|
||||
|
||||
/**
|
||||
* This must be called before using the RAF to ensure it is open
|
||||
* locking: this
|
||||
*/
|
||||
public synchronized RandomAccessFile checkRAF() throws IOException {
|
||||
if (raf != null)
|
||||
RAFtime = System.currentTimeMillis();
|
||||
else
|
||||
openRAF();
|
||||
return raf;
|
||||
}
|
||||
|
||||
/**
|
||||
* locking: this
|
||||
*/
|
||||
private synchronized void openRAF() throws IOException {
|
||||
openRAF(_probablyComplete);
|
||||
}
|
||||
|
||||
/**
|
||||
* locking: this
|
||||
*/
|
||||
private synchronized void openRAF(boolean readonly) throws IOException {
|
||||
raf = new RandomAccessFile(RAFfile, (readonly || !RAFfile.canWrite()) ? "r" : "rw");
|
||||
RAFtime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close if last used time older than cutoff.
|
||||
* locking: this
|
||||
*/
|
||||
public synchronized void closeRAF(long cutoff) {
|
||||
if (RAFtime > 0 && RAFtime < cutoff) {
|
||||
try {
|
||||
closeRAF();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be called even if not open
|
||||
* locking: this
|
||||
*/
|
||||
public synchronized void closeRAF() throws IOException {
|
||||
RAFtime = 0;
|
||||
if (raf == null)
|
||||
return;
|
||||
raf.close();
|
||||
raf = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This creates a (presumably) sparse file so that reads won't fail with IOE.
|
||||
* Sets isSparse[nr] = true. balloonFile(nr) should be called later to
|
||||
* defrag the file.
|
||||
*
|
||||
* This calls openRAF(); caller must synchronize and call closeRAF().
|
||||
*/
|
||||
public synchronized void allocateFile() throws IOException {
|
||||
// caller synchronized
|
||||
openRAF(false); // RW
|
||||
raf.setLength(length);
|
||||
// don't bother ballooning later on Windows since there is no sparse file support
|
||||
// until JDK7 using the JSR-203 interface.
|
||||
// RAF seeks/writes do not create sparse files.
|
||||
// Windows will zero-fill up to the point of the write, which
|
||||
// will make the file fairly unfragmented, on average, at least until
|
||||
// near the end where it will get exponentially more fragmented.
|
||||
if (!_isWindows)
|
||||
isSparse = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This "balloons" the file with zeros to eliminate disk fragmentation.,
|
||||
* Overwrites the entire file with zeros. Sets isSparse[nr] = false.
|
||||
*
|
||||
* Caller must synchronize and call checkRAF() or openRAF().
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public synchronized void balloonFile() throws IOException
|
||||
{
|
||||
long remaining = length;
|
||||
final int ZEROBLOCKSIZE = (int) Math.min(remaining, 32*1024);
|
||||
byte[] zeros = new byte[ZEROBLOCKSIZE];
|
||||
raf.seek(0);
|
||||
// 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);
|
||||
raf.write(zeros, 0, size);
|
||||
remaining -= size;
|
||||
}
|
||||
} finally {
|
||||
remaining = length;
|
||||
if (remaining > 20*1024*1024)
|
||||
_allocateCount.decrementAndGet();
|
||||
}
|
||||
isSparse = false;
|
||||
}
|
||||
|
||||
public int compareTo(TorrentFile tf) {
|
||||
return name.compareTo(tf.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() { return RAFfile.getAbsolutePath().hashCode(); }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof TorrentFile) &&
|
||||
RAFfile.getAbsolutePath().equals(((TorrentFile)o).RAFfile.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return name; }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
package org.klomp.snark;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
@@ -38,7 +36,6 @@ import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.ConvertToHash;
|
||||
@@ -86,6 +83,7 @@ public class TrackerClient implements Runnable {
|
||||
private final static int LONG_SLEEP = 30*60*1000; // sleep a while after lots of fails
|
||||
private final static long MIN_TRACKER_ANNOUNCE_INTERVAL = 15*60*1000;
|
||||
private final static long MIN_DHT_ANNOUNCE_INTERVAL = 10*60*1000;
|
||||
public static final int PORT = 6881;
|
||||
|
||||
private final I2PSnarkUtil _util;
|
||||
private final MetaInfo meta;
|
||||
@@ -113,6 +111,7 @@ public class TrackerClient implements Runnable {
|
||||
private long lastDHTAnnounce;
|
||||
private final List<TCTracker> trackers;
|
||||
private final List<TCTracker> backupTrackers;
|
||||
private long _startedOn;
|
||||
|
||||
/**
|
||||
* Call start() to start it.
|
||||
@@ -134,11 +133,11 @@ public class TrackerClient implements Runnable {
|
||||
this.coordinator = coordinator;
|
||||
this.snark = snark;
|
||||
|
||||
this.port = 6881; //(port == -1) ? 9 : port;
|
||||
this.port = PORT; //(port == -1) ? 9 : port;
|
||||
this.infoHash = urlencode(snark.getInfoHash());
|
||||
this.peerID = urlencode(snark.getID());
|
||||
this.trackers = new ArrayList(2);
|
||||
this.backupTrackers = new ArrayList(2);
|
||||
this.trackers = new ArrayList<TCTracker>(2);
|
||||
this.backupTrackers = new ArrayList<TCTracker>(2);
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
@@ -272,7 +271,7 @@ public class TrackerClient implements Runnable {
|
||||
primary = meta.getAnnounce();
|
||||
else if (additionalTrackerURL != null)
|
||||
primary = additionalTrackerURL;
|
||||
Set<Hash> trackerHashes = new HashSet(8);
|
||||
Set<Hash> trackerHashes = new HashSet<Hash>(8);
|
||||
|
||||
// primary tracker
|
||||
if (primary != null) {
|
||||
@@ -334,6 +333,7 @@ public class TrackerClient implements Runnable {
|
||||
}
|
||||
}
|
||||
this.completed = coordinator.getLeft() == 0;
|
||||
_startedOn = _util.getContext().clock().now();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,15 +377,24 @@ public class TrackerClient implements Runnable {
|
||||
if (dht != null && (meta == null || !meta.isPrivate()))
|
||||
dht.announce(snark.getInfoHash());
|
||||
|
||||
int oldSeenPeers = snark.getTrackerSeenPeers();
|
||||
int maxSeenPeers = 0;
|
||||
if (!trackers.isEmpty())
|
||||
if (!trackers.isEmpty()) {
|
||||
maxSeenPeers = getPeersFromTrackers(trackers);
|
||||
// fast update for UI at startup
|
||||
if (maxSeenPeers > oldSeenPeers)
|
||||
snark.setTrackerSeenPeers(maxSeenPeers);
|
||||
}
|
||||
int p = getPeersFromPEX();
|
||||
if (p > maxSeenPeers)
|
||||
maxSeenPeers = p;
|
||||
p = getPeersFromDHT();
|
||||
if (p > maxSeenPeers)
|
||||
if (p > maxSeenPeers) {
|
||||
maxSeenPeers = p;
|
||||
// fast update for UI at startup
|
||||
if (maxSeenPeers > oldSeenPeers)
|
||||
snark.setTrackerSeenPeers(maxSeenPeers);
|
||||
}
|
||||
// backup if DHT needs bootstrapping
|
||||
if (trackers.isEmpty() && !backupTrackers.isEmpty() && dht != null && dht.size() < 16) {
|
||||
p = getPeersFromTrackers(backupTrackers);
|
||||
@@ -461,6 +470,9 @@ public class TrackerClient implements Runnable {
|
||||
{
|
||||
long uploaded = coordinator.getUploaded();
|
||||
long downloaded = coordinator.getDownloaded();
|
||||
long len = snark.getTotalLength();
|
||||
if (len > 0 && downloaded > len)
|
||||
downloaded = len;
|
||||
left = coordinator.getLeft();
|
||||
String event;
|
||||
if (!tr.started) {
|
||||
@@ -482,12 +494,31 @@ public class TrackerClient implements Runnable {
|
||||
consecutiveFails = 0;
|
||||
runStarted = true;
|
||||
tr.started = true;
|
||||
|
||||
Set<Peer> peers = info.getPeers();
|
||||
tr.seenPeers = info.getPeerCount();
|
||||
if (snark.getTrackerSeenPeers() < tr.seenPeers) // update rising number quickly
|
||||
snark.setTrackerSeenPeers(tr.seenPeers);
|
||||
|
||||
// auto stop
|
||||
// These are very high thresholds for now, not configurable,
|
||||
// just for update torrent
|
||||
if (completed &&
|
||||
tr.isPrimary &&
|
||||
snark.isAutoStoppable() &&
|
||||
!snark.isChecking() &&
|
||||
info.getSeedCount() > 100 &&
|
||||
coordinator.getPeerCount() <= 0 &&
|
||||
_util.getContext().clock().now() > _startedOn + 2*60*60*1000 &&
|
||||
snark.getTotalLength() > 0 &&
|
||||
uploaded >= 2 * snark.getTotalLength()) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Auto stopping " + snark.getBaseName());
|
||||
snark.setAutoStoppable(false);
|
||||
snark.stopTorrent();
|
||||
return tr.seenPeers;
|
||||
}
|
||||
|
||||
Set<Peer> peers = info.getPeers();
|
||||
|
||||
// pass everybody over to our tracker
|
||||
DHT dht = _util.getDHT();
|
||||
if (dht != null) {
|
||||
@@ -499,7 +530,7 @@ public class TrackerClient implements Runnable {
|
||||
if (coordinator.needOutboundPeers()) {
|
||||
// we only want to talk to new people if we need things
|
||||
// from them (duh)
|
||||
List<Peer> ordered = new ArrayList(peers);
|
||||
List<Peer> ordered = new ArrayList<Peer>(peers);
|
||||
Random r = _util.getContext().random();
|
||||
Collections.shuffle(ordered, r);
|
||||
Iterator<Peer> it = ordered.iterator();
|
||||
@@ -564,7 +595,7 @@ public class TrackerClient implements Runnable {
|
||||
if (!pids.isEmpty()) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Got " + pids.size() + " from PEX");
|
||||
List<Peer> peers = new ArrayList(pids.size());
|
||||
List<Peer> peers = new ArrayList<Peer>(pids.size());
|
||||
for (PeerID pID : pids) {
|
||||
peers.add(new Peer(pID, snark.getID(), snark.getInfoHash(), snark.getMetaInfo()));
|
||||
}
|
||||
@@ -596,33 +627,29 @@ public class TrackerClient implements Runnable {
|
||||
// FIXME this needs to be in its own thread
|
||||
int rv = 0;
|
||||
DHT dht = _util.getDHT();
|
||||
if (dht != null && (meta == null || !meta.isPrivate()) && (!stop) &&
|
||||
_util.getContext().clock().now() > lastDHTAnnounce + MIN_DHT_ANNOUNCE_INTERVAL) {
|
||||
if (dht != null &&
|
||||
(meta == null || !meta.isPrivate()) &&
|
||||
(!stop) &&
|
||||
(meta == null || _util.getContext().clock().now() > lastDHTAnnounce + MIN_DHT_ANNOUNCE_INTERVAL)) {
|
||||
int numwant;
|
||||
if (!coordinator.needOutboundPeers())
|
||||
numwant = 1;
|
||||
else
|
||||
numwant = _util.getMaxConnections();
|
||||
Collection<Hash> hashes = dht.getPeers(snark.getInfoHash(), numwant, 2*60*1000);
|
||||
Collection<Hash> hashes = dht.getPeersAndAnnounce(snark.getInfoHash(), numwant, 5*60*1000, 1, 3*60*1000);
|
||||
if (!hashes.isEmpty()) {
|
||||
runStarted = true;
|
||||
lastDHTAnnounce = _util.getContext().clock().now();
|
||||
rv = hashes.size();
|
||||
} else {
|
||||
lastDHTAnnounce = 0;
|
||||
}
|
||||
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) {
|
||||
// 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());
|
||||
List<Peer> peers = new ArrayList<Peer>(hashes.size());
|
||||
for (Hash h : hashes) {
|
||||
try {
|
||||
PeerID pID = new PeerID(h.getData(), _util);
|
||||
@@ -689,6 +716,9 @@ public class TrackerClient implements Runnable {
|
||||
_log.debug("Running unannounce " + _threadName + " to " + tr.announce);
|
||||
long uploaded = coordinator.getUploaded();
|
||||
long downloaded = coordinator.getDownloaded();
|
||||
long len = snark.getTotalLength();
|
||||
if (len > 0 && downloaded > len)
|
||||
downloaded = len;
|
||||
long left = coordinator.getLeft();
|
||||
try
|
||||
{
|
||||
|
||||
@@ -59,7 +59,7 @@ class TrackerInfo
|
||||
this(be.bdecodeMap().getMap(), my_id, infohash, metainfo, util);
|
||||
}
|
||||
|
||||
private TrackerInfo(Map m, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
|
||||
private TrackerInfo(Map<String, BEValue> m, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
|
||||
throws IOException
|
||||
{
|
||||
BEValue reason = (BEValue)m.get("failure reason");
|
||||
@@ -80,7 +80,7 @@ class TrackerInfo
|
||||
|
||||
BEValue bePeers = (BEValue)m.get("peers");
|
||||
if (bePeers == null) {
|
||||
peers = Collections.EMPTY_SET;
|
||||
peers = Collections.emptySet();
|
||||
} else {
|
||||
Set<Peer> p;
|
||||
try {
|
||||
@@ -127,7 +127,7 @@ class TrackerInfo
|
||||
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());
|
||||
Set<Peer> peers = new HashSet<Peer>(l.size());
|
||||
|
||||
for (BEValue bev : l) {
|
||||
PeerID peerID;
|
||||
@@ -161,7 +161,7 @@ class TrackerInfo
|
||||
throws IOException
|
||||
{
|
||||
int count = l.length / HASH_LENGTH;
|
||||
Set<Peer> peers = new HashSet(count);
|
||||
Set<Peer> peers = new HashSet<Peer>(count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
PeerID peerID;
|
||||
@@ -190,6 +190,12 @@ class TrackerInfo
|
||||
return Math.max(pc, complete + incomplete - 1);
|
||||
}
|
||||
|
||||
/** @since 0.9.9 */
|
||||
public int getSeedCount()
|
||||
{
|
||||
return complete;
|
||||
}
|
||||
|
||||
public String getFailureReason()
|
||||
{
|
||||
return failure_reason;
|
||||
|
||||
@@ -42,10 +42,10 @@ class UpdateHandler implements Updater {
|
||||
*/
|
||||
public UpdateTask update(UpdateType type, UpdateMethod method, List<URI> updateSources,
|
||||
String id, String newVersion, long maxTime) {
|
||||
if (type != UpdateType.ROUTER_SIGNED ||
|
||||
if ((type != UpdateType.ROUTER_SIGNED && type != UpdateType.ROUTER_SIGNED_SU3) ||
|
||||
method != UpdateMethod.TORRENT || updateSources.isEmpty())
|
||||
return null;
|
||||
UpdateRunner update = new UpdateRunner(_context, _umgr, _smgr, updateSources, newVersion);
|
||||
UpdateRunner update = new UpdateRunner(_context, _umgr, _smgr, type, updateSources, newVersion);
|
||||
_umgr.notifyProgress(update, "<b>" + _smgr.util().getString("Updating") + "</b>");
|
||||
return update;
|
||||
}
|
||||
|
||||
@@ -6,11 +6,9 @@ import java.util.List;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.TrustedUpdate;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.update.*;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
/**
|
||||
* The downloader for router signed updates.
|
||||
@@ -22,6 +20,7 @@ class UpdateRunner implements UpdateTask, CompleteListener {
|
||||
private final Log _log;
|
||||
private final UpdateManager _umgr;
|
||||
private final SnarkManager _smgr;
|
||||
private final UpdateType _type;
|
||||
private final List<URI> _urls;
|
||||
private volatile boolean _isRunning;
|
||||
private volatile boolean _hasMetaInfo;
|
||||
@@ -36,11 +35,12 @@ class UpdateRunner implements UpdateTask, CompleteListener {
|
||||
private static final long CHECK_INTERVAL = 3*60*1000;
|
||||
|
||||
public UpdateRunner(I2PAppContext ctx, UpdateManager umgr, SnarkManager smgr,
|
||||
List<URI> uris, String newVersion) {
|
||||
UpdateType type, List<URI> uris, String newVersion) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(getClass());
|
||||
_umgr = umgr;
|
||||
_smgr = smgr;
|
||||
_type = type;
|
||||
_urls = uris;
|
||||
_newVersion = newVersion;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ class UpdateRunner implements UpdateTask, CompleteListener {
|
||||
}
|
||||
}
|
||||
|
||||
public UpdateType getType() { return UpdateType.ROUTER_SIGNED; }
|
||||
public UpdateType getType() { return _type; }
|
||||
|
||||
public UpdateMethod getMethod() { return UpdateMethod.TORRENT; }
|
||||
|
||||
@@ -111,7 +111,7 @@ class UpdateRunner implements UpdateTask, CompleteListener {
|
||||
}
|
||||
_snark = _smgr.addMagnet(name, ih, trackerURL, true, true, this);
|
||||
if (_snark != null) {
|
||||
updateStatus("<b>" + _smgr.util().getString("Updating from {0}", updateURL) + "</b>");
|
||||
updateStatus("<b>" + _smgr.util().getString("Updating from {0}", linkify(updateURL)) + "</b>");
|
||||
new Timeout();
|
||||
break;
|
||||
}
|
||||
@@ -264,6 +264,7 @@ class UpdateRunner implements UpdateTask, CompleteListener {
|
||||
}
|
||||
_hasMetaInfo = true;
|
||||
notifyProgress();
|
||||
snark.setAutoStoppable(true);
|
||||
return _smgr.gotMetaInfo(snark);
|
||||
}
|
||||
|
||||
@@ -291,6 +292,12 @@ class UpdateRunner implements UpdateTask, CompleteListener {
|
||||
|
||||
//////// end CompleteListener methods
|
||||
|
||||
private static String linkify(String url) {
|
||||
String durl = url.length() <= 28 ? url :
|
||||
url.substring(0, 25) + "…";
|
||||
return "<a target=\"_blank\" href=\"" + url + "\"/>" + durl + "</a>";
|
||||
}
|
||||
|
||||
private void updateStatus(String s) {
|
||||
_umgr.notifyProgress(this, s);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public class BDecoder
|
||||
private int indicator = 0;
|
||||
|
||||
// Used for ugly hack to get SHA hash over the metainfo info map
|
||||
private final String special_map = "info";
|
||||
private static final String special_map = "info";
|
||||
private boolean in_special_map = false;
|
||||
/** creation deferred until we encounter the special map, to make processing of announce replies more efficient */
|
||||
private MessageDigest sha_digest;
|
||||
@@ -281,7 +281,7 @@ public class BDecoder
|
||||
+ (char)c + "'");
|
||||
indicator = 0;
|
||||
|
||||
List result = new ArrayList();
|
||||
List<BEValue> result = new ArrayList<BEValue>();
|
||||
c = getNextIndicator();
|
||||
while (c != 'e')
|
||||
{
|
||||
@@ -308,7 +308,7 @@ public class BDecoder
|
||||
+ (char)c + "'");
|
||||
indicator = 0;
|
||||
|
||||
Map result = new HashMap();
|
||||
Map<String, BEValue> result = new HashMap<String, BEValue>();
|
||||
c = getNextIndicator();
|
||||
while (c != 'e')
|
||||
{
|
||||
|
||||
@@ -49,12 +49,12 @@ public class BEValue
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public BEValue(List value)
|
||||
public BEValue(List<BEValue> value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public BEValue(Map value)
|
||||
public BEValue(Map<String, BEValue> value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
@@ -142,11 +142,12 @@ public class BEValue
|
||||
* succeeds when the BEValue is actually a List, otherwise it will
|
||||
* throw a InvalidBEncodingException.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<BEValue> getList() throws InvalidBEncodingException
|
||||
{
|
||||
try
|
||||
{
|
||||
return (List)value;
|
||||
return (List<BEValue>)value;
|
||||
}
|
||||
catch (ClassCastException cce)
|
||||
{
|
||||
@@ -159,11 +160,12 @@ public class BEValue
|
||||
* values. This operation only succeeds when the BEValue is actually
|
||||
* a Map, otherwise it will throw a InvalidBEncodingException.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, BEValue> getMap() throws InvalidBEncodingException
|
||||
{
|
||||
try
|
||||
{
|
||||
return (Map)value;
|
||||
return (Map<String, BEValue>)value;
|
||||
}
|
||||
catch (ClassCastException cce)
|
||||
{
|
||||
|
||||
@@ -59,9 +59,9 @@ public class BEncoder
|
||||
else if (o instanceof Number)
|
||||
bencode((Number)o, out);
|
||||
else if (o instanceof List)
|
||||
bencode((List)o, out);
|
||||
bencode((List<?>)o, out);
|
||||
else if (o instanceof Map)
|
||||
bencode((Map<String, Object>)o, out);
|
||||
bencode((Map<?, ?>)o, out);
|
||||
else if (o instanceof BEValue)
|
||||
bencode(((BEValue)o).getValue(), out);
|
||||
else
|
||||
@@ -110,7 +110,7 @@ public class BEncoder
|
||||
out.write('e');
|
||||
}
|
||||
|
||||
public static byte[] bencode(List l)
|
||||
public static byte[] bencode(List<?> l)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -124,10 +124,10 @@ public class BEncoder
|
||||
}
|
||||
}
|
||||
|
||||
public static void bencode(List l, OutputStream out) throws IOException
|
||||
public static void bencode(List<?> l, OutputStream out) throws IOException
|
||||
{
|
||||
out.write('l');
|
||||
Iterator it = l.iterator();
|
||||
Iterator<?> it = l.iterator();
|
||||
while (it.hasNext())
|
||||
bencode(it.next(), out);
|
||||
out.write('e');
|
||||
@@ -155,7 +155,7 @@ public class BEncoder
|
||||
out.write(bs);
|
||||
}
|
||||
|
||||
public static byte[] bencode(Map<String, Object> m)
|
||||
public static byte[] bencode(Map<?, ?> m)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -169,23 +169,29 @@ public class BEncoder
|
||||
}
|
||||
}
|
||||
|
||||
public static void bencode(Map<String, Object> m, OutputStream out) throws IOException
|
||||
public static void bencode(Map<?, ?> m, OutputStream out)
|
||||
throws IOException, IllegalArgumentException
|
||||
{
|
||||
out.write('d');
|
||||
|
||||
// Keys must be sorted. XXX - But is this the correct order?
|
||||
Set<String> s = m.keySet();
|
||||
List<String> l = new ArrayList(s);
|
||||
Set<?> s = m.keySet();
|
||||
List<String> l = new ArrayList<String>(s.size());
|
||||
for (Object k : s) {
|
||||
// Keys must be Strings.
|
||||
if (String.class.isAssignableFrom(k.getClass()))
|
||||
l.add((String) k);
|
||||
else
|
||||
throw new IllegalArgumentException("Cannot bencode map: contains non-String key of type " + k.getClass());
|
||||
}
|
||||
Collections.sort(l);
|
||||
|
||||
Iterator<String> it = l.iterator();
|
||||
while(it.hasNext())
|
||||
{
|
||||
// Keys must be Strings.
|
||||
String key = it.next();
|
||||
Object value = m.get(key);
|
||||
bencode(key, out);
|
||||
bencode(value, out);
|
||||
bencode(m.get(key), out);
|
||||
}
|
||||
|
||||
out.write('e');
|
||||
|
||||
@@ -36,16 +36,18 @@ public interface DHT {
|
||||
public void ping(Destination dest, int port);
|
||||
|
||||
/**
|
||||
* Get peers for a torrent.
|
||||
* Get peers for a torrent, and announce to the closest node we find.
|
||||
* Blocking!
|
||||
* Caller should run in a thread.
|
||||
*
|
||||
* @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
|
||||
* @param annMax the number of peers to announce to
|
||||
* @param annMaxWait the maximum total time to wait for announces, may be 0 to return immediately without waiting for acks
|
||||
* @return possibly empty (never null)
|
||||
*/
|
||||
public Collection<Hash> getPeers(byte[] ih, int max, long maxWait);
|
||||
public Collection<Hash> getPeersAndAnnounce(byte[] ih, int max, long maxWait, int annMax, long annMaxWait);
|
||||
|
||||
/**
|
||||
* Announce to ourselves.
|
||||
|
||||
@@ -5,11 +5,8 @@ package org.klomp.snark.dht;
|
||||
|
||||
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;
|
||||
@@ -53,8 +50,8 @@ class DHTNodes {
|
||||
_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));
|
||||
_nodeMap = new ConcurrentHashMap<NID, NodeInfo>();
|
||||
_kad = new KBucketSet<NID>(ctx, me, KAD_K, KAD_B, new KBTrimmer(ctx, KAD_K));
|
||||
}
|
||||
|
||||
public void start() {
|
||||
@@ -120,7 +117,7 @@ class DHTNodes {
|
||||
else
|
||||
key = new NID(h.getData());
|
||||
List<NID> keys = _kad.getClosest(key, numWant);
|
||||
List<NodeInfo> rv = new ArrayList(keys.size());
|
||||
List<NodeInfo> rv = new ArrayList<NodeInfo>(keys.size());
|
||||
for (NID nid : keys) {
|
||||
NodeInfo ninfo = _nodeMap.get(nid);
|
||||
if (ninfo != null)
|
||||
|
||||
@@ -104,10 +104,10 @@ class DHTTracker {
|
||||
List<Hash> getPeers(InfoHash ih, int max) {
|
||||
Peers peers = _torrents.get(ih);
|
||||
if (peers == null)
|
||||
return Collections.EMPTY_LIST;
|
||||
return Collections.emptyList();
|
||||
|
||||
int size = peers.size();
|
||||
List<Hash> rv = new ArrayList(peers.values());
|
||||
List<Hash> rv = new ArrayList<Hash>(peers.values());
|
||||
if (max < size) {
|
||||
Collections.shuffle(rv, _context.random());
|
||||
rv = rv.subList(0, max);
|
||||
|
||||
@@ -10,7 +10,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -24,7 +23,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.client.I2PSessionMuxedListener;
|
||||
@@ -32,17 +30,16 @@ import net.i2p.client.SendMessageOptions;
|
||||
import net.i2p.client.datagram.I2PDatagramDissector;
|
||||
import net.i2p.client.datagram.I2PDatagramMaker;
|
||||
import net.i2p.client.datagram.I2PInvalidDatagramException;
|
||||
import net.i2p.crypto.SHA1Hash;
|
||||
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.ConcurrentHashSet;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
import org.klomp.snark.TrackerClient;
|
||||
import org.klomp.snark.bencode.BDecoder;
|
||||
import org.klomp.snark.bencode.BEncoder;
|
||||
import org.klomp.snark.bencode.BEValue;
|
||||
@@ -149,6 +146,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private static final long MAX_MSGID_AGE = 2*60*1000;
|
||||
/** how long since sent do we wait for a reply */
|
||||
private static final long DEFAULT_QUERY_TIMEOUT = 75*1000;
|
||||
private static final long DEST_LOOKUP_TIMEOUT = 10*1000;
|
||||
/** stagger with other cleaners */
|
||||
private static final long CLEAN_TIME = 63*1000;
|
||||
private static final long EXPLORE_TIME = 877*1000;
|
||||
@@ -167,15 +165,15 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
_log = ctx.logManager().getLog(KRPC.class);
|
||||
_tracker = new DHTTracker(ctx);
|
||||
|
||||
_sentQueries = new ConcurrentHashMap();
|
||||
_outgoingTokens = new ConcurrentHashMap();
|
||||
_incomingTokens = new ConcurrentHashMap();
|
||||
_blacklist = new ConcurrentHashSet();
|
||||
_sentQueries = new ConcurrentHashMap<MsgID, ReplyWaiter>();
|
||||
_outgoingTokens = new ConcurrentHashMap<Token, NodeInfo>();
|
||||
_incomingTokens = new ConcurrentHashMap<NID, Token>();
|
||||
_blacklist = new ConcurrentHashSet<NID>();
|
||||
|
||||
// Construct my NodeInfo
|
||||
// Pick ports over a big range to marginally increase security
|
||||
// If we add a search DHT, adjust to stay out of each other's way
|
||||
_qPort = 2555 + ctx.random().nextInt(61111);
|
||||
_qPort = TrackerClient.PORT + 10 + ctx.random().nextInt(65535 - 20 - TrackerClient.PORT);
|
||||
_rPort = _qPort + 1;
|
||||
if (SECURE_NID) {
|
||||
_myNID = NodeInfo.generateNID(session.getMyDestination().calculateHash(), _qPort, _context.random());
|
||||
@@ -244,9 +242,9 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
_log.info("DHT is empty, cannot explore");
|
||||
return;
|
||||
}
|
||||
SortedSet<NodeInfo> toTry = new TreeSet(new NodeInfoComparator(target));
|
||||
SortedSet<NodeInfo> toTry = new TreeSet<NodeInfo>(new NodeInfoComparator(target));
|
||||
toTry.addAll(nodes);
|
||||
Set<NodeInfo> tried = new HashSet();
|
||||
Set<NodeInfo> tried = new HashSet<NodeInfo>();
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Starting explore of " + target);
|
||||
@@ -307,7 +305,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peers for a torrent.
|
||||
* Get peers for a torrent, and announce to the closest node we find.
|
||||
* This is an iterative lookup in the DHT.
|
||||
* Blocking!
|
||||
* Caller should run in a thread.
|
||||
@@ -315,30 +313,38 @@ public class KRPC implements I2PSessionMuxedListener, 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
|
||||
* @param annMax the number of peers to announce to
|
||||
* @param annMaxWait the maximum total time to wait for announces, may be 0 to return immediately without waiting for acks
|
||||
* @return possibly empty (never null)
|
||||
*/
|
||||
public Collection<Hash> getPeers(byte[] ih, int max, long maxWait) {
|
||||
public Collection<Hash> getPeersAndAnnounce(byte[] ih, int max, long maxWait, int annMax, long annMaxWait) {
|
||||
// check local tracker first
|
||||
InfoHash iHash = new InfoHash(ih);
|
||||
Collection<Hash> rv = _tracker.getPeers(iHash, max);
|
||||
rv.remove(_myNodeInfo.getHash());
|
||||
if (rv.size() >= max)
|
||||
return rv;
|
||||
rv = new HashSet(rv);
|
||||
rv = new HashSet<Hash>(rv);
|
||||
long endTime = _context.clock().now() + maxWait;
|
||||
|
||||
// needs to be much higher than log(size) since many lookups will fail
|
||||
// at first and we will give up too early
|
||||
int maxNodes = 30;
|
||||
// Initial set to try, will get added to as we go
|
||||
int maxNodes = 12;
|
||||
List<NodeInfo> nodes = _knownNodes.findClosest(iHash, maxNodes);
|
||||
SortedSet<NodeInfo> toTry = new TreeSet(new NodeInfoComparator(iHash));
|
||||
NodeInfoComparator comp = new NodeInfoComparator(iHash);
|
||||
SortedSet<NodeInfo> toTry = new TreeSet<NodeInfo>(comp);
|
||||
SortedSet<NodeInfo> heardFrom = new TreeSet<NodeInfo>(comp);
|
||||
toTry.addAll(nodes);
|
||||
Set<NodeInfo> tried = new HashSet();
|
||||
SortedSet<NodeInfo> tried = new TreeSet<NodeInfo>(comp);
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Starting getPeers for " + iHash + " with " + nodes.size() + " to try");
|
||||
_log.info("Starting getPeers for " + iHash + " (b64: " + new NID(ih) + ") " + " with " + nodes.size() + " to try");
|
||||
for (int i = 0; i < maxNodes; i++) {
|
||||
if (!_isRunning)
|
||||
break;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Now to try: " + toTry);
|
||||
NodeInfo nInfo;
|
||||
try {
|
||||
nInfo = toTry.first();
|
||||
@@ -347,13 +353,15 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
toTry.remove(nInfo);
|
||||
tried.add(nInfo);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Try " + i + ": " + nInfo);
|
||||
|
||||
ReplyWaiter waiter = sendGetPeers(nInfo, iHash);
|
||||
if (waiter == null)
|
||||
continue;
|
||||
synchronized(waiter) {
|
||||
try {
|
||||
waiter.wait(Math.max(20*1000, (Math.min(40*1000, endTime - _context.clock().now()))));
|
||||
waiter.wait(Math.max(30*1000, (Math.min(45*1000, endTime - _context.clock().now()))));
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
|
||||
@@ -365,18 +373,24 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Got pong");
|
||||
} else if (replyType == REPLY_PEERS) {
|
||||
heardFrom.add(waiter.getSentTo());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Got peers");
|
||||
List<Hash> reply = (List<Hash>) waiter.getReplyObject();
|
||||
// shouldn't send us an empty peers list but through
|
||||
// 0.9.8.1 it will
|
||||
if (!reply.isEmpty()) {
|
||||
for (int j = 0; j < reply.size() && rv.size() < max; j++) {
|
||||
rv.add(reply.get(j));
|
||||
Hash h = reply.get(j);
|
||||
if (!h.equals(_myNodeInfo.getHash()))
|
||||
rv.add(h);
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Finished get Peers, got " + rv.size() + " from DHT, returning " + reply.size());
|
||||
return rv;
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Finished get Peers, got " + reply.size() + " from DHT, returning " + rv.size());
|
||||
break;
|
||||
} else if (replyType == REPLY_NODES) {
|
||||
heardFrom.add(waiter.getSentTo());
|
||||
List<NodeInfo> reply = (List<NodeInfo>) waiter.getReplyObject();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Got " + reply.size() + " nodes");
|
||||
@@ -392,9 +406,45 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
if (_context.clock().now() > endTime)
|
||||
break;
|
||||
if (!toTry.isEmpty() && !heardFrom.isEmpty() &&
|
||||
comp.compare(toTry.first(), heardFrom.first()) >= 0) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Finished get Peers, nothing closer to try after " + (i+1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// now announce
|
||||
if (!heardFrom.isEmpty()) {
|
||||
announce(ih);
|
||||
// announce to the closest we've heard from
|
||||
int annCnt = 0;
|
||||
long start = _context.clock().now();
|
||||
for (Iterator<NodeInfo> iter = heardFrom.iterator(); iter.hasNext() && annCnt < annMax && _isRunning; ) {
|
||||
NodeInfo annTo = iter.next();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Announcing to closest from get peers: " + annTo);
|
||||
long toWait = annMaxWait > 0 ? Math.min(annMaxWait, 60*1000) : 0;
|
||||
if (announce(ih, annTo, toWait))
|
||||
annCnt++;
|
||||
if (annMaxWait > 0) {
|
||||
annMaxWait -= _context.clock().now() - start;
|
||||
if (annMaxWait < 1000)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// spray it, but unlikely to work, we just went through the kbuckets,
|
||||
// so this is essentially just a retry
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Announcing to closest in kbuckets after get peers failed");
|
||||
announce(ih, annMax, annMaxWait);
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
_log.info("Finished get Peers, returning " + rv.size());
|
||||
_log.info("Tried: " + tried);
|
||||
_log.info("Heard from: " + heardFrom);
|
||||
_log.info("Not tried: " + toTry);
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Finished get Peers, " + rv.size() + " from local and none from DHT");
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -436,13 +486,16 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
|
||||
/**
|
||||
* Not recommended - use getPeersAndAnnounce().
|
||||
*
|
||||
* Announce to the closest peers in the local DHT.
|
||||
* This is NOT iterative - call getPeers() first to get the closest
|
||||
* peers into the local DHT.
|
||||
* Blocking unless maxWait <= 0
|
||||
* Caller should run in a thread.
|
||||
* This also automatically announces ourself to our local tracker.
|
||||
* For best results do a getPeers() first so we have tokens.
|
||||
* For best results do a getPeersAndAnnounce() instead, as this announces to
|
||||
* the closest in the kbuckets, it does NOT sort through the known nodes hashmap.
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param max maximum number of peers to announce to
|
||||
@@ -640,9 +693,9 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private ReplyWaiter sendPing(NodeInfo nInfo) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending ping to: " + nInfo);
|
||||
Map<String, Object> map = new HashMap();
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("q", "ping");
|
||||
Map<String, Object> args = new HashMap();
|
||||
Map<String, Object> args = new HashMap<String, Object>();
|
||||
map.put("a", args);
|
||||
return sendQuery(nInfo, map, true);
|
||||
}
|
||||
@@ -657,9 +710,9 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private ReplyWaiter sendFindNode(NodeInfo nInfo, NID tID) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending find node of " + tID + " to: " + nInfo);
|
||||
Map<String, Object> map = new HashMap();
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("q", "find_node");
|
||||
Map<String, Object> args = new HashMap();
|
||||
Map<String, Object> args = new HashMap<String, Object>();
|
||||
args.put("target", tID.getData());
|
||||
map.put("a", args);
|
||||
return sendQuery(nInfo, map, true);
|
||||
@@ -674,9 +727,9 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private ReplyWaiter sendGetPeers(NodeInfo nInfo, InfoHash ih) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending get peers of " + ih + " to: " + nInfo);
|
||||
Map<String, Object> map = new HashMap();
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("q", "get_peers");
|
||||
Map<String, Object> args = new HashMap();
|
||||
Map<String, Object> args = new HashMap<String, Object>();
|
||||
args.put("info_hash", ih.getData());
|
||||
map.put("a", args);
|
||||
ReplyWaiter rv = sendQuery(nInfo, map, true);
|
||||
@@ -695,12 +748,12 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private ReplyWaiter sendAnnouncePeer(NodeInfo nInfo, InfoHash ih, Token token) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending announce of " + ih + " to: " + nInfo);
|
||||
Map<String, Object> map = new HashMap();
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("q", "announce_peer");
|
||||
Map<String, Object> args = new HashMap();
|
||||
Map<String, Object> args = new HashMap<String, Object>();
|
||||
args.put("info_hash", ih.getData());
|
||||
// port ignored
|
||||
args.put("port", Integer.valueOf(6881));
|
||||
args.put("port", Integer.valueOf(TrackerClient.PORT));
|
||||
args.put("token", token.getData());
|
||||
map.put("a", args);
|
||||
// an announce need not be signed, we have a token
|
||||
@@ -718,8 +771,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private boolean sendPong(NodeInfo nInfo, MsgID msgID) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending pong to: " + nInfo);
|
||||
Map<String, Object> map = new HashMap();
|
||||
Map<String, Object> resps = new HashMap();
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
Map<String, Object> resps = new HashMap<String, Object>();
|
||||
map.put("r", resps);
|
||||
return sendResponse(nInfo, msgID, map);
|
||||
}
|
||||
@@ -737,8 +790,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private boolean sendNodes(NodeInfo nInfo, MsgID msgID, Token token, byte[] ids) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending nodes to: " + nInfo);
|
||||
Map<String, Object> map = new HashMap();
|
||||
Map<String, Object> resps = new HashMap();
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
Map<String, Object> resps = new HashMap<String, Object>();
|
||||
map.put("r", resps);
|
||||
if (token != null)
|
||||
resps.put("token", token.getData());
|
||||
@@ -750,8 +803,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private boolean sendPeers(NodeInfo nInfo, MsgID msgID, Token token, List<byte[]> peers) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending peers to: " + nInfo);
|
||||
Map<String, Object> map = new HashMap();
|
||||
Map<String, Object> resps = new HashMap();
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
Map<String, Object> resps = new HashMap<String, Object>();
|
||||
map.put("r", resps);
|
||||
resps.put("token", token.getData());
|
||||
resps.put("values", peers);
|
||||
@@ -767,8 +820,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private boolean sendError(NodeInfo nInfo, MsgID msgID, int err, String msg) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending error " + msg + " to: " + nInfo);
|
||||
Map<String, Object> map = new HashMap();
|
||||
Map<String, Object> resps = new HashMap();
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
Map<String, Object> resps = new HashMap<String, Object>();
|
||||
map.put("r", resps);
|
||||
return sendResponse(nInfo, msgID, map);
|
||||
}
|
||||
@@ -800,8 +853,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
// Lookup the dest for the hash
|
||||
// TODO spin off into thread or queue? We really don't want to block here
|
||||
if (!lookupDest(nInfo)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Dropping repliable query, no dest for " + nInfo);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Dropping repliable query, no dest for " + nInfo);
|
||||
timeout(nInfo);
|
||||
return null;
|
||||
}
|
||||
@@ -891,7 +944,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
_log.info("looking up dest for " + nInfo);
|
||||
try {
|
||||
// use a short timeout for now
|
||||
Destination dest = _session.lookupDest(nInfo.getHash(), 5*1000);
|
||||
Destination dest = _session.lookupDest(nInfo.getHash(), DEST_LOOKUP_TIMEOUT);
|
||||
if (dest != null) {
|
||||
nInfo.setDestination(dest);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
@@ -1190,6 +1243,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
_log.info("Stored new OB token: " + token + " for: " + nInfo);
|
||||
|
||||
List<Hash> peers = _tracker.getPeers(ih, MAX_WANT);
|
||||
peers.remove(nInfo.getHash()); // him
|
||||
if (peers.isEmpty()) {
|
||||
// similar to find node, but with token
|
||||
// get closest from DHT
|
||||
@@ -1202,11 +1256,9 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
sendNodes(nInfo, msgID, token, nodeArray);
|
||||
} else {
|
||||
List<byte[]> hashes = new ArrayList(peers.size());
|
||||
Hash him = nInfo.getHash();
|
||||
List<byte[]> hashes = new ArrayList<byte[]>(peers.size());
|
||||
for (Hash peer : peers) {
|
||||
if (!peer.equals(him))
|
||||
hashes.add(peer.getData());
|
||||
hashes.add(peer.getData());
|
||||
}
|
||||
sendPeers(nInfo, msgID, token, hashes);
|
||||
}
|
||||
@@ -1290,7 +1342,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
*/
|
||||
private List<NodeInfo> receiveNodes(NodeInfo nInfo, byte[] ids) throws InvalidBEncodingException {
|
||||
int max = Math.min(K, ids.length / NodeInfo.LENGTH);
|
||||
List<NodeInfo> rv = new ArrayList(max);
|
||||
List<NodeInfo> rv = new ArrayList<NodeInfo>(max);
|
||||
for (int off = 0; off < ids.length && rv.size() < max; off += NodeInfo.LENGTH) {
|
||||
NodeInfo nInf = new NodeInfo(ids, off);
|
||||
if (_blacklist.contains(nInf.getNID())) {
|
||||
@@ -1314,7 +1366,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Rcvd peers from: " + nInfo);
|
||||
int max = Math.min(MAX_WANT, peers.size());
|
||||
List<Hash> rv = new ArrayList(max);
|
||||
List<Hash> rv = new ArrayList<Hash>(max);
|
||||
for (BEValue bev : peers) {
|
||||
byte[] b = bev.getBytes();
|
||||
//Hash h = new Hash(b);
|
||||
@@ -1324,7 +1376,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
break;
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Rcvd peers from: " + nInfo + ": " + DataHelper.toString(rv));
|
||||
_log.info("Rcvd " + peers.size() + " peers from: " + nInfo + ": " + DataHelper.toString(rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,10 @@ abstract class PersistDHT {
|
||||
public static synchronized void loadDHT(KRPC krpc, File file) {
|
||||
Log log = I2PAppContext.getGlobalContext().logManager().getLog(PersistDHT.class);
|
||||
int count = 0;
|
||||
FileInputStream in = null;
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
in = new FileInputStream(file);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(in, "ISO-8859-1"));
|
||||
br = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(file), "ISO-8859-1"));
|
||||
String line = null;
|
||||
while ( (line = br.readLine()) != null) {
|
||||
if (line.startsWith("#"))
|
||||
@@ -61,7 +61,7 @@ abstract class PersistDHT {
|
||||
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 (br != null) try { br.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("Loaded " + count + " nodes from " + file);
|
||||
|
||||
@@ -7,7 +7,6 @@ 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
|
||||
|
||||
@@ -27,7 +27,6 @@ import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.UnavailableException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
@@ -36,6 +35,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SystemVersion;
|
||||
@@ -66,9 +66,8 @@ import net.i2p.util.SystemVersion;
|
||||
* The default servlet.
|
||||
* This servlet, normally mapped to /, provides the handling for static
|
||||
* content, OPTION and TRACE methods for the context.
|
||||
* The following initParameters are supported, these can be set either
|
||||
* on the servlet itself or as ServletContext initParameters with a prefix
|
||||
* of org.mortbay.jetty.servlet.Default. :
|
||||
* The following initParameters are supported, these can be set
|
||||
* on the servlet itself:
|
||||
* <PRE>
|
||||
*
|
||||
* resourceBase Set to replace the context resource base
|
||||
@@ -325,7 +324,7 @@ class BasicServlet extends HttpServlet
|
||||
long content_length = content.getContentLength();
|
||||
|
||||
// see if there are any range headers
|
||||
Enumeration reqRanges = request.getHeaders("Range");
|
||||
Enumeration<?> reqRanges = request.getHeaders("Range");
|
||||
|
||||
if (reqRanges == null || !reqRanges.hasMoreElements()) {
|
||||
// if there were no ranges, send entire entity
|
||||
@@ -469,7 +468,7 @@ class BasicServlet extends HttpServlet
|
||||
{
|
||||
String cpath = getServletContext().getContextPath();
|
||||
// this won't work if we aren't at top level
|
||||
String cname = cpath == "" ? "i2psnark" : cpath.substring(1).replace("/", "_");
|
||||
String cname = "".equals(cpath) ? "i2psnark" : cpath.substring(1).replace("/", "_");
|
||||
return (new File(_context.getBaseDir(), "webapps/" + cname + ".war")).lastModified();
|
||||
}
|
||||
|
||||
@@ -578,7 +577,7 @@ class BasicServlet extends HttpServlet
|
||||
byte[] buf = ba.getData();
|
||||
try {
|
||||
if (skip > 0)
|
||||
in.skip(skip);
|
||||
DataHelper.skip(in, skip);
|
||||
int read = 0;
|
||||
long tot = 0;
|
||||
boolean done = false;
|
||||
|
||||
@@ -13,11 +13,13 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PSocketEepGet;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.crypto.SHA1;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.EepGet;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFile;
|
||||
|
||||
import org.klomp.snark.I2PSnarkUtil;
|
||||
import org.klomp.snark.MetaInfo;
|
||||
import org.klomp.snark.Snark;
|
||||
import org.klomp.snark.SnarkManager;
|
||||
@@ -95,7 +97,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
|
||||
add(file);
|
||||
} else {
|
||||
_mgr.addMessage(_("Torrent was not retrieved from {0}", urlify(_url)) +
|
||||
((_failCause != null) ? (": " + _failCause) : ""));
|
||||
((_failCause != null) ? (": " + DataHelper.stripHTML(_failCause)) : ""));
|
||||
}
|
||||
if (file != null)
|
||||
file.delete();
|
||||
@@ -130,6 +132,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
|
||||
return null;
|
||||
_eepGet = new I2PSocketEepGet(_ctx, manager, RETRIES, out.getAbsolutePath(), _url);
|
||||
_eepGet.addStatusListener(this);
|
||||
_eepGet.addHeader("User-Agent", I2PSnarkUtil.EEPGET_USER_AGENT);
|
||||
if (_eepGet.fetch()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Fetch successful [" + _url + "]: size=" + out.length());
|
||||
@@ -160,8 +163,8 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
|
||||
return;
|
||||
}
|
||||
|
||||
name = Storage.filterName(name);
|
||||
name = name + ".torrent";
|
||||
String originalName = Storage.filterName(name);
|
||||
name = originalName + ".torrent";
|
||||
File torrentFile = new File(_mgr.getDataDir(), name);
|
||||
|
||||
String canonical = torrentFile.getCanonicalPath();
|
||||
@@ -174,6 +177,8 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
|
||||
} else {
|
||||
// This may take a LONG time to create the storage.
|
||||
_mgr.copyAndAddTorrent(file, canonical);
|
||||
snark = _mgr.getTorrentByBaseName(originalName);
|
||||
snark.startTorrent();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_mgr.addMessage(_("Torrent at {0} was not valid", urlify(_url)) + ": " + ioe.getMessage());
|
||||
@@ -196,7 +201,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
|
||||
//_total = -1;
|
||||
_transferred = 0;
|
||||
_failCause = null;
|
||||
_started = _ctx.clock().now();
|
||||
_started = _util.getContext().clock().now();
|
||||
_isRunning = true;
|
||||
_active = false;
|
||||
_thread = new I2PAppThread(this, "Torrent File EepGet", true);
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
package org.klomp.snark.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.text.Collator;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -16,7 +12,6 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -26,14 +21,11 @@ import java.util.TreeSet;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import org.klomp.snark.I2PSnarkUtil;
|
||||
@@ -208,7 +200,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
resp.sendError(404);
|
||||
} else {
|
||||
String base = addPaths(req.getRequestURI(), "/");
|
||||
String listing = getListHTML(resource, base, true, method.equals("POST") ? req.getParameterMap() : null);
|
||||
@SuppressWarnings("unchecked") // TODO-Java6: Remove cast, return type is correct
|
||||
String listing = getListHTML(resource, base, true, method.equals("POST") ? (Map<String, String[]>) req.getParameterMap() : null);
|
||||
if (method.equals("POST")) {
|
||||
// P-R-G
|
||||
sendRedirect(req, resp, "");
|
||||
@@ -509,7 +502,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
// show startall too
|
||||
out.write("<br>");
|
||||
if (isDegraded)
|
||||
out.write("<a href=\"/" + _contextPath + "/?action=StartAll&nonce=" + _nonce + "\"><img title=\"");
|
||||
out.write("<a href=\"" + _contextPath + "/?action=StartAll&nonce=" + _nonce + "\"><img title=\"");
|
||||
else
|
||||
out.write("<input type=\"image\" name=\"action_StartAll\" value=\"foo\" title=\"");
|
||||
out.write(_("Start all stopped torrents"));
|
||||
@@ -523,7 +516,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
} else if ((!_manager.util().isConnecting()) && !snarks.isEmpty()) {
|
||||
if (isDegraded)
|
||||
out.write("<a href=\"/" + _contextPath + "/?action=StartAll&nonce=" + _nonce + "\"><img title=\"");
|
||||
out.write("<a href=\"" + _contextPath + "/?action=StartAll&nonce=" + _nonce + "\"><img title=\"");
|
||||
else
|
||||
out.write("<input type=\"image\" name=\"action_StartAll\" value=\"foo\" title=\"");
|
||||
out.write(_("Start all torrents and the I2P tunnel"));
|
||||
@@ -684,7 +677,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
String action = req.getParameter("action");
|
||||
if (action == null) {
|
||||
// http://www.onenaught.com/posts/382/firefox-4-change-input-type-image-only-submits-x-and-y-not-name
|
||||
Map params = req.getParameterMap();
|
||||
@SuppressWarnings("unchecked") // TODO-Java6: Remove cast, return type is correct
|
||||
Map<String, String[]> params = req.getParameterMap();
|
||||
for (Object o : params.keySet()) {
|
||||
String key = (String) o;
|
||||
if (key.startsWith("action_") && key.endsWith(".x")) {
|
||||
@@ -758,8 +752,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (torrent != null) {
|
||||
byte infoHash[] = Base64.decode(torrent);
|
||||
if ( (infoHash != null) && (infoHash.length == 20) ) { // valid sha1
|
||||
for (Iterator iter = _manager.listTorrentFiles().iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
for (String name : _manager.listTorrentFiles() ) {
|
||||
Snark snark = _manager.getTorrent(name);
|
||||
if ( (snark != null) && (DataHelper.eq(infoHash, snark.getInfoHash())) ) {
|
||||
_manager.stopTorrent(snark, false);
|
||||
@@ -781,8 +774,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (torrent != null) {
|
||||
byte infoHash[] = Base64.decode(torrent);
|
||||
if ( (infoHash != null) && (infoHash.length == 20) ) { // valid sha1
|
||||
for (Iterator iter = _manager.listTorrentFiles().iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
for (String name : _manager.listTorrentFiles() ) {
|
||||
Snark snark = _manager.getTorrent(name);
|
||||
if ( (snark != null) && (DataHelper.eq(infoHash, snark.getInfoHash())) ) {
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
@@ -809,8 +801,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (torrent != null) {
|
||||
byte infoHash[] = Base64.decode(torrent);
|
||||
if ( (infoHash != null) && (infoHash.length == 20) ) { // valid sha1
|
||||
for (Iterator iter = _manager.listTorrentFiles().iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
for (String name : _manager.listTorrentFiles() ) {
|
||||
Snark snark = _manager.getTorrent(name);
|
||||
if ( (snark != null) && (DataHelper.eq(infoHash, snark.getInfoHash())) ) {
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
@@ -849,7 +840,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
}
|
||||
// step 2 make Set of dirs with reverse sort
|
||||
Set<File> dirs = new TreeSet(Collections.reverseOrder());
|
||||
Set<File> dirs = new TreeSet<File>(Collections.reverseOrder());
|
||||
for (List<String> list : files) {
|
||||
for (int i = 1; i < list.size(); i++) {
|
||||
dirs.add(Storage.getFileFromNames(f, list.subList(0, i)));
|
||||
@@ -922,8 +913,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (announceURL.equals("none"))
|
||||
announceURL = null;
|
||||
_lastAnnounceURL = announceURL;
|
||||
List<String> backupURLs = new ArrayList();
|
||||
Enumeration e = req.getParameterNames();
|
||||
List<String> backupURLs = new ArrayList<String>();
|
||||
Enumeration<?> e = req.getParameterNames();
|
||||
while (e.hasMoreElements()) {
|
||||
Object o = e.nextElement();
|
||||
if (!(o instanceof String))
|
||||
@@ -946,7 +937,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
boolean hasPrivate = false;
|
||||
boolean hasPublic = false;
|
||||
for (String url : backupURLs) {
|
||||
if (_manager.getPrivateTrackers().contains(announceURL))
|
||||
if (_manager.getPrivateTrackers().contains(url))
|
||||
hasPrivate = true;
|
||||
else
|
||||
hasPublic = true;
|
||||
@@ -955,7 +946,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
_manager.addMessage(_("Error - Cannot mix private and public trackers in a torrent"));
|
||||
return;
|
||||
}
|
||||
announceList = new ArrayList(backupURLs.size());
|
||||
announceList = new ArrayList<List<String>>(backupURLs.size());
|
||||
for (String url : backupURLs) {
|
||||
announceList.add(Collections.singletonList(url));
|
||||
}
|
||||
@@ -976,7 +967,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (announceURL != null && !_manager.util().getOpenTrackers().contains(announceURL))
|
||||
_manager.addMessage(_("Many I2P trackers require you to register new torrents before seeding - please do so before starting \"{0}\"", baseFile.getName()));
|
||||
} catch (IOException ioe) {
|
||||
_manager.addMessage(_("Error creating a torrent for \"{0}\"", baseFile.getAbsolutePath()) + ": " + ioe.getMessage());
|
||||
_manager.addMessage(_("Error creating a torrent for \"{0}\"", baseFile.getAbsolutePath()) + ": " + ioe);
|
||||
_log.error("Error creating a torrent", ioe);
|
||||
}
|
||||
} else {
|
||||
_manager.addMessage(_("Cannot create a torrent for the nonexistent data: {0}", baseFile.getAbsolutePath()));
|
||||
@@ -1016,10 +1008,10 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (action.equals(_("Delete selected")) || action.equals(_("Save tracker configuration"))) {
|
||||
boolean changed = false;
|
||||
Map<String, Tracker> trackers = _manager.getTrackerMap();
|
||||
List<String> removed = new ArrayList();
|
||||
List<String> open = new ArrayList();
|
||||
List<String> priv = new ArrayList();
|
||||
Enumeration e = req.getParameterNames();
|
||||
List<String> removed = new ArrayList<String>();
|
||||
List<String> open = new ArrayList<String>();
|
||||
List<String> priv = new ArrayList<String>();
|
||||
Enumeration<?> e = req.getParameterNames();
|
||||
while (e.hasMoreElements()) {
|
||||
Object o = e.nextElement();
|
||||
if (!(o instanceof String))
|
||||
@@ -1044,7 +1036,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
|
||||
open.removeAll(removed);
|
||||
List<String> oldOpen = new ArrayList(_manager.util().getOpenTrackers());
|
||||
List<String> oldOpen = new ArrayList<String>(_manager.util().getOpenTrackers());
|
||||
Collections.sort(oldOpen);
|
||||
Collections.sort(open);
|
||||
if (!open.equals(oldOpen))
|
||||
@@ -1053,7 +1045,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
priv.removeAll(removed);
|
||||
// open trumps private
|
||||
priv.removeAll(open);
|
||||
List<String> oldPriv = new ArrayList(_manager.getPrivateTrackers());
|
||||
List<String> oldPriv = new ArrayList<String>(_manager.getPrivateTrackers());
|
||||
Collections.sort(oldPriv);
|
||||
Collections.sort(priv);
|
||||
if (!priv.equals(oldPriv))
|
||||
@@ -1073,11 +1065,11 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
_manager.saveTrackerMap();
|
||||
// open trumps private
|
||||
if (req.getParameter("_add_open_") != null) {
|
||||
List newOpen = new ArrayList(_manager.util().getOpenTrackers());
|
||||
List<String> newOpen = new ArrayList<String>(_manager.util().getOpenTrackers());
|
||||
newOpen.add(aurl);
|
||||
_manager.saveOpenTrackers(newOpen);
|
||||
} else if (req.getParameter("_add_private_") != null) {
|
||||
List newPriv = new ArrayList(_manager.getPrivateTrackers());
|
||||
List<String> newPriv = new ArrayList<String>(_manager.getPrivateTrackers());
|
||||
newPriv.add(aurl);
|
||||
_manager.savePrivateTrackers(newPriv);
|
||||
}
|
||||
@@ -1116,55 +1108,32 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
/**
|
||||
* Sort alphabetically in current locale, ignore case, ignore leading "the "
|
||||
* (I guess this is worth it, a lot of torrents start with "The "
|
||||
* These are full path names which makes it harder
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private class TorrentNameComparator implements Comparator<String> {
|
||||
private static class TorrentNameComparator implements Comparator<Snark> {
|
||||
private final Comparator collator = Collator.getInstance();
|
||||
private final String skip;
|
||||
|
||||
public TorrentNameComparator() {
|
||||
String s;
|
||||
try {
|
||||
s = _manager.getDataDir().getCanonicalPath();
|
||||
} catch (IOException ioe) {
|
||||
s = _manager.getDataDir().getAbsolutePath();
|
||||
}
|
||||
skip = s + File.separator;
|
||||
}
|
||||
|
||||
public int compare(String l, String r) {
|
||||
if (l.startsWith(skip))
|
||||
l = l.substring(skip.length());
|
||||
if (r.startsWith(skip))
|
||||
r = r.substring(skip.length());
|
||||
String llc = l.toLowerCase(Locale.US);
|
||||
public int compare(Snark l, Snark r) {
|
||||
// put downloads and magnets first
|
||||
if (l.getStorage() == null && r.getStorage() != null)
|
||||
return -1;
|
||||
if (l.getStorage() != null && r.getStorage() == null)
|
||||
return 1;
|
||||
String ls = l.getBaseName();
|
||||
String llc = ls.toLowerCase(Locale.US);
|
||||
if (llc.startsWith("the ") || llc.startsWith("the.") || llc.startsWith("the_"))
|
||||
l = l.substring(4);
|
||||
String rlc = r.toLowerCase(Locale.US);
|
||||
ls = ls.substring(4);
|
||||
String rs = r.getBaseName();
|
||||
String rlc = rs.toLowerCase(Locale.US);
|
||||
if (rlc.startsWith("the ") || rlc.startsWith("the.") || rlc.startsWith("the_"))
|
||||
r = r.substring(4);
|
||||
return collator.compare(l, r);
|
||||
rs = rs.substring(4);
|
||||
return collator.compare(ls, rs);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Snark> getSortedSnarks(HttpServletRequest req) {
|
||||
Set<String> files = _manager.listTorrentFiles();
|
||||
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) {
|
||||
// put downloads and magnets first
|
||||
if (snark.getStorage() == null)
|
||||
rv.add(magnet++, snark);
|
||||
else
|
||||
rv.add(snark);
|
||||
}
|
||||
}
|
||||
ArrayList<Snark> rv = new ArrayList<Snark>(_manager.getTorrents());
|
||||
Collections.sort(rv, new TorrentNameComparator());
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -1200,23 +1169,13 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (statsOnly)
|
||||
return;
|
||||
|
||||
String filename = snark.getName();
|
||||
if (snark.getMetaInfo() != null) {
|
||||
// Only do this if not a magnet or torrent download
|
||||
// Strip full path down to the local name
|
||||
File f = new File(filename);
|
||||
filename = f.getName();
|
||||
}
|
||||
int i = filename.lastIndexOf(".torrent");
|
||||
if (i > 0)
|
||||
filename = filename.substring(0, i);
|
||||
String fullFilename = filename;
|
||||
if (filename.length() > MAX_DISPLAYED_FILENAME_LENGTH) {
|
||||
String start = filename.substring(0, MAX_DISPLAYED_FILENAME_LENGTH);
|
||||
String basename = snark.getBaseName();
|
||||
String fullBasename = basename;
|
||||
if (basename.length() > MAX_DISPLAYED_FILENAME_LENGTH) {
|
||||
String start = basename.substring(0, MAX_DISPLAYED_FILENAME_LENGTH);
|
||||
if (start.indexOf(" ") < 0 && start.indexOf("-") < 0) {
|
||||
// browser has nowhere to break it
|
||||
fullFilename = filename;
|
||||
filename = start + "…";
|
||||
basename = start + "…";
|
||||
}
|
||||
}
|
||||
// includes skipped files, -1 for magnet mode
|
||||
@@ -1245,10 +1204,10 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
String statusString;
|
||||
if (snark.isChecking()) {
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Checking") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Checking");
|
||||
"<td class=\"snarkTorrentStatus\">" + _("Checking");
|
||||
} else if (snark.isAllocating()) {
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Allocating") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Allocating");
|
||||
"<td class=\"snarkTorrentStatus\">" + _("Allocating");
|
||||
} else if (err != null && curPeers == 0) {
|
||||
// Also don't show if seeding... but then we won't see the not-registered error
|
||||
// && remaining != 0 && needed != 0) {
|
||||
@@ -1262,18 +1221,18 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
//else if (isRunning)
|
||||
if (isRunning)
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "trackererror.png\" title=\"" + err + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Tracker Error") +
|
||||
"<td class=\"snarkTorrentStatus\">" + _("Tracker Error") +
|
||||
": " + curPeers + thinsp(noThinsp) +
|
||||
ngettext("1 peer", "{0} peers", knownPeers);
|
||||
else {
|
||||
if (err.length() > MAX_DISPLAYED_ERROR_LENGTH)
|
||||
err = err.substring(0, MAX_DISPLAYED_ERROR_LENGTH) + "…";
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "trackererror.png\" title=\"" + err + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Tracker Error");
|
||||
"<td class=\"snarkTorrentStatus\">" + _("Tracker Error");
|
||||
}
|
||||
} else if (snark.isStarting()) {
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Starting") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Starting");
|
||||
"<td class=\"snarkTorrentStatus\">" + _("Starting");
|
||||
} else if (remaining == 0 || needed == 0) { // < 0 means no meta size yet
|
||||
// partial complete or seeding
|
||||
if (isRunning) {
|
||||
@@ -1289,60 +1248,60 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
if (curPeers > 0 && !showPeers)
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + img + ".png\" title=\"" + txt + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + txt +
|
||||
"<td class=\"snarkTorrentStatus\">" + txt +
|
||||
": <a href=\"" + uri + "?p=" + Base64.encode(snark.getInfoHash()) + stParam + "\">" +
|
||||
curPeers + thinsp(noThinsp) +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
||||
else
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + img + ".png\" title=\"" + txt + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + txt +
|
||||
"<td class=\"snarkTorrentStatus\">" + txt +
|
||||
": " + curPeers + thinsp(noThinsp) +
|
||||
ngettext("1 peer", "{0} peers", knownPeers);
|
||||
} else {
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "complete.png\" title=\"" + _("Complete") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Complete");
|
||||
"<td class=\"snarkTorrentStatus\">" + _("Complete");
|
||||
}
|
||||
} else {
|
||||
if (isRunning && curPeers > 0 && downBps > 0 && !showPeers)
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "downloading.png\" title=\"" + _("OK") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("OK") +
|
||||
"<td class=\"snarkTorrentStatus\">" + _("OK") +
|
||||
": <a href=\"" + uri + "?p=" + Base64.encode(snark.getInfoHash()) + stParam + "\">" +
|
||||
curPeers + thinsp(noThinsp) +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
||||
else if (isRunning && curPeers > 0 && downBps > 0)
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "downloading.png\" title=\"" + _("OK") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("OK") +
|
||||
"<td class=\"snarkTorrentStatus\">" + _("OK") +
|
||||
": " + curPeers + thinsp(noThinsp) +
|
||||
ngettext("1 peer", "{0} peers", knownPeers);
|
||||
else if (isRunning && curPeers > 0 && !showPeers)
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Stalled") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Stalled") +
|
||||
"<td class=\"snarkTorrentStatus\">" + _("Stalled") +
|
||||
": <a href=\"" + uri + "?p=" + Base64.encode(snark.getInfoHash()) + stParam + "\">" +
|
||||
curPeers + thinsp(noThinsp) +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
||||
else if (isRunning && curPeers > 0)
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Stalled") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Stalled") +
|
||||
"<td class=\"snarkTorrentStatus\">" + _("Stalled") +
|
||||
": " + curPeers + thinsp(noThinsp) +
|
||||
ngettext("1 peer", "{0} peers", knownPeers);
|
||||
else if (isRunning && knownPeers > 0)
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "nopeers.png\" title=\"" + _("No Peers") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("No Peers") +
|
||||
"<td class=\"snarkTorrentStatus\">" + _("No Peers") +
|
||||
": 0" + thinsp(noThinsp) + knownPeers ;
|
||||
else if (isRunning)
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "nopeers.png\" title=\"" + _("No Peers") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("No Peers");
|
||||
"<td class=\"snarkTorrentStatus\">" + _("No Peers");
|
||||
else
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stopped.png\" title=\"" + _("Stopped") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Stopped");
|
||||
"<td class=\"snarkTorrentStatus\">" + _("Stopped");
|
||||
}
|
||||
|
||||
out.write("<tr class=\"" + rowClass + "\">");
|
||||
out.write("<td class=\"center " + rowClass + "\">");
|
||||
out.write("<td class=\"center\">");
|
||||
out.write(statusString + "</td>\n\t");
|
||||
|
||||
// (i) icon column
|
||||
out.write("<td class=\"" + rowClass + "\">");
|
||||
out.write("<td>");
|
||||
if (isValid && meta.getAnnounce() != null) {
|
||||
// Link to local details page - note that trailing slash on a single-file torrent
|
||||
// gets us to the details page instead of the file.
|
||||
@@ -1359,9 +1318,9 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
out.write(trackerLink);
|
||||
}
|
||||
|
||||
String encodedBaseName = urlEncode(snark.getBaseName());
|
||||
String encodedBaseName = urlEncode(fullBasename);
|
||||
// File type icon column
|
||||
out.write("</td>\n<td class=\"" + rowClass + "\">");
|
||||
out.write("</td>\n<td>");
|
||||
if (isValid) {
|
||||
// Link to local details page - note that trailing slash on a single-file torrent
|
||||
// gets us to the details page instead of the file.
|
||||
@@ -1388,7 +1347,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
|
||||
// Torrent name column
|
||||
out.write("</td><td class=\"snarkTorrentName " + rowClass + "\">");
|
||||
out.write("</td><td class=\"snarkTorrentName\">");
|
||||
if (remaining == 0 || isMultiFile) {
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
buf.append("<a href=\"").append(encodedBaseName);
|
||||
@@ -1402,15 +1361,15 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
buf.append("\">");
|
||||
out.write(buf.toString());
|
||||
}
|
||||
out.write(filename);
|
||||
out.write(basename);
|
||||
if (remaining == 0 || isMultiFile)
|
||||
out.write("</a>");
|
||||
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentETA " + rowClass + "\">");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentETA\">");
|
||||
if(isRunning && remainingSeconds > 0 && !snark.isChecking())
|
||||
out.write(DataHelper.formatDuration2(Math.max(remainingSeconds, 10) * 1000)); // (eta 6h)
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentDownloaded " + rowClass + "\">");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentDownloaded\">");
|
||||
if (remaining > 0)
|
||||
out.write(formatSize(total-remaining) + thinsp(noThinsp) + formatSize(total));
|
||||
else if (remaining == 0)
|
||||
@@ -1418,7 +1377,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
//else
|
||||
// out.write("??"); // no meta size yet
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentUploaded " + rowClass + "\">");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentUploaded\">");
|
||||
if(isRunning && isValid)
|
||||
out.write(formatSize(uploaded));
|
||||
out.write("</td>\n\t");
|
||||
@@ -1430,7 +1389,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if(isRunning && isValid)
|
||||
out.write(formatSize(upBps) + "ps");
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td align=\"center\" class=\"snarkTorrentAction " + rowClass + "\">");
|
||||
out.write("<td align=\"center\" class=\"snarkTorrentAction\">");
|
||||
String b64 = Base64.encode(snark.getInfoHash());
|
||||
if (snark.isChecking()) {
|
||||
// show no buttons
|
||||
@@ -1451,7 +1410,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
// Start Button
|
||||
// This works in Opera but it's displayed a little differently, so use noThinsp here too so all 3 icons are consistent
|
||||
if (noThinsp)
|
||||
out.write("<a href=\"/" + _contextPath + "/?action=Start_" + b64 + "&nonce=" + _nonce + stParam + "\"><img title=\"");
|
||||
out.write("<a href=\"" + _contextPath + "/?action=Start_" + b64 + "&nonce=" + _nonce + stParam + "\"><img title=\"");
|
||||
else
|
||||
out.write("<input type=\"image\" name=\"action_Start_" + b64 + "\" value=\"foo\" title=\"");
|
||||
out.write(_("Start the torrent"));
|
||||
@@ -1473,7 +1432,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
// Can't figure out how to escape double quotes inside the onclick string.
|
||||
// Single quotes in translate strings with parameters must be doubled.
|
||||
// Then the remaining single quote must be escaped
|
||||
out.write(_("Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?", fullFilename));
|
||||
out.write(_("Are you sure you want to delete the file \\''{0}\\'' (downloaded data will not be deleted) ?", snark.getName()));
|
||||
out.write("')) { return false; }\"");
|
||||
out.write(" src=\"" + _imgPath + "remove.png\" alt=\"");
|
||||
out.write(_("Remove"));
|
||||
@@ -1493,7 +1452,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
// Can't figure out how to escape double quotes inside the onclick string.
|
||||
// Single quotes in translate strings with parameters must be doubled.
|
||||
// Then the remaining single quote must be escaped
|
||||
out.write(_("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", fullFilename));
|
||||
out.write(_("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", fullBasename));
|
||||
out.write("')) { return false; }\"");
|
||||
out.write(" src=\"" + _imgPath + "delete.png\" alt=\"");
|
||||
out.write(_("Delete"));
|
||||
@@ -1511,7 +1470,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (!peer.isConnected())
|
||||
continue;
|
||||
out.write("<tr class=\"" + rowClass + "\"><td></td>");
|
||||
out.write("<td colspan=\"4\" align=\"right\" class=\"" + rowClass + "\">");
|
||||
out.write("<td colspan=\"4\" align=\"right\">");
|
||||
String ch = peer.toString().substring(0, 4);
|
||||
String client;
|
||||
if ("AwMD".equals(ch))
|
||||
@@ -1536,13 +1495,13 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (showDebug)
|
||||
out.write(" inactive " + (peer.getInactiveTime() / 1000) + "s");
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td class=\"snarkTorrentStatus " + rowClass + "\">");
|
||||
out.write("<td class=\"snarkTorrentStatus\">");
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentStatus " + rowClass + "\">");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentStatus\">");
|
||||
float pct;
|
||||
if (isValid) {
|
||||
pct = (float) (100.0 * peer.completed() / meta.getPieces());
|
||||
if (pct == 100.0)
|
||||
if (pct >= 100.0)
|
||||
out.write(_("Seed"));
|
||||
else {
|
||||
String ps = String.valueOf(pct);
|
||||
@@ -1556,9 +1515,9 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
//out.write("??");
|
||||
}
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td class=\"snarkTorrentStatus " + rowClass + "\">");
|
||||
out.write("<td class=\"snarkTorrentStatus\">");
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentStatus " + rowClass + "\">");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentStatus\">");
|
||||
if (needed > 0) {
|
||||
if (peer.isInteresting() && !peer.isChoked()) {
|
||||
out.write("<span class=\"unchoked\">");
|
||||
@@ -1580,7 +1539,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
//}
|
||||
}
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentStatus " + rowClass + "\">");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentStatus\">");
|
||||
if (isValid && pct < 100.0) {
|
||||
if (peer.isInterested() && !peer.isChoking()) {
|
||||
out.write("<span class=\"unchoked\">");
|
||||
@@ -1596,10 +1555,10 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
}
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td class=\"snarkTorrentStatus " + rowClass + "\">");
|
||||
out.write("<td class=\"snarkTorrentStatus\">");
|
||||
out.write("</td></tr>\n\t");
|
||||
if (showDebug)
|
||||
out.write("<tr class=\"" + rowClass + "\"><td></td><td colspan=\"10\" align=\"right\" class=\"" + rowClass + "\">" + peer.getSocket() + "</td></tr>");
|
||||
out.write("<tr class=\"" + rowClass + "\"><td></td><td colspan=\"10\" align=\"right\">" + peer.getSocket() + "</td></tr>");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1948,7 +1907,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
//out.write("port: <input type=\"text\" name=\"eepPort\" value=\""
|
||||
// + _manager.util().getEepProxyPort() + "\" size=\"5\" maxlength=\"5\" /><br>\n");
|
||||
|
||||
Map<String, String> options = new TreeMap(_manager.util().getI2CPOptions());
|
||||
Map<String, String> options = new TreeMap<String, String>(_manager.util().getI2CPOptions());
|
||||
out.write("<tr><td>");
|
||||
out.write(_("Inbound Settings"));
|
||||
out.write(":<td>");
|
||||
@@ -2039,12 +1998,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (privateTrackers.contains(t.announceURL)) {
|
||||
buf.append(" checked=\"checked\"");
|
||||
} else {
|
||||
for (int i = 1; i < SnarkManager.DEFAULT_TRACKERS.length; i += 2) {
|
||||
if (SnarkManager.DEFAULT_TRACKERS[i].contains(t.announceURL)) {
|
||||
buf.append(" disabled=\"disabled\"");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (SnarkManager.DEFAULT_TRACKER_ANNOUNCES.contains(t.announceURL))
|
||||
buf.append(" disabled=\"disabled\"");
|
||||
}
|
||||
buf.append(">" +
|
||||
"</td><td>").append(urlify(announceURL, 35))
|
||||
@@ -2244,7 +2199,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
* @return String of HTML or null if postParams != null
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private String getListHTML(File r, String base, boolean parent, Map postParams)
|
||||
private String getListHTML(File r, String base, boolean parent, Map<String, String[]> postParams)
|
||||
throws IOException
|
||||
{
|
||||
File[] ls = null;
|
||||
@@ -2420,7 +2375,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
.append(":</b> ")
|
||||
.append(formatSize(needed));
|
||||
if (meta != null) {
|
||||
List files = meta.getFiles();
|
||||
List<List<String>> files = meta.getFiles();
|
||||
int fileCount = files != null ? files.size() : 1;
|
||||
buf.append(" <img alt=\"\" border=\"0\" src=\"" + _imgPath + "file.png\" > <b>")
|
||||
.append(_("Files"))
|
||||
@@ -2519,7 +2474,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
Storage storage = snark.getStorage();
|
||||
try {
|
||||
File f = item;
|
||||
if (f != null) {
|
||||
long remaining = storage.remaining(f.getCanonicalPath());
|
||||
if (remaining < 0) {
|
||||
complete = true;
|
||||
@@ -2539,9 +2493,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
(100 * (length - remaining) / length) + "% " + _("complete") +
|
||||
" (" + DataHelper.formatSize2(remaining) + "B " + _("remaining") + ")";
|
||||
}
|
||||
} else {
|
||||
status = "Not a file?";
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
status = "Not a file? " + ioe;
|
||||
}
|
||||
@@ -2553,8 +2504,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
path=addPaths(path,"/");
|
||||
String icon = toIcon(item);
|
||||
|
||||
buf.append("<TD class=\"snarkFileIcon ")
|
||||
.append(rowClass).append("\">");
|
||||
buf.append("<TD class=\"snarkFileIcon\">");
|
||||
if (complete) {
|
||||
buf.append("<a href=\"").append(path).append("\">");
|
||||
// thumbnail ?
|
||||
@@ -2569,24 +2519,23 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
} else {
|
||||
buf.append(toImg(icon));
|
||||
}
|
||||
buf.append("</TD><TD class=\"snarkFileName ")
|
||||
.append(rowClass).append("\">");
|
||||
buf.append("</TD><TD class=\"snarkFileName\">");
|
||||
if (complete)
|
||||
buf.append("<a href=\"").append(path).append("\">");
|
||||
buf.append(item.getName());
|
||||
if (complete)
|
||||
buf.append("</a>");
|
||||
buf.append("</TD><TD ALIGN=right class=\"").append(rowClass).append(" snarkFileSize\">");
|
||||
buf.append("</TD><TD ALIGN=right class=\"snarkFileSize\">");
|
||||
if (!item.isDirectory())
|
||||
buf.append(DataHelper.formatSize2(length)).append('B');
|
||||
buf.append("</TD><TD class=\"").append(rowClass).append(" snarkFileStatus\">");
|
||||
buf.append("</TD><TD class=\"snarkFileStatus\">");
|
||||
//buf.append(dfmt.format(new Date(item.lastModified())));
|
||||
buf.append(status);
|
||||
buf.append("</TD>");
|
||||
if (showPriority) {
|
||||
buf.append("<td class=\"priority\">");
|
||||
File f = item;
|
||||
if ((!complete) && (!item.isDirectory()) && f != null) {
|
||||
if ((!complete) && (!item.isDirectory())) {
|
||||
int pri = snark.getStorage().getPriority(f.getCanonicalPath());
|
||||
buf.append("<input type=\"radio\" value=\"5\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
|
||||
if (pri > 0)
|
||||
@@ -2643,8 +2592,10 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (mime.equals("text/html"))
|
||||
icon = "html";
|
||||
else if (mime.equals("text/plain") ||
|
||||
mime.equals("text/x-sfv") ||
|
||||
mime.equals("application/rtf") ||
|
||||
mime.equals("application/epub+zip"))
|
||||
mime.equals("application/epub+zip") ||
|
||||
mime.equals("application/x-mobipocket-ebook"))
|
||||
icon = "page";
|
||||
else if (mime.equals("application/java-archive") ||
|
||||
plc.endsWith(".deb"))
|
||||
@@ -2684,17 +2635,16 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
|
||||
/** @since 0.8.1 */
|
||||
private void savePriorities(Snark snark, Map postParams) {
|
||||
private void savePriorities(Snark snark, Map<String, String[]> postParams) {
|
||||
Storage storage = snark.getStorage();
|
||||
if (storage == null)
|
||||
return;
|
||||
Set<Map.Entry> entries = postParams.entrySet();
|
||||
for (Map.Entry entry : entries) {
|
||||
String key = (String)entry.getKey();
|
||||
for (Map.Entry<String, String[]> entry : postParams.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (key.startsWith("pri.")) {
|
||||
try {
|
||||
String file = key.substring(4);
|
||||
String val = ((String[])entry.getValue())[0]; // jetty arrays
|
||||
String val = entry.getValue()[0]; // jetty arrays
|
||||
int pri = Integer.parseInt(val);
|
||||
storage.setPriority(file, pri);
|
||||
//System.err.println("Priority now " + pri + " for " + file);
|
||||
|
||||
@@ -73,7 +73,7 @@ public class InclusiveByteRange
|
||||
* @param size Size of the resource.
|
||||
* @return List of satisfiable ranges
|
||||
*/
|
||||
public static List<InclusiveByteRange> satisfiableRanges(Enumeration headers, long size)
|
||||
public static List<InclusiveByteRange> satisfiableRanges(Enumeration<?> headers, long size)
|
||||
{
|
||||
List<InclusiveByteRange> satRanges = null;
|
||||
|
||||
@@ -128,7 +128,7 @@ public class InclusiveByteRange
|
||||
if (first < size)
|
||||
{
|
||||
if (satRanges == null)
|
||||
satRanges = new ArrayList(4);
|
||||
satRanges = new ArrayList<InclusiveByteRange>(4);
|
||||
InclusiveByteRange range = new InclusiveByteRange(first,last);
|
||||
satRanges.add(range);
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@ import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
@@ -48,7 +46,7 @@ class MimeTypes
|
||||
private final Map<String, String> _mimeMap;
|
||||
|
||||
public MimeTypes() {
|
||||
_mimeMap = new ConcurrentHashMap();
|
||||
_mimeMap = new ConcurrentHashMap<String, String>();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
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
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
1249
apps/i2psnark/locale/messages_ro.po
Normal file
1249
apps/i2psnark/locale/messages_ro.po
Normal file
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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user