forked from I2P_Developers/i2p.i2p
Compare commits
1752 Commits
i2p-0.9.13
...
i2p-0.9.25
Author | SHA1 | Date | |
---|---|---|---|
49ff78cf0e | |||
c1b6e1be87 | |||
c84dd527b7 | |||
b0aaf64cec | |||
25514e9848 | |||
d00c08dcd7 | |||
d64e6bb17d | |||
855215e840 | |||
674a77baca | |||
c9f025a44d | |||
a61c44ba42 | |||
c869d3adc4 | |||
387dc98e11 | |||
319d40146d | |||
2e3f5d0de9 | |||
dc36de667d | |||
0904500398 | |||
74e8cf79bd | |||
b7498b564a | |||
6d40e87032 | |||
1f088ff3e5 | |||
50d038af5d | |||
ffa4d6580d | |||
bf2f3762af | |||
35e4b3c859 | |||
4910413482 | |||
7ccca9ef3c | |||
6e7f015d4b | |||
80860232d7 | |||
d28f983c46 | |||
b3f37db33f | |||
5f7d636738 | |||
90a915b8b5 | |||
248deaecbb | |||
a79b25d7b1 | |||
d9a7dc0233 | |||
f34a05c35d | |||
8e4b7e3847 | |||
eb094ba0ef | |||
87d7e10841 | |||
7d35a4e1b9 | |||
fc1268dd5b | |||
abb52331a5 | |||
fa44a952ed | |||
a3cac88c91 | |||
04614ac6f8 | |||
bd49b1d4bd | |||
40894663c2 | |||
7d6fe011db | |||
451cb2573a | |||
238ebc23e2 | |||
910822ecf2 | |||
9fba12519f | |||
3a28680162 | |||
fa12967873 | |||
9f0640ca2d | |||
c385ad8f1d | |||
8faafc00b0 | |||
8498d7d128 | |||
455f32faa7 | |||
27d0a81bcb | |||
52a37d170f | |||
236155791d | |||
8ef593fe98 | |||
e65bd26ad5 | |||
071769679d | |||
6ab5b84979 | |||
981b708230 | |||
651c1b6545 | |||
e402bfaa81 | |||
241bb3812c | |||
55addfc739 | |||
84b94368a9 | |||
8f667a0463 | |||
d962be9d7e | |||
691e274ca7 | |||
b1eaa772a1 | |||
79bb3f6cc4 | |||
b6deae9b23 | |||
edde533e1b | |||
ceb7791541 | |||
68c617950c | |||
62ad7996f1 | |||
270bc24b62 | |||
9b004bc61f | |||
f9cf6bdc85 | |||
ebc4ca8698 | |||
d4d720524e | |||
6be7c46038 | |||
7901784a71 | |||
4e55edc049 | |||
acdaa60de3 | |||
a70a7a7ed5 | |||
2fb1ad035f | |||
28a2b82795 | |||
bf51d5d9c4 | |||
5d1d8b6d4d | |||
129fb973f2 | |||
f094bacd67 | |||
be97e84d83 | |||
da3086bbef | |||
8badb609e4 | |||
a1a895e462 | |||
9ed185f3d1 | |||
7fdfb5cf12 | |||
b4d4c93047 | |||
66299cb081 | |||
79450bcda6 | |||
3a72b0cc63 | |||
f200d5cb03 | |||
3ad1db8d74 | |||
bccefb949f | |||
c6136b5cdb | |||
d2d5a464a3 | |||
6ab814a649 | |||
df0aceb26d | |||
ddf056cf1d | |||
ddb9777638 | |||
374996d8b2 | |||
6192aa6910 | |||
8e47ec325d | |||
ed9d403281 | |||
f38cfcc937 | |||
649d7122a2 | |||
ad2561125e | |||
403044fc6c | |||
e7081491ca | |||
594abdee55 | |||
c9063f9d9b | |||
e276febf0a | |||
592f2449d2 | |||
d08f29d7d6 | |||
4342aa6bce | |||
207bfb44f2 | |||
726adaf2bb | |||
d16db7b56c | |||
c4ea50f06d | |||
5004626d7a | |||
48d32943b2 | |||
49d8235661 | |||
21e2600c40 | |||
bdd6066fc3 | |||
04d7c9dfb4 | |||
cc21de3fee | |||
cf3accb181 | |||
a4f75d7b32 | |||
9cdd0fc829 | |||
f29ed21090 | |||
cd5db63286 | |||
5074002327 | |||
9067dedcc2 | |||
179a4a2e56 | |||
8243b6922d | |||
ec27458393 | |||
2007e881e5 | |||
d82591ae70 | |||
9d5e8dd785 | |||
07e85e095d | |||
9bb1a00325 | |||
f0dc76983a | |||
71c4505617 | |||
acfb0a1e3b | |||
ff66d9db67 | |||
6edd2b97b9 | |||
cdfd4ca2f4 | |||
308c9da384 | |||
ca00ea7a76 | |||
e2b7f504b0 | |||
20547238fc | |||
9caddc166b | |||
c546b283fd | |||
c8197b8181 | |||
35739289cd | |||
68d8c6e556 | |||
f85d03085b | |||
6917203530 | |||
144f54eb8c | |||
46af643ca8 | |||
ee1852f3a4 | |||
a141d50902 | |||
ab5d4b59fd | |||
3dbc8408f1 | |||
08a9a01bfb | |||
2698076fb6 | |||
2f09389ddd | |||
8da3257856 | |||
a4546e1045 | |||
3bce2f5d46 | |||
074c5aa16c | |||
879b70617b | |||
cad0ab17dc | |||
4250f78ddf | |||
cc4bf8ea16 | |||
05b40a220d | |||
64f5c662fa | |||
e9146ebc77 | |||
d5990cc0f2 | |||
b6bd497e52 | |||
2246e21340 | |||
c60f3970d1 | |||
0b94d866f0 | |||
fa6643c5a2 | |||
d0eaf4d899 | |||
c59496f30f | |||
8226e92973 | |||
af26f73f99 | |||
95946606ef | |||
3c5f9d0bc3 | |||
2155347e4f | |||
db86850d15 | |||
97ae1e5034 | |||
fee755bdb7 | |||
4fe24790fd | |||
68ecd82755 | |||
2c1b9c2d37 | |||
cddc1b362e | |||
89bdbedc0f | |||
3a4e82f025 | |||
c8aca62d03 | |||
8b9bcbc777 | |||
00d6a49653 | |||
ea9c4a1957 | |||
7680ecbdc4 | |||
00a5d19534 | |||
2d1ac7b266 | |||
2852383e4e | |||
393b593785 | |||
32df925fa6 | |||
9b2bbe03ee | |||
7e872088d0 | |||
77a6db1cab | |||
bb56a11bda | |||
7ea2be387e | |||
81cb62fda7 | |||
8b42896cc6 | |||
9ba5ad7bb1 | |||
f7ede4bf6f | |||
64f2318720 | |||
34202e6c4e | |||
af8b8ecddd | |||
0558bc41a3 | |||
d45dc8d0f3 | |||
b6e8431bce | |||
826bb54984 | |||
fdc160cf1d | |||
5a7fc3f7f4 | |||
a35ecda992 | |||
89e60fa8c5 | |||
6e2e4ca6d8 | |||
eaae06028e | |||
997ef73d50 | |||
ff4d575196 | |||
68c312139e | |||
cab69f6583 | |||
5bd0041f8b | |||
53ae4125e5 | |||
b53fe37a30 | |||
348805f012 | |||
72527f4d33 | |||
dfbbe3e928 | |||
f778c23f0b | |||
3c8cc16273 | |||
1c1511267d | |||
55f729986b | |||
23df322056 | |||
d5717ca12d | |||
74fac4b1d8 | |||
a5a702744f | |||
1db7613519 | |||
68b4ad2238 | |||
513e1b9ff8 | |||
dffd441304 | |||
87fa1cb1ac | |||
38c8e017a8 | |||
7b83e23269 | |||
415b51bc49 | |||
a03339b120 | |||
b1668bbc11 | |||
9ce8fced02 | |||
01d23713af | |||
2849aec3c2 | |||
cb979fb685 | |||
bafec18093 | |||
5adbf9050a | |||
3a25a91c33 | |||
0519ea476e | |||
48d7f4969c | |||
ed1567e9f7 | |||
9f625a03fb | |||
e77c5bd05c | |||
31ace20256 | |||
4291450f37 | |||
6373c8a9ed | |||
bd048b04cc | |||
b9ab933550 | |||
626f5415c7 | |||
9367aca50a | |||
e5f186f61a | |||
807e5bf966 | |||
8d7edaae61 | |||
868e5e988c | |||
612e01cbbf | |||
13fd613bb8 | |||
6b67a70bbd | |||
6934599eed | |||
730dea377a | |||
5d07294cc6 | |||
6081856dd1 | |||
92bb2dbda7 | |||
5c4189abdf | |||
2400a77e25 | |||
110a0a1b7a | |||
302ec7767a | |||
1215a70aab | |||
ce96234fdb | |||
9a9832cb77 | |||
d30c1ec319 | |||
7649132259 | |||
9efb3c8751 | |||
07c9ddb38f | |||
be498eaab8 | |||
9e8597aa05 | |||
5b4a4f6c84 | |||
a468b3e8b4 | |||
c7d68c2a6c | |||
16549aa49a | |||
b59a8027bb | |||
8d9d3fcf95 | |||
1a7bf2a0c3 | |||
bb8e6127d3 | |||
13987b7d50 | |||
dfb8830802 | |||
9483e095d9 | |||
46f42432a2 | |||
599989deba | |||
1e89fac192 | |||
4c72c08d65 | |||
679fe9b044 | |||
6fb0692d57 | |||
38a1a96db2 | |||
bbaa6f7f87 | |||
046ef07efd | |||
fc7939b404 | |||
dd6a3f14ec | |||
99c9b30e49 | |||
f5ae9c23fe | |||
23cb4ca764 | |||
231040ddd8 | |||
7a75e2e662 | |||
e6644236ed | |||
8a1f02aa89 | |||
ded249dd3d | |||
a028bba997 | |||
c609781927 | |||
51c5da3f72 | |||
37a4fcb469 | |||
e93e76a362 | |||
c1afbd37d7 | |||
3fa2fb4c8d | |||
ffddf415c0 | |||
03a99adaab | |||
48f294024c | |||
123b4ca460 | |||
c944fcce96 | |||
1451dc6ece | |||
1aed266f70 | |||
e120a8a3a3 | |||
a3e16614ae | |||
bdde11c0ef | |||
63ddf11799 | |||
a3b55ccdea | |||
8e77188560 | |||
1e5a35c7f8 | |||
83b923151c | |||
e4ebb9a77d | |||
077c4a073f | |||
f5bf4ec8ea | |||
c901010d96 | |||
9f0f1f5ec8 | |||
7175b1cdb9 | |||
ca4642e0f0 | |||
6bb156a436 | |||
19090343ba | |||
b15138dd67 | |||
5f50f23fe1 | |||
d5e2defb5f | |||
c1d77dfe5c | |||
eca234c187 | |||
1a6074a62b | |||
9baeedbc27 | |||
3f91e448c0 | |||
3e25ff251b | |||
f8830a759e | |||
b15ea8ba2f | |||
ef428d559e | |||
39d749ba16 | |||
a3a092a454 | |||
787921aa89 | |||
bbb6da2ac6 | |||
b7dc55e326 | |||
805979b987 | |||
c37cc7ad52 | |||
02c1417cc5 | |||
627d0d29db | |||
c595895877 | |||
6efce31eed | |||
f713a19785 | |||
abc0f4c720 | |||
71bc55b470 | |||
5f175455c7 | |||
9bddba56a0 | |||
4e6ddfcea3 | |||
3411a7c884 | |||
70921a2b09 | |||
dd36176997 | |||
fe26052189 | |||
ba1488bcce | |||
39b218b216 | |||
b43417bf77 | |||
649a63db6f | |||
6aa8ed1280 | |||
9224afb78d | |||
5e879b85a8 | |||
2c03b434e1 | |||
55a6f44651 | |||
971a2652e3 | |||
68aa8800b6 | |||
dd4d12f287 | |||
7063609f05 | |||
b32c8d5fa4 | |||
843e2a8a0e | |||
419d6a8e18 | |||
03f9df4ff0 | |||
f4a6cf2002 | |||
f93da93cf0 | |||
b068f9a262 | |||
5fa059b4a8 | |||
2f92b27446 | |||
5d345f65a3 | |||
ccc8c04782 | |||
58ccfed41d | |||
59b05d4214 | |||
f46a902256 | |||
39b810bd79 | |||
22417715e7 | |||
d21777fbc1 | |||
b22a6bc163 | |||
1c3527e1a4 | |||
4d7ad6ef7f | |||
3ea8b477d8 | |||
ea4dd12bff | |||
a13552dd8d | |||
89c14c2e9a | |||
22b9876b68 | |||
04690bed9f | |||
0faa5ba2f4 | |||
04d653a8b9 | |||
3f213cf1db | |||
53ae727935 | |||
62acfc0cae | |||
5a2f22b00f | |||
7dd438b5f0 | |||
6685b81834 | |||
c56f686d8c | |||
b81cbedd5c | |||
02a0ef3526 | |||
cfc0664756 | |||
2a3b55f3a4 | |||
287f94ad19 | |||
462c882f4e | |||
b8a909c4cc | |||
83791b2d10 | |||
ff420278c5 | |||
1a385b6dca | |||
64889b2bc2 | |||
bfc6534b20 | |||
84abfa0190 | |||
d5a0d95c61 | |||
1de840ce59 | |||
0f6176b7bf | |||
3d533a406d | |||
37597b8c7d | |||
addc9c5ca3 | |||
a2e38503fe | |||
7912d7650d | |||
6f5739b9d8 | |||
ed3e444d1e | |||
ac1a28e988 | |||
7117438b04 | |||
d5cbccf186 | |||
fd606064d9 | |||
9d05424202 | |||
157d494dee | |||
fa792a9d5e | |||
ab134261f0 | |||
de2431e9ee | |||
c4cbd7d5c4 | |||
e978bb81a0 | |||
2c6edf401f | |||
fe69d3b8f7 | |||
61edd01e3d | |||
483d7c43ee | |||
7c703953be | |||
f577a94012 | |||
b10b8581cc | |||
601376561b | |||
5a11a28a35 | |||
fde0ae8349 | |||
b5944045fb | |||
ecd0231cd0 | |||
44b35f328b | |||
f3bb20d750 | |||
20cb284f9d | |||
b4993d42b3 | |||
9b466f3261 | |||
0bf9cb3bf2 | |||
9efe60d7a8 | |||
45fe238227 | |||
e704baddd8 | |||
db9555dba3 | |||
4b34b49dc1 | |||
1652bb39e3 | |||
5eda1e0031 | |||
b19866cbc4 | |||
48bcc031da | |||
f1998e6377 | |||
6f1bb85397 | |||
d848a19ab0 | |||
8dcbc9958e | |||
63555acd21 | |||
c451014eea | |||
9fad9347c1 | |||
841e27f35c | |||
bfde521cf9 | |||
fea6b8aec3 | |||
8d3fb0c9a1 | |||
d662514f74 | |||
44bd14bd4d | |||
1681598dec | |||
809a533573 | |||
265e4b58a5 | |||
93854e93b5 | |||
f6605d05d9 | |||
c20772702a | |||
ba5af15c6f | |||
9af197e590 | |||
2f59a4b3e6 | |||
8b14afd605 | |||
63e934f8f2 | |||
dd5f804150 | |||
35b0e99ff0 | |||
1ed1e4414b | |||
d087fd674b | |||
1f9bb046f5 | |||
914cc120ad | |||
631a0674ab | |||
17d26976d5 | |||
dc9d60e261 | |||
2c191e7bf8 | |||
817888c23c | |||
1eaf376ee7 | |||
6cb3d1d330 | |||
2681c4b42f | |||
05959d5199 | |||
113a8a52f3 | |||
98a4460bde | |||
3645c906e8 | |||
fcdd8be7a7 | |||
34f6f65104 | |||
4c516cd2af | |||
8ea6805f8d | |||
23f2261bd9 | |||
6e06d326e3 | |||
072e4dc2bf | |||
f56ac66d64 | |||
c662f17823 | |||
246b376ed9 | |||
194f20e18c | |||
9b2d416154 | |||
12385f04ec | |||
49e68bcc86 | |||
b82c1ead72 | |||
33672e6a86 | |||
876729c24e | |||
b6cb074c04 | |||
dd47389ad1 | |||
25268e7cb2 | |||
355b2a1528 | |||
975149d049 | |||
af394e13ad | |||
e3f64f6edf | |||
2fbbfa388e | |||
0b4d4ddcbc | |||
428d89a307 | |||
feff6c003b | |||
699d550992 | |||
c6896c4418 | |||
1b2d4c75eb | |||
586defc802 | |||
2499aad51d | |||
addb142ecd | |||
20c796e87a | |||
cd62d7170c | |||
acc647822f | |||
1cf544f1d4 | |||
0f4e09500c | |||
7c5dfaee20 | |||
8d9cced128 | |||
8096e4f65d | |||
5878fae88f | |||
036b77746b | |||
233cce8311 | |||
bc85543ef2 | |||
627f7076b0 | |||
863e120204 | |||
53cfba4cbd | |||
3a774b7c37 | |||
0ad34a4b00 | |||
2b9ffc1270 | |||
93c7860d2b | |||
25f6c3d9e1 | |||
b9e07bc9aa | |||
09f68e44ca | |||
013b5fd85b | |||
8962bfb6bc | |||
605602e001 | |||
f341e5566b | |||
7b84676f4a | |||
c666f8a4f9 | |||
e067761947 | |||
226bee64ef | |||
1a40e57413 | |||
f73101b014 | |||
fef65c996f | |||
cbc2f899a6 | |||
099515adff | |||
ff2ea9ac3e | |||
97aeecd865 | |||
8098d705f9 | |||
fa8c390267 | |||
e8f4e19bac | |||
9041a2c69f | |||
384e9118c6 | |||
0936a2ee23 | |||
bc6b0c12ac | |||
f6f051cfa4 | |||
fb131a040c | |||
9f2ded6073 | |||
55e36ee458 | |||
7c13fb2ba0 | |||
663ccb72d7 | |||
78e0a37fc9 | |||
09cdc00939 | |||
2590e7d4ff | |||
27f56776ca | |||
657f13af29 | |||
e2ca74963f | |||
9304cb2bbc | |||
362086994a | |||
f57e37d588 | |||
d96ddd1a0e | |||
7b711ebba0 | |||
0762715264 | |||
8a69dc0a97 | |||
39dc60cf8a | |||
09e867b194 | |||
dc9256f274 | |||
272f63dbbd | |||
06104118d0 | |||
525ec01c1e | |||
f8594c316f | |||
3c89bd4e19 | |||
1f8408f417 | |||
915b35f0c1 | |||
4521156ecb | |||
c58fd8f84e | |||
f02b401b7a | |||
4fdcb6ce29 | |||
94824e4d2b | |||
280fc05c91 | |||
89745f5002 | |||
7715e6484c | |||
c807194e93 | |||
3602f73497 | |||
4bf115b5f6 | |||
7ab85a0a20 | |||
fba0372339 | |||
03dfa6515b | |||
5e33ed1169 | |||
11ab7fc56c | |||
716bff41d7 | |||
1d8842cfc8 | |||
042b03d6b8 | |||
ab753651b9 | |||
4ea99b8a10 | |||
3d07e1a10b | |||
86525e7239 | |||
195171f9ed | |||
33c4be5b2f | |||
fea2c3c6b2 | |||
281686ba58 | |||
0b91fcb636 | |||
807f1381fb | |||
ac56a63809 | |||
e4798b9ed8 | |||
7584346c82 | |||
29330aa5d3 | |||
65ff2c0afe | |||
de4d47de95 | |||
ae41a3f316 | |||
2dc3d68418 | |||
5eb43b6ae4 | |||
0c672ecc49 | |||
3b6d98fe38 | |||
b3472cfe80 | |||
38f2b93c7a | |||
e7af87a981 | |||
d698a67660 | |||
b38f2d62a8 | |||
10556bca75 | |||
1f17d2a149 | |||
dc777c8de5 | |||
1fb9643916 | |||
081f1865a8 | |||
0e17c560b3 | |||
a3b1327934 | |||
e68ca573f0 | |||
b5455cee6e | |||
cbdc1403bf | |||
40130a8a61 | |||
ca14055976 | |||
8303016b48 | |||
287862887d | |||
f25d2a3d3f | |||
7f30f481b2 | |||
5ee6826241 | |||
68951c4c6b | |||
5dc7497802 | |||
31cfddc218 | |||
c1e70ac7d2 | |||
dd9abd3f09 | |||
2f5e64e532 | |||
b12f988390 | |||
9f3d5bf57b | |||
7f9e958e5a | |||
c4877ea092 | |||
2aafc23774 | |||
77c9a644ac | |||
abd8ca34dc | |||
31435685bf | |||
7337fd0670 | |||
f7b7a98b9d | |||
2226936737 | |||
8b293b2190 | |||
94bba8d11f | |||
5c2b5075f9 | |||
ca6820a4c0 | |||
2fafa3337f | |||
b5f75a4bb9 | |||
707bfbbf8b | |||
1eba6c5167 | |||
a14208b841 | |||
83966f9a7f | |||
d89f06015b | |||
49f786c928 | |||
e8bc0bd5d1 | |||
8d69b69357 | |||
e7b9a230e6 | |||
6385c412fd | |||
bb33b358b4 | |||
572f071cfe | |||
42cb89f525 | |||
1868d2b50f | |||
4588f1ec75 | |||
629f7f05c7 | |||
0f18686243 | |||
2a2587b13d | |||
489fdd5e4b | |||
fe680eb192 | |||
613440ff63 | |||
64121b1e92 | |||
f16927f316 | |||
cb50c1bd8b | |||
921ad86274 | |||
ac76107752 | |||
2359b1edd2 | |||
2750681d78 | |||
eaac4d3de0 | |||
f243968dfa | |||
8d9e2bdc71 | |||
6dbbb6b61b | |||
f89bf32390 | |||
ef195aa4ef | |||
843230a1cb | |||
84e63f3b38 | |||
b90816fdf8 | |||
40c4a42921 | |||
26f89391d3 | |||
aaae72cf84 | |||
3e55cff153 | |||
bd778a2204 | |||
235c196f14 | |||
e475c161cb | |||
08e96109a7 | |||
81ad33d9e3 | |||
aecc95825b | |||
37c6ac3a88 | |||
772d0beac3 | |||
64fdfd81ee | |||
1b09b9faa4 | |||
6f0ebb2d94 | |||
cbe91e3012 | |||
238501919b | |||
ae3a5f7b25 | |||
638cadc3c9 | |||
da0036581c | |||
59a58ea310 | |||
bebe5f8a4e | |||
c3af99685d | |||
e1d9e05b8d | |||
212f6b472a | |||
fdada78edf | |||
638c5429d2 | |||
b67bbd7065 | |||
1caf3e778b | |||
fd82fff07a | |||
a6ac8f8c09 | |||
19a26f8e22 | |||
46e85cf265 | |||
8f321b5427 | |||
e1f8f1a3f4 | |||
935a5b573d | |||
8c2636aa99 | |||
03ddb1075c | |||
72eb2c058c | |||
a100d2ccf9 | |||
ecfb3e94c8 | |||
c31d6b1ac1 | |||
65993e1d50 | |||
47c4c0d6bb | |||
b2872e6110 | |||
b8c8d5b447 | |||
32049d7bfc | |||
f0fdb35ba6 | |||
d8baf62966 | |||
be8f7f9676 | |||
57b641bf63 | |||
ff5d29de1a | |||
91e98ba447 | |||
6a644dd0e5 | |||
7b82393336 | |||
22993e1ea6 | |||
341bd6d7ca | |||
13d5a36cfc | |||
f3bb84f2c0 | |||
1d496404be | |||
51233371e0 | |||
bc0a7ebbbc | |||
72c78b3870 | |||
5555c52376 | |||
e1842be049 | |||
6ceb4fcf42 | |||
50b68d4e1c | |||
3f46228f0b | |||
6c954f0b68 | |||
69c2ed77a0 | |||
6f09224bdc | |||
568c90806d | |||
6e451c8d4d | |||
12fd585625 | |||
997fbb3392 | |||
089626f6b1 | |||
71d2049fe8 | |||
e5aee3001f | |||
ec62bcbf8e | |||
a8f013f3e4 | |||
037cd78dc7 | |||
b31ae4bae5 | |||
54dba980b4 | |||
dc19d2fab3 | |||
3a57310fbe | |||
749e19a1c3 | |||
de6608f6b8 | |||
cd6d9cdd94 | |||
e45413d417 | |||
11c3230150 | |||
dd99978b19 | |||
dd265bbd54 | |||
f5ba1b1b97 | |||
7825f0f84f | |||
69a0324e86 | |||
957d3545b6 | |||
466348a8c5 | |||
e5b7e97ff4 | |||
4613e5f847 | |||
44f8154f07 | |||
5486874d1a | |||
d868ca4740 | |||
780479be4b | |||
4705f01bc5 | |||
2f5f91a084 | |||
e44fe98c7e | |||
d8fbc9c170 | |||
facbe8f9a0 | |||
4d8e577ffd | |||
80eb7635c1 | |||
e3103762b6 | |||
cce710e377 | |||
013c79bc45 | |||
1e375886bd | |||
d1ac24c65d | |||
6aa1284848 | |||
bb082c35fc | |||
f7577e7de8 | |||
b5df13d8b7 | |||
9d76790cc5 | |||
706ee243a5 | |||
351a1a8d27 | |||
6916cd7977 | |||
a444c25c2c | |||
45bc533e38 | |||
03e890b01c | |||
0c90162e20 | |||
ddc3ef8db3 | |||
fcec43b7ca | |||
edb614d970 | |||
820b99e3d3 | |||
cf0453cee0 | |||
75a8d8f6d3 | |||
1ac8d99145 | |||
b7b5512e7a | |||
485acd6c8d | |||
bb68728c82 | |||
f3b2eb69d2 | |||
168d688fc9 | |||
ade93ea76d | |||
44503af88b | |||
eb7693561b | |||
3ccb03f9be | |||
f3a2af8f10 | |||
2ef615a3f7 | |||
20197fc3ec | |||
fadc624f7c | |||
22c4149358 | |||
c770c6bc6a | |||
891408191e | |||
9a8fa246a9 | |||
83c3152b5d | |||
956730c5e9 | |||
72b9c92a6e | |||
349255d252 | |||
ac902badcd | |||
9dc2ae0d7e | |||
188bd6db7b | |||
3a8ce64c84 | |||
f3d573cab0 | |||
9e18c7ea18 | |||
a975dc4427 | |||
b875e284af | |||
46fe4298b9 | |||
9790d3ba64 | |||
2d31f30a22 | |||
2fefe93922 | |||
399b068a4e | |||
dcffde6eeb | |||
78074f6a7e | |||
79dc01f7e4 | |||
0f6040ecb1 | |||
a0ab72e362 | |||
2c45378c6d | |||
44c75187f5 | |||
2609a4d124 | |||
2d58501db3 | |||
a337185820 | |||
9c0aa0c271 | |||
b2e908f094 | |||
ef32d37073 | |||
f0961a9658 | |||
876b5714be | |||
825cd7ff4c | |||
47f3476078 | |||
7b10ebc117 | |||
e5cdfd206d | |||
dd4c62b560 | |||
aae801efaf | |||
e02d44433d | |||
590a3c98e5 | |||
7f472e4ee9 | |||
a3802d4d8b | |||
59348f8dbd | |||
8742a66f2f | |||
a2f027e136 | |||
cb4359cd0a | |||
163c172823 | |||
80a2d2c1f5 | |||
486f282999 | |||
1293dccf35 | |||
91fe62eee3 | |||
d3f5596cb2 | |||
d7a88db87a | |||
0af1f67c33 | |||
8dde7b70db | |||
64faeef6c4 | |||
c79e4aeaea | |||
8b6a86e391 | |||
92daf4a8df | |||
819b07a52a | |||
b8f8c6129d | |||
25d1ae195a | |||
d22b05e114 | |||
db25eff74a | |||
c927441d66 | |||
7e4832d5f2 | |||
819b35c760 | |||
071498c413 | |||
7125ed0492 | |||
de201bdd9c | |||
4fccd258e6 | |||
56d705739b | |||
2a9d61b1ed | |||
a9f6839a04 | |||
5b555855ef | |||
76cf80a3d0 | |||
4c6aaa32b6 | |||
74ab1bff53 | |||
b5bba5e3c8 | |||
7e5bd17714 | |||
ec6207fc78 | |||
36d47a0ba9 | |||
0289cefd8d | |||
521eb2d8f8 | |||
0494266649 | |||
8fac5c064e | |||
0b6f74e646 | |||
b8b272a5b8 | |||
a570e09166 | |||
1919e36c30 | |||
812c00f11e | |||
419e27cfd1 | |||
d761c02909 | |||
c7d1d2b69a | |||
0972b6b56a | |||
6e3cf7869f | |||
f7337b4891 | |||
55161dec17 | |||
b65b53b0df | |||
49e1e1c8a4 | |||
9b73fcda40 | |||
b92e1ee9aa | |||
04ac54cd35 | |||
d47916f753 | |||
b0ea1d691a | |||
ce041dfbc1 | |||
4613da093d | |||
2d5f7aaae5 | |||
5a7a7ac83d | |||
f217af2deb | |||
6d58f9a354 | |||
29953ea5e4 | |||
bb9cef1e40 | |||
ece2f1484c | |||
a3c8a4363d | |||
2f90b5a201 | |||
d4bbdc28f3 | |||
f41df969b7 | |||
f87d006a1c | |||
f4fa9a7d8f | |||
c52047e6cd | |||
9163d41228 | |||
1be9bb29e8 | |||
522a89a045 | |||
06b9b6a7fb | |||
7f9c565cd7 | |||
201afc823e | |||
f4c79c885a | |||
656202c9db | |||
72b64072d5 | |||
b72271f9a4 | |||
b0d09d28f4 | |||
b9197e35b5 | |||
e431be2cbe | |||
761c883c1f | |||
60f86f342b | |||
1234b6b148 | |||
6f45242fc8 | |||
36c45ccb7b | |||
90cf71b5bc | |||
7165dc7860 | |||
5491287931 | |||
7256096b8a | |||
b6008b5414 | |||
1042d21278 | |||
47eff7ee86 | |||
3da850a6b9 | |||
a1358deda2 | |||
df0bbfd615 | |||
0568ac3aa5 | |||
e63a69170e | |||
711f8dedd9 | |||
09c3737a94 | |||
9384424173 | |||
4936f08212 | |||
03f4ebbe35 | |||
0671785ab2 | |||
381dbc4b4a | |||
84cf531f5f | |||
175806115b | |||
86b45ab1e5 | |||
17939036bc | |||
5bf515441e | |||
06edb9f2a6 | |||
33b58f5fab | |||
47a012a4dd | |||
e5801be43e | |||
d5a6ac591c | |||
59373f9bdf | |||
5da492b9e5 | |||
9bcc951f10 | |||
3270ba840e | |||
45b3e44cc2 | |||
7ed855b2d2 | |||
f08552c2d1 | |||
9a4c19b24b | |||
690b695373 | |||
65348b2365 | |||
285c13d900 | |||
0a938d9048 | |||
a02a265802 | |||
eeeeef81cf | |||
bcb9fe5f24 | |||
37f34d83f8 | |||
b3238079c3 | |||
ee1edb3383 | |||
7767430af2 | |||
2e5185aa99 | |||
6e847a4cc4 | |||
045f6dccf8 | |||
d7895a456a | |||
7753d05b61 | |||
043b4776c3 | |||
5db764de5f | |||
3ae846a713 | |||
927e29b8ef | |||
d271411552 | |||
31d98ac4a5 | |||
78f4cc8e30 | |||
cce30a8f42 | |||
a9e928fb46 | |||
60017f7c55 | |||
eb46f74e24 | |||
5e890bd781 | |||
20facf78d0 | |||
96db43cc8e | |||
ab4f209c10 | |||
fa51a0aef4 | |||
aa6a5e053c | |||
03df6c2ba0 | |||
501f645e60 | |||
23534b31c6 | |||
d35363cdbc | |||
ba34c90b7f | |||
94a19171ed | |||
8099591589 | |||
df6bbc59b3 | |||
05a616aa0d | |||
c84105e783 | |||
262721cc90 | |||
c24168d5cd | |||
4e529a68d3 | |||
4f3244e93b | |||
b2e17916e4 | |||
57ac344e7f | |||
98e275d908 | |||
8420b6c715 | |||
3dfcb2d5cc | |||
540720a912 | |||
f86200e3ae | |||
9e43618028 | |||
aacdba1bc7 | |||
c28d060d52 | |||
bf3fdbb1ab | |||
0a7a637d46 | |||
e842165265 | |||
2db82da910 | |||
9953bc3024 | |||
2ba4992d88 | |||
5e67008d26 | |||
e7b50c5940 | |||
78d7277298 | |||
fb641187b8 | |||
4b2715c36f | |||
f1e9f5d4fd | |||
2d43d349ab | |||
7ab6708a3c | |||
7010d9b524 | |||
947a3a2181 | |||
0ff87ef8cb | |||
ec20150ffd | |||
30876a9cd3 | |||
1773fc0e0d | |||
6d6f7fb89b | |||
449ce3176e | |||
be8832e87f | |||
5999690665 | |||
285fa6cbc9 | |||
9700f30c35 | |||
a38bd0b5cf | |||
5383f9f097 | |||
a16d17c422 | |||
5f2b620819 | |||
fd47cb88de | |||
77e7982e74 | |||
04cd1cedda | |||
3ef89f49e7 | |||
2a681608b5 | |||
a52c06a6c6 | |||
49b8a65ad9 | |||
9781cb72ac | |||
f7e83fb839 | |||
ce2a2cf684 | |||
c88fa70f82 | |||
f76744a0c0 | |||
31cc0764a9 | |||
15137d9b62 | |||
8f8adfa39e | |||
5044f3e58f | |||
279e102d7a | |||
7f72830ec8 | |||
2caaad95ec | |||
09b995aca6 | |||
a0bf8433e4 | |||
9104bd7304 | |||
2f2aa7f5a8 | |||
0773a30578 | |||
962f5efe6b | |||
6dc3cd9650 | |||
397ae536f9 | |||
f19ec4bd44 | |||
fd7e549915 | |||
7a7ae77c83 | |||
1a9fb381ed | |||
ae7bfceafb | |||
a961843aa6 | |||
43c6a4ddac | |||
ae1d5648d5 | |||
2d3e8e0c4e | |||
4691fc69d5 | |||
cb87f9f307 | |||
5f1e5bc271 | |||
1c6d5ad2db | |||
555189f123 | |||
049044b827 | |||
cfd9e2c3ab | |||
a0b457b9a1 | |||
23f24c7d39 | |||
6112cc5a38 | |||
7deb8c1bcb | |||
76e4b49d9d | |||
5ae267a8a2 | |||
f524351041 | |||
893d1bb45f | |||
945988dfb7 | |||
62698664a2 | |||
0d2892c75d | |||
ecc72e6825 | |||
531d520ceb | |||
4e72e150ad | |||
b28628b8e1 | |||
702830ad0e | |||
6ca0c54ba7 | |||
634bf5f7e3 | |||
2284c963af | |||
ad2052395f | |||
e9a1dbf8f1 | |||
703b21e89b | |||
36ea2cca6b | |||
8b2cf770a5 | |||
7d6d801943 | |||
d705b43f3a | |||
a18ed194cb | |||
a3e4293fd8 | |||
df144d8434 | |||
bab1e05235 | |||
a1fdd41b0e | |||
60d9c1651a | |||
ec1380dfa1 | |||
798275608e | |||
0f2affd414 | |||
7695b51d89 | |||
fb99122d83 | |||
6d53838e20 | |||
ec3fd9a7d7 | |||
304f2ebb7b | |||
4976e84488 | |||
2ebacb1b9b | |||
d085f9ea66 | |||
e275117569 | |||
eae277fb77 | |||
d7130c15cc | |||
937a17c5dd | |||
b6234e1d5e | |||
7955b8ae71 | |||
a36ef62358 | |||
bcbda3cd27 | |||
239fe518a9 | |||
44d6e117d5 | |||
8a12b7cb41 | |||
4d4308c486 | |||
abcdcf2e8c | |||
44b753d1e5 | |||
83b3f242a9 | |||
86c43f4734 | |||
be0cb84f97 | |||
3bea7f5ad5 | |||
266a20d55e | |||
d2c6a80d24 | |||
cd51fbc2a6 | |||
73256f6030 | |||
e081f94d9f | |||
e96cc09d75 | |||
d87178fec3 | |||
28ad95f892 | |||
8270a92a44 | |||
088290c544 | |||
6685acfef4 | |||
1d1a05ee7b | |||
bbeb429a59 | |||
55af588c2c | |||
80d0313fe5 | |||
853d309960 | |||
564400597a | |||
1c2b6fc00e | |||
7b6f32e5b2 | |||
dd4acc88a1 | |||
6e566f6e3d | |||
fff7fbe121 | |||
a50afeb5d5 | |||
49eeb99d43 | |||
bfd51097c9 | |||
a21e3cd842 | |||
0f298cf48e | |||
4b074b8dcf | |||
2c79853ffe | |||
72f57255f0 | |||
1053bc8bb0 | |||
4835e6fcb9 | |||
7ec02a1620 | |||
10993cc6ef | |||
81409369bc | |||
edb8590da8 | |||
3a7bfd28fc | |||
36fdb4ee2f | |||
b06f772647 | |||
012cc740d6 | |||
1407cff49d | |||
3331e1c152 | |||
591f48856d | |||
479b9691fd | |||
0e48557b48 | |||
3fae6f06dd | |||
7639c24bfe | |||
39fd1c3ab8 | |||
abf9dbae6d | |||
98062f830a | |||
c259347917 | |||
9c4558d891 | |||
9e7e2948e3 | |||
43430da25f | |||
eca7ac21a0 | |||
bc463f6d0b | |||
ec2708a1fd | |||
082922de01 | |||
ea02a7c70b | |||
ab7e25bd52 | |||
d4876dd25e | |||
89ee0bbab4 | |||
06ae882064 | |||
6517fe7515 | |||
d510aad2ab | |||
3db297de95 | |||
8688f26f15 | |||
85d38e7af2 | |||
0448348154 | |||
ceab4f1ffc | |||
3781b8db09 | |||
7a450c526c | |||
c1e8ea0e4a | |||
f248a33eaa | |||
67fb4e7007 | |||
0a41052f3f | |||
a7763a08dc | |||
fcfb471a8a | |||
b9e383130e | |||
cd2159b873 | |||
e492d5e0cf | |||
9a0f6490ba | |||
5183b44d8b | |||
156d86835a | |||
eab4397b0f | |||
d808b999c6 | |||
603b345405 | |||
682534f468 | |||
42eb43f713 | |||
0817b58b9d | |||
9ab766375d | |||
6c2799fe53 | |||
ef81a575cd | |||
cce0d94fbb | |||
22b5203334 | |||
d4be5abe67 | |||
9985a02efc | |||
41c2c60ab0 | |||
f285364f46 | |||
1c5e9b7fe3 | |||
12cc501e25 | |||
49118b8bf1 | |||
09dfea7dea | |||
00bd469f8e | |||
18e7e56a6c | |||
34d14a720b | |||
c4d7f9924f | |||
80d6921a66 | |||
3c95144b83 | |||
84ad155ab8 | |||
330a5ddd0f | |||
3b2f1d35c4 | |||
0f1036b0e1 | |||
86935f10a8 | |||
1078c42a14 | |||
09cf973712 | |||
5af749a226 | |||
f84b86a752 | |||
ca7873eda7 | |||
f87ebaf214 | |||
a9802eb6a7 | |||
5d5a68cb3e | |||
c6b1f5053f | |||
1d2e01c8cd | |||
0c5c18a767 | |||
6826ba05e7 | |||
053ce88743 | |||
4a216c57d4 | |||
03cec7fd5a | |||
1238001add | |||
fa1c077fdd | |||
8a7c3390f5 | |||
2302aee819 | |||
a72866ee6a | |||
0f7a3dba87 | |||
5decf18eb5 | |||
c318760398 | |||
629eff20dd | |||
f6e508ca14 | |||
588ab86abb | |||
387629372b | |||
0a01700e3e | |||
59504deb7f | |||
8ee660c238 | |||
176c106427 | |||
ed4fe56e7e | |||
310cd54aa0 | |||
3217b4ac90 | |||
7bf1949061 | |||
51f9d6d421 | |||
ddb32c65fb | |||
c5c158e983 | |||
f83007e038 | |||
2b9a368b18 | |||
6ad6974452 | |||
308923448b | |||
04ad7de2e1 | |||
54563b0b42 | |||
593779b54f | |||
34d3704680 | |||
613f90bcf7 | |||
6ff500f7cb | |||
c79e33896e | |||
68b15aadca | |||
819504f08f | |||
e65ec2a589 | |||
1bc355b8fd | |||
d76164679f | |||
efebecfc67 | |||
7b64586c87 | |||
0fe15b8e1d | |||
a1cb00b5a3 | |||
5041d819a9 | |||
02ab6eac62 | |||
d7feab116f | |||
4f9e13d0f6 | |||
d0b0e6a58e | |||
a12f898096 | |||
c921ecca05 | |||
975378b224 | |||
915e003355 | |||
51e45d128a | |||
57650ef058 | |||
dee6e16e6c | |||
c860674613 | |||
33b7f08d5c | |||
66bbe21a87 | |||
51995cc428 | |||
a3e3a305ce | |||
15facc72b3 | |||
3839c8d1c0 | |||
d5edcbc6e1 | |||
eb97ef4cb2 | |||
9c38e1e191 | |||
7c3d3b4128 | |||
367cea4b1f | |||
a63bfeaeec | |||
41672dde64 | |||
3b18cb7eca | |||
c9ce1751c1 | |||
4ba40b340a | |||
e3be1d1a04 | |||
6fa2a416be | |||
fdb54c315b | |||
c7de4e46c1 | |||
22a7757461 | |||
0bacbbc553 | |||
fbdc535287 | |||
03d8314842 | |||
fe4d98f0df | |||
b1d60122a3 | |||
64ec9f6a00 | |||
2d6f71dc12 | |||
c9e20c5d23 | |||
381f494754 | |||
506419964b | |||
35bb8c5348 | |||
79fe799aeb | |||
2878a6487e | |||
d4722e0d2c | |||
9655e79d26 | |||
d1a2e24f0e | |||
086381d958 | |||
7187f6f714 | |||
e10e05166f | |||
b0f8d84a7f | |||
0e9ceba057 | |||
fe3059f0ab | |||
bd566f52cf | |||
b7e0dabe61 | |||
2d2348f671 | |||
b28eb708a4 | |||
bf9c4b2346 | |||
d33aa097fe | |||
8673c232b6 | |||
d3ea5d2122 | |||
370d2555c7 | |||
5332cee3e8 | |||
1246e1c498 | |||
d6b0b1b93c | |||
1e0c970c95 | |||
db9f49c7d4 | |||
1603353ae8 | |||
6753d23309 | |||
ca5755b0fd | |||
2c8223274d | |||
f0dd09cf9c | |||
4746d9eb80 | |||
99401c5639 | |||
58578d9020 | |||
af575d6c95 | |||
e9c8748c0b | |||
08409d016b | |||
42bfbfc60b | |||
c7c087d964 | |||
89764c12e7 | |||
bd45d5483f | |||
328d7d0008 | |||
cca5bef8c1 | |||
ce4874d825 | |||
9b408b67ef | |||
c3bf100082 | |||
b282ccd890 | |||
f38b741813 | |||
3a899d52d1 | |||
a2567b0ee2 | |||
4b0019c732 | |||
5d21738410 | |||
df81006b42 | |||
2c7006e9bd | |||
b1caa8d5a3 | |||
8b2ffada10 | |||
0998738e94 | |||
c04062bbdf | |||
0c7a3a3a39 | |||
f364a83f4f | |||
9dabc75866 | |||
2c185ea76c | |||
39e859c368 | |||
2cfe5e678a | |||
d48991f71f | |||
dfbe3c4eb1 | |||
b8170a544b | |||
4e7f92ec89 | |||
292683268b | |||
dc14abd4d3 | |||
dd782f08f7 | |||
d57dc9a8a2 | |||
4e463d57ce | |||
e0c0cc8b63 | |||
cc50d47376 | |||
4da7548caa | |||
91a676cb36 | |||
48a32fb3b8 | |||
845b45a57d | |||
7b7a620999 | |||
82217d5ebc | |||
b95ec70d7d | |||
aa3d3670a4 | |||
8198c83982 | |||
75ff7987b8 | |||
9c87685c02 | |||
ce2bb85440 | |||
43fdff2292 | |||
06525adf3d | |||
cff4210dfd | |||
d855c5de50 | |||
f1a738340f | |||
9827c48527 | |||
367d68e552 | |||
1498ed361e | |||
91bc16ce05 | |||
861a1e26d7 | |||
a5b2f9a5e9 | |||
9550484037 | |||
b33284bb8e | |||
ce2694e8fb | |||
44073732e2 | |||
fef591412e | |||
f191e50b14 | |||
3379432e5f | |||
bb9129b61b | |||
0fc3029aaa | |||
d8c8586ccf | |||
38a4728283 | |||
7888705b01 | |||
31938f49d6 | |||
c95ed2ea96 | |||
b5ed247a53 | |||
22aff49747 | |||
e4430f05e4 | |||
1047691c64 | |||
f3180b3f6f | |||
616866cc9e | |||
58512b8230 | |||
ca4555c496 | |||
bc99bc7206 | |||
8f2dc67430 | |||
1420c773a6 | |||
888ef37808 | |||
690b40ed77 | |||
986de4c1d6 | |||
01da32364f | |||
8b1abc08db | |||
69e56f8f6b | |||
b611d0238a | |||
c987a9735d | |||
3b9549c2c1 | |||
2dcc9b7a1e | |||
3e54b5d544 | |||
8845ce6e1c | |||
ff189e796c | |||
89c07ac969 | |||
a8e878f894 | |||
1f8f3eb4d9 | |||
8fd2a05bf9 | |||
002d057c92 | |||
ab44488e4c | |||
653ffbc82e | |||
95fd0291e3 | |||
2a269ff1a9 | |||
83ccfb4596 | |||
e968828916 | |||
ed85a2b82b | |||
e692e21dc9 | |||
662fe3ebc2 | |||
1bf8fd92e4 | |||
4dd8a6421a | |||
884b285bf5 | |||
cb340152df | |||
299a44e7eb | |||
40e5bcbdbb | |||
d328e78727 | |||
1bcb9b24b6 | |||
3c1c130bf0 | |||
df3442563c | |||
331b1fa742 | |||
b97a53177e | |||
633b71ba19 | |||
f3dd42143d | |||
7c79f5d5e5 | |||
af5c0bd8a7 | |||
c07bfe34ab | |||
49681415e2 | |||
190e8c01b7 | |||
6ae86f7d81 | |||
0aeb3ca75e | |||
1d3e12abb7 | |||
5e8428ef6c | |||
19e3064529 | |||
f9dbd74ad8 | |||
ff837cf66e | |||
e0914c358e | |||
0e9bb23c7b | |||
7ff5d36f07 | |||
5a3eab0c7c | |||
c28f707f55 | |||
ef96c88719 | |||
faa2435e33 | |||
0537a221d3 | |||
99c5a1978f | |||
dee84e70ae | |||
09995b77b4 | |||
06894f9f0b | |||
73943b1a08 | |||
b573dab05f | |||
a766eca283 | |||
4167cd955b | |||
d1bd893a7b | |||
2467856011 | |||
d32b4e9f24 | |||
1acd5caaa8 | |||
f69b757305 | |||
8371b8f806 | |||
a93666cd36 | |||
dbb7eb3d88 | |||
39169f0450 | |||
d2e3547a2e | |||
8d9790fd77 | |||
91408cbdce | |||
97c1ba2d02 | |||
284802bfa5 | |||
48b6e0693e | |||
d867f9f36e | |||
55d92fc9f2 | |||
2e2d3c39e6 | |||
3cd01acb73 | |||
02c0ddb3d3 | |||
ce397f5858 | |||
611f991fdd | |||
47712a39ac | |||
18146daad8 | |||
a83c88e886 | |||
a2e7fa8b7b | |||
30ccf1b334 | |||
01b153488a | |||
bcfc7630d1 | |||
3b7daafad7 |
@ -28,9 +28,9 @@ web-fragment.xml
|
||||
web-out.xml
|
||||
|
||||
# Temporary/build dirs
|
||||
^build
|
||||
^build$
|
||||
^pkg-temp
|
||||
/build
|
||||
/build$
|
||||
/classes
|
||||
/dist
|
||||
^installer/resources/locale/mo
|
||||
|
81
.tx/config
81
.tx/config
@ -12,15 +12,18 @@ 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.nn = apps/i2ptunnel/locale/messages_nn.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.ro = apps/i2ptunnel/locale/messages_ro.po
|
||||
trans.ru_RU = apps/i2ptunnel/locale/messages_ru.po
|
||||
trans.sk = apps/i2ptunnel/locale/messages_sk.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
|
||||
trans.zh_TW = apps/i2ptunnel/locale/messages_zh_TW.po
|
||||
|
||||
[I2P.proxy]
|
||||
source_file = apps/i2ptunnel/locale-proxy/messages_en.po
|
||||
@ -31,6 +34,8 @@ 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
|
||||
;; Java converts id to in
|
||||
trans.id = apps/i2ptunnel/locale-proxy/messages_in.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
|
||||
@ -39,6 +44,7 @@ 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.sk = apps/i2ptunnel/locale-proxy/messages_sk.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
|
||||
@ -71,6 +77,7 @@ trans.tr_TR = apps/routerconsole/locale/messages_tr.po
|
||||
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
|
||||
trans.zh_TW = apps/routerconsole/locale/messages_zh_TW.po
|
||||
|
||||
[I2P.welcome]
|
||||
source_file = apps/routerconsole/locale-news/messages_en.po
|
||||
@ -78,10 +85,16 @@ 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.fi = apps/routerconsole/locale-news/messages_fi.po
|
||||
trans.fr = apps/routerconsole/locale-news/messages_fr.po
|
||||
trans.he = apps/routerconsole/locale-news/messages_he.po
|
||||
trans.ja = apps/routerconsole/locale-news/messages_ja.po
|
||||
;; Java converts id to in
|
||||
trans.id = apps/routerconsole/locale-news/messages_in.po
|
||||
trans.it = apps/routerconsole/locale-news/messages_it.po
|
||||
trans.ja = apps/routerconsole/locale-news/messages_ja.po
|
||||
trans.ko = apps/routerconsole/locale-news/messages_ko.po
|
||||
trans.mg = apps/routerconsole/locale-news/messages_mg.po
|
||||
trans.nb = apps/routerconsole/locale-news/messages_nb.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
|
||||
@ -89,14 +102,18 @@ 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.sk = apps/routerconsole/locale-news/messages_sk.po
|
||||
trans.sq = apps/routerconsole/locale-news/messages_sq.po
|
||||
trans.sv_SE = apps/routerconsole/locale-news/messages_sv.po
|
||||
trans.tr_TR = apps/routerconsole/locale-news/messages_tr.po
|
||||
trans.uk_UA = apps/routerconsole/locale-news/messages_uk.po
|
||||
trans.zh_CN = apps/routerconsole/locale-news/messages_zh.po
|
||||
trans.zh_TW = apps/routerconsole/locale-news/messages_zh_TW.po
|
||||
|
||||
[I2P.countries]
|
||||
type = PO
|
||||
source_file = apps/routerconsole/locale-countries/messages_en.po
|
||||
source_lang = en
|
||||
trans.ca = apps/routerconsole/locale-countries/messages_ca.po
|
||||
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
|
||||
@ -107,6 +124,7 @@ 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.mg = apps/routerconsole/locale-countries/messages_mg.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
|
||||
@ -115,10 +133,13 @@ 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.sk = apps/routerconsole/locale-countries/messages_sk.po
|
||||
trans.sq = apps/routerconsole/locale-countries/messages_sq.po
|
||||
trans.sv_SE = apps/routerconsole/locale-countries/messages_sv.po
|
||||
trans.uk_UA = apps/routerconsole/locale-countries/messages_uk.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
|
||||
trans.zh_TW = apps/routerconsole/locale-countries/messages_zh_TW.po
|
||||
|
||||
[I2P.i2psnark]
|
||||
source_file = apps/i2psnark/locale/messages_en.po
|
||||
@ -134,6 +155,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.pt_BR = apps/i2psnark/locale/messages_pt_bR.po
|
||||
trans.ro = apps/i2psnark/locale/messages_ro.po
|
||||
trans.ru_RU = apps/i2psnark/locale/messages_ru.po
|
||||
trans.sk = apps/i2psnark/locale/messages_sk.po
|
||||
@ -161,6 +183,7 @@ 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.tr_TR = apps/susidns/locale/messages_tr.po
|
||||
trans.uk_UA = apps/susidns/locale/messages_uk.po
|
||||
trans.vi = apps/susidns/locale/messages_vi.po
|
||||
trans.zh_CN = apps/susidns/locale/messages_zh.po
|
||||
@ -194,19 +217,25 @@ trans.zh_CN = apps/desktopgui/locale/messages_zh.po
|
||||
source_file = apps/susimail/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.cs = apps/susimail/locale/messages_cs.po
|
||||
trans.da = apps/susimail/locale/messages_da.po
|
||||
trans.de = apps/susimail/locale/messages_de.po
|
||||
trans.es = apps/susimail/locale/messages_es.po
|
||||
trans.fi = apps/susimail/locale/messages_fi.po
|
||||
trans.fr = apps/susimail/locale/messages_fr.po
|
||||
trans.hu = apps/susimail/locale/messages_hu.po
|
||||
;; Java converts id to in
|
||||
trans.id = apps/susimail/locale/messages_in.po
|
||||
trans.it = apps/susimail/locale/messages_it.po
|
||||
trans.ja = apps/susimail/locale/messages_ja.po
|
||||
trans.mg = apps/susimail/locale/messages_mg.po
|
||||
trans.nl = apps/susimail/locale/messages_nl.po
|
||||
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.ru_RU = apps/susimail/locale/messages_ru.po
|
||||
trans.sq = apps/susimail/locale/messages_sq.po
|
||||
trans.sv_SE = apps/susimail/locale/messages_sv.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
|
||||
@ -218,16 +247,21 @@ trans.cs = debian/po/cs.po
|
||||
trans.de = debian/po/de.po
|
||||
trans.el = debian/po/el.po
|
||||
trans.es = debian/po/es.po
|
||||
trans.fi = debian/po/fi.po
|
||||
trans.fr = debian/po/fr.po
|
||||
trans.id = debian/po/id.po
|
||||
trans.it = debian/po/it.po
|
||||
trans.hu = debian/po/hu.po
|
||||
trans.ja = debian/po/ja.po
|
||||
trans.ko = debian/po/ko.po
|
||||
trans.nl = debian/po/nl.po
|
||||
trans.pl = debian/po/pl.po
|
||||
trans.pt = debian/po/pt.po
|
||||
trans.pt_BR = debian/po/pt_BR.po
|
||||
trans.ro = debian/po/ro.po
|
||||
trans.ru_RU = debian/po/ru.po
|
||||
trans.sk = debian/po/sk.po
|
||||
trans.sq = debian/po/sq.po
|
||||
trans.sv_SE = debian/po/sv.po
|
||||
trans.uk_UA = debian/po/uk.po
|
||||
trans.tr_TR = debian/po/tr.po
|
||||
@ -236,12 +270,21 @@ trans.zh_CN = debian/po/zh.po
|
||||
[I2P.i2prouter-script]
|
||||
source_file = installer/resources/locale/po/messages_en.po
|
||||
source_lang = en
|
||||
;; currently fails check
|
||||
;;trans.ca = installer/resources/locale/po/messages_ca.po
|
||||
trans.de = installer/resources/locale/po/messages_de.po
|
||||
trans.es = installer/resources/locale/po/messages_es.po
|
||||
;; currently fails check
|
||||
;;trans.fi = installer/resources/locale/po/messages_fi.po
|
||||
trans.fr = installer/resources/locale/po/messages_fr.po
|
||||
trans.id = installer/resources/locale/po/messages_id.po
|
||||
trans.it = installer/resources/locale/po/messages_it.po
|
||||
trans.pl = installer/resources/locale/po/messages_pl.po
|
||||
trans.ja = installer/resources/locale/po/messages_ja.po
|
||||
;; currently fails check
|
||||
;;trans.ko = installer/resources/locale/po/messages_ko.po
|
||||
trans.nl = installer/resources/locale/po/messages_nl.po
|
||||
trans.pl = installer/resources/locale/po/messages_pl.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
|
||||
@ -249,6 +292,8 @@ trans.ru_RU = installer/resources/locale/po/messages_ru.po
|
||||
trans.sk = installer/resources/locale/po/messages_sk.po
|
||||
trans.sv_SE = installer/resources/locale/po/messages_sv.po
|
||||
trans.tr_TR = installer/resources/locale/po/messages_tr.po
|
||||
;; currently fails check
|
||||
;;trans.uk_UA = installer/resources/locale/po/messages_uk.po
|
||||
trans.zh_CN = installer/resources/locale/po/messages_zh.po
|
||||
|
||||
[I2P.getopt]
|
||||
@ -258,19 +303,47 @@ type = PROPERTIES
|
||||
trans.cs = core/java/src/gnu/getopt/MessagesBundle_cs.properties
|
||||
trans.de = core/java/src/gnu/getopt/MessagesBundle_de.properties
|
||||
trans.es = core/java/src/gnu/getopt/MessagesBundle_es.properties
|
||||
trans.fi = core/java/src/gnu/getopt/MessagesBundle_fi.properties
|
||||
trans.fr = core/java/src/gnu/getopt/MessagesBundle_fr.properties
|
||||
trans.hu = core/java/src/gnu/getopt/MessagesBundle_hu.properties
|
||||
;; Java converts id to in
|
||||
trans.id = core/java/src/gnu/getopt/MessagesBundle_in.properties
|
||||
trans.it = core/java/src/gnu/getopt/MessagesBundle_it.properties
|
||||
trans.ja = core/java/src/gnu/getopt/MessagesBundle_ja.properties
|
||||
trans.ko = core/java/src/gnu/getopt/MessagesBundle_ko.properties
|
||||
trans.nl = core/java/src/gnu/getopt/MessagesBundle_nl.properties
|
||||
trans.nb = core/java/src/gnu/getopt/MessagesBundle_nb.properties
|
||||
trans.pl = core/java/src/gnu/getopt/MessagesBundle_pl.properties
|
||||
trans.pt_BR = core/java/src/gnu/getopt/MessagesBundle_pt_BR.properties
|
||||
;; currently corrupt, non-UTF-8
|
||||
;;trans.pt = core/java/src/gnu/getopt/MessagesBundle_pt.properties
|
||||
;; currently corrupt, non-UTF-8
|
||||
;;trans.pt_BR = core/java/src/gnu/getopt/MessagesBundle_pt_BR.properties
|
||||
trans.ro = core/java/src/gnu/getopt/MessagesBundle_ro.properties
|
||||
trans.ru_RU = core/java/src/gnu/getopt/MessagesBundle_ru.properties
|
||||
trans.sk = core/java/src/gnu/getopt/MessagesBundle_sk.properties
|
||||
;; currently corrupt, non-UTF-8
|
||||
;;trans.sq = core/java/src/gnu/getopt/MessagesBundle_sq.properties
|
||||
trans.uk_UA = core/java/src/gnu/getopt/MessagesBundle_uk.properties
|
||||
trans.zh_CN = core/java/src/gnu/getopt/MessagesBundle_zh.properties
|
||||
|
||||
[I2P.streaming]
|
||||
source_file = apps/ministreaming/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ca = apps/ministreaming/locale/messages_ca.po
|
||||
trans.de = apps/ministreaming/locale/messages_de.po
|
||||
trans.es = apps/ministreaming/locale/messages_es.po
|
||||
trans.fr = apps/ministreaming/locale/messages_fr.po
|
||||
;; Java converts id to in
|
||||
trans.id = apps/ministreaming/locale/messages_in.po
|
||||
trans.it = apps/ministreaming/locale/messages_it.po
|
||||
trans.nb = apps/ministreaming/locale/messages_nb.po
|
||||
trans.pl = apps/ministreaming/locale/messages_pl.po
|
||||
trans.ro = apps/ministreaming/locale/messages_ro.po
|
||||
trans.ru_RU = apps/ministreaming/locale/messages_ru.po
|
||||
trans.sv_SE = apps/ministreaming/locale/messages_sv.po
|
||||
trans.uk_UA = apps/ministreaming/locale/messages_uk.po
|
||||
trans.zh_CN = apps/ministreaming/locale/messages_zh.po
|
||||
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
|
@ -25,21 +25,22 @@ where there are comments labeled "PORTABLE". Do this before you
|
||||
run I2P for the first time.
|
||||
|
||||
To start I2P:
|
||||
(*nix): sh i2prouter start
|
||||
(*nix, BSD, Mac): sh i2prouter start
|
||||
(win*): I2P.exe
|
||||
(non-x86 platforms PPC, ARM, etc): sh runplain.sh
|
||||
(platforms without wrapper support): sh runplain.sh
|
||||
|
||||
To stop I2P (gracefully):
|
||||
lynx http://localhost:7657/summaryframe (click "Shutdown")
|
||||
or (*nix, BSD, Mac) sh i2prouter graceful
|
||||
|
||||
To stop I2P immediately:
|
||||
sh i2prouter stop
|
||||
(*nix, BSD, Mac) sh i2prouter stop
|
||||
|
||||
To uninstall I2P:
|
||||
rm -rf $I2PInstallDir ~/.i2p
|
||||
|
||||
Supported JVMs:
|
||||
All platforms: Java 1.6 or higher required; 1.7 or higher recommended
|
||||
All platforms: Java 1.7 or higher required
|
||||
Windows: OpenJDK or Oracle from http://java.com/download
|
||||
Linux: OpenJDK or Oracle from http://java.com/download
|
||||
FreeBSD: OpenJDK or Oracle from http://java.com/download
|
||||
|
23
INSTALL.txt
23
INSTALL.txt
@ -1,11 +1,13 @@
|
||||
I2P source installation instructions
|
||||
|
||||
Prerequisites to build from source:
|
||||
Java SDK (preferably Oracle/Sun or OpenJDK) 1.6.0 or higher
|
||||
Java SDK (preferably Oracle/Sun or OpenJDK) 1.7.0 or higher
|
||||
Non-linux operating systems and JVMs: See https://trac.i2p2.de/wiki/java
|
||||
Certain subsystems for embedded (core, router, mstreaming, streaming, i2ptunnel) require only Java 1.6
|
||||
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/
|
||||
from the GNU gettext package http://www.gnu.org/software/gettext/
|
||||
Build environment must use a UTF-8 locale.
|
||||
|
||||
To build and install I2P from source, you must first build
|
||||
and package up the appropriate installer by running:
|
||||
@ -40,29 +42,30 @@ or on Windows, just double-click on i2pinstall.exe.
|
||||
Or move the i2pupdate.zip file into an existing installation directory and restart.
|
||||
|
||||
To start I2P:
|
||||
(*nix): sh i2prouter start
|
||||
(*nix, BSD, Mac): sh i2prouter start
|
||||
(win*): I2P.exe or i2prouter.bat
|
||||
(non-x86 platforms PPC, ARM, etc): sh runplain.sh
|
||||
(platforms without wrapper support): sh runplain.sh
|
||||
|
||||
To install I2P as a system service:
|
||||
(*nix) sh i2prouter install
|
||||
(*nix, BSD, Mac) sh i2prouter install
|
||||
(win*) install_i2p_service_winnt.bat
|
||||
|
||||
To uninstall I2P as a system service:
|
||||
(*nix) sh i2prouter remove
|
||||
(*nix, BSD, Mac) sh i2prouter remove
|
||||
(win*) uninstall_i2p-service_winnt.bat
|
||||
|
||||
To stop I2P (gracefully):
|
||||
lynx http://localhost:7657/summaryframe (click "Shutdown")
|
||||
or (*nix, BSD, Mac) sh i2prouter graceful
|
||||
|
||||
To stop I2P immediately:
|
||||
sh i2prouter stop
|
||||
(*nix, BSD, Mac) sh i2prouter stop
|
||||
|
||||
To uninstall I2P:
|
||||
rm -rf $I2PInstallDir ~/.i2p
|
||||
|
||||
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)
|
||||
Windows: Latest available from http://java.com/download (1.7+ supported)
|
||||
Linux: Latest available from http://java.com/download (1.7+ supported)
|
||||
FreeBSD: 1.7-compatible (NIO required)
|
||||
Other operating systems and JVMs: See http://trac.i2p2.de/wiki/java
|
||||
|
29
LICENSE.txt
29
LICENSE.txt
@ -40,6 +40,10 @@ Public domain except as listed below:
|
||||
Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
|
||||
See licenses/LICENSE-SHA256.txt
|
||||
|
||||
ElGamal:
|
||||
Copyright (c) 2000 - 2013 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org)
|
||||
See licenses/LICENSE-SHA256.txt
|
||||
|
||||
AES code:
|
||||
Copyright (c) 1995-2005 The Cryptix Foundation Limited.
|
||||
See licenses/LICENSE-Cryptix.txt
|
||||
@ -80,6 +84,10 @@ Public domain except as listed below:
|
||||
Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com)
|
||||
See licenses/LICENSE-LGPLv2.1.txt
|
||||
|
||||
HostnameVerifier:
|
||||
From Apache HttpClient 4.4.1 and HttpCore 4.4.1
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
|
||||
Router (router.jar):
|
||||
Public domain except as listed below:
|
||||
@ -87,7 +95,7 @@ Public domain except as listed below:
|
||||
From freenet
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
UPnP subsystem (CyberLink) 2.1:
|
||||
UPnP subsystem (CyberLink) 3.0:
|
||||
Copyright (C) 2003-2010 Satoshi Konno
|
||||
See licenses/LICENSE-UPnP.txt
|
||||
|
||||
@ -137,7 +145,7 @@ Installer:
|
||||
|
||||
|
||||
|
||||
Java Service Wrapper Community Edition 32-bit 3.5.19:
|
||||
Java Service Wrapper Community Edition 32-bit 3.5.25:
|
||||
Copyright (C) 1999-2011 Tanuki Software, Ltd. All Rights Reserved.
|
||||
See licenses/LICENSE-Wrapper.txt
|
||||
|
||||
@ -182,7 +190,18 @@ Applications:
|
||||
By welterde.
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
Jetty 8.1.14.v20131031:
|
||||
Imagegen:
|
||||
Identicon:
|
||||
Copyright (c) 2007-2014 Don Park <donpark@docuverse.com>
|
||||
See licenses/LICENSE-Identicon.txt
|
||||
RandomArt:
|
||||
Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
|
||||
Copyright (c) 2008 Alexander von Gernler. All rights reserved.
|
||||
See licenses/LICENSE-BSD.txt
|
||||
Zxing:
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
Jetty 8.1.17.v20150415:
|
||||
See licenses/ABOUT-Jetty.html
|
||||
See licenses/NOTICE-Jetty.html
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
@ -248,8 +267,8 @@ Applications:
|
||||
Bundles systray4j-2.4.1:
|
||||
See licenses/LICENSE-LGPLv2.1.txt
|
||||
|
||||
Tomcat 6.0.37:
|
||||
Copyright 1999-2013 The Apache Software Foundation
|
||||
Tomcat 6.0.44:
|
||||
Copyright 1999-2015 The Apache Software Foundation
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
See licenses/NOTICE-Tomcat.txt
|
||||
|
||||
|
16
README.txt
16
README.txt
@ -1,9 +1,11 @@
|
||||
Prerequisites to build from source:
|
||||
Java SDK (preferably Oracle/Sun or OpenJDK) 1.6.0 or higher
|
||||
Java SDK (preferably Oracle/Sun or OpenJDK) 1.7.0 or higher
|
||||
Non-linux operating systems and JVMs: See https://trac.i2p2.de/wiki/java
|
||||
Certain subsystems for embedded (core, router, mstreaming, streaming, i2ptunnel) require only Java 1.6
|
||||
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/
|
||||
Build environment must use a UTF-8 locale.
|
||||
|
||||
To build:
|
||||
On x86 systems do:
|
||||
@ -19,7 +21,8 @@ To build:
|
||||
|
||||
Documentation:
|
||||
https://geti2p.net/how
|
||||
API: run 'ant javadoc' then start at build/javadoc/index.html
|
||||
API: http://docs.i2p-projekt.de/javadoc/
|
||||
or run 'ant javadoc' then start at build/javadoc/index.html
|
||||
|
||||
Latest release:
|
||||
https://geti2p.net/download
|
||||
@ -34,6 +37,15 @@ Need help?
|
||||
IRC irc.freenode.net #i2p
|
||||
http://forum.i2p/
|
||||
|
||||
Bug reports:
|
||||
https://trac.i2p2.de/report/1
|
||||
|
||||
Contact information, security issues, press inquiries:
|
||||
https://geti2p.net/en/contact
|
||||
|
||||
Twitter:
|
||||
@i2p, @geti2p
|
||||
|
||||
Licenses:
|
||||
See LICENSE.txt
|
||||
|
||||
|
@ -1,30 +1,70 @@
|
||||
ou will need atleast monotone > = 0.41 to get the most recent build source
|
||||
and connect it to an already running i2p router.
|
||||
Getting the sources
|
||||
===================
|
||||
|
||||
OR:
|
||||
Monotone
|
||||
--------
|
||||
|
||||
You may download the actual "stable" source from
|
||||
http://code.google.com/p/i2p/downloads/list
|
||||
The bleeding-edge source code is available both within and outside of I2P. The
|
||||
I2P project uses Monotone to maintain the codebase.
|
||||
|
||||
You will need to follwing tools to build the i2p and i2p-base packages:
|
||||
Information on retrieving the source with monotone is available within I2P at
|
||||
http://i2p-projekt.i2p/monotone and from the internet at https://geti2p.net/monotone.
|
||||
|
||||
bash >= 3.1.017
|
||||
requiredbuilder >= 0.16.3 ( http://www.stabellini.net/requiredbuilder.html )
|
||||
jre >= 6u11
|
||||
jdk >= 6u11
|
||||
apache-ant >= 1.7.1
|
||||
perl >= 5.10.0
|
||||
python >= 2.5.2
|
||||
Monotone 1.0 Slackbuilds are available at http://slackbuilds.org/.
|
||||
|
||||
Reccomended:
|
||||
monotone >= 0.41 ( http://pkgs.dr.ea.ms )
|
||||
Git
|
||||
---
|
||||
|
||||
See also:
|
||||
Git is also an option to retrieve the I2P source code. That said, the I2P
|
||||
Monotone servers are *the* authoritative source.
|
||||
|
||||
i2p/readme.txt
|
||||
Public Git repositories hosting the I2P source and managed by I2P project team members include
|
||||
|
||||
AND
|
||||
- https://github.com/i2p/i2p.i2p
|
||||
- http://git.repo.i2p/w/i2p.i2p.git (mirrored from Github)
|
||||
- http://sourceforge.net/p/i2p/code/
|
||||
|
||||
i2p-base/readme.txt
|
||||
|
||||
for information and handy tips.
|
||||
Tarball
|
||||
-------
|
||||
|
||||
The latest stable release is always available from the I2P homepage at
|
||||
https://geti2p.net/get/.
|
||||
|
||||
|
||||
This SlackBuild
|
||||
===============
|
||||
|
||||
Requirements
|
||||
-------------
|
||||
|
||||
The following are needed to build the i2p package:
|
||||
|
||||
* jre >= 6
|
||||
* jdk >= 6
|
||||
* gettext
|
||||
* apache-ant >= 1.7.1
|
||||
|
||||
If you don't care about bundling the translations, the gettext requirement can
|
||||
be avoided by adding -Drequire.gettext=false to the ant lines in
|
||||
i2p/i2p.SlackBuild
|
||||
|
||||
A JRE >= v6 is the only requirement to run I2P.
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
As the root user, run either $I2PSRC/Slackware/i2p/i2p.SlackBuild or `ant slackpkg` to create a package
|
||||
in $I2PSRC/Slackware/i2p which can be installed using the Slackware packaging tools.
|
||||
|
||||
See also
|
||||
========
|
||||
|
||||
Please also take a look at
|
||||
|
||||
* i2p/README
|
||||
* eepget(1)
|
||||
* i2prouter(1)
|
||||
* http://i2p-projekt.i2p / https://geti2p.net
|
||||
|
||||
for additional information and tips.
|
||||
|
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<project basedir="." default="slackpkg">
|
||||
<target name="slackpkg">
|
||||
<echo message="Building Slackware package." />
|
||||
<exec executable="./i2p-base.SlackBuild">
|
||||
</exec>
|
||||
</target>
|
||||
</project>
|
@ -1,157 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
#
|
||||
# Now in the future we only need to look for '#I2P' and '#/I2P'
|
||||
# for modifications to rc.local and rc.local_shutdown.
|
||||
# I was a moron for not doing it this way in the first place :-) -- Sponge
|
||||
#
|
||||
#
|
||||
|
||||
touch /etc/rc.d/rc.local
|
||||
touch /etc/rc.d/rc.local_shutdown
|
||||
|
||||
echo
|
||||
echo -n "Check 1: /etc/rc.d/rc.local "
|
||||
I2PRCA=`grep -c /etc/rc.d/rc.local -e '/etc/rc.d/rc.i2p'`
|
||||
|
||||
if [ $I2PRCA -eq 0 ] ; then
|
||||
echo '#I2P' >> /etc/rc.d/rc.local
|
||||
echo '( cd /tmp ; rm -Rf i2p-*.tmp )' >> /etc/rc.d/rc.local
|
||||
echo "if [ -x /etc/rc.d/rc.i2p ] ; then" >> /etc/rc.d/rc.local
|
||||
echo " sh /etc/rc.d/rc.i2p start" >> /etc/rc.d/rc.local
|
||||
echo "fi" >> /etc/rc.d/rc.local
|
||||
echo '#/I2P' >> /etc/rc.d/rc.local
|
||||
echo "modified."
|
||||
else
|
||||
echo -n "looks OK so far,"
|
||||
# Fix old installs, or where people have modified.
|
||||
|
||||
echo -n " Check 1A: "
|
||||
I2PRCC=`grep -c /etc/rc.d/rc.local -e 'i2p-\*\.tmp'`
|
||||
|
||||
if [ $I2PRCC -eq 0 ] ; then
|
||||
DATA=$(cat /etc/rc.d/rc.local | sed -re 's/if \[ -x \/etc\/rc\.d\/rc\.i2p \] ; then/#I2P\n\( cd \/tmp ; rm -Rf i2p-*.tmp \)\nif \[ -x \/etc\/rc.d\/rc.i2p \] ; then/')
|
||||
echo "${DATA}" > /etc/rc.d/rc.local
|
||||
echo -n "additional modifications applied,"
|
||||
else
|
||||
echo -n "looks OK so far,"
|
||||
fi
|
||||
|
||||
echo -n " Check 1B: "
|
||||
I2PRCE=`grep -c /etc/rc.d/rc.local -e 'i2p-\*\.tmp'`
|
||||
if [ $I2PRCE -eq 0 ] ; then
|
||||
DATATOP=$(cat /etc/rc.d/rc.local | sed -n '0,/i2p-\*\.tmp/p' | sed '$d' )
|
||||
DATABOT=$(cat /etc/rc.d/rc.local | sed -n '/i2p-\*\.tmp/,$p' | sed -n '/^fi/,$p' | sed "1d")
|
||||
echo "${DATATOP}" > /etc/rc.d/rc.local
|
||||
echo '#I2P' >> /etc/rc.d/rc.local
|
||||
echo '( cd /tmp ; rm -Rf i2p-*.tmp )' >> /etc/rc.d/rc.local
|
||||
echo "if [ -x /etc/rc.d/rc.i2p ] ; then" >> /etc/rc.d/rc.local
|
||||
echo " sh /etc/rc.d/rc.i2p start" >> /etc/rc.d/rc.local
|
||||
echo "fi" >> /etc/rc.d/rc.local
|
||||
echo '#/I2P' >> /etc/rc.d/rc.local
|
||||
echo "${DATABOT}" >> /etc/rc.d/rc.local
|
||||
|
||||
echo -n "additional modifications applied,"
|
||||
else
|
||||
echo -n "looks ok so far,"
|
||||
fi
|
||||
echo -n " Check 1C: "
|
||||
I2PRCF=`grep -c /etc/rc.d/rc.local -e '#/I2P'`
|
||||
if [ $I2PRCF -eq 0 ] ; then
|
||||
DATATOP=$(cat /etc/rc.d/rc.local | sed -n '0,/^#I2P/p' | sed '$d' )
|
||||
DATABOT=$(cat /etc/rc.d/rc.local | sed -n '/^#I2P/,$p' | sed -n '/^fi/,$p' | sed "1d")
|
||||
echo "${DATATOP}" > /etc/rc.d/rc.local
|
||||
echo '#I2P' >> /etc/rc.d/rc.local
|
||||
echo '( cd /tmp ; rm -Rf i2p-*.tmp )' >> /etc/rc.d/rc.local
|
||||
echo "if [ -x /etc/rc.d/rc.i2p ] ; then" >> /etc/rc.d/rc.local
|
||||
echo " sh /etc/rc.d/rc.i2p start" >> /etc/rc.d/rc.local
|
||||
echo "fi" >> /etc/rc.d/rc.local
|
||||
echo '#/I2P' >> /etc/rc.d/rc.local
|
||||
echo "${DATABOT}" >> /etc/rc.d/rc.local
|
||||
|
||||
echo -n "additional modifications applied,"
|
||||
else
|
||||
echo -n "looks ok so far,"
|
||||
fi
|
||||
echo " Done."
|
||||
fi
|
||||
|
||||
echo -n "Check 2: /etc/rc.d/rc.local_shutdown "
|
||||
I2PRCB=`grep -c /etc/rc.d/rc.local_shutdown -e '/etc/rc.d/rc.i2p'`
|
||||
if [ $I2PRCB -eq 0 ] ; then
|
||||
echo "#I2P" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "if [ -x /etc/rc.d/rc.i2p ] ; then" >> /etc/rc.d/rc.local_shutdown
|
||||
echo " sh /etc/rc.d/rc.i2p stop" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "fi" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "#/I2P" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "modified."
|
||||
else
|
||||
echo -n "looks OK so far,"
|
||||
# Fix old installs
|
||||
|
||||
echo -n " Check 1A: "
|
||||
I2PRCG=`grep -c /etc/rc.d/rc.local_shutdown -e '#I2P'`
|
||||
if [ $I2PRCG -eq 0 ] ; then
|
||||
DATATOP=$(cat /etc/rc.d/rc.local_shutdown | sed -n '0,/^if \[ -x \/etc\/rc\.d\/rc\.i2p \] ; then/p' | sed '$d' )
|
||||
DATABOT=$(cat /etc/rc.d/rc.local_shutdown | sed -n '/^if \[ -x \/etc\/rc\.d\/rc\.i2p \] ; then/,$p' | sed -n '/^fi/,$p' | sed "1d")
|
||||
echo "${DATATOP}" > /etc/rc.d/rc.local_shutdown
|
||||
echo '#I2P' >> /etc/rc.d/rc.local_shutdown
|
||||
echo "if [ -x /etc/rc.d/rc.i2p ] ; then" >> /etc/rc.d/rc.local_shutdown
|
||||
echo " sh /etc/rc.d/rc.i2p stop" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "fi" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "#/I2P" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "${DATABOT}" >> /etc/rc.d/rc.local_shutdown
|
||||
echo -n "additional modifications applied,"
|
||||
else
|
||||
echo -n "looks OK so far,"
|
||||
fi
|
||||
echo -n " Check 1B: "
|
||||
I2PRCH=`grep -c /etc/rc.d/rc.local_shutdown -e '#/I2P'`
|
||||
if [ $I2PRCH -eq 0 ] ; then
|
||||
DATATOP=$(cat /etc/rc.d/rc.local_shutdown | sed -n '0,/^#I2P/p' | sed '$d' )
|
||||
DATABOT=$(cat /etc/rc.d/rc.local_shutdown | sed -n '/^#I2P/,$p' | sed -n '/^fi/,$p' | sed "1d")
|
||||
echo "${DATATOP}" > /etc/rc.d/rc.local_shutdown
|
||||
echo '#I2P' >> /etc/rc.d/rc.local_shutdown
|
||||
echo "if [ -x /etc/rc.d/rc.i2p ] ; then" >> /etc/rc.d/rc.local_shutdown
|
||||
echo " sh /etc/rc.d/rc.i2p stop" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "fi" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "#/I2P" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "${DATABOT}" >> /etc/rc.d/rc.local_shutdown
|
||||
echo -n "additional modifications applied,"
|
||||
else
|
||||
echo -n "looks OK so far,"
|
||||
fi
|
||||
echo " Done."
|
||||
fi
|
||||
|
||||
if [ -f /etc/rc.d/rc.i2p ] ; then
|
||||
if [ -x /etc/rc.d/rc.i2p ] ; then
|
||||
chmod +x /etc/rc.d/rc.i2p.new
|
||||
fi
|
||||
# Hopefully get admin's attention.
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -e "\007" ; sleep 0.3
|
||||
echo "It apears that you already have /etc/rc.d/rc.i2p"
|
||||
echo "You should replace it with /etc/rc.d/rc.i2p.new as soon as possible"
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -e "\007" ; sleep 0.3
|
||||
else
|
||||
mv /etc/rc.d/rc.i2p.new /etc/rc.d/rc.i2p
|
||||
echo
|
||||
echo "Installation finished. The i2p start/stop script has been"
|
||||
echo "installed in /etc/rc.d . You should chmod +x"
|
||||
echo '/etc/rc.d/rc.i2p to start it on boot.'
|
||||
echo
|
||||
fi
|
||||
|
||||
exit
|
@ -1,57 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Heavily based on the Slackware 12.2 SlackBuild
|
||||
# Slackware build script for I2P
|
||||
#
|
||||
# PLEASE READ THIS:
|
||||
# How to start I2P:
|
||||
# After installpkg command, doinst.sh will execute a post-installation script
|
||||
# needed by I2P. After that you have to chmod +x /etc/rc.d/rc.i2p and start
|
||||
# I2P service with /etc/rc.d/rc.i2p start.
|
||||
#
|
||||
# Now tell your browser to user this proxy: localhost on port 4444 and open
|
||||
# this page: http://localhost:7657/index.jsp
|
||||
#
|
||||
# Here you can configure I2P, watch network status and navigate anonimously.
|
||||
# It's suggested to subscribe to various dns host, like i2host.i2p
|
||||
# For any additional information, visit i2host.i2p and forum.i2p
|
||||
#
|
||||
|
||||
CWD=$(pwd)
|
||||
TMP=/tmp
|
||||
PKG=/$TMP/package-base-i2p
|
||||
NAME=i2p-base
|
||||
VERSION=0.0.4
|
||||
BUILD=1sponge
|
||||
ARCH=noarch
|
||||
INSTALL_DIR=opt
|
||||
|
||||
# Less than slackware 13?
|
||||
SLKPLT=$(cat /etc/slackware-version | sed -re "s/(Slackware )([0-9]*)(.*)/\2/")
|
||||
if [ $SLKPLT -lt 13 ] ; then
|
||||
EXT=tgz
|
||||
else
|
||||
EXT=txz
|
||||
fi
|
||||
|
||||
rm -rf $PKG
|
||||
mkdir -p $PKG
|
||||
cd $PKG
|
||||
chown -R root:root .
|
||||
|
||||
mkdir -p $PKG/etc/rc.d
|
||||
mkdir -p $PKG/install
|
||||
sed "s|directory|/$INSTALL_DIR/i2p/i2prouter|g" "$CWD/rc.i2p_def" > $PKG/etc/rc.d/rc.i2p.new
|
||||
chmod 644 $PKG/etc/rc.d/rc.i2p.new
|
||||
cat "$CWD/doinst.sh" > $PKG/install/doinst.sh
|
||||
cat "$CWD/slack-desc" > $PKG/install/slack-desc
|
||||
|
||||
cd $PKG
|
||||
#
|
||||
# Not really that important to exec this
|
||||
# as there aren't any deps we don't know.
|
||||
#
|
||||
# requiredbuilder -v -y -s $CWD $PKG
|
||||
#
|
||||
cat "$CWD/slack-required" > $PKG/install/slack-required
|
||||
makepkg -l y -c n $CWD/${NAME}-$VERSION-$ARCH-$BUILD.$EXT
|
@ -1,68 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Start/stop i2p service.
|
||||
|
||||
i2p_start() {
|
||||
# Check if router is up first!
|
||||
/bin/su - -c "/bin/bash -l -c '( export PATH=\"$PATH:/usr/lib/java/bin:/usr/lib/java/jre/bin\" ; directory status )'" > /dev/null
|
||||
if [ $? -eq 0 ] ; then {
|
||||
# I2p is already running, so tell the user.
|
||||
echo "I2P is already running..."
|
||||
i2p_status
|
||||
}
|
||||
else
|
||||
{
|
||||
# Just in-case there are leftover junk in /tmp...
|
||||
rm -Rf `grep /tmp/hsperfdata_root/* -le i2p` /tmp/i2p-*.tmp /tmp/router.ping
|
||||
# Now that all junk is cleaned up, start.
|
||||
/bin/su - -c "/bin/bash -l -c '( export PATH=\"$PATH:/usr/lib/java/bin:/usr/lib/java/jre/bin\" ; directory start )'"
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
i2p_stop() {
|
||||
/bin/su - -c "/bin/bash -l -c '( export PATH=\"$PATH:/usr/lib/java/bin:/usr/lib/java/jre/bin\" ; directory stop )'"
|
||||
rm -Rf `grep /tmp/hsperfdata_root/* -le i2p` /tmp/i2p-*.tmp /tmp/router.ping
|
||||
}
|
||||
|
||||
i2p_restart() {
|
||||
# We want a FULL cycle here, not the wrappers idea of this!
|
||||
i2p_stop
|
||||
i2p_start
|
||||
}
|
||||
|
||||
i2p_status() {
|
||||
/bin/su - -c "/bin/bash -l -c '( export PATH=\"$PATH:/usr/lib/java/bin:/usr/lib/java/jre/bin\" ; directory status )'"
|
||||
}
|
||||
|
||||
i2p_console() {
|
||||
/bin/su - -c "/bin/bash -l -c '( export PATH=\"$PATH:/usr/lib/java/bin:/usr/lib/java/jre/bin\" ; directory console )'"
|
||||
}
|
||||
|
||||
i2p_dump() {
|
||||
/bin/su - -c "/bin/bash -l -c '( export PATH=\"$PATH:/usr/lib/java/bin:/usr/lib/java/jre/bin\" ; directory dump )'"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
'start')
|
||||
i2p_start
|
||||
;;
|
||||
'stop')
|
||||
i2p_stop
|
||||
;;
|
||||
'restart')
|
||||
i2p_restart
|
||||
;;
|
||||
'status')
|
||||
i2p_status
|
||||
;;
|
||||
'console')
|
||||
i2p_console
|
||||
;;
|
||||
'dump')
|
||||
i2p_dump
|
||||
;;
|
||||
*)
|
||||
echo "usage $0 start|stop|restart|status|console|dump"
|
||||
;;
|
||||
esac
|
||||
|
@ -1,10 +0,0 @@
|
||||
An rc file called rc.i2p has been placed into the /etc/rc.d directory.
|
||||
If you want to change installation dir, change the variable INSTALL_DIR
|
||||
on base-i2p.SlackBuild and rebuild the package. You also will need to do the
|
||||
same for the i2p package.
|
||||
|
||||
The install script will insert everything needed into /etc/rc.d/rc.local and
|
||||
into /etc/rc.d/rc.local_shutdown automatically.
|
||||
|
||||
If you want to start I2P at boot you have to chmod +x /etc/rc.d/rc.i2p
|
||||
|
@ -1,19 +0,0 @@
|
||||
# HOW TO EDIT THIS FILE:
|
||||
# The "handy ruler" below makes it easier to edit a package description. Line
|
||||
# up the first '|' above the ':' following the base package name, and the '|' on
|
||||
# the right side marks the last column you can put a character in. You must make
|
||||
# exactly 11 lines for the formatting to be correct. It's also customary to
|
||||
# leave one space after the ':'.
|
||||
|
||||
|-----handy-ruler------------------------------------------------------|
|
||||
base-i2p: base-i2p (I2P anonymizing network base config files)
|
||||
base-i2p:
|
||||
base-i2p: I2P is an anonymizing network, offering a simple layer that
|
||||
base-i2p: identity-sensitive applications can use to securely communicate. All
|
||||
base-i2p: data is wrapped with several layers of encryption, and the network is
|
||||
base-i2p: both distributed and dynamic, with no trusted parties.
|
||||
base-i2p: Many applications are available that interface with I2P, including
|
||||
base-i2p: mail, peer-peer file sharing, IRC chat, and others.
|
||||
base-i2p:
|
||||
base-i2p: This package provides the startup files.
|
||||
base-i2p:
|
@ -1 +0,0 @@
|
||||
bash >= 3.1.017
|
89
Slackware/i2p/README
Normal file
89
Slackware/i2p/README
Normal file
@ -0,0 +1,89 @@
|
||||
The I2P package will be installed to /opt/i2p.
|
||||
|
||||
To install to another location, set the variable INSTALL_DIR in i2p.SlackBuild
|
||||
and rebuild the package.
|
||||
|
||||
|
||||
Installing and Upgrading:
|
||||
=========================
|
||||
|
||||
I2P has an auto-update function but generally speaking packages are managed by
|
||||
packaging systems. Changing a package's files outside of the package system can
|
||||
cause problems. To upgrade when there's a new I2P release, retrieve the new
|
||||
source, re-run the SlackBuild, then use upgradepkg to update.
|
||||
|
||||
To ignore all this and upgrade "in-network", simply change the permissions of the installation
|
||||
directory (/opt/i2p by default). Something like the following would suffice:
|
||||
|
||||
chown -R USERNAME /opt/i2p
|
||||
|
||||
|
||||
Starting and using I2P
|
||||
======================
|
||||
|
||||
Using the initscript
|
||||
--------------------
|
||||
|
||||
To start I2P at boot, set the executable bit on /etc/rc.d/rc.i2p, add this
|
||||
script to rc.local*, and set the variable "RUN_AS_USER" in /etc/rc.d/rc.i2p.
|
||||
|
||||
Optionally, create a new user/group with limited rights to run I2P.
|
||||
|
||||
Something like the following would work to start I2P at system boot, running under the
|
||||
"i2p" account:
|
||||
|
||||
echo '[ -x /etc/rc.d/rc.i2p ] && /etc/rc.d/rc.i2p start' >> /etc/rc.d/rc.local
|
||||
echo '[ -x /etc/rc.d/rc.i2p ] && /etc/rc.d/rc.i2p stop' >> /etc/.rc.d/rc.local_shutdown
|
||||
sed -i .bak 's/^.*\(RUN_AS_USER\)=.*/\1=i2p/' /etc/rc.d/rc.i2p
|
||||
chmod 755 /etc/rc.d/rc.i2p
|
||||
|
||||
Unless running as a user named "i2psvc", I2P's config directory defaults to
|
||||
$HOME/.i2p. In the case of the "i2psvc" user, the default config directory is
|
||||
/var/lib/i2p/i2p-config.
|
||||
|
||||
It should not need to be said that using the "root" account is not recommended.
|
||||
|
||||
When running using the initscript, I2P will write its logs to /var/log/i2p.
|
||||
|
||||
Starting I2P "on-demand"
|
||||
------------------------
|
||||
|
||||
As with a normal installation, I2P can be started with "i2prouter start". The
|
||||
i2prouter and eepget scripts hve been installed to /usr/bin so they'll be
|
||||
accessible in the default system PATH.
|
||||
|
||||
When running using the i2prouter script, I2P will write its logs to $HOME/.i2p.
|
||||
|
||||
|
||||
Configuring your browser
|
||||
------------------------
|
||||
|
||||
In order to access eepSites (I2P-internal web sites) your web browser needs to
|
||||
be configured. Set the HTTP Proxy to 127.0.0.1 and port 4444. For more information, see
|
||||
https://geti2p.net/en/about/browser-config
|
||||
|
||||
The I2P router console is reachable at http://127.0.0.1:7657.
|
||||
|
||||
|
||||
Addressbook subscriptions
|
||||
-------------------------
|
||||
|
||||
Please see the FAQs at http://i2p-projekt.i2p/faq or https://geti2p.net/faq for information about
|
||||
the various addressbook services.
|
||||
|
||||
|
||||
Chatting on IRC
|
||||
---------------
|
||||
|
||||
I2P comes preconfigured with a tunnel pointing to the I2P-only IRC network,
|
||||
Irc2P. Signing on is easy, just connect to 127.0.0.1 on port 6668. Do not
|
||||
configure a proxy in your IRC client.
|
||||
|
||||
|
||||
Additional information
|
||||
======================
|
||||
|
||||
Within I2P: http://i2p-projekt.i2p/, http://forum.i2p/, http://zzz.i2p/, http://trac.i2p2.i2p/
|
||||
On the Internet: https://geti2p.net/, https://trac.i2p2.de
|
||||
Manpages: i2prouter(1), eepget(1)
|
||||
|
@ -2,7 +2,7 @@
|
||||
<project basedir="." default="slackpkg">
|
||||
<target name="slackpkg">
|
||||
<echo message="Building Slackware package." />
|
||||
<exec executable="./i2p.SlackBuild">
|
||||
</exec>
|
||||
<chmod file="./i2p.Slackbuild" perm="755" />
|
||||
<exec executable="./i2p.SlackBuild" failifexecutionfails="true" />
|
||||
</target>
|
||||
</project>
|
||||
|
@ -1,72 +1,60 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
# Abort on error or unset variables
|
||||
set -e
|
||||
set -u
|
||||
|
||||
# This is changed by i2p.SlackBuild
|
||||
INST_DIR=directory
|
||||
PKGNAME="%pkg"
|
||||
|
||||
( cd install
|
||||
config() {
|
||||
NEW="$1"
|
||||
OLD="$(dirname $NEW)/$(basename $NEW .new)"
|
||||
if [ ! -r $NEW ]; then
|
||||
# If we get here there's a flaw in the packaging. We'll just return so that
|
||||
# we don't emit a spurious error for the user. (It's not the user's problem).
|
||||
return
|
||||
fi
|
||||
|
||||
echo
|
||||
for i in *.config ; {
|
||||
if [ -f $INST_DIR/$i ] ; then
|
||||
echo "Please check ${INST_DIR}${i}, as there is a new version."
|
||||
cp $i $INST_DIR/$i.new
|
||||
else
|
||||
cp $i $INST_DIR/$i
|
||||
fi
|
||||
# If this file doesn't exist yet, drop the .new extension.
|
||||
if [ ! -r $OLD ]; then
|
||||
mv $NEW $OLD
|
||||
return
|
||||
elif [ "$(md5sum $OLD | cut -d' ' -f1)" = "$(md5sum $NEW | cut -d' ' -f1)" ]; then
|
||||
# If there are no differences in the files, remove the file with the .new extension.
|
||||
rm $NEW
|
||||
return
|
||||
fi
|
||||
# Alert the admin if they differ, but let's not be terribly obnoxious about it.
|
||||
echo "WARNING: The files $OLD and $NEW differ." >&2
|
||||
}
|
||||
|
||||
)
|
||||
# Unlike previous versions of the package, we install i2prouter and eepget to /usr/bin
|
||||
# to make them available within the system PATH.
|
||||
|
||||
( cd $INST_DIR
|
||||
if [ -f blocklist.txt ] ; then
|
||||
echo "Please check ${INST_DIR}blocklist.txt, as there is a new version."
|
||||
else
|
||||
mv blocklist.txt.new blocklist.txt
|
||||
fi
|
||||
)
|
||||
# Users might still want to /opt/i2p/i2prouter or /opt/i2p/eepget so we'll create symlinks
|
||||
# in the installation directory.
|
||||
ln -sf /usr/bin/eepget $INST_DIR
|
||||
ln -sf /usr/bin/i2prouter $INST_DIR
|
||||
(cd /usr/doc/$PKGNAME; ln -sf $INST_DIR/history.txt changelog)
|
||||
|
||||
( cd $INST_DIR/eepsite
|
||||
if [ -f jetty.xml ] ; then
|
||||
echo "Please check ${INST_DIR}/eepsite, as there are new files."
|
||||
else
|
||||
find $PKG/$INSTALL_DIR/i2p -name "*.xml.new" -exec sh -c 'mv "$0" "${0/.new}"' {} \;
|
||||
fi
|
||||
)
|
||||
|
||||
( cd $INST_DIR/eepsite/docroot
|
||||
if [ -f index.html ] ; then
|
||||
rm index.html.new
|
||||
else
|
||||
mv index.html.new index.html
|
||||
fi
|
||||
if [ -f favicon.ico ] ; then
|
||||
rm favicon.ico.new
|
||||
else
|
||||
mv favicon.ico.new favicon.ico
|
||||
fi
|
||||
)
|
||||
|
||||
echo
|
||||
echo "FINISHING I2P INSTALLATION. PLEASE WAIT."
|
||||
|
||||
cd $INST_DIR
|
||||
|
||||
|
||||
|
||||
OS_ARCH=`uname -m`
|
||||
X86_64=`echo "$OS_ARCH" | grep x86_64`
|
||||
if [ "X$X86_64" = "X" ]; then
|
||||
wrapperpath="./lib/wrapper/linux"
|
||||
if $(uname -m | grep -q '64'); then
|
||||
(cd $INST_DIR; ln -sf i2psvc-linux-x86-64 i2psvc)
|
||||
else
|
||||
wrapperpath="./lib/wrapper/linux64"
|
||||
(cd $INST_DIR; ln -sf i2psvc-linux-x86-32 i2psvc)
|
||||
fi
|
||||
cp $wrapperpath/libwrapper.so ./lib/
|
||||
cp $wrapperpath/wrapper.jar ./lib/
|
||||
cp $wrapperpath/i2psvc .
|
||||
rm -rf ./lib/wrapper
|
||||
chmod 744 ./i2psvc
|
||||
|
||||
echo
|
||||
echo "Installation finished."
|
||||
echo
|
||||
config /etc/rc.d/rc.i2p.new
|
||||
config $INST_DIR/wrapper.config.new
|
||||
|
||||
exit
|
||||
if [ -e /var/log/packages/i2p-base* ]; then
|
||||
echo "Warning: This package supercedes the 'i2p-base' package." >&2
|
||||
echo
|
||||
echo "You may want to 'removepkg i2p-base'" >&2
|
||||
echo "and check the contents of /etc/rc.d/rc.local*" >&2
|
||||
echo "for correctness" >&2
|
||||
fi
|
||||
|
||||
# Remove extraneous 'sh' from sponge's set-up
|
||||
sed -i 's|sh /etc/rc\.d/rc\.i2p|/etc/rc.d/rc.i2p|g' /etc/rc.d/rc.local*
|
||||
|
@ -3,135 +3,124 @@
|
||||
# Heavily based on the Slackware 12.2 SlackBuild
|
||||
# Slackware build script for I2P
|
||||
#
|
||||
# PLEASE READ THIS:
|
||||
# Probably you will never have to update I2P packages with upgradepkg,
|
||||
# just because I2P has an auto-update function.
|
||||
# Really you should not ever use any "upgrade" method.
|
||||
#
|
||||
# The correct way to upgrade is to:
|
||||
# 1: install the upgrade
|
||||
# 2: remove the old package
|
||||
#
|
||||
# It is a terrible shame that upgradepkg doesn't do this, infact,
|
||||
# it would actually be the correct way for *any* package!
|
||||
# Packages are generally prohibited from being updated outside
|
||||
# of the package manager; this I2P SlackBuild is no different.
|
||||
#
|
||||
# If you'd like to use the I2P "in-network" updates anyway, you'll need to
|
||||
# grant the user that I2P will run as write permission to the installation directory
|
||||
# (/opt/i2p by default).
|
||||
#
|
||||
# For safety's sake, a user's I2P config files will *never* be overwritten by any upgrade method.
|
||||
# In the future this SlackBuild may alert when a default config file is updated.
|
||||
##
|
||||
|
||||
BUILD=1sponge
|
||||
# Make sure makepkg and friends can be found
|
||||
PATH=$PATH:/sbin
|
||||
|
||||
# abort on error and unset variables (same as set -e and set -u, respectively)
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
if [ $(id -ur) -ne 0 ]; then
|
||||
echo "ERROR: SlackBuilds require root access." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUILD=1kytv
|
||||
# INSTALL_DIR is referenced from /, don't prefix it with a '/'
|
||||
INSTALL_DIR=opt
|
||||
NAME=i2p
|
||||
ARCH=noarch
|
||||
|
||||
# Less than slackware 13?
|
||||
SLKPLT=$(cat /etc/slackware-version | sed -re "s/(Slackware )([0-9]*)(.*)/\2/")
|
||||
if [ $SLKPLT -lt 13 ] ; then
|
||||
EXT=tgz
|
||||
else
|
||||
EXT=txz
|
||||
fi
|
||||
|
||||
|
||||
#
|
||||
# This mess is here due to the totally moronic way i2p does versioning.
|
||||
# We correct it here.
|
||||
#
|
||||
ROUTER=$(echo -ne "_")$(cat ../../router/java/src/net/i2p/router/RouterVersion.java | grep -e "public final static long BUILD" | cut -f2 -d"=" | cut -f1 -d";" | sed -re "s/ //g")
|
||||
if [ "$ROUTER" == "_" ] ; then
|
||||
ROUTER="_0"
|
||||
fi
|
||||
|
||||
#
|
||||
# That was the easy one, now for the tough one.
|
||||
#
|
||||
|
||||
CORE=$(cat ../../core/java/src/net/i2p/CoreVersion.java | grep -e "public final static String VERSION" | cut -f2 -d'"' | sed -re "s/ //g")
|
||||
CORE1=$(echo -n $CORE.x.x | sed -re "s/(.*)\.(.*)\.(.*)\.(.*)/\1/")
|
||||
CORE2=$(echo -n $CORE.x | sed -re "s/(.*)\.(.*)\.(.*)\.(.*)/\1/")
|
||||
|
||||
if [ "$CORE.x.x" == "$CORE1" ] ; then
|
||||
CORE=$(echo -ne $CORE".0.0")
|
||||
fi
|
||||
if [ "$CORE.x" == "$CORE2" ] ; then
|
||||
CORE=$(echo -ne $CORE".0")
|
||||
fi
|
||||
|
||||
VERSION=$(echo $CORE$ROUTER)
|
||||
#
|
||||
# Whew!
|
||||
# OK, let's build i2p
|
||||
#
|
||||
|
||||
CWD=$(pwd)
|
||||
CWD=$(readlink -m $(dirname $0))
|
||||
I2PSRC=$(readlink -m $CWD/../../)
|
||||
TMP=/tmp
|
||||
|
||||
PKG=$TMP/package-i2p
|
||||
rm -rf $PKG
|
||||
mkdir -p $PKG
|
||||
|
||||
cd $CWD/../../
|
||||
if [ -e "/etc/slackware-version" ]; then
|
||||
# Older than Slackware 13?
|
||||
SLACKVER=$(sed -e "s/Slackware\s\+\([0-9]\+\)\.\?\([0-9]\+\)\?/\1/" /etc/slackware-version)
|
||||
if [ $SLACKVER -lt 13 ] ; then
|
||||
EXT=tgz
|
||||
else
|
||||
EXT=txz
|
||||
fi
|
||||
else
|
||||
echo "ERROR: This script is only intended for use on Slackware systems.">&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract version strings
|
||||
I2PBUILD=$(sed -e '/^.\+long\s\+BUILD/!d' -e 's/^.\+long\s\+BUILD\s\+=\s\+\([0-9]\+\);/\1/' $I2PSRC/router/java/src/net/i2p/router/RouterVersion.java)
|
||||
# Thanks to user "ihavei2p" for the second awk statement
|
||||
# If the version is x.y, it'll be set to x.y.0. Otherwise the version string will be unchanged
|
||||
CORE=$(awk -F'"' '/static\s+String\s+VERSION/{print $2}' $I2PSRC/core/java/src/net/i2p/CoreVersion.java | \
|
||||
awk -F. '{ if (NF > 3) { print; exit } else if ($3 == "") { $3=0; print $1"."$2"."$3 } else print }')
|
||||
VERSION="${CORE}_${I2PBUILD}"
|
||||
|
||||
[ -d $PKG ] && rm -rf $PKG
|
||||
mkdir -p $PKG/$INSTALL_DIR $PKG/install
|
||||
|
||||
cd "$I2PSRC"
|
||||
ant distclean
|
||||
#ant dist
|
||||
ant tarball
|
||||
ant jbigi-linux-x86-only preppkg-unix
|
||||
|
||||
tar xjvf i2p.tar.bz2 -C $TMP
|
||||
chown -R root:root $I2PSRC/pkg-temp
|
||||
cp -a $I2PSRC/pkg-temp $PKG/$INSTALL_DIR/i2p
|
||||
|
||||
cd $TMP/i2p
|
||||
chown -R root:root .
|
||||
|
||||
mkdir -p $PKG/$INSTALL_DIR/
|
||||
cp -a ../i2p $PKG/$INSTALL_DIR/
|
||||
|
||||
mkdir -p $PKG/install
|
||||
|
||||
#############################################################################
|
||||
# Preconfigureation to make package smaller, and...
|
||||
# we keep as much as reasonable in the installation directory.
|
||||
# This makes the install map fairly well to the standard installation.
|
||||
# It also makes it easier to find the log and pid files!
|
||||
#############################################################################
|
||||
# $INSTALL_DIR is used by this SlackBuild.
|
||||
# [%$]INSTALL_PATH , [%$]SYSTEM_java_io_tmpdir, and [%$]USER_HOME have the correct paths set
|
||||
# by the IzPack installer.
|
||||
cd $PKG/$INSTALL_DIR/i2p
|
||||
for file in wrapper.config eepget i2prouter runplain.sh; do
|
||||
sed -i "s|[%$]INSTALL_PATH|/$INSTALL_DIR/i2p|g;s|[$%]SYSTEM_java_io_tmpdir|$TMP|g;s/[%$]USER_HOME/\$HOME/g" $file
|
||||
done
|
||||
mv wrapper.config wrapper.config.new
|
||||
|
||||
# wrapper.config $INSTALL_PATH and $SYSTEM_java_io_tmpdir
|
||||
sed "s|\$INSTALL_PATH|/$INSTALL_DIR/i2p|g" wrapper.config > a
|
||||
sed "s|\$SYSTEM_java_io_tmpdir|/$INSTALL_DIR/i2p|g" a > wrapper.config
|
||||
# eepget %INSTALL_PATH
|
||||
sed "s|\$INSTALL_PATH|/$INSTALL_DIR/i2p|g" eepget > a
|
||||
rm eepget
|
||||
mv a eepget
|
||||
# runplain.sh %INSTALL_PATH and %SYSTEM_java_io_tmpdir
|
||||
sed "s|%INSTALL_PATH|/$INSTALL_DIR/i2p|g" runplain.sh > a
|
||||
sed "s|%SYSTEM_java_io_tmpdir|/$INSTALL_DIR/i2p|g" a > runplain.sh
|
||||
# i2prouter %INSTALL_PATH and %SYSTEM_java_io_tmpdir
|
||||
sed "s|%INSTALL_PATH|/$INSTALL_DIR/i2p|g" i2prouter > a
|
||||
rm i2prouter
|
||||
mv a i2prouter
|
||||
sed "s|%SYSTEM_java_io_tmpdir|/$INSTALL_DIR/i2p|g" i2prouter > a
|
||||
sed "s|#ALLOW_ROOT=true|ALLOW_ROOT=true|g" a > i2prouter
|
||||
install -d $PKG/usr/bin
|
||||
install -d $PKG/usr/doc/$NAME-$VERSION
|
||||
install -d $PKG/etc/rc.d
|
||||
mv licenses LICENSE.txt -t $PKG/usr/doc/$NAME-$VERSION
|
||||
|
||||
chmod 744 ./i2prouter
|
||||
chmod 744 ./osid
|
||||
chmod 744 ./runplain.sh
|
||||
chmod 744 ./eepget
|
||||
chmod 744 ./scripts/i2pbench.sh
|
||||
chmod 744 ./scripts/i2ptest.sh
|
||||
rm -Rf ./lib/*.dll ./*.bat ./*.exe ./installer ./icons ./a postinstall.sh
|
||||
# runplain.sh will live in the installation directory. eepget and i2prouter will go to /usr/bin
|
||||
# with symlinks in INST_DIR (created in doinst.sh)
|
||||
install -m755 i2prouter $PKG/usr/bin
|
||||
install -m755 eepget $PKG/usr/bin
|
||||
chmod 755 ./runplain.sh
|
||||
|
||||
mv $PKG/$INSTALL_DIR/i2p/*.config $PKG/install
|
||||
mv $PKG/$INSTALL_DIR/i2p/blocklist.txt $PKG/$INSTALL_DIR/i2p/blocklist.txt.new
|
||||
find $PKG/$INSTALL_DIR/i2p -name "*.xml" -exec mv {} {}.new \;
|
||||
mv $PKG/$INSTALL_DIR/i2p/eepsite/docroot/index.html $PKG/$INSTALL_DIR/i2p/eepsite/docroot/index.html.new
|
||||
mv $PKG/$INSTALL_DIR/i2p/eepsite/docroot/favicon.ico $PKG/$INSTALL_DIR/i2p/eepsite/docroot/favicon.ico.new
|
||||
sed "s|directory|/$INSTALL_DIR/i2p/|g" $CWD/doinst.sh > $PKG/install/doinst.sh
|
||||
cat $CWD/slack-desc > $PKG/install/slack-desc
|
||||
if [ $INSTALL_DIR != 'opt' ]; then
|
||||
sed "s|\(The I2P package\)\s\+will be\s\+\(installed to\).+|\1 has been \2 $INSTALL_DIR/i2p|g" $CWD/README > $PKG/usr/doc/$NAME-$VERSION/README
|
||||
else
|
||||
sed "s|will be installed|has been installed|" $CWD/README > $PKG/usr/doc/$NAME-$VERSION/README
|
||||
fi
|
||||
|
||||
install -d $PKG/usr/man/man1
|
||||
gzip -9 man/*.1
|
||||
install -m644 man/*.1.gz $PKG/usr/man/man1
|
||||
rm -rf ./man
|
||||
|
||||
# We install all x86 wrapper binaries.
|
||||
# The i2prouter script will try to determine the OS (linux), the bits (32 VS 64) and should be able
|
||||
# to figure out the correct wrapper binary to use.
|
||||
|
||||
# However: In case the i2prouter script's detection fails, "$INST_DIR/i2psvc" will point to
|
||||
# what 'we' think the correct binary is.
|
||||
#
|
||||
# A good reason for installing all binaries: in case the user, for whatever reason, switches from an
|
||||
# x64 JRE to an x86 JRE, I2P should continue to work without needing to be reinstalled.
|
||||
install -m755 $I2PSRC/installer/lib/wrapper/linux/i2psvc ./i2psvc-linux-x86-32
|
||||
install -m644 $I2PSRC/installer/lib/wrapper/linux/libwrapper.so ./lib/libwrapper-linux-x86-32.so
|
||||
install -m755 $I2PSRC/installer/lib/wrapper/linux64/i2psvc ./i2psvc-linux-x86-64
|
||||
install -m644 $I2PSRC/installer/lib/wrapper/linux64/libwrapper.so ./lib/libwrapper-linux-x86-64.so
|
||||
install -m644 $I2PSRC/installer/lib/wrapper/all/wrapper.jar ./lib/wrapper.jar
|
||||
install -m644 $I2PSRC/build/jbigi.jar $PKG/$INSTALL_DIR/i2p/lib/jbigi.jar
|
||||
|
||||
rm -f ./postinstall.sh ./osid ./INSTALL-*.txt
|
||||
sed "s|directory|/$INSTALL_DIR/i2p|" $CWD/doinst.sh > $PKG/install/doinst.sh
|
||||
sed -i "s|%pkg|$NAME-$VERSION|" $PKG/install/doinst.sh
|
||||
sed "s|%INST_DIR|/$INSTALL_DIR/i2p|" $CWD/rc.i2p> $PKG/etc/rc.d/rc.i2p.new
|
||||
cp $CWD/slack-desc $PKG/install/slack-desc
|
||||
|
||||
cd $PKG
|
||||
#
|
||||
# requiredbuilder messes up REALLY bad, and thinks java is perl?!
|
||||
# It also did not catch the shell requirements! BOOOOOOOOOOO! HISSSSSSSS!
|
||||
#
|
||||
# requiredbuilder -v -y -s $CWD $PKG
|
||||
#
|
||||
cat $CWD/slack-required > $PKG/install/slack-required
|
||||
cp $CWD/slack-required $PKG/install/slack-required
|
||||
makepkg -l y -c n $CWD/${NAME}-$VERSION-$ARCH-$BUILD.$EXT
|
||||
|
185
Slackware/i2p/rc.i2p
Normal file
185
Slackware/i2p/rc.i2p
Normal file
@ -0,0 +1,185 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Abort on errors
|
||||
set -e
|
||||
|
||||
##
|
||||
# This *must* be configured. Set this to the user that will run I2P.
|
||||
# Note: If you don't want I2P to start automatically at boot,
|
||||
# use "i2prouter start" as a non-root user to start I2P.
|
||||
#RUN_AS_USER=
|
||||
##
|
||||
|
||||
##
|
||||
# Set the locale as desired.
|
||||
# Note: this is not the same as the language shown in the I2P router console.
|
||||
# This affects the locale used in wrapper.log. For best results, use a
|
||||
# unicode enabled locale. This is especially important for foreign language torrents.
|
||||
#
|
||||
# If not set the user's configured locale will be used.
|
||||
#RCLOCALE=
|
||||
##
|
||||
|
||||
#####################################################
|
||||
# Nothing below this point should need to be edited #
|
||||
#####################################################
|
||||
# %INST_DIR is set by i2p.SlackBuild
|
||||
INSTALL_DIR="%INST_DIR"
|
||||
# Make sure the package is installed and that the wrapper can be found
|
||||
[ -d $INSTALL_DIR ] && [ -x $INSTALL_DIR/i2psvc ] || (echo "The I2P package is not installed" >&2 ; exit 1)
|
||||
|
||||
if [ -z $RUN_AS_USER ]; then
|
||||
echo "ERROR: RUN_AS_USER not configured in $0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $(id -ur) -ne 0 ]; then
|
||||
echo 'ERROR: You must be root to start this service.' >&2
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z $RCLOCALE ]; then
|
||||
if [ ! $(locale -a |grep -q "en_US\.utf8") ]; then
|
||||
RCLOCALE="en_US.utf8"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Abort this script if any referenced variables haven't been set
|
||||
set -u
|
||||
|
||||
if $(uname -m |grep -q '64'); then
|
||||
BITS='64'
|
||||
else
|
||||
BITS=''
|
||||
fi
|
||||
|
||||
PATH="$PATH:/usr/lib$BITS/java/bin:/usr/lib$BITS/java/jre/bin"
|
||||
RUN=/var/run/i2p
|
||||
PIDFILE="$RUN/i2p.pid"
|
||||
WRAPPER_CONF="$INSTALL_DIR/wrapper.config"
|
||||
WRAPPERLOG=/var/log/i2p/wrapper.log
|
||||
I2PTEMP="/tmp/i2p-daemon"
|
||||
DESC="The I2P daemon"
|
||||
JAVABINARY=$(awk -F'=' '/^ *wrapper\.java\.command/{print $2}' "$WRAPPER_CONF")
|
||||
|
||||
if [ ! $(which $JAVABINARY 2>/dev/null) ]; then
|
||||
for rc in /etc/profile.d/*jdk*.sh /etc/profile.d/*java*.sh; do
|
||||
[ -r $rc ] && . $rc
|
||||
done
|
||||
if [ ! $(which $JAVABINARY 2>/dev/null) ]; then
|
||||
echo "ERROR: Cannot find java. Please set the path to java in $WRAPPER_CONF" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
JAVA=$(which $JAVABINARY 2>/dev/null)
|
||||
|
||||
I2P_ARGS="$WRAPPER_CONF \
|
||||
wrapper.java.additional.1=-DloggerFilenameOverride=/var/log/i2p/log-router-@.txt \
|
||||
wrapper.java.additional.10=-Dwrapper.logfile=$WRAPPERLOG \
|
||||
wrapper.java.additional.11=-Di2p.dir.pid=$RUN \
|
||||
wrapper.java.additional.12=-Di2p.dir.temp=$I2PTEMP \
|
||||
wrapper.java.command=$JAVA \
|
||||
wrapper.logfile=$WRAPPERLOG \
|
||||
wrapper.pidfile=$PIDFILE \
|
||||
wrapper.daemonize=TRUE"
|
||||
|
||||
LC_ALL=$RCLOCALE
|
||||
LANG=$RCLOCALE
|
||||
export LC_ALL LANG
|
||||
|
||||
is_running() {
|
||||
if [ -r $PIDFILE ]; then
|
||||
PID="$(cat ${PIDFILE})" 2>/dev/null 2>&1
|
||||
if ! kill -0 $PID >/dev/null 2>&1; then
|
||||
rm "$PIDFILE"
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
start() {
|
||||
if is_running; then
|
||||
echo "ERROR: $DESC is already running." >&2
|
||||
fi
|
||||
|
||||
for DIR in $RUN $I2PTEMP; do
|
||||
test -d $DIR && rm -rf $DIR
|
||||
mkdir -p $DIR
|
||||
chown -R $RUN_AS_USER $DIR
|
||||
done
|
||||
[ -d /var/log/i2p ] || mkdir /var/log/i2p
|
||||
chown -R $RUN_AS_USER /var/log/i2p
|
||||
|
||||
echo -n "Starting $DESC..."
|
||||
TZ=UTC su $RUN_AS_USER -c "$INSTALL_DIR/i2psvc $I2P_ARGS"
|
||||
is_running
|
||||
echo "[pid: $PID]"
|
||||
}
|
||||
|
||||
stop(){
|
||||
if is_running; then
|
||||
echo -n "Stopping $DESC [pid: $PID] (this could take a while)."
|
||||
kill "$PID" >/dev/null 2>&1
|
||||
while kill -0 "$PID" > /dev/null 2>&1; do
|
||||
echo -n .
|
||||
sleep 1
|
||||
done
|
||||
rm -rf "$RUN" "$I2PTEMP"
|
||||
echo done.
|
||||
return 0
|
||||
else
|
||||
echo "$DESC is not running." >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Unset +u to allow the 'usage' text to be displayed
|
||||
set +u
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
status)
|
||||
if is_running; then
|
||||
echo "$DESC is running [pid: $PID]" >&2
|
||||
else
|
||||
echo "$DESC is not running." >&2
|
||||
fi
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
graceful)
|
||||
if is_running; then
|
||||
kill -HUP $PID
|
||||
echo "Graceful shutdown of $DESC initiated." >&2
|
||||
echo "This may take up to 11 minutes." >&2
|
||||
fi
|
||||
;;
|
||||
dump)
|
||||
if is_running; then
|
||||
kill -3 $PID
|
||||
echo "Threads dumped to $WRAPPERLOG" >&2
|
||||
else
|
||||
echo "$DESC is not running." >&2
|
||||
fi
|
||||
;;
|
||||
restart)
|
||||
if is_running; then
|
||||
stop
|
||||
start
|
||||
else
|
||||
echo "$DESC is not running." >&2
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 start|stop|status|restart|graceful|dump"
|
||||
;;
|
||||
esac
|
@ -1,47 +0,0 @@
|
||||
Building:
|
||||
The i2p package will be installed in /opt/i2p
|
||||
|
||||
If you want to change installation dir, change the variable INSTALL_DIR
|
||||
on i2p.SlackBuild and rebuild the package. You will also need to do the same
|
||||
in the base-i2p package.
|
||||
|
||||
Installation and Upgrade:
|
||||
Probably you will never have to update i2p packages. However if you do,
|
||||
be sure to installpkg first, then removepkg or custom config files can
|
||||
be lost with upgradepkg. I2P has an auto-update function. However using
|
||||
installpkg then removepkg lowers the demand on the I2P network as a
|
||||
whole, and is by far faster.
|
||||
|
||||
After installpkg command, doinst.sh will execute a postinstallation script
|
||||
needed by I2P. Be sure to also install the base-i2p package.
|
||||
|
||||
Optional:
|
||||
|
||||
chmod +x /etc/rc.d/rc.i2p only if you want it to start on boot and stop on
|
||||
shutdown.
|
||||
|
||||
How to start I2P:
|
||||
|
||||
Start I2P service with-
|
||||
sh /etc/rc.d/rc.i2p start
|
||||
|
||||
Now tell your browser to user this proxy: localhost on port 4444 and open
|
||||
this page: http://localhost:7657/index.jsp
|
||||
Here you can configure I2P, watch network status and navigate anonimously.
|
||||
It's suggested to subscribe to various addressbook hosts so that you can
|
||||
get to the many available eepsites and other service on I2P. These are not
|
||||
set up by default for security reasons.
|
||||
|
||||
Please see the faqs on http://www.i2p2.i2p/ or http://www.i2p2.de/ on how
|
||||
to subscribe to the various addressbook services.
|
||||
|
||||
To stop I2P:
|
||||
/etc/rc.d/rc.i2p stop
|
||||
|
||||
|
||||
For any additional information:
|
||||
|
||||
Within I2P- http://www.i2p2.i2p/, http://forum.i2p/, http://zzz.i2p
|
||||
|
||||
Internet (not reccomended!) - http://www.i2p2.de/, http://forum.i2p2.de/
|
||||
|
@ -6,7 +6,7 @@
|
||||
# leave one space after the ':'.
|
||||
|
||||
|-----handy-ruler----------------------------------------------------------|
|
||||
i2p: i2p (an anonymizing network)
|
||||
i2p: I2P (an anonymizing network)
|
||||
i2p:
|
||||
i2p: I2P is an anonymizing network, offering a simple layer that
|
||||
i2p: identity-sensitive applications can use to securely communicate. All
|
||||
@ -14,6 +14,6 @@ i2p: data is wrapped with several layers of encryption, and the network is
|
||||
i2p: both distributed and dynamic, with no trusted parties.
|
||||
i2p: Many applications are available that interface with I2P, including
|
||||
i2p: mail, peer-peer file sharing, IRC chat, and others.
|
||||
i2p: WARNING: To upgrade installpkg FIRST _THEN_ removepkg.
|
||||
i2p: For more information, see: http://www.i2p2.de/
|
||||
i2p:
|
||||
i2p: For more information, see: https://geti2p.net/
|
||||
i2p:
|
||||
|
@ -1,4 +1 @@
|
||||
jre >= 5
|
||||
i2p-base >= 0.0.1
|
||||
bash >= 3.1.017
|
||||
|
||||
jre >= 6
|
||||
|
@ -66,7 +66,7 @@ public class Main {
|
||||
}
|
||||
|
||||
static void wrtxt(OutputStream CMDout, String s) throws IOException {
|
||||
CMDout.write(s.getBytes());
|
||||
CMDout.write(DataHelper.getUTF8(s));
|
||||
CMDout.write('\n');
|
||||
CMDout.flush();
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ public class Main {
|
||||
}
|
||||
|
||||
static void wrtxt(OutputStream CMDout, String s) throws IOException {
|
||||
CMDout.write(s.getBytes());
|
||||
CMDout.write(DataHelper.getUTF8(s));
|
||||
CMDout.write('\n');
|
||||
CMDout.flush();
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.*;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
@ -119,15 +118,16 @@ public class BOB implements Runnable, ClientApp {
|
||||
public final static String PROP_BOB_HOST = "BOB.host";
|
||||
public final static String PROP_CFG_VER = "BOB.CFG.VER";
|
||||
|
||||
/** unused when started via the ClientApp interface */
|
||||
private static BOB _bob;
|
||||
|
||||
private NamedDB database;
|
||||
private Properties props = new Properties();
|
||||
private AtomicBoolean spin = new AtomicBoolean(true);
|
||||
private final NamedDB database;
|
||||
private final Properties props = new Properties();
|
||||
private final 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 AtomicBoolean lock = new AtomicBoolean(false);
|
||||
private final AtomicBoolean lock = new AtomicBoolean(false);
|
||||
// no longer used.
|
||||
// private static int maxConnections = 0;
|
||||
|
||||
@ -143,8 +143,10 @@ public class BOB implements Runnable, ClientApp {
|
||||
* Stop BOB gracefully
|
||||
* @deprecated unused
|
||||
*/
|
||||
public static void stop() {
|
||||
_bob.shutdown(null);
|
||||
@Deprecated
|
||||
public synchronized static void stop() {
|
||||
if (_bob != null)
|
||||
_bob.shutdown(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,7 +191,7 @@ public class BOB implements Runnable, ClientApp {
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
public synchronized static void main(String[] args) {
|
||||
try {
|
||||
_bob = new BOB(I2PAppContext.getGlobalContext(), null, args);
|
||||
_bob.startup();
|
||||
@ -212,9 +214,7 @@ public class BOB implements Runnable, ClientApp {
|
||||
// Re-reading the config file in each thread is pretty damn stupid.
|
||||
String configLocation = System.getProperty(PROP_CONFIG_LOCATION, "bob.config");
|
||||
// This is here just to ensure there is no interference with our threadgroups.
|
||||
SimpleScheduler Y1 = SimpleScheduler.getInstance();
|
||||
SimpleTimer2 Y2 = SimpleTimer2.getInstance();
|
||||
i = Y1.hashCode();
|
||||
i = Y2.hashCode();
|
||||
{
|
||||
File cfg = new File(configLocation);
|
||||
@ -248,11 +248,11 @@ public class BOB implements Runnable, ClientApp {
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("inbound.length")) {
|
||||
props.setProperty("inbound.length", "1");
|
||||
props.setProperty("inbound.length", "3");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("outbound.length")) {
|
||||
props.setProperty("outbound.length", "1");
|
||||
props.setProperty("outbound.length", "3");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("inbound.lengthVariance")) {
|
||||
@ -339,7 +339,7 @@ public class BOB implements Runnable, ClientApp {
|
||||
|
||||
if (g) {
|
||||
DoCMDS conn_c = new DoCMDS(spin, lock, server, props, database, _log);
|
||||
Thread t = new Thread(conn_c);
|
||||
Thread t = new I2PAppThread(conn_c);
|
||||
t.setName("BOB.DoCMDS " + i);
|
||||
t.start();
|
||||
i++;
|
||||
|
@ -25,12 +25,13 @@ import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
//import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
//import net.i2p.i2ptunnel.I2PTunnel;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
// needed only for debugging.
|
||||
// import java.util.logging.Level;
|
||||
// import java.util.logging.Logger;
|
||||
@ -909,6 +910,8 @@ public class DoCMDS implements Runnable {
|
||||
|
||||
} else if (Command.equals(C_getnick)) {
|
||||
// Get the NamedDB to work with...
|
||||
boolean nsfail = false;
|
||||
|
||||
try {
|
||||
database.getReadLock();
|
||||
} catch (Exception ex) {
|
||||
@ -919,6 +922,7 @@ public class DoCMDS implements Runnable {
|
||||
ns = true;
|
||||
} catch (RuntimeException b) {
|
||||
try {
|
||||
nsfail = true;
|
||||
nns(out);
|
||||
} catch (Exception ex) {
|
||||
try {
|
||||
@ -931,7 +935,7 @@ public class DoCMDS implements Runnable {
|
||||
}
|
||||
|
||||
database.releaseReadLock();
|
||||
if (ns) {
|
||||
if (ns && !nsfail) {
|
||||
try {
|
||||
rlock();
|
||||
} catch (Exception e) {
|
||||
@ -1307,7 +1311,7 @@ public class DoCMDS implements Runnable {
|
||||
// wait
|
||||
}
|
||||
tunnel = new MUXlisten(lock, database, nickinfo, _log);
|
||||
Thread t = new Thread(tunnel);
|
||||
Thread t = new I2PAppThread(tunnel);
|
||||
t.start();
|
||||
// try {
|
||||
// Thread.sleep(1000 * 10); // Slow down the startup.
|
||||
|
@ -18,10 +18,12 @@ package net.i2p.BOB;
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
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.I2PAppThread;
|
||||
|
||||
/**
|
||||
* Listen on I2P and connect to TCP
|
||||
@ -30,16 +32,15 @@ import net.i2p.client.streaming.I2PSocketManager;
|
||||
*/
|
||||
public class I2Plistener implements Runnable {
|
||||
|
||||
private NamedDB info, database;
|
||||
private Logger _log;
|
||||
public I2PSocketManager socketManager;
|
||||
public I2PServerSocket serverSocket;
|
||||
private AtomicBoolean lives;
|
||||
private final NamedDB info, database;
|
||||
private final Logger _log;
|
||||
private final I2PServerSocket serverSocket;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param SS
|
||||
* @param S
|
||||
* @param S unused
|
||||
* @param info
|
||||
* @param database
|
||||
* @param _log
|
||||
@ -48,7 +49,6 @@ public class I2Plistener implements Runnable {
|
||||
this.database = database;
|
||||
this.info = info;
|
||||
this._log = _log;
|
||||
this.socketManager = S;
|
||||
this.serverSocket = SS;
|
||||
this.lives = lives;
|
||||
}
|
||||
@ -79,7 +79,7 @@ public class I2Plistener implements Runnable {
|
||||
conn++;
|
||||
// toss the connection to a new thread.
|
||||
I2PtoTCP conn_c = new I2PtoTCP(sessSocket, info, database, lives);
|
||||
Thread t = new Thread(conn_c, Thread.currentThread().getName() + " I2PtoTCP " + conn);
|
||||
Thread t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " I2PtoTCP " + conn);
|
||||
t.start();
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,10 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
* Process I2P->TCP
|
||||
@ -104,15 +107,15 @@ public class I2PtoTCP implements Runnable {
|
||||
|
||||
if (tell) {
|
||||
// tell who is connecting
|
||||
out.write(I2P.getPeerDestination().toBase64().getBytes());
|
||||
out.write(DataHelper.getASCII(I2P.getPeerDestination().toBase64()));
|
||||
out.write(10); // nl
|
||||
out.flush(); // not really needed, but...
|
||||
}
|
||||
// setup to cross the streams
|
||||
TCPio conn_c = new TCPio(in, Iout, lives); // app -> I2P
|
||||
TCPio conn_a = new TCPio(Iin, out, lives); // I2P -> app
|
||||
t = new Thread(conn_c, Thread.currentThread().getName() + " TCPioA");
|
||||
q = new Thread(conn_a, Thread.currentThread().getName() + " TCPioB");
|
||||
t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " TCPioA");
|
||||
q = new I2PAppThread(conn_a, Thread.currentThread().getName() + " TCPioB");
|
||||
// Fire!
|
||||
t.start();
|
||||
q.start();
|
||||
|
@ -21,11 +21,13 @@ import java.net.InetAddress;
|
||||
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;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -201,14 +203,14 @@ public class MUXlisten implements Runnable {
|
||||
// I2P -> TCP
|
||||
SS = socketManager.getServerSocket();
|
||||
I2Plistener conn = new I2Plistener(SS, socketManager, info, database, _log, lives);
|
||||
t = new Thread(tg, conn, "BOBI2Plistener " + N);
|
||||
t = new I2PAppThread(tg, conn, "BOBI2Plistener " + N);
|
||||
t.start();
|
||||
}
|
||||
|
||||
if (come_in) {
|
||||
// TCP -> I2P
|
||||
TCPlistener conn = new TCPlistener(listener, socketManager, info, database, _log, lives);
|
||||
q = new Thread(tg, conn, "BOBTCPlistener " + N);
|
||||
q = new I2PAppThread(tg, conn, "BOBTCPlistener " + N);
|
||||
q.start();
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
@ -31,12 +30,10 @@ public class Main {
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// THINK THINK THINK THINK THINK THINK
|
||||
SimpleScheduler Y1 = SimpleScheduler.getInstance();
|
||||
SimpleTimer2 Y2 = SimpleTimer2.getInstance();
|
||||
|
||||
BOB.main(args);
|
||||
|
||||
Y2.stop();
|
||||
Y1.stop();
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ public class NamedDB {
|
||||
}
|
||||
|
||||
/**
|
||||
* Find objects in the array, returns it's index or throws exception
|
||||
* Find objects in the array, returns its index or throws exception
|
||||
* @param key
|
||||
* @return an objects index
|
||||
* @throws ArrayIndexOutOfBoundsException when key does not exist
|
||||
|
@ -20,8 +20,10 @@ import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
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.I2PAppThread;
|
||||
|
||||
/**
|
||||
* Listen on TCP port and connect to I2P
|
||||
@ -30,12 +32,11 @@ import net.i2p.client.streaming.I2PSocketManager;
|
||||
*/
|
||||
public class TCPlistener implements Runnable {
|
||||
|
||||
private NamedDB info, database;
|
||||
private Logger _log;
|
||||
public I2PSocketManager socketManager;
|
||||
public I2PServerSocket serverSocket;
|
||||
private ServerSocket listener;
|
||||
private AtomicBoolean lives;
|
||||
private final NamedDB info, database;
|
||||
private final Logger _log;
|
||||
private final I2PSocketManager socketManager;
|
||||
private final ServerSocket listener;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -76,7 +77,7 @@ public class TCPlistener implements Runnable {
|
||||
conn++;
|
||||
// toss the connection to a new thread.
|
||||
TCPtoI2P conn_c = new TCPtoI2P(socketManager, server, info, database, lives);
|
||||
Thread t = new Thread(conn_c, Thread.currentThread().getName() + " TCPtoI2P " + conn);
|
||||
Thread t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " TCPtoI2P " + conn);
|
||||
t.start();
|
||||
g = false;
|
||||
}
|
||||
|
@ -24,13 +24,14 @@ import java.net.NoRouteToHostException;
|
||||
import java.net.Socket;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
//import net.i2p.i2ptunnel.I2PTunnel;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -158,8 +159,8 @@ public class TCPtoI2P implements Runnable {
|
||||
// setup to cross the streams
|
||||
TCPio conn_c = new TCPio(in, Iout, lives); // app -> I2P
|
||||
TCPio conn_a = new TCPio(Iin, out, lives); // I2P -> app
|
||||
t = new Thread(conn_c, Thread.currentThread().getName() + " TCPioA");
|
||||
q = new Thread(conn_a, Thread.currentThread().getName() + " TCPioB");
|
||||
t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " TCPioA");
|
||||
q = new I2PAppThread(conn_a, Thread.currentThread().getName() + " TCPioB");
|
||||
// Fire!
|
||||
t.start();
|
||||
q.start();
|
||||
|
@ -34,15 +34,18 @@ import net.i2p.util.Log;
|
||||
* The skeletal frame is here, just needs to be finished.
|
||||
*
|
||||
* @author sponge
|
||||
* @deprecated incomplete, unused
|
||||
*/
|
||||
@Deprecated
|
||||
public class UDPIOthread implements I2PSessionListener, Runnable {
|
||||
|
||||
private NamedDB info;
|
||||
private Log _log;
|
||||
private Socket socket;
|
||||
private final NamedDB info;
|
||||
private final Log _log;
|
||||
private final Socket socket;
|
||||
private DataInputStream in;
|
||||
private DataOutputStream out;
|
||||
private I2PSession _session;
|
||||
private final I2PSession _session;
|
||||
// FIXME never set
|
||||
private Destination _peerDestination;
|
||||
private boolean up;
|
||||
|
||||
@ -58,7 +61,6 @@ public class UDPIOthread implements I2PSessionListener, Runnable {
|
||||
this._log = _log;
|
||||
this.socket = socket;
|
||||
this._session = _session;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,6 +27,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.EepGet;
|
||||
@ -49,6 +50,26 @@ class AddressBook {
|
||||
private boolean modified;
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final int MIN_DEST_LENGTH = 516;
|
||||
private static final int MAX_DEST_LENGTH = MIN_DEST_LENGTH + 100; // longer than any known cert type for now
|
||||
|
||||
/**
|
||||
* 5-67 chars lower/upper case
|
||||
*/
|
||||
private static final Pattern HOST_PATTERN =
|
||||
Pattern.compile("^[0-9a-zA-Z\\.-]{5,67}$");
|
||||
|
||||
/**
|
||||
* 52 chars lower/upper case
|
||||
* Always ends in 'a' or 'q'
|
||||
*/
|
||||
private static final Pattern B32_PATTERN =
|
||||
Pattern.compile("^[2-7a-zA-Z]{51}[aAqQ]$");
|
||||
|
||||
/** not a complete qualification, just a quick check */
|
||||
private static final Pattern B64_PATTERN =
|
||||
Pattern.compile("^[0-9a-zA-Z~-]{" + MIN_DEST_LENGTH + ',' + MAX_DEST_LENGTH + "}={0,2}$");
|
||||
|
||||
/**
|
||||
* Construct an AddressBook from the contents of the Map addresses.
|
||||
*
|
||||
@ -159,8 +180,13 @@ class AddressBook {
|
||||
* @since 0.8.7
|
||||
*/
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
if (this.subFile != null)
|
||||
return new ConfigIterator(this.subFile);
|
||||
if (this.subFile != null) {
|
||||
try {
|
||||
return new ConfigIterator(this.subFile);
|
||||
} catch (IOException ioe) {
|
||||
return new ConfigIterator();
|
||||
}
|
||||
}
|
||||
return this.addresses.entrySet().iterator();
|
||||
}
|
||||
|
||||
@ -201,9 +227,6 @@ class AddressBook {
|
||||
return "Map containing " + this.addresses.size() + " entries";
|
||||
}
|
||||
|
||||
private static final int MIN_DEST_LENGTH = 516;
|
||||
private static final int MAX_DEST_LENGTH = MIN_DEST_LENGTH + 100; // longer than any known cert type for now
|
||||
|
||||
/**
|
||||
* Do basic validation of the hostname
|
||||
* hostname was already converted to lower case by ConfigParser.parse()
|
||||
@ -220,9 +243,10 @@ class AddressBook {
|
||||
host.indexOf("..") < 0 &&
|
||||
// IDN - basic check, not complete validation
|
||||
(host.indexOf("--") < 0 || host.startsWith("xn--") || host.indexOf(".xn--") > 0) &&
|
||||
host.replaceAll("[a-z0-9.-]", "").length() == 0 &&
|
||||
HOST_PATTERN.matcher(host).matches() &&
|
||||
// Base32 spoofing (52chars.i2p)
|
||||
(! (host.length() == 56 && host.substring(0,52).replaceAll("[a-z2-7]", "").length() == 0)) &&
|
||||
// We didn't do it this way, we use a .b32.i2p suffix, but let's prohibit it anyway
|
||||
(! (host.length() == 56 && B32_PATTERN.matcher(host.substring(0,52)).matches())) &&
|
||||
// ... or maybe we do Base32 this way ...
|
||||
(! host.equals("b32.i2p")) &&
|
||||
(! host.endsWith(".b32.i2p")) &&
|
||||
@ -246,7 +270,7 @@ class AddressBook {
|
||||
(dest.length() > MIN_DEST_LENGTH && dest.length() <= MAX_DEST_LENGTH)) &&
|
||||
// B64 comes in groups of 2, 3, or 4 chars, but never 1
|
||||
((dest.length() % 4) != 1) &&
|
||||
dest.replaceAll("[a-zA-Z0-9~-]", "").length() == 0
|
||||
B64_PATTERN.matcher(dest).matches()
|
||||
;
|
||||
}
|
||||
|
||||
@ -332,4 +356,27 @@ class AddressBook {
|
||||
protected void finalize() {
|
||||
delete();
|
||||
}
|
||||
|
||||
/****
|
||||
public static void main(String[] args) {
|
||||
String[] tests = { "foo.i2p",
|
||||
"3bnipzzu67cdq2rcygyxz52xhvy6ylokn4zfrk36ywn6pixmaoza.b32.i2p",
|
||||
"9rhEy4dT9fMlcSOhDzfWRxCV2aen4Zp4eSthOf5f9gVKMa4PtQJ-wEzm2KEYeDXkbM6wEDvMQ6ou4LIniSE6bSAwy7fokiXk5oabels-sJmftnQWRbZyyXEAsLc3gpJJvp9km7kDyZ0z0YGL5tf3S~OaWdptB5tSBOAOjm6ramcYZMWhyUqm~xSL1JyXUqWEHRYwhoDJNL6-L516VpDYVigMBpIwskjeFGcqK8BqWAe0bRwxIiFTPN6Ck8SDzQvS1l1Yj-zfzg3X3gOknzwR8nrHUkjsWtEB6nhbOr8AR21C9Hs0a7MUJvSe2NOuBoNTrtxT76jDruI78JcG5r~WKl6M12yM-SqeBNE9hQn2QCHeHAKju7FdRCbqaZ99IwyjfwvZbkiYYQVN1xlUuGaXrj98XDzK7GORYdH-PrVGfEbMXQ40KLHUWHz8w4tQXAOQrCHEichod0RIzuuxo3XltCWKrf1xGZhkAo9bk2qXi6digCijvYNaKmQdXZYWW~RtAAAA",
|
||||
"6IZTYacjlXjSAxu-uXEO5oGsj-f4tfePHEvGjs5pu-AMXMwD7-xFdi8kdobDMJp9yRAl96U7yLl~0t9zHeqqYmNeZnDSkTmAcSC2PT45ZJDXBobKi1~a77zuqfPwnzEatYfW3GL1JQAEkAmiwNJoG7ThTZ3zT7W9ekVJpHi9mivpTbaI~rALLfuAg~Mvr60nntZHjqhEZuiU4dTXrmc5nykl~UaMnBdwHL4jKmoN5CotqHyLYZfp74fdD-Oq4SkhuBhU8wkBIM3lz3Ul1o6-s0lNUMdYJq1CyxnyP7jeekdfAlSx4P4sU4M0dPaYvPdOFWPWwBuEh0pCs5Mj01B2xeEBhpV~xSLn6ru5Vq98TrmaR33KHxd76OYYFsWwzVbBuMVSd800XpBghGFucGw01YHYsPh3Afb01sXbf8Nb1bkxCy~DsrmoH4Ww3bpx66JhRTWvg5al3oWlCX51CnJUqaaK~dPL-pBvAyLKIA5aYvl8ca66jtA7AFDxsOb2texBBQAEAAcAAA==",
|
||||
"te9Ky7XvVcLLr5vQqvfmOasg915P3-ddP3iDqpMMk7v5ufFKobLAX~1k-E4WVsJVlkYvkHVOjxix-uT1IdewKmLd81s5wZtz0GQ3ZC6p0C3S2cOxz7kQqf7QYSR0BrhZC~2du3-GdQO9TqNmsnHrah5lOZf0LN2JFEFPqg8ZB5JNm3JjJeSqePBRk3zAUogNaNK3voB1MVI0ZROKopXAJM4XMERNqI8tIH4ngGtV41SEJJ5pUFrrTx~EiUPqmSEaEA6UDYZiqd23ZlewZ31ExXQj97zvkuhKCoS9A9MNkzZejJhP-TEXWF8~KHur9f51H--EhwZ42Aj69-3GuNjsMdTwglG5zyIfhd2OspxJrXzCPqIV2sXn80IbPgwxHu0CKIJ6X43B5vTyVu87QDI13MIRNGWNZY5KmM5pilGP7jPkOs4xQDo4NHzpuJR5igjWgJIBPU6fI9Pzq~BMzjLiZOMp8xNWey1zKC96L0eX4of1MG~oUvq0qmIHGNa1TlUwBQAEAAEAAA==",
|
||||
"(*&(*&(*&(*",
|
||||
"9rhEy4dT9fMlcSOhDzfWRxCV2aen4Zp4eSthOf5f9gVKMa4PtQJ-wEzm2KEYeDXkbM6wEDvMQ6ou4LIniSE6bSAwy7fokiXk5oabels-sJmftnQWRbZyyXEAsLc3gpJJvp9km7kDyZ0z0YGL5tf3S~OaWdptB5tSBOAOjm6ramcYZMWhyUqm~xSL1JyXUqWEHRYwhoDJNL6-L516VpDYVigMBpIwskjeFGcqK8BqWAe0bRwxIiFTPN6Ck8SDzQvS1l1Yj-zfzg3X3gOknzwR8nrHUkjsWtEB6nhbOr8AR21C9Hs0a7MUJvSe2NOuBoNTrtxT76jDruI78JcG5r~WKl6M12yM-SqeBNE9hQn2QCHeHAKju7FdRCbqaZ99IwyjfwvZbkiYYQVN1xlUuGaXrj98XDzK7GORYdH-PrVGfEbMXQ40KLHUWHz8w4tQXAOQrCHEichod0RIzuuxo3XltCWKrf1xGZhkAo9bk2qXi6digCijvYNaKmQdXZYWW~RtAAA",
|
||||
"6IZTYacjlXjSAxu-uXEO5oGsj-f4tfePHEvGjs5pu-AMXMwD7-xFdi8kdobDMJp9yRAl96U7yLl~0t9zHeqqYmNeZnDSkTmAcSC2PT45ZJDXBobKi1~a77zuqfPwnzEatYfW3GL1JQAEkAmiwNJoG7ThTZ3zT7W9ekVJpHi9mivpTbaI~rALLfuAg~Mvr60nntZHjqhEZuiU4dTXrmc5nykl~UaMnBdwHL4jKmoN5CotqHyLYZfp74fdD-Oq4SkhuBhU8wkBIM3lz3Ul1o6-s0lNUMdYJq1CyxnyP7jeekdfAlSx4P4sU4M0dPaYvPdOFWPWwBuEh0pCs5Mj01B2xeEBhpV~xSLn6ru5Vq98TrmaR33KHxd76OYYFsWwzVbBuMVSd800XpBghGFucGw01YHYsPh3Afb01sXbf8Nb1bkxCy~DsrmoH4Ww3bpx66JhRTWvg5al3oWlCX51CnJUqaaK~dPL-pBvAyLKIA5aYvl8ca66jtA7AFDxsOb2texBBQAEAAcAAA===",
|
||||
"!e9Ky7XvVcLLr5vQqvfmOasg915P3-ddP3iDqpMMk7v5ufFKobLAX~1k-E4WVsJVlkYvkHVOjxix-uT1IdewKmLd81s5wZtz0GQ3ZC6p0C3S2cOxz7kQqf7QYSR0BrhZC~2du3-GdQO9TqNmsnHrah5lOZf0LN2JFEFPqg8ZB5JNm3JjJeSqePBRk3zAUogNaNK3voB1MVI0ZROKopXAJM4XMERNqI8tIH4ngGtV41SEJJ5pUFrrTx~EiUPqmSEaEA6UDYZiqd23ZlewZ31ExXQj97zvkuhKCoS9A9MNkzZejJhP-TEXWF8~KHur9f51H--EhwZ42Aj69-3GuNjsMdTwglG5zyIfhd2OspxJrXzCPqIV2sXn80IbPgwxHu0CKIJ6X43B5vTyVu87QDI13MIRNGWNZY5KmM5pilGP7jPkOs4xQDo4NHzpuJR5igjWgJIBPU6fI9Pzq~BMzjLiZOMp8xNWey1zKC96L0eX4of1MG~oUvq0qmIHGNa1TlUwBQAEAAEAAA==",
|
||||
"x"
|
||||
};
|
||||
for (String s : tests) {
|
||||
test(s);
|
||||
}
|
||||
}
|
||||
|
||||
public static void test(String s) {
|
||||
System.out.println(s + " valid host? " + isValidKey(s) + " valid dest? " + isValidDest(s));
|
||||
}
|
||||
****/
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
package net.i2p.addressbook;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@ -31,6 +32,8 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* A class to iterate through a hosts.txt or config file without
|
||||
* reading the whole thing into memory.
|
||||
@ -41,7 +44,7 @@ import java.util.NoSuchElementException;
|
||||
*
|
||||
* @since 0.8.7
|
||||
*/
|
||||
class ConfigIterator implements Iterator<Map.Entry<String, String>> {
|
||||
class ConfigIterator implements Iterator<Map.Entry<String, String>>, Closeable {
|
||||
|
||||
private BufferedReader input;
|
||||
private ConfigEntry next;
|
||||
@ -54,11 +57,9 @@ class ConfigIterator implements Iterator<Map.Entry<String, String>> {
|
||||
/**
|
||||
* An iterator over the key/value pairs in the file.
|
||||
*/
|
||||
public ConfigIterator(File file) {
|
||||
try {
|
||||
public ConfigIterator(File file) throws IOException {
|
||||
FileInputStream fileStream = new FileInputStream(file);
|
||||
input = new BufferedReader(new InputStreamReader(fileStream));
|
||||
} catch (IOException ioe) {}
|
||||
input = new BufferedReader(new InputStreamReader(fileStream, "UTF-8"));
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
@ -70,7 +71,7 @@ class ConfigIterator implements Iterator<Map.Entry<String, String>> {
|
||||
String inputLine = input.readLine();
|
||||
while (inputLine != null) {
|
||||
inputLine = ConfigParser.stripComments(inputLine);
|
||||
String[] splitLine = inputLine.split("=");
|
||||
String[] splitLine = DataHelper.split(inputLine, "=");
|
||||
if (splitLine.length == 2) {
|
||||
next = new ConfigEntry(splitLine[0].trim().toLowerCase(Locale.US), splitLine[1].trim());
|
||||
return true;
|
||||
|
@ -35,6 +35,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.SecureFile;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.SystemVersion;
|
||||
@ -64,10 +65,11 @@ class ConfigParser {
|
||||
if (inputLine.startsWith(";")) {
|
||||
return "";
|
||||
}
|
||||
if (inputLine.split("#").length > 0) {
|
||||
return inputLine.split("#")[0];
|
||||
int hash = inputLine.indexOf('#');
|
||||
if (hash >= 0) {
|
||||
return inputLine.substring(0, hash);
|
||||
} else {
|
||||
return "";
|
||||
return inputLine;
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +94,7 @@ class ConfigParser {
|
||||
inputLine = input.readLine();
|
||||
while (inputLine != null) {
|
||||
inputLine = stripComments(inputLine);
|
||||
String[] splitLine = inputLine.split("=");
|
||||
String[] splitLine = DataHelper.split(inputLine, "=");
|
||||
if (splitLine.length == 2) {
|
||||
result.put(splitLine[0].trim().toLowerCase(Locale.US), splitLine[1].trim());
|
||||
}
|
||||
@ -115,7 +117,7 @@ class ConfigParser {
|
||||
public static Map<String, String> parse(File file) throws IOException {
|
||||
FileInputStream fileStream = new FileInputStream(file);
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(
|
||||
fileStream));
|
||||
fileStream, "UTF-8"));
|
||||
Map<String, String> rv = parse(input);
|
||||
try {
|
||||
fileStream.close();
|
||||
@ -204,7 +206,7 @@ class ConfigParser {
|
||||
public static List<String> parseSubscriptions(File file) throws IOException {
|
||||
FileInputStream fileStream = new FileInputStream(file);
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(
|
||||
fileStream));
|
||||
fileStream, "UTF-8"));
|
||||
List<String> rv = parseSubscriptions(input);
|
||||
try {
|
||||
fileStream.close();
|
||||
|
@ -25,6 +25,7 @@ import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.naming.NamingServiceUpdater;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
* A thread that waits five minutes, then runs the addressbook daemon.
|
||||
@ -32,7 +33,7 @@ import net.i2p.client.naming.NamingServiceUpdater;
|
||||
* @author Ragnarok
|
||||
*
|
||||
*/
|
||||
public class DaemonThread extends Thread implements NamingServiceUpdater {
|
||||
public class DaemonThread extends I2PAppThread implements NamingServiceUpdater {
|
||||
|
||||
private String[] args;
|
||||
|
||||
|
@ -23,8 +23,9 @@ package net.i2p.addressbook;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
@ -56,8 +57,8 @@ class Log {
|
||||
public void append(String entry) {
|
||||
BufferedWriter bw = null;
|
||||
try {
|
||||
bw = new BufferedWriter(new FileWriter(this.file,
|
||||
true));
|
||||
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(this.file,
|
||||
true), "UTF-8"));
|
||||
String timestamp = new Date().toString();
|
||||
bw.write(timestamp + " -- " + entry);
|
||||
bw.newLine();
|
||||
|
@ -41,7 +41,9 @@ import javax.servlet.http.HttpServletResponse;
|
||||
*
|
||||
*/
|
||||
public class Servlet extends HttpServlet {
|
||||
private DaemonThread thread;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private transient DaemonThread thread;
|
||||
//private String nonce;
|
||||
//private static final String PROP_NONCE = "addressbook.nonce";
|
||||
|
||||
|
@ -26,6 +26,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.PortMapper;
|
||||
|
||||
/**
|
||||
* An iterator over the subscriptions in a SubscriptionList. Note that this iterator
|
||||
@ -69,11 +70,14 @@ class SubscriptionIterator implements Iterator<AddressBook> {
|
||||
* Yes, the EepGet fetch() is done in here in next().
|
||||
*
|
||||
* see java.util.Iterator#next()
|
||||
* @return an AddressBook (empty if the minimum delay has not been met)
|
||||
* @return non-null AddressBook (empty if the minimum delay has not been met,
|
||||
* or there is no proxy tunnel, or the fetch otherwise fails)
|
||||
*/
|
||||
public AddressBook next() {
|
||||
Subscription sub = this.subIterator.next();
|
||||
if (sub.getLastFetched() + this.delay < I2PAppContext.getGlobalContext().clock().now()) {
|
||||
if (sub.getLastFetched() + this.delay < I2PAppContext.getGlobalContext().clock().now() &&
|
||||
I2PAppContext.getGlobalContext().portMapper().getPort(PortMapper.SVC_HTTP_PROXY) >= 0 &&
|
||||
!I2PAppContext.getGlobalContext().getBooleanProperty("i2p.vmCommSystem")) {
|
||||
//System.err.println("Fetching addressbook from " + sub.getLocation());
|
||||
return new AddressBook(sub, this.proxyHost, this.proxyPort);
|
||||
} else {
|
||||
|
11
apps/addressbook/java/src/net/i2p/addressbook/package.html
Normal file
11
apps/addressbook/java/src/net/i2p/addressbook/package.html
Normal file
@ -0,0 +1,11 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
The addressbook application, which fetches hosts.txt files from subscription URLS via
|
||||
HTTP and adds new hosts to the local database.
|
||||
While implemented as a webapp, this application contains no user interface.
|
||||
May also be packaged as a jar, as is done for Android.
|
||||
The webapp named 'addressbook' in the console is actually SusiDNS.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
@ -4,6 +4,15 @@
|
||||
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
|
||||
|
||||
<web-app>
|
||||
<filter>
|
||||
<filter-name>XSSFilter</filter-name>
|
||||
<filter-class>net.i2p.servlet.filters.XSSFilter</filter-class>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>XSSFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>addressbook</servlet-name>
|
||||
<servlet-class>net.i2p.addressbook.Servlet</servlet-class>
|
||||
@ -19,4 +28,11 @@
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- this webapp doesn't actually use sessions or cookies -->
|
||||
<session-config>
|
||||
<session-timeout>30</session-timeout>
|
||||
<cookie-config>
|
||||
<http-only>true</http-only>
|
||||
</cookie-config>
|
||||
</session-config>
|
||||
</web-app>
|
||||
|
@ -11,6 +11,7 @@ import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.DataHelper
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.I2PThread;
|
||||
@ -47,7 +48,7 @@ class AdminRunner implements Runnable {
|
||||
reply(out, "this is not a website");
|
||||
} else if ( (command.indexOf("routerStats.html") >= 0) || (command.indexOf("oldstats.jsp") >= 0) ) {
|
||||
try {
|
||||
out.write("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n".getBytes());
|
||||
out.write(DataHelper.getASCII("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n"));
|
||||
_generator.generateStatsPage(new OutputStreamWriter(out));
|
||||
out.close();
|
||||
} catch (IOException ioe) {
|
||||
@ -61,7 +62,7 @@ class AdminRunner implements Runnable {
|
||||
reply(out, shutdown(command));
|
||||
} else if (true || command.indexOf("routerConsole.html") > 0) {
|
||||
try {
|
||||
out.write("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n".getBytes());
|
||||
out.write(DataHelper.getASCII("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n"));
|
||||
_context.router().renderStatusHTML(new OutputStreamWriter(out));
|
||||
out.close();
|
||||
} catch (IOException ioe) {
|
||||
@ -80,7 +81,7 @@ class AdminRunner implements Runnable {
|
||||
reply.append("Content-type: text/html\n\n");
|
||||
reply.append(content);
|
||||
try {
|
||||
out.write(reply.toString().getBytes());
|
||||
out.write(DataHelper.getASCII(reply.toString()));
|
||||
out.close();
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
@ -97,7 +98,7 @@ class AdminRunner implements Runnable {
|
||||
reply.append("Content-type: text/plain\n\n");
|
||||
reply.append(content);
|
||||
try {
|
||||
out.write(reply.toString().getBytes());
|
||||
out.write(DataHelper.getASCII(reply.toString()));
|
||||
out.close();
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
|
99
apps/apparmor/home.i2p.i2prouter
Normal file
99
apps/apparmor/home.i2p.i2prouter
Normal file
@ -0,0 +1,99 @@
|
||||
#Last Modified: Sun Dec 06 12:30:32 2015
|
||||
# vim:syntax=apparmor et ts=8 sw=4
|
||||
|
||||
#include <tunables/global>
|
||||
|
||||
$INSTALL_PATH/{i2prouter,runplain.sh} flags=(complain) {
|
||||
#include <abstractions/base>
|
||||
#include <abstractions/fonts>
|
||||
#include <abstractions/nameservice>
|
||||
#include <abstractions/ssl_certs>
|
||||
|
||||
capability sys_ptrace,
|
||||
network inet stream,
|
||||
network inet6 stream,
|
||||
|
||||
$INSTALL_PATH/ r,
|
||||
$INSTALL_PATH/{i2psvc,wrapper} rmix,
|
||||
owner $INSTALL_PATH/** rwkm,
|
||||
|
||||
# Needed for Java
|
||||
owner @{PROC} r,
|
||||
owner @{PROC}/[0-9]*/ r,
|
||||
owner @{PROC}/[0-9]*/status r,
|
||||
owner @{PROC}/[0-9]*/stat r,
|
||||
owner @{PROC}/[0-9]*/cmdline r,
|
||||
@{PROC}/uptime r,
|
||||
@{PROC}/sys/kernel/pid_max r,
|
||||
/sys/devices/system/cpu/ r,
|
||||
/sys/devices/system/cpu/** r,
|
||||
|
||||
/dev/random r,
|
||||
/dev/urandom r,
|
||||
|
||||
@{PROC}/1/comm r,
|
||||
|
||||
/etc/ssl/certs/java/** r,
|
||||
/etc/timezone r,
|
||||
/usr/share/javazi/** r,
|
||||
|
||||
# Debian
|
||||
/etc/java-{6,7,8}-openjdk/** r,
|
||||
/usr/lib/jvm/default-java/jre/bin/java rix,
|
||||
|
||||
# Debian, Ubuntu, openSUSE
|
||||
/usr/lib{,32,64}/jvm/java-*-openjdk-*/jre/bin/java rix,
|
||||
/usr/lib{,32,64}/jvm/java-*-openjdk-*/jre/bin/keytool rix,
|
||||
|
||||
# Raspbian
|
||||
/usr/lib/jvm/jdk-*-oracle-*/jre/bin/java rix,
|
||||
/usr/lib/jvm/jdk-*-oracle-*/jre/bin/keytool rix,
|
||||
|
||||
|
||||
# Fonts are needed for I2P's graphs
|
||||
/usr/share/java/java-atk-wrapper.jar r,
|
||||
|
||||
# Used by some plugins
|
||||
/usr/share/java/eclipse-ecj-*.jar r,
|
||||
|
||||
/{,var/}tmp/ rwm,
|
||||
owner /{,var/}tmp/** rwkm,
|
||||
|
||||
/{,usr/}bin/{,b,d}ash rix,
|
||||
/{,usr/}bin/cat rix,
|
||||
/{,usr/}bin/cut rix,
|
||||
/{,usr/}bin/dirname rix,
|
||||
/{,usr/}bin/expr rix,
|
||||
/{,usr/}bin/{,g,m}awk rix,
|
||||
/{,usr/}bin/grep rix,
|
||||
/{,usr/}bin/id rix,
|
||||
/{,usr/}bin/ldd rix,
|
||||
/{,usr/}bin/ls rix,
|
||||
/{,usr/}bin/mkdir rix,
|
||||
/{,usr/}bin/nohup rix,
|
||||
/{,usr/}bin/ps rix,
|
||||
/{,usr/}bin/rm rix,
|
||||
/{,usr/}bin/sed rix,
|
||||
/{,usr/}bin/sleep rix,
|
||||
/{,usr/}bin/tail rix,
|
||||
/{,usr/}bin/tr rix,
|
||||
/{,usr/}bin/uname rix,
|
||||
/{,usr/}bin/which rix,
|
||||
|
||||
@{HOME}/.java/fonts/** r,
|
||||
owner @{HOME}/.i2p/ rw,
|
||||
owner @{HOME}/.i2p/** rwk,
|
||||
|
||||
# Prevent spamming the logs
|
||||
deny owner @{HOME}/.java/ wk,
|
||||
deny @{HOME}/.fontconfig/ wk,
|
||||
deny @{HOME}/.java/fonts/** w,
|
||||
deny /dev/tty rw,
|
||||
deny /dev/pts/[0-9]* rw,
|
||||
deny @{PROC}/[0-9]*/fd/ r,
|
||||
deny /usr/local/share/fonts/ r,
|
||||
deny /var/cache/fontconfig/ wk,
|
||||
# Used by some versions of the Tanuki wrapper but never used by I2P
|
||||
deny /usr/share/java/hamcrest*.jar r,
|
||||
deny /usr/share/java/junit*.jar r,
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/i2p_router"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/i2p_sdk"/>
|
||||
<classpathentry kind="lib" path="/lib/wrapper/all/wrapper.jar"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/installer"/>
|
||||
<classpathentry kind="output" path="build"/>
|
||||
</classpath>
|
||||
|
@ -26,12 +26,12 @@ then
|
||||
fi
|
||||
|
||||
# on windows, one must specify the path of commnad find
|
||||
# since windows has its own retarded version of find.
|
||||
# since windows has its own version of find.
|
||||
if which find|grep -q -i windows ; then
|
||||
export PATH=.:/bin:/usr/local/bin:$PATH
|
||||
fi
|
||||
# Fast mode - update ondemond
|
||||
# set LG2 to the language you need in envrionment varibales to enable this
|
||||
# set LG2 to the language you need in environment variables to enable this
|
||||
|
||||
# add ../java/ so the refs will work in the po file
|
||||
JPATHS="src"
|
||||
@ -64,19 +64,19 @@ do
|
||||
echo "Updating the $i file from the tags..."
|
||||
# extract strings from java and jsp files, and update messages.po files
|
||||
# translate calls must be one of the forms:
|
||||
# _("foo")
|
||||
# _t("foo")
|
||||
# _x("foo")
|
||||
# intl._("foo")
|
||||
# intl._t("foo")
|
||||
# intl.title("foo")
|
||||
# handler._("foo")
|
||||
# formhandler._("foo")
|
||||
# handler._t("foo")
|
||||
# formhandler._t("foo")
|
||||
# net.i2p.router.web.Messages.getString("foo")
|
||||
# In a jsp, you must use a helper or handler that has the context set.
|
||||
# To start a new translation, copy the header from an old translation to the new .po file,
|
||||
# then ant distclean updater.
|
||||
find $JPATHS -name *.java > $TMPFILE
|
||||
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 --add-comments\
|
||||
--keyword=_ --keyword=_x --keyword=intl._ --keyword=intl.title \
|
||||
--keyword=_t --keyword=_x --keyword=intl._ --keyword=intl.title \
|
||||
--keyword=handler._ --keyword=formhandler._ \
|
||||
--keyword=net.i2p.router.web.Messages.getString \
|
||||
-o ${i}t
|
||||
|
@ -3,20 +3,22 @@
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# <kia___@hushmail.com>, 2011.
|
||||
# Translators:
|
||||
# Aesthese, 2016
|
||||
# KIA <kia___@hushmail.com>, 2011
|
||||
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"
|
||||
"PO-Revision-Date: 2011-07-12 19:41+0000\n"
|
||||
"Last-Translator: KIA <kia___@hushmail.com>\n"
|
||||
"Language-Team: Danish (http://www.transifex.net/projects/p/I2P/team/da/)\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-01-09 19:27+0000\n"
|
||||
"PO-Revision-Date: 2016-01-08 07:54+0000\n"
|
||||
"Last-Translator: Aesthese\n"
|
||||
"Language-Team: Danish (http://www.transifex.com/otf/I2P/language/da/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: da\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:23
|
||||
msgid "Start I2P"
|
||||
@ -24,7 +26,7 @@ msgstr "Start I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P starter nu!"
|
||||
msgstr "I2P starter!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
|
||||
msgid "Starting"
|
||||
@ -46,12 +48,10 @@ msgstr "Genstart I2P"
|
||||
msgid "Stop I2P"
|
||||
msgstr "Stop I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:43
|
||||
msgid "Tray icon configuration"
|
||||
msgstr "Konfiguration af processbar ikonet"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:46
|
||||
msgid "Should tray icon be enabled?"
|
||||
msgstr "Skal processbar ikonet være aktivt?"
|
||||
|
||||
|
||||
|
@ -3,20 +3,22 @@
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# <b790979@klzlk.com>, 2011.
|
||||
# Translators:
|
||||
# PolishAnon <b790979@klzlk.com>, 2011
|
||||
# polacco <polacco@i2pmail.org>, 2015
|
||||
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"
|
||||
"PO-Revision-Date: 2011-05-25 18:36+0000\n"
|
||||
"Last-Translator: PolishAnon <b790979@klzlk.com>\n"
|
||||
"Language-Team: Polish (http://www.transifex.net/projects/p/I2P/team/pl/)\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-01-09 19:27+0000\n"
|
||||
"PO-Revision-Date: 2015-02-17 20:54+0000\n"
|
||||
"Last-Translator: polacco <polacco@i2pmail.org>\n"
|
||||
"Language-Team: Polish (http://www.transifex.com/projects/p/I2P/language/pl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: pl\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:23
|
||||
msgid "Start I2P"
|
||||
@ -32,7 +34,7 @@ msgstr "Uruchamianie"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:26
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Uruchom Przeglądarke I2P"
|
||||
msgstr "Uruchom przeglądarkę I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:50
|
||||
msgid "Configure desktopgui"
|
||||
@ -46,12 +48,10 @@ msgstr "Zrestartuj I2P"
|
||||
msgid "Stop I2P"
|
||||
msgstr "Zatrzymaj I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:43
|
||||
msgid "Tray icon configuration"
|
||||
msgstr "Konfiguracja ikony zasobnika"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:46
|
||||
msgid "Should tray icon be enabled?"
|
||||
msgstr "Czy ikona zasobnika powinna być aktywna?"
|
||||
|
||||
|
||||
|
@ -4,15 +4,17 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# testsubject67 <deborinha97@hotmail.com>, 2014
|
||||
# blueboy, 2013
|
||||
# blueboy, 2015
|
||||
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"
|
||||
"POT-Creation-Date: 2014-01-09 19:27+0000\n"
|
||||
"PO-Revision-Date: 2015-12-23 00:12+0000\n"
|
||||
"Last-Translator: blueboy\n"
|
||||
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/I2P/language/pt_BR/)\n"
|
||||
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/otf/I2P/language/pt_BR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@ -33,11 +35,11 @@ msgstr "Conectando"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:26
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr ""
|
||||
msgstr "Lançar o navegador I2P "
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:50
|
||||
msgid "Configure desktopgui"
|
||||
msgstr ""
|
||||
msgstr "Configurar desktopgui"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:67
|
||||
msgid "Restart I2P"
|
||||
@ -47,10 +49,10 @@ msgstr "Reinicializar o roteador I2P"
|
||||
msgid "Stop I2P"
|
||||
msgstr "Interromper o roteador I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:43
|
||||
msgid "Tray icon configuration"
|
||||
msgstr ""
|
||||
msgstr "Configuração do ícone de bandeja"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:46
|
||||
msgid "Should tray icon be enabled?"
|
||||
msgstr ""
|
||||
msgstr "Ativar ícone de bandeja?"
|
||||
|
@ -6,20 +6,21 @@
|
||||
# Translators:
|
||||
# ducki2p <ducki2p@gmail.com>, 2011
|
||||
# foo <foo@bar>, 2009
|
||||
# Роман Азаренко <transifex@basicxp.ru>, 2013
|
||||
# Foster Snowhill, 2013
|
||||
# brianhopes <voganc-12@live.ru>, 2015
|
||||
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-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"
|
||||
"POT-Creation-Date: 2014-01-09 19:27+0000\n"
|
||||
"PO-Revision-Date: 2015-11-01 08:19+0000\n"
|
||||
"Last-Translator: brianhopes <voganc-12@live.ru>\n"
|
||||
"Language-Team: Russian (Russia) (http://www.transifex.com/otf/I2P/language/ru_RU/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ru_RU\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:23
|
||||
msgid "Start I2P"
|
||||
@ -39,7 +40,7 @@ msgstr "Запустить браузер I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:50
|
||||
msgid "Configure desktopgui"
|
||||
msgstr "Настроить desktopgui"
|
||||
msgstr "Настроить рабочий стол"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:67
|
||||
msgid "Restart I2P"
|
||||
@ -49,10 +50,10 @@ msgstr "Перезапустить I2P"
|
||||
msgid "Stop I2P"
|
||||
msgstr "Остановить I2P"
|
||||
|
||||
#: 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 "Отображать ли значок в области уведомлений?"
|
||||
|
@ -3,20 +3,23 @@
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# <gribua@gmail.com>, 2011.
|
||||
# Translators:
|
||||
# Denis Lysenko <gribua@gmail.com>, 2011
|
||||
# LinuxChata, 2014
|
||||
# madjong <madjong@i2pmail.org>, 2014
|
||||
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"
|
||||
"PO-Revision-Date: 2011-06-19 14:01+0000\n"
|
||||
"Last-Translator: Pharmasolin <gribua@gmail.com>\n"
|
||||
"Language-Team: Ukrainian (Ukraine) (http://www.transifex.net/projects/p/I2P/team/uk_UA/)\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-01-09 19:27+0000\n"
|
||||
"PO-Revision-Date: 2015-08-07 16:31+0000\n"
|
||||
"Last-Translator: Denis Lysenko <gribua@gmail.com>\n"
|
||||
"Language-Team: Ukrainian (Ukraine) (http://www.transifex.com/otf/I2P/language/uk_UA/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: uk_UA\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:23
|
||||
msgid "Start I2P"
|
||||
@ -46,12 +49,10 @@ msgstr "Перезапустити I2P"
|
||||
msgid "Stop I2P"
|
||||
msgstr "Зупинити I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:43
|
||||
msgid "Tray icon configuration"
|
||||
msgstr "Настройка трей-іконки"
|
||||
msgstr "Налаштування трей-іконки"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:46
|
||||
msgid "Should tray icon be enabled?"
|
||||
msgstr "Чм повинна трей-іконка бути включена?"
|
||||
|
||||
|
||||
msgstr "Чи повинна трей-іконка бути включена?"
|
||||
|
@ -20,7 +20,7 @@ public class ExternalTrayManager extends TrayManager {
|
||||
@Override
|
||||
public PopupMenu getMainMenu() {
|
||||
PopupMenu popup = new PopupMenu();
|
||||
MenuItem startItem = new MenuItem(_("Start I2P"));
|
||||
MenuItem startItem = new MenuItem(_t("Start I2P"));
|
||||
startItem.addActionListener(new ActionListener() {
|
||||
|
||||
@Override
|
||||
@ -35,7 +35,7 @@ public class ExternalTrayManager extends TrayManager {
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
trayIcon.displayMessage(_("Starting"), _("I2P is starting!"), TrayIcon.MessageType.INFO);
|
||||
trayIcon.displayMessage(_t("Starting"), _t("I2P is starting!"), TrayIcon.MessageType.INFO);
|
||||
//Hide the tray icon.
|
||||
//We cannot stop the desktopgui program entirely,
|
||||
//since that risks killing the I2P process as well.
|
||||
|
@ -23,7 +23,7 @@ public class InternalTrayManager extends TrayManager {
|
||||
public PopupMenu getMainMenu() {
|
||||
PopupMenu popup = new PopupMenu();
|
||||
|
||||
MenuItem browserLauncher = new MenuItem(_("Launch I2P Browser"));
|
||||
MenuItem browserLauncher = new MenuItem(_t("Launch I2P Browser"));
|
||||
browserLauncher.addActionListener(new ActionListener() {
|
||||
|
||||
@Override
|
||||
@ -47,7 +47,7 @@ public class InternalTrayManager extends TrayManager {
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
MenuItem desktopguiConfigurationLauncher = new MenuItem(_("Configure desktopgui"));
|
||||
MenuItem desktopguiConfigurationLauncher = new MenuItem(_t("Configure desktopgui"));
|
||||
desktopguiConfigurationLauncher.addActionListener(new ActionListener() {
|
||||
|
||||
@Override
|
||||
@ -64,7 +64,7 @@ public class InternalTrayManager extends TrayManager {
|
||||
}
|
||||
|
||||
});
|
||||
MenuItem restartItem = new MenuItem(_("Restart I2P"));
|
||||
MenuItem restartItem = new MenuItem(_t("Restart I2P"));
|
||||
restartItem.addActionListener(new ActionListener() {
|
||||
|
||||
@Override
|
||||
@ -82,7 +82,7 @@ public class InternalTrayManager extends TrayManager {
|
||||
}
|
||||
|
||||
});
|
||||
MenuItem stopItem = new MenuItem(_("Stop I2P"));
|
||||
MenuItem stopItem = new MenuItem(_t("Stop I2P"));
|
||||
stopItem.addActionListener(new ActionListener() {
|
||||
|
||||
@Override
|
||||
|
@ -78,7 +78,7 @@ public abstract class TrayManager {
|
||||
return image;
|
||||
}
|
||||
|
||||
protected static String _(String s) {
|
||||
return DesktopguiTranslator._(s);
|
||||
protected static String _t(String s) {
|
||||
return DesktopguiTranslator._t(s);
|
||||
}
|
||||
}
|
||||
|
@ -40,10 +40,10 @@ public class DesktopguiConfigurationFrame extends javax.swing.JFrame {
|
||||
cancelButton = new javax.swing.JButton();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setTitle(_("Tray icon configuration"));
|
||||
setTitle(_t("Tray icon configuration"));
|
||||
|
||||
desktopguiEnabled.setSelected(true);
|
||||
desktopguiEnabled.setText(_("Should tray icon be enabled?"));
|
||||
desktopguiEnabled.setText(_t("Should tray icon be enabled?"));
|
||||
desktopguiEnabled.setActionCommand("shouldDesktopguiBeEnabled");
|
||||
|
||||
okButton.setText("OK");
|
||||
@ -98,8 +98,8 @@ public class DesktopguiConfigurationFrame extends javax.swing.JFrame {
|
||||
configureDesktopgui();
|
||||
}//GEN-LAST:event_okButtonMouseReleased
|
||||
|
||||
protected static String _(String s) {
|
||||
return DesktopguiTranslator._(s);
|
||||
protected static String _t(String s) {
|
||||
return DesktopguiTranslator._t(s);
|
||||
}
|
||||
|
||||
private void configureDesktopgui() {
|
||||
|
@ -16,11 +16,11 @@ public class DesktopguiTranslator {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public static String _(String s) {
|
||||
public static String _t(String s) {
|
||||
return Translate.getString(s, getRouterContext(), BUNDLE_NAME);
|
||||
}
|
||||
|
||||
public static String _(String s, Object o) {
|
||||
public static String _t(String s, Object o) {
|
||||
return Translate.getString(s, o, getRouterContext(), BUNDLE_NAME);
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ public class ConfigurationManager {
|
||||
* @return The value of a configuration: true if found, defaultValue if not found.
|
||||
*/
|
||||
public boolean getBooleanConfiguration(String arg, boolean defaultValue) {
|
||||
Boolean value = ((Boolean) booleanConfigurations.get("startWithI2P"));
|
||||
Boolean value = booleanConfigurations.get("startWithI2P");
|
||||
System.out.println(value);
|
||||
if(value != null) {
|
||||
return value;
|
||||
|
@ -1,340 +1 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
See ../../licenses/LICENSE-GPLv2.txt
|
||||
|
@ -1,24 +0,0 @@
|
||||
- I2PSnark:
|
||||
- add multitorrent support by checking the metainfo hash in the
|
||||
PeerAcceptor and feeding it off to the appropriate coordinator
|
||||
- add a web interface
|
||||
|
||||
- BEncode
|
||||
- Byte array length indicator can overflow.
|
||||
- Support really big BigNums (only 256 chars allowed now)
|
||||
- Better BEValue toString(). Uses stupid heuristic now for debugging.
|
||||
- Implemented bencoding.
|
||||
- Remove application level hack to calculate sha1 hash for metainfo
|
||||
(But can it be done as efficiently?)
|
||||
|
||||
- Storage
|
||||
- Check file name filter.
|
||||
|
||||
- TrackerClient
|
||||
- Support undocumented &numwant= request.
|
||||
|
||||
- PeerCoordinator
|
||||
- Disconnect from other seeds as soon as you are a seed yourself.
|
||||
|
||||
- Text UI
|
||||
- Make it completely silent.
|
@ -100,15 +100,15 @@
|
||||
<target name="war" depends="jar, bundle, warUpToDate, listChangedFiles" unless="war.uptodate" >
|
||||
<!-- set if unset -->
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
<copy todir="build/icons/.icons" >
|
||||
<fileset dir="../icons/" />
|
||||
<copy todir="build/resources/.resources" >
|
||||
<fileset dir="../resources/" />
|
||||
</copy>
|
||||
<!-- mime.properties must be in with the classes -->
|
||||
<copy file="../mime.properties" todir="build/obj/org/klomp/snark/web" />
|
||||
<war destfile="../i2psnark.war" webxml="../web.xml" >
|
||||
<!-- include only the web stuff, as of 0.7.12 the router will add i2psnark.jar to the classpath for the war -->
|
||||
<classes dir="./build/obj" includes="**/web/*" />
|
||||
<fileset dir="build/icons/" />
|
||||
<fileset dir="build/resources/" />
|
||||
<manifest>
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
@ -121,7 +121,7 @@
|
||||
|
||||
<target name="warUpToDate">
|
||||
<uptodate property="war.uptodate" targetfile="../i2psnark.war" >
|
||||
<srcfiles dir= "." includes="build/obj/org/klomp/snark/web/*.class ../icons/* ../web.xml" />
|
||||
<srcfiles dir= "." includes="build/obj/org/klomp/snark/web/*.class ../resources/**/* ../web.xml" />
|
||||
</uptodate>
|
||||
</target>
|
||||
|
||||
|
@ -25,12 +25,12 @@ then
|
||||
fi
|
||||
|
||||
# on windows, one must specify the path of commnad find
|
||||
# since windows has its own retarded version of find.
|
||||
# since windows has its own version of find.
|
||||
if which find|grep -q -i windows ; then
|
||||
export PATH=.:/bin:/usr/local/bin:$PATH
|
||||
fi
|
||||
# Fast mode - update ondemond
|
||||
# set LG2 to the language you need in envrionment varibales to enable this
|
||||
# set LG2 to the language you need in environment variables to enable this
|
||||
|
||||
# add ../java/ so the refs will work in the po file
|
||||
JPATHS="../java/src"
|
||||
@ -63,13 +63,13 @@ do
|
||||
echo "Updating the $i file from the tags..."
|
||||
# extract strings from java and jsp files, and update messages.po files
|
||||
# translate calls must be one of the forms:
|
||||
# _("foo")
|
||||
# _t("foo")
|
||||
# _x("foo")
|
||||
# To start a new translation, copy the header from an old translation to the new .po file,
|
||||
# then ant distclean poupdate.
|
||||
find $JPATHS -name *.java > $TMPFILE
|
||||
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 --add-comments\
|
||||
--keyword=_ --keyword=_x \
|
||||
--keyword=_t --keyword=_x \
|
||||
-o ${i}t
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
package org.klomp.snark;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
/**
|
||||
* Container of a byte array representing set and unset bits.
|
||||
@ -66,7 +68,7 @@ public class BitField
|
||||
|
||||
/**
|
||||
* This returns the actual byte array used. Changes to this array
|
||||
* effect this BitField. Note that some bits at the end of the byte
|
||||
* affect this BitField. Note that some bits at the end of the byte
|
||||
* array are supposed to be always unset if they represent bits
|
||||
* bigger then the size of the bitfield.
|
||||
*/
|
||||
@ -105,6 +107,37 @@ public class BitField
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given bit to false.
|
||||
*
|
||||
* @exception IndexOutOfBoundsException if bit is smaller then zero
|
||||
* bigger then size (inclusive).
|
||||
* @since 0.9.22
|
||||
*/
|
||||
public void clear(int bit)
|
||||
{
|
||||
if (bit < 0 || bit >= size)
|
||||
throw new IndexOutOfBoundsException(Integer.toString(bit));
|
||||
int index = bit/8;
|
||||
int mask = 128 >> (bit % 8);
|
||||
synchronized(this) {
|
||||
if ((bitfield[index] & mask) != 0) {
|
||||
count--;
|
||||
bitfield[index] &= ~mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all bits to true.
|
||||
*
|
||||
* @since 0.9.21
|
||||
*/
|
||||
public void setAll() {
|
||||
Arrays.fill(bitfield, (byte) 0xff);
|
||||
count = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the bit is set or false if it is not.
|
||||
*
|
||||
|
@ -54,7 +54,15 @@ public interface CompleteListener {
|
||||
*/
|
||||
public void gotPiece(Snark snark);
|
||||
|
||||
// not really listeners but the easiest way to get back to an optional SnarkManager
|
||||
/** not really listeners but the easiest way to get back to an optional SnarkManager */
|
||||
public long getSavedTorrentTime(Snark snark);
|
||||
public BitField getSavedTorrentBitField(Snark snark);
|
||||
/**
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public boolean getSavedPreserveNamesSetting(Snark snark);
|
||||
/**
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public long getSavedUploaded(Snark snark);
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ConnectException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
@ -213,6 +214,20 @@ class ConnectionAcceptor implements Runnable
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ConnectException ioe)
|
||||
{
|
||||
// This is presumed to be due to socket closing by I2PSnarkUtil.disconnect(),
|
||||
// which does not currently call our halt(), although it should
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Error while accepting", ioe);
|
||||
synchronized(this) {
|
||||
if (!stop) {
|
||||
locked_halt();
|
||||
thread = null;
|
||||
stop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
int level = stop ? Log.WARN : Log.ERROR;
|
||||
|
@ -31,7 +31,7 @@ abstract class ExtensionHandler {
|
||||
public static final int ID_DHT = 3;
|
||||
/** not using the option bit since the compact format is different */
|
||||
public static final String TYPE_DHT = "i2p_dht";
|
||||
/** Pieces * SHA1 Hash length, + 25% extra for file names, benconding overhead, etc */
|
||||
/** Pieces * SHA1 Hash length, + 25% extra for file names, bencoding overhead, etc */
|
||||
private static final int MAX_METADATA_SIZE = Storage.MAX_PIECES * 20 * 5 / 4;
|
||||
private static final int PARALLEL_REQUESTS = 3;
|
||||
|
||||
@ -42,7 +42,7 @@ abstract class ExtensionHandler {
|
||||
* @param dht advertise DHT capability
|
||||
* @return bencoded outgoing handshake message
|
||||
*/
|
||||
public static byte[] getHandshake(int metasize, boolean pexAndMetadata, boolean dht) {
|
||||
public static byte[] getHandshake(int metasize, boolean pexAndMetadata, boolean dht, boolean uploadOnly) {
|
||||
Map<String, Object> handshake = new HashMap<String, Object>();
|
||||
Map<String, Integer> m = new HashMap<String, Integer>();
|
||||
if (pexAndMetadata) {
|
||||
@ -59,6 +59,9 @@ abstract class ExtensionHandler {
|
||||
handshake.put("p", Integer.valueOf(TrackerClient.PORT));
|
||||
handshake.put("v", "I2PSnark");
|
||||
handshake.put("reqq", Integer.valueOf(5));
|
||||
// BEP 21
|
||||
if (uploadOnly)
|
||||
handshake.put("upload_only", Integer.valueOf(1));
|
||||
return BEncoder.bencode(handshake);
|
||||
}
|
||||
|
||||
@ -90,17 +93,20 @@ abstract class ExtensionHandler {
|
||||
peer.setHandshakeMap(map);
|
||||
Map<String, BEValue> msgmap = map.get("m").getMap();
|
||||
|
||||
if (msgmap.get(TYPE_PEX) != null) {
|
||||
if (log.shouldLog(Log.DEBUG))
|
||||
log.debug("Peer supports PEX extension: " + peer);
|
||||
// peer state calls peer listener calls sendPEX()
|
||||
}
|
||||
if (log.shouldLog(Log.DEBUG))
|
||||
log.debug("Peer " + peer + " supports extensions: " + msgmap.keySet());
|
||||
|
||||
if (msgmap.get(TYPE_DHT) != null) {
|
||||
if (log.shouldLog(Log.DEBUG))
|
||||
log.debug("Peer supports DHT extension: " + peer);
|
||||
// peer state calls peer listener calls sendDHT()
|
||||
}
|
||||
//if (msgmap.get(TYPE_PEX) != null) {
|
||||
// if (log.shouldLog(Log.DEBUG))
|
||||
// log.debug("Peer supports PEX extension: " + peer);
|
||||
// // peer state calls peer listener calls sendPEX()
|
||||
//}
|
||||
|
||||
//if (msgmap.get(TYPE_DHT) != null) {
|
||||
// if (log.shouldLog(Log.DEBUG))
|
||||
// log.debug("Peer supports DHT extension: " + peer);
|
||||
// // peer state calls peer listener calls sendDHT()
|
||||
//}
|
||||
|
||||
MagnetState state = peer.getMagnetState();
|
||||
|
||||
@ -204,30 +210,31 @@ abstract class ExtensionHandler {
|
||||
if (log.shouldLog(Log.DEBUG))
|
||||
log.debug("Got request for " + piece + " from: " + peer);
|
||||
byte[] pc;
|
||||
int totalSize;
|
||||
synchronized(state) {
|
||||
pc = state.getChunk(piece);
|
||||
totalSize = state.getSize();
|
||||
}
|
||||
sendPiece(peer, piece, pc);
|
||||
sendPiece(peer, piece, pc, totalSize);
|
||||
// Do this here because PeerConnectionOut only reports for PIECE messages
|
||||
peer.uploaded(pc.length);
|
||||
listener.uploaded(peer, pc.length);
|
||||
} else if (type == TYPE_DATA) {
|
||||
int size = map.get("total_size").getInt();
|
||||
if (log.shouldLog(Log.DEBUG))
|
||||
log.debug("Got data for " + piece + " length " + size + " from: " + peer);
|
||||
// On close reading of BEP 9, this is the total metadata size.
|
||||
// Prior to 0.9.21, we sent the piece size, so we can't count on it.
|
||||
// just ignore it. The actual length will be verified in saveChunk()
|
||||
//int size = map.get("total_size").getInt();
|
||||
//if (log.shouldLog(Log.DEBUG))
|
||||
// log.debug("Got data for " + piece + " length " + size + " from: " + peer);
|
||||
boolean done;
|
||||
int chk = -1;
|
||||
synchronized(state) {
|
||||
if (state.isComplete())
|
||||
return;
|
||||
int len = is.available();
|
||||
if (len != size) {
|
||||
// probably fatal
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("total_size " + size + " but avail data " + len);
|
||||
}
|
||||
peer.downloaded(len);
|
||||
listener.downloaded(peer, len);
|
||||
// this checks the size
|
||||
done = state.saveChunk(piece, bs, bs.length - len, len);
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("Got chunk " + piece + " from " + peer);
|
||||
@ -290,11 +297,15 @@ abstract class ExtensionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendPiece(Peer peer, int piece, byte[] data) {
|
||||
private static void sendPiece(Peer peer, int piece, byte[] data, int totalSize) {
|
||||
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));
|
||||
// BEP 9
|
||||
// "This key has the same semantics as the 'metadata_size' in the extension header"
|
||||
// which apparently means the same value. Fixed in 0.9.21.
|
||||
//map.put("total_size", Integer.valueOf(data.length));
|
||||
map.put("total_size", Integer.valueOf(totalSize));
|
||||
byte[] dict = BEncoder.bencode(map);
|
||||
byte[] payload = new byte[dict.length + data.length];
|
||||
System.arraycopy(dict, 0, payload, 0, dict.length);
|
||||
|
@ -3,14 +3,18 @@ package org.klomp.snark;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
@ -71,9 +75,7 @@ public class I2PSnarkUtil {
|
||||
private static final int EEPGET_CONNECT_TIMEOUT_SHORT = 5*1000;
|
||||
public static final int DEFAULT_STARTUP_DELAY = 3;
|
||||
public static final boolean DEFAULT_USE_OPENTRACKERS = true;
|
||||
public static final String DEFAULT_OPENTRACKERS = "http://tracker.welterde.i2p/a";
|
||||
public static final int DEFAULT_MAX_UP_BW = 8; //KBps
|
||||
public static final int MAX_CONNECTIONS = 16; // per torrent
|
||||
public static final int MAX_CONNECTIONS = 24; // 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";
|
||||
@ -95,18 +97,17 @@ public class I2PSnarkUtil {
|
||||
setI2CPConfig("127.0.0.1", 7654, null);
|
||||
_banlist = new ConcurrentHashSet<Hash>();
|
||||
_maxUploaders = Snark.MAX_TOTAL_UPLOADERS;
|
||||
_maxUpBW = DEFAULT_MAX_UP_BW;
|
||||
_maxUpBW = SnarkManager.DEFAULT_MAX_UP_BW;
|
||||
_maxConnections = MAX_CONNECTIONS;
|
||||
_startupDelay = DEFAULT_STARTUP_DELAY;
|
||||
_shouldUseOT = DEFAULT_USE_OPENTRACKERS;
|
||||
// FIXME split if default has more than one
|
||||
_openTrackers = Collections.singletonList(DEFAULT_OPENTRACKERS);
|
||||
_openTrackers = Collections.emptyList();
|
||||
_shouldUseDHT = DEFAULT_USE_DHT;
|
||||
// This is used for both announce replies and .torrent file downloads,
|
||||
// so it must be available even if not connected to I2CP.
|
||||
// so much for multiple instances
|
||||
_tmpDir = new SecureDirectory(ctx.getTempDir(), baseName);
|
||||
FileUtil.rmdir(_tmpDir, false);
|
||||
_tmpDir = new SecureDirectory(ctx.getTempDir(), baseName + '-' + ctx.random().nextInt());
|
||||
//FileUtil.rmdir(_tmpDir, false);
|
||||
_tmpDir.mkdirs();
|
||||
}
|
||||
|
||||
@ -135,6 +136,7 @@ public class I2PSnarkUtil {
|
||||
|
||||
public boolean configured() { return _configured; }
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void setI2CPConfig(String i2cpHost, int i2cpPort, Map opts) {
|
||||
if (i2cpHost != null)
|
||||
_i2cpHost = i2cpHost;
|
||||
@ -255,6 +257,8 @@ public class I2PSnarkUtil {
|
||||
opts.setProperty("i2p.streaming.disableRejectLogging", "true");
|
||||
if (opts.getProperty("i2p.streaming.answerPings") == null)
|
||||
opts.setProperty("i2p.streaming.answerPings", "false");
|
||||
if (opts.getProperty(I2PClient.PROP_SIGTYPE) == null)
|
||||
opts.setProperty(I2PClient.PROP_SIGTYPE, "EdDSA_SHA512_Ed25519");
|
||||
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
|
||||
_connecting = false;
|
||||
}
|
||||
@ -328,7 +332,7 @@ public class I2PSnarkUtil {
|
||||
return rv;
|
||||
} catch (I2PException ie) {
|
||||
_banlist.add(dest);
|
||||
_context.simpleScheduler().addEvent(new Unbanlist(dest), 10*60*1000);
|
||||
_context.simpleTimer2().addEvent(new Unbanlist(dest), 10*60*1000);
|
||||
IOException ioe = new IOException("Unable to reach the peer " + peer);
|
||||
ioe.initCause(ie);
|
||||
throw ioe;
|
||||
@ -456,7 +460,7 @@ public class I2PSnarkUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
String getOurIPString() {
|
||||
public String getOurIPString() {
|
||||
Destination dest = getMyDestination();
|
||||
if (dest != null)
|
||||
return dest.toBase64();
|
||||
@ -565,12 +569,12 @@ public class I2PSnarkUtil {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/** @param ot non-null */
|
||||
/** @param ot non-null list of announce URLs */
|
||||
public void setOpenTrackers(List<String> ot) {
|
||||
_openTrackers = ot;
|
||||
}
|
||||
|
||||
/** List of open trackers to use as backups
|
||||
/** List of open tracker announce URLs to use as backups
|
||||
* @return non-null, possibly unmodifiable, empty if disabled
|
||||
*/
|
||||
public List<String> getOpenTrackers() {
|
||||
@ -580,7 +584,22 @@ public class I2PSnarkUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* List of open trackers to use as backups even if disabled
|
||||
* Is this announce URL probably for an open tracker?
|
||||
*
|
||||
* @since 0.9.17
|
||||
*/
|
||||
public boolean isKnownOpenTracker(String url) {
|
||||
try {
|
||||
URI u = new URI(url);
|
||||
String host = u.getHost();
|
||||
return host != null && SnarkManager.KNOWN_OPENTRACKERS.contains(host);
|
||||
} catch (URISyntaxException use) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List of open tracker announce URLs to use as backups even if disabled
|
||||
* @return non-null
|
||||
* @since 0.9.4
|
||||
*/
|
||||
@ -642,7 +661,7 @@ public class I2PSnarkUtil {
|
||||
* The {0} will be replaced by the parameter.
|
||||
* Single quotes must be doubled, i.e. ' -> '' in the string.
|
||||
* @param o parameter, not translated.
|
||||
* To tranlslate parameter also, use _("foo {0} bar", _("baz"))
|
||||
* To translate parameter also, use _t("foo {0} bar", _t("baz"))
|
||||
* Do not double the single quotes in the parameter.
|
||||
* Use autoboxing to call with ints, longs, floats, etc.
|
||||
*/
|
||||
|
@ -29,6 +29,9 @@ class IdleChecker extends SimpleTimer2.TimedEvent {
|
||||
private int _consec;
|
||||
private int _consecNotRunning;
|
||||
private boolean _isIdle;
|
||||
private String _lastIn = "3";
|
||||
private String _lastOut = "3";
|
||||
private final Object _lock = new Object();
|
||||
|
||||
private static final long CHECK_TIME = 63*1000;
|
||||
private static final int MAX_CONSEC_IDLE = 4;
|
||||
@ -46,16 +49,19 @@ class IdleChecker extends SimpleTimer2.TimedEvent {
|
||||
}
|
||||
|
||||
public void timeReached() {
|
||||
synchronized (_lock) {
|
||||
locked_timeReached();
|
||||
}
|
||||
}
|
||||
|
||||
private void locked_timeReached() {
|
||||
if (_util.connected()) {
|
||||
boolean torrentRunning = false;
|
||||
boolean hasPeers = false;
|
||||
int peerCount = 0;
|
||||
for (PeerCoordinator pc : _pcs) {
|
||||
if (!pc.halted()) {
|
||||
torrentRunning = true;
|
||||
if (pc.getPeers() > 0) {
|
||||
hasPeers = true;
|
||||
break;
|
||||
}
|
||||
peerCount += pc.getPeers();
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,25 +72,29 @@ class IdleChecker extends SimpleTimer2.TimedEvent {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Closing tunnels on idle");
|
||||
_util.disconnect();
|
||||
_mgr.addMessage(_util.getString("I2P tunnel closed."));
|
||||
_mgr.addMessage(_util.getString("No more torrents running.") + ' ' +
|
||||
_util.getString("I2P tunnel closed."));
|
||||
schedule(3 * CHECK_TIME);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasPeers) {
|
||||
if (_isIdle)
|
||||
restoreTunnels();
|
||||
if (peerCount > 0) {
|
||||
restoreTunnels(peerCount);
|
||||
} else {
|
||||
if (!_isIdle) {
|
||||
if (_consec++ >= MAX_CONSEC_IDLE)
|
||||
reduceTunnels();
|
||||
else
|
||||
restoreTunnels(1); // pretend we have one peer for now
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_isIdle = false;
|
||||
_consec = 0;
|
||||
_consecNotRunning = 0;
|
||||
_lastIn = "3";
|
||||
_lastOut = "3";
|
||||
}
|
||||
schedule(CHECK_TIME);
|
||||
}
|
||||
@ -100,26 +110,50 @@ class IdleChecker extends SimpleTimer2.TimedEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore tunnel count
|
||||
* Restore or adjust tunnel count based on current peer count
|
||||
* @param peerCount greater than zero
|
||||
*/
|
||||
private void restoreTunnels() {
|
||||
_isIdle = false;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
private void restoreTunnels(int peerCount) {
|
||||
if (_isIdle && _log.shouldLog(Log.INFO))
|
||||
_log.info("Restoring tunnels on activity");
|
||||
_isIdle = false;
|
||||
Map<String, String> opts = _util.getI2CPOptions();
|
||||
String i = opts.get("inbound.quantity");
|
||||
if (i == null)
|
||||
i = "3";
|
||||
i = Integer.toString(SnarkManager.DEFAULT_TUNNEL_QUANTITY);
|
||||
String o = opts.get("outbound.quantity");
|
||||
if (o == null)
|
||||
o = "3";
|
||||
o = Integer.toString(SnarkManager.DEFAULT_TUNNEL_QUANTITY);
|
||||
String ib = opts.get("inbound.backupQuantity");
|
||||
if (ib == null)
|
||||
ib = "0";
|
||||
String ob= opts.get("outbound.backupQuantity");
|
||||
if (ob == null)
|
||||
ob = "0";
|
||||
setTunnels(i, o, ib, ob);
|
||||
// we don't need more tunnels than we have peers, reduce if so
|
||||
// reduce to max(peerCount / 2, 2)
|
||||
int in, out;
|
||||
try {
|
||||
in = Integer.parseInt(i);
|
||||
} catch (NumberFormatException nfe) {
|
||||
in = 3;
|
||||
}
|
||||
try {
|
||||
out = Integer.parseInt(o);
|
||||
} catch (NumberFormatException nfe) {
|
||||
out = 3;
|
||||
}
|
||||
int target = Math.max(peerCount / 2, 2);
|
||||
if (target < in && in > 2) {
|
||||
in = target;
|
||||
i = Integer.toString(in);
|
||||
}
|
||||
if (target < out && out > 2) {
|
||||
out = target;
|
||||
o = Integer.toString(out);
|
||||
}
|
||||
if (!(_lastIn.equals(i) && _lastOut.equals(o)))
|
||||
setTunnels(i, o, ib, ob);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,12 +165,16 @@ class IdleChecker extends SimpleTimer2.TimedEvent {
|
||||
if (mgr != null) {
|
||||
I2PSession sess = mgr.getSession();
|
||||
if (sess != null) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("New tunnel settings " + i + " / " + o + " / " + ib + " / " + ob);
|
||||
Properties newProps = new Properties();
|
||||
newProps.setProperty("inbound.quantity", i);
|
||||
newProps.setProperty("outbound.quantity", o);
|
||||
newProps.setProperty("inbound.backupQuantity", ib);
|
||||
newProps.setProperty("outbound.backupQuantity", ob);
|
||||
sess.updateOptions(newProps);
|
||||
_lastIn = i;
|
||||
_lastOut = o;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ class MagnetState {
|
||||
* @return true if this was the last piece
|
||||
* @throws NPE, IllegalArgumentException, IOException, ...
|
||||
*/
|
||||
public MetaInfo buildMetaInfo() throws Exception {
|
||||
private MetaInfo buildMetaInfo() throws Exception {
|
||||
// top map has nothing in it but the info map (no announce)
|
||||
Map<String, BEValue> map = new HashMap<String, BEValue>();
|
||||
InputStream is = new ByteArrayInputStream(metainfoBytes);
|
||||
|
@ -42,7 +42,7 @@ public class MagnetURI {
|
||||
name = util.getString("Magnet") + ' ' + ihash;
|
||||
String dn = getParam("dn", url);
|
||||
if (dn != null)
|
||||
name += " (" + Storage.filterName(dn) + ')';
|
||||
name += " (" + dn + ')';
|
||||
} else if (url.startsWith(MAGGOT)) {
|
||||
// maggot://0691e40aae02e552cfcb57af1dca56214680c0c5:0b557bbdf8718e95d352fbe994dec3a383e2ede7
|
||||
ihash = url.substring(MAGGOT.length()).trim();
|
||||
@ -82,7 +82,7 @@ public class MagnetURI {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return pretty name or null
|
||||
* @return pretty name or null, NOT HTML escaped
|
||||
*/
|
||||
public String getName() {
|
||||
return _name;
|
||||
@ -175,18 +175,25 @@ public class MagnetURI {
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode %xx encoding, convert to UTF-8 if necessary
|
||||
* Copied from i2ptunnel LocalHTTPServer
|
||||
* Decode %xx encoding, convert to UTF-8 if necessary.
|
||||
* Copied from i2ptunnel LocalHTTPServer.
|
||||
* Also converts '+' to ' ' so the dn parameter comes out right
|
||||
* These are coming in via a application/x-www-form-urlencoded form so
|
||||
* the pluses are in there...
|
||||
* hopefully any real + is encoded as %2B.
|
||||
*
|
||||
* @since 0.9.1
|
||||
*/
|
||||
private static String decode(String s) {
|
||||
if (!s.contains("%"))
|
||||
if (!(s.contains("%") || s.contains("+")))
|
||||
return s;
|
||||
StringBuilder buf = new StringBuilder(s.length());
|
||||
boolean utf8 = false;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c != '%') {
|
||||
if (c == '+') {
|
||||
buf.append(' ');
|
||||
} else if (c != '%') {
|
||||
buf.append(c);
|
||||
} else {
|
||||
try {
|
||||
|
@ -55,11 +55,13 @@ class Message
|
||||
byte type;
|
||||
|
||||
// Used for HAVE, REQUEST, PIECE and CANCEL messages.
|
||||
// Also SUGGEST, REJECT, ALLOWED_FAST
|
||||
// low byte used for EXTENSION message
|
||||
// low two bytes used for PORT message
|
||||
int piece;
|
||||
|
||||
// Used for REQUEST, PIECE and CANCEL messages.
|
||||
// Also REJECT
|
||||
int begin;
|
||||
int length;
|
||||
|
||||
@ -104,15 +106,18 @@ class Message
|
||||
int datalen = 1;
|
||||
|
||||
// piece is 4 bytes.
|
||||
if (type == HAVE || type == REQUEST || type == PIECE || type == CANCEL)
|
||||
if (type == HAVE || type == REQUEST || type == PIECE || type == CANCEL ||
|
||||
type == SUGGEST || type == REJECT || type == ALLOWED_FAST)
|
||||
datalen += 4;
|
||||
|
||||
// begin/offset is 4 bytes
|
||||
if (type == REQUEST || type == PIECE || type == CANCEL)
|
||||
if (type == REQUEST || type == PIECE || type == CANCEL ||
|
||||
type == REJECT)
|
||||
datalen += 4;
|
||||
|
||||
// length is 4 bytes
|
||||
if (type == REQUEST || type == CANCEL)
|
||||
if (type == REQUEST || type == CANCEL ||
|
||||
type == REJECT)
|
||||
datalen += 4;
|
||||
|
||||
// msg type is 1 byte
|
||||
@ -131,15 +136,18 @@ class Message
|
||||
dos.writeByte(type & 0xFF);
|
||||
|
||||
// Send additional info (piece number)
|
||||
if (type == HAVE || type == REQUEST || type == PIECE || type == CANCEL)
|
||||
if (type == HAVE || type == REQUEST || type == PIECE || type == CANCEL ||
|
||||
type == SUGGEST || type == REJECT || type == ALLOWED_FAST)
|
||||
dos.writeInt(piece);
|
||||
|
||||
// Send additional info (begin/offset)
|
||||
if (type == REQUEST || type == PIECE || type == CANCEL)
|
||||
if (type == REQUEST || type == PIECE || type == CANCEL ||
|
||||
type == REJECT)
|
||||
dos.writeInt(begin);
|
||||
|
||||
// Send additional info (length); for PIECE this is implicit.
|
||||
if (type == REQUEST || type == CANCEL)
|
||||
if (type == REQUEST || type == CANCEL ||
|
||||
type == REJECT)
|
||||
dos.writeInt(length);
|
||||
|
||||
if (type == EXTENSION)
|
||||
@ -173,21 +181,32 @@ class Message
|
||||
case UNINTERESTED:
|
||||
return "UNINTERESTED";
|
||||
case HAVE:
|
||||
return "HAVE(" + piece + ")";
|
||||
return "HAVE(" + piece + ')';
|
||||
case BITFIELD:
|
||||
return "BITFIELD";
|
||||
case REQUEST:
|
||||
return "REQUEST(" + piece + "," + begin + "," + length + ")";
|
||||
return "REQUEST(" + piece + ',' + begin + ',' + length + ')';
|
||||
case PIECE:
|
||||
return "PIECE(" + piece + "," + begin + "," + length + ")";
|
||||
return "PIECE(" + piece + ',' + begin + ',' + length + ')';
|
||||
case CANCEL:
|
||||
return "CANCEL(" + piece + "," + begin + "," + length + ")";
|
||||
return "CANCEL(" + piece + ',' + begin + ',' + length + ')';
|
||||
case PORT:
|
||||
return "PORT(" + piece + ")";
|
||||
return "PORT(" + piece + ')';
|
||||
case EXTENSION:
|
||||
return "EXTENSION(" + piece + ',' + data.length + ')';
|
||||
// fast extensions below here
|
||||
case SUGGEST:
|
||||
return "SUGGEST(" + piece + ')';
|
||||
case HAVE_ALL:
|
||||
return "HAVE_ALL";
|
||||
case HAVE_NONE:
|
||||
return "HAVE_NONE";
|
||||
case REJECT:
|
||||
return "REJECT(" + piece + ',' + begin + ',' + length + ')';
|
||||
case ALLOWED_FAST:
|
||||
return "ALLOWED_FAST(" + piece + ')';
|
||||
default:
|
||||
return "<UNKNOWN>";
|
||||
return "UNKNOWN (" + type + ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,10 +74,11 @@ public class MetaInfo
|
||||
* @param files null for single-file torrent
|
||||
* @param lengths null for single-file torrent
|
||||
* @param announce_list may be null
|
||||
* @param created_by may be null
|
||||
*/
|
||||
MetaInfo(String announce, String name, String name_utf8, List<List<String>> files, List<Long> lengths,
|
||||
int piece_length, byte[] piece_hashes, long length, boolean privateTorrent,
|
||||
List<List<String>> announce_list)
|
||||
List<List<String>> announce_list, String created_by)
|
||||
{
|
||||
this.announce = announce;
|
||||
this.name = name;
|
||||
@ -91,8 +92,8 @@ public class MetaInfo
|
||||
this.privateTorrent = privateTorrent;
|
||||
this.announce_list = announce_list;
|
||||
this.comment = null;
|
||||
this.created_by = null;
|
||||
this.creation_date = 0;
|
||||
this.created_by = created_by;
|
||||
this.creation_date = I2PAppContext.getGlobalContext().clock().now();
|
||||
|
||||
// TODO if we add a parameter for other keys
|
||||
//if (other != null) {
|
||||
@ -220,7 +221,9 @@ public class MetaInfo
|
||||
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.
|
||||
// Transmission does numbers. So does libtorrent.
|
||||
// We handle both as of 0.9.9.
|
||||
// We switch to storing as number as of 0.9.14.
|
||||
privateTorrent = "1".equals(o) ||
|
||||
((o instanceof Number) && ((Number) o).intValue() == 1);
|
||||
} else {
|
||||
@ -442,7 +445,7 @@ public class MetaInfo
|
||||
|
||||
/**
|
||||
* The creation date (ms) or zero.
|
||||
* Not available for locally-created torrents.
|
||||
* As of 0.9.19, available for locally-created torrents.
|
||||
* @since 0.9.7
|
||||
*/
|
||||
public long getCreationDate() {
|
||||
@ -515,9 +518,10 @@ public class MetaInfo
|
||||
|
||||
sha1.update(bs, off, length);
|
||||
byte[] hash = sha1.digest();
|
||||
for (int i = 0; i < 20; i++)
|
||||
for (int i = 0; i < 20; i++) {
|
||||
if (hash[i] != piece_hashes[20 * piece + i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -537,9 +541,10 @@ public class MetaInfo
|
||||
_log.warn("Error checking", ioe);
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < 20; i++)
|
||||
for (int i = 0; i < 20; i++) {
|
||||
if (hash[i] != piece_hashes[20 * piece + i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -591,6 +596,14 @@ public class MetaInfo
|
||||
m.put("announce", announce);
|
||||
if (announce_list != null)
|
||||
m.put("announce-list", announce_list);
|
||||
// misc. optional top-level stuff
|
||||
if (comment != null)
|
||||
m.put("comment", comment);
|
||||
if (created_by != null)
|
||||
m.put("created by", created_by);
|
||||
if (creation_date != 0)
|
||||
m.put("creation date", creation_date / 1000);
|
||||
|
||||
Map<String, BEValue> info = createInfoMap();
|
||||
m.put("info", info);
|
||||
// don't save this locally, we should only do this once
|
||||
@ -621,7 +634,9 @@ public class MetaInfo
|
||||
info.put("name.utf-8", new BEValue(DataHelper.getUTF8(name_utf8)));
|
||||
// BEP 27
|
||||
if (privateTorrent)
|
||||
info.put("private", new BEValue(DataHelper.getUTF8("1")));
|
||||
// switched to number in 0.9.14
|
||||
//info.put("private", new BEValue(DataHelper.getUTF8("1")));
|
||||
info.put("private", new BEValue(Integer.valueOf(1)));
|
||||
|
||||
info.put("piece length", new BEValue(Integer.valueOf(piece_length)));
|
||||
info.put("pieces", new BEValue(piece_hashes));
|
||||
|
@ -108,7 +108,8 @@ class PartialPiece implements Comparable<PartialPiece> {
|
||||
|
||||
/**
|
||||
* Convert this PartialPiece to a request for the next chunk.
|
||||
* Used by PeerState only.
|
||||
* Used by PeerState only. This depends on the downloaded value
|
||||
* as set by setDownloaded() or read().
|
||||
*/
|
||||
|
||||
public Request getRequest() {
|
||||
@ -128,14 +129,16 @@ class PartialPiece implements Comparable<PartialPiece> {
|
||||
}
|
||||
|
||||
/**
|
||||
* How many bytes are good - only valid by setDownloaded()
|
||||
* How many bytes are good - as set by setDownloaded() or read()
|
||||
*/
|
||||
public int getDownloaded() {
|
||||
return this.off;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this before returning a PartialPiece to the PeerCoordinator
|
||||
* Call this if necessary before returning a PartialPiece to the PeerCoordinator.
|
||||
* We do not use a bitmap to track individual chunks received.
|
||||
* Any chunks after a 'hole' will be lost.
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public void setDownloaded(int offset) {
|
||||
@ -191,11 +194,20 @@ class PartialPiece implements Comparable<PartialPiece> {
|
||||
|
||||
/**
|
||||
* Blocking.
|
||||
* If offset matches the previous downloaded amount
|
||||
* (as set by a previous call to read() or setDownlaoded()),
|
||||
* the downloaded amount will be incremented by len.
|
||||
*
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public void read(DataInputStream din, int off, int len) throws IOException {
|
||||
public void read(DataInputStream din, int offset, int len) throws IOException {
|
||||
if (bs != null) {
|
||||
din.readFully(bs, off, len);
|
||||
din.readFully(bs, offset, len);
|
||||
synchronized (this) {
|
||||
// only works for in-order chunks
|
||||
if (this.off == offset)
|
||||
this.off += len;
|
||||
}
|
||||
} else {
|
||||
// read in fully before synching on raf
|
||||
ByteArray ba;
|
||||
@ -211,8 +223,11 @@ class PartialPiece implements Comparable<PartialPiece> {
|
||||
synchronized (this) {
|
||||
if (raf == null)
|
||||
createTemp();
|
||||
raf.seek(off);
|
||||
raf.seek(offset);
|
||||
raf.write(tmp);
|
||||
// only works for in-order chunks
|
||||
if (this.off == offset)
|
||||
this.off += len;
|
||||
}
|
||||
if (ba != null)
|
||||
_cache.release(ba, false);
|
||||
|
@ -57,12 +57,12 @@ public class Peer implements Comparable<Peer>
|
||||
private DataOutputStream dout;
|
||||
|
||||
/** running counters */
|
||||
private long downloaded;
|
||||
private long uploaded;
|
||||
private final AtomicLong downloaded = new AtomicLong();
|
||||
private final AtomicLong uploaded = new AtomicLong();
|
||||
|
||||
// Keeps state for in/out connections. Non-null when the handshake
|
||||
// was successful, the connection setup and runs
|
||||
PeerState state;
|
||||
volatile PeerState state;
|
||||
|
||||
/** shared across all peers on this torrent */
|
||||
MagnetState magnetState;
|
||||
@ -79,15 +79,15 @@ public class Peer implements Comparable<Peer>
|
||||
private long uploaded_old[] = {-1,-1,-1};
|
||||
private long downloaded_old[] = {-1,-1,-1};
|
||||
|
||||
// bytes per bt spec: 0011223344556677
|
||||
static final long OPTION_EXTENSION = 0x0000000000100000l;
|
||||
static final long OPTION_FAST = 0x0000000000000004l;
|
||||
static final long OPTION_DHT = 0x0000000000000001l;
|
||||
// bytes per bt spec: 0011223344556677
|
||||
private static final long OPTION_EXTENSION = 0x0000000000100000l;
|
||||
private static final long OPTION_FAST = 0x0000000000000004l;
|
||||
//private static final long OPTION_DHT = 0x0000000000000001l;
|
||||
/** we use a different bit since the compact format is different */
|
||||
/* no, let's use an extension message
|
||||
static final long OPTION_I2P_DHT = 0x0000000040000000l;
|
||||
*/
|
||||
static final long OPTION_AZMP = 0x1000000000000000l;
|
||||
//private static final long OPTION_AZMP = 0x1000000000000000l;
|
||||
private long options;
|
||||
|
||||
/**
|
||||
@ -194,9 +194,9 @@ public class Peer implements Comparable<Peer>
|
||||
* Compares the PeerIDs.
|
||||
* @deprecated unused?
|
||||
*/
|
||||
public int compareTo(Peer o)
|
||||
@Deprecated
|
||||
public int compareTo(Peer p)
|
||||
{
|
||||
Peer p = (Peer)o;
|
||||
int rv = peerID.compareTo(p.peerID);
|
||||
if (rv == 0) {
|
||||
if (_id > p._id) return 1;
|
||||
@ -218,9 +218,11 @@ public class Peer implements Comparable<Peer>
|
||||
*
|
||||
* If the given BitField is non-null it is send to the peer as first
|
||||
* message.
|
||||
*
|
||||
* @param uploadOnly if we are complete with skipped files, i.e. a partial seed
|
||||
*/
|
||||
public void runConnection(I2PSnarkUtil util, PeerListener listener, BitField bitfield, MagnetState mState)
|
||||
{
|
||||
public void runConnection(I2PSnarkUtil util, PeerListener listener, BitField bitfield,
|
||||
MagnetState mState, boolean uploadOnly) {
|
||||
if (state != null)
|
||||
throw new IllegalStateException("Peer already started");
|
||||
|
||||
@ -276,17 +278,9 @@ public class Peer implements Comparable<Peer>
|
||||
int metasize = metainfo != null ? metainfo.getInfoBytes().length : -1;
|
||||
boolean pexAndMetadata = metainfo == null || !metainfo.isPrivate();
|
||||
boolean dht = util.getDHT() != null;
|
||||
out.sendExtension(0, ExtensionHandler.getHandshake(metasize, pexAndMetadata, dht));
|
||||
out.sendExtension(0, ExtensionHandler.getHandshake(metasize, pexAndMetadata, dht, uploadOnly));
|
||||
}
|
||||
|
||||
// Old DHT PORT message
|
||||
//if ((options & OPTION_I2P_DHT) != 0 && util.getDHT() != null) {
|
||||
// if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Peer supports DHT, sending PORT message");
|
||||
// int port = util.getDHT().getPort();
|
||||
// out.sendPort(port);
|
||||
//}
|
||||
|
||||
// Send our bitmap
|
||||
if (bitfield != null)
|
||||
s.out.sendBitfield(bitfield);
|
||||
@ -298,7 +292,7 @@ public class Peer implements Comparable<Peer>
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Start running the reader with " + toString());
|
||||
// Use this thread for running the incomming connection.
|
||||
// Use this thread for running the incoming connection.
|
||||
// The outgoing connection creates its own Thread.
|
||||
out.startup();
|
||||
Thread.currentThread().setName("Snark reader from " + peerID);
|
||||
@ -339,6 +333,9 @@ public class Peer implements Comparable<Peer>
|
||||
dout.write("BitTorrent protocol".getBytes("UTF-8"));
|
||||
// Handshake write - options
|
||||
long myOptions = OPTION_EXTENSION;
|
||||
// we can't handle HAVE_ALL or HAVE_NONE if we don't know the number of pieces
|
||||
if (metainfo != null)
|
||||
myOptions |= OPTION_FAST;
|
||||
// FIXME get util here somehow
|
||||
//if (util.getDHT() != null)
|
||||
// myOptions |= OPTION_I2P_DHT;
|
||||
@ -386,15 +383,15 @@ public class Peer implements Comparable<Peer>
|
||||
if (options != 0) {
|
||||
// send them something in runConnection() above
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Peer supports options 0x" + Long.toString(options, 16) + ": " + toString());
|
||||
_log.debug("Peer supports options 0x" + Long.toHexString(options) + ": " + toString());
|
||||
}
|
||||
|
||||
return bs;
|
||||
}
|
||||
|
||||
/** @since 0.8.4 */
|
||||
public long getOptions() {
|
||||
return options;
|
||||
/** @since 0.9.21 */
|
||||
public boolean supportsFast() {
|
||||
return (options & OPTION_FAST) != 0;
|
||||
}
|
||||
|
||||
/** @since 0.8.4 */
|
||||
@ -535,6 +532,7 @@ public class Peer implements Comparable<Peer>
|
||||
* @deprecated deadlocks
|
||||
* @since 0.8.1
|
||||
*/
|
||||
@Deprecated
|
||||
boolean isRequesting(int p) {
|
||||
PeerState s = state;
|
||||
return s != null && s.isRequesting(p);
|
||||
@ -567,6 +565,7 @@ public class Peer implements Comparable<Peer>
|
||||
* us then we start downloading from it. Has no effect when not connected.
|
||||
* @deprecated unused
|
||||
*/
|
||||
@Deprecated
|
||||
public void setInteresting(boolean interest)
|
||||
{
|
||||
PeerState s = state;
|
||||
@ -619,7 +618,7 @@ public class Peer implements Comparable<Peer>
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public void downloaded(int size) {
|
||||
downloaded += size;
|
||||
downloaded.addAndGet(size);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -627,7 +626,7 @@ public class Peer implements Comparable<Peer>
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public void uploaded(int size) {
|
||||
uploaded += size;
|
||||
uploaded.addAndGet(size);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -636,7 +635,7 @@ public class Peer implements Comparable<Peer>
|
||||
*/
|
||||
public long getDownloaded()
|
||||
{
|
||||
return downloaded;
|
||||
return downloaded.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -645,7 +644,7 @@ public class Peer implements Comparable<Peer>
|
||||
*/
|
||||
public long getUploaded()
|
||||
{
|
||||
return uploaded;
|
||||
return uploaded.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -653,8 +652,8 @@ public class Peer implements Comparable<Peer>
|
||||
*/
|
||||
public void resetCounters()
|
||||
{
|
||||
downloaded = 0;
|
||||
uploaded = 0;
|
||||
downloaded.set(0);
|
||||
uploaded.set(0);
|
||||
}
|
||||
|
||||
public long getInactiveTime() {
|
||||
|
@ -75,6 +75,8 @@ class PeerCheckerTask implements Runnable
|
||||
List<Peer> removed = new ArrayList<Peer>();
|
||||
int uploadLimit = coordinator.allowedUploaders();
|
||||
boolean overBWLimit = coordinator.overUpBWLimit();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("peers: " + peerList.size() + " limit: " + uploadLimit + " overBW? " + overBWLimit);
|
||||
DHT dht = _util.getDHT();
|
||||
for (Peer peer : peerList) {
|
||||
|
||||
@ -221,7 +223,8 @@ class PeerCheckerTask implements Runnable
|
||||
peer.keepAlive();
|
||||
// announce them to local tracker (TrackerClient does this too)
|
||||
if (dht != null && (_runCount % 5) == 0) {
|
||||
dht.announce(coordinator.getInfoHash(), peer.getPeerID().getDestHash());
|
||||
dht.announce(coordinator.getInfoHash(), peer.getPeerID().getDestHash(),
|
||||
peer.isCompleted());
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,13 +267,29 @@ class PeerCheckerTask implements Runnable
|
||||
|
||||
// close out unused files, but we don't need to do it every time
|
||||
Storage storage = coordinator.getStorage();
|
||||
if (storage != null && (_runCount % 4) == 0) {
|
||||
if (storage != null) {
|
||||
// The more files a torrent has, the more often we call the cleaner,
|
||||
// to keep from running out of FDs
|
||||
int files = storage.getFileCount();
|
||||
int skip;
|
||||
if (files == 1)
|
||||
skip = 6;
|
||||
else if (files <= 4)
|
||||
skip = 4;
|
||||
else if (files <= 20)
|
||||
skip = 3;
|
||||
else if (files <= 50)
|
||||
skip = 2;
|
||||
else
|
||||
skip = 1;
|
||||
|
||||
if ((_runCount % skip) == 0)
|
||||
storage.cleanRAFs();
|
||||
}
|
||||
|
||||
// announce ourselves to local tracker (TrackerClient does this too)
|
||||
if (dht != null && (_runCount % 16) == 0) {
|
||||
dht.announce(coordinator.getInfoHash());
|
||||
dht.announce(coordinator.getInfoHash(), coordinator.completed());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class PeerConnectionIn implements Runnable
|
||||
private static final int MAX_MSG_SIZE = Math.max(PeerState.PARTSIZE + 9,
|
||||
MagnetState.CHUNK_SIZE + 100); // 100 for the ext msg dictionary
|
||||
|
||||
private Thread thread;
|
||||
private volatile Thread thread;
|
||||
private volatile boolean quit;
|
||||
|
||||
long lastRcvd;
|
||||
@ -75,9 +75,12 @@ class PeerConnectionIn implements Runnable
|
||||
thread = Thread.currentThread();
|
||||
try
|
||||
{
|
||||
PeerState ps = peer.state;
|
||||
while (!quit && ps != null)
|
||||
while (!quit)
|
||||
{
|
||||
final PeerState ps = peer.state;
|
||||
if (ps == null)
|
||||
break;
|
||||
|
||||
// Common variables used for some messages.
|
||||
int piece;
|
||||
int begin;
|
||||
@ -91,59 +94,64 @@ class PeerConnectionIn implements Runnable
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
ps.keepAliveMessage();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received keepalive from " + peer);
|
||||
ps.keepAliveMessage();
|
||||
continue;
|
||||
}
|
||||
|
||||
byte b = din.readByte();
|
||||
Message m = new Message();
|
||||
m.type = b;
|
||||
switch (b)
|
||||
{
|
||||
case 0:
|
||||
ps.chokeMessage(true);
|
||||
case Message.CHOKE:
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received choke from " + peer);
|
||||
ps.chokeMessage(true);
|
||||
break;
|
||||
case 1:
|
||||
ps.chokeMessage(false);
|
||||
|
||||
case Message.UNCHOKE:
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received unchoke from " + peer);
|
||||
ps.chokeMessage(false);
|
||||
break;
|
||||
case 2:
|
||||
ps.interestedMessage(true);
|
||||
|
||||
case Message.INTERESTED:
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received interested from " + peer);
|
||||
ps.interestedMessage(true);
|
||||
break;
|
||||
case 3:
|
||||
ps.interestedMessage(false);
|
||||
|
||||
case Message.UNINTERESTED:
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received not interested from " + peer);
|
||||
ps.interestedMessage(false);
|
||||
break;
|
||||
case 4:
|
||||
|
||||
case Message.HAVE:
|
||||
piece = din.readInt();
|
||||
ps.haveMessage(piece);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received havePiece(" + piece + ") from " + peer);
|
||||
ps.haveMessage(piece);
|
||||
break;
|
||||
case 5:
|
||||
|
||||
case Message.BITFIELD:
|
||||
byte[] bitmap = new byte[i-1];
|
||||
din.readFully(bitmap);
|
||||
ps.bitfieldMessage(bitmap);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received bitmap from " + peer + ": size=" + (i-1) /* + ": " + ps.bitfield */ );
|
||||
ps.bitfieldMessage(bitmap);
|
||||
break;
|
||||
case 6:
|
||||
|
||||
case Message.REQUEST:
|
||||
piece = din.readInt();
|
||||
begin = din.readInt();
|
||||
len = din.readInt();
|
||||
ps.requestMessage(piece, begin, len);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received request(" + piece + "," + begin + ") from " + peer);
|
||||
ps.requestMessage(piece, begin, len);
|
||||
break;
|
||||
case 7:
|
||||
|
||||
case Message.PIECE:
|
||||
piece = din.readInt();
|
||||
begin = din.readInt();
|
||||
len = i-9;
|
||||
@ -151,9 +159,9 @@ class PeerConnectionIn implements Runnable
|
||||
if (req != null)
|
||||
{
|
||||
req.read(din);
|
||||
ps.pieceMessage(req);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received data(" + piece + "," + begin + ") from " + peer);
|
||||
ps.pieceMessage(req);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -165,21 +173,24 @@ class PeerConnectionIn implements Runnable
|
||||
_log.debug("Received UNWANTED data(" + piece + "," + begin + ") from " + peer);
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
|
||||
case Message.CANCEL:
|
||||
piece = din.readInt();
|
||||
begin = din.readInt();
|
||||
len = din.readInt();
|
||||
ps.cancelMessage(piece, begin, len);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received cancel(" + piece + "," + begin + ") from " + peer);
|
||||
ps.cancelMessage(piece, begin, len);
|
||||
break;
|
||||
case 9: // PORT message
|
||||
|
||||
case Message.PORT:
|
||||
int port = din.readUnsignedShort();
|
||||
ps.portMessage(port);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received port message from " + peer);
|
||||
ps.portMessage(port);
|
||||
break;
|
||||
case 20: // Extension message
|
||||
|
||||
case Message.EXTENSION:
|
||||
int id = din.readUnsignedByte();
|
||||
byte[] payload = new byte[i-2];
|
||||
din.readFully(payload);
|
||||
@ -187,6 +198,43 @@ class PeerConnectionIn implements Runnable
|
||||
_log.debug("Received extension message from " + peer);
|
||||
ps.extensionMessage(id, payload);
|
||||
break;
|
||||
|
||||
// fast extensions below here
|
||||
case Message.SUGGEST:
|
||||
piece = din.readInt();
|
||||
ps.suggestMessage(piece);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received suggest(" + piece + ") from " + peer);
|
||||
break;
|
||||
|
||||
case Message.HAVE_ALL:
|
||||
ps.haveMessage(true);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received have_all from " + peer);
|
||||
break;
|
||||
|
||||
case Message.HAVE_NONE:
|
||||
ps.haveMessage(false);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received have_none from " + peer);
|
||||
break;
|
||||
|
||||
case Message.REJECT:
|
||||
piece = din.readInt();
|
||||
begin = din.readInt();
|
||||
len = din.readInt();
|
||||
ps.rejectMessage(piece, begin, len);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received reject(" + piece + ',' + begin + ',' + len + ") from " + peer);
|
||||
break;
|
||||
|
||||
case Message.ALLOWED_FAST:
|
||||
piece = din.readInt();
|
||||
ps.allowedFastMessage(piece);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received allowed_fast(" + piece + ") from " + peer);
|
||||
break;
|
||||
|
||||
default:
|
||||
byte[] bs = new byte[i-1];
|
||||
din.readFully(bs);
|
||||
@ -202,11 +250,9 @@ class PeerConnectionIn implements Runnable
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("IOError talking with " + peer, ioe);
|
||||
}
|
||||
catch (Throwable t)
|
||||
catch (RuntimeException t)
|
||||
{
|
||||
_log.error("Error talking with " + peer, t);
|
||||
if (t instanceof OutOfMemoryError)
|
||||
throw (OutOfMemoryError)t;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -22,15 +22,15 @@ package org.klomp.snark;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
//import net.i2p.util.SimpleScheduler;
|
||||
//import net.i2p.util.SimpleTimer;
|
||||
|
||||
class PeerConnectionOut implements Runnable
|
||||
@ -43,7 +43,7 @@ class PeerConnectionOut implements Runnable
|
||||
private boolean quit;
|
||||
|
||||
// Contains Messages.
|
||||
private final List<Message> sendQueue = new ArrayList<Message>();
|
||||
private final BlockingQueue<Message> sendQueue = new LinkedBlockingQueue<Message>();
|
||||
|
||||
private static final AtomicLong __id = new AtomicLong();
|
||||
private final long _id;
|
||||
@ -125,6 +125,16 @@ class PeerConnectionOut implements Runnable
|
||||
if (state.choking) {
|
||||
it.remove();
|
||||
//SimpleTimer.getInstance().removeEvent(nm.expireEvent);
|
||||
if (peer.supportsFast()) {
|
||||
Message r = new Message();
|
||||
r.type = Message.REJECT;
|
||||
r.piece = nm.piece;
|
||||
r.begin = nm.begin;
|
||||
r.length = nm.length;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Send " + peer + ": " + r);
|
||||
r.sendMessage(dout);
|
||||
}
|
||||
}
|
||||
nm = null;
|
||||
}
|
||||
@ -142,8 +152,8 @@ class PeerConnectionOut implements Runnable
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
if (m == null && !sendQueue.isEmpty()) {
|
||||
m = sendQueue.remove(0);
|
||||
if (m == null) {
|
||||
m = sendQueue.poll();
|
||||
//SimpleTimer.getInstance().removeEvent(m.expireEvent);
|
||||
}
|
||||
}
|
||||
@ -160,6 +170,8 @@ class PeerConnectionOut implements Runnable
|
||||
lastSent = System.currentTimeMillis();
|
||||
|
||||
// Remove all piece messages after sending a choke message.
|
||||
// FiXME this causes REJECT messages to be sent before sending the CHOKE;
|
||||
// BEP 6 recommends sending them after.
|
||||
if (m.type == Message.CHOKE)
|
||||
removeMessage(Message.PIECE);
|
||||
|
||||
@ -234,7 +246,7 @@ class PeerConnectionOut implements Runnable
|
||||
{
|
||||
synchronized(sendQueue)
|
||||
{
|
||||
sendQueue.add(m);
|
||||
sendQueue.offer(m);
|
||||
sendQueue.notifyAll();
|
||||
}
|
||||
}
|
||||
@ -278,11 +290,22 @@ class PeerConnectionOut implements Runnable
|
||||
while (it.hasNext())
|
||||
{
|
||||
Message m = it.next();
|
||||
if (m.type == type)
|
||||
{
|
||||
if (m.type == type) {
|
||||
it.remove();
|
||||
removed = true;
|
||||
}
|
||||
if (type == Message.PIECE && peer.supportsFast()) {
|
||||
Message r = new Message();
|
||||
r.type = Message.REJECT;
|
||||
r.piece = m.piece;
|
||||
r.begin = m.begin;
|
||||
r.length = m.length;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Send " + peer + ": " + r);
|
||||
try {
|
||||
r.sendMessage(dout);
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
sendQueue.notifyAll();
|
||||
}
|
||||
@ -297,7 +320,7 @@ class PeerConnectionOut implements Runnable
|
||||
synchronized(sendQueue)
|
||||
{
|
||||
if(sendQueue.isEmpty())
|
||||
sendQueue.add(m);
|
||||
sendQueue.offer(m);
|
||||
sendQueue.notifyAll();
|
||||
}
|
||||
}
|
||||
@ -350,12 +373,19 @@ class PeerConnectionOut implements Runnable
|
||||
|
||||
void sendBitfield(BitField bitfield)
|
||||
{
|
||||
Message m = new Message();
|
||||
m.type = Message.BITFIELD;
|
||||
m.data = bitfield.getFieldBytes();
|
||||
m.off = 0;
|
||||
m.len = m.data.length;
|
||||
addMessage(m);
|
||||
boolean fast = peer.supportsFast();
|
||||
if (fast && bitfield.complete()) {
|
||||
sendHaveAll();
|
||||
} else if (fast && bitfield.count() <= 0) {
|
||||
sendHaveNone();
|
||||
} else {
|
||||
Message m = new Message();
|
||||
m.type = Message.BITFIELD;
|
||||
m.data = bitfield.getFieldBytes();
|
||||
m.off = 0;
|
||||
m.len = m.data.length;
|
||||
addMessage(m);
|
||||
}
|
||||
}
|
||||
|
||||
/** reransmit requests not received in 7m */
|
||||
@ -480,7 +510,6 @@ class PeerConnectionOut implements Runnable
|
||||
m.len = length;
|
||||
// since we have the data already loaded, queue a timeout to remove it
|
||||
// no longer prefetched
|
||||
//SimpleScheduler.getInstance().addEvent(new RemoveTooSlow(m), SEND_TIMEOUT);
|
||||
addMessage(m);
|
||||
}
|
||||
|
||||
@ -511,7 +540,8 @@ class PeerConnectionOut implements Runnable
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all Request messages from the queue
|
||||
* Remove all Request messages from the queue.
|
||||
* Does not send a cancel message.
|
||||
* @since 0.8.2
|
||||
*/
|
||||
void cancelRequestMessages() {
|
||||
@ -523,9 +553,12 @@ class PeerConnectionOut implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
// Called by the PeerState when the other side doesn't want this
|
||||
// request to be handled anymore. Removes any pending Piece Message
|
||||
// from out send queue.
|
||||
/**
|
||||
* Called by the PeerState when the other side doesn't want this
|
||||
* request to be handled anymore. Removes any pending Piece Message
|
||||
* from out send queue.
|
||||
* Does not send a cancel message.
|
||||
*/
|
||||
void cancelRequest(int piece, int begin, int length)
|
||||
{
|
||||
synchronized (sendQueue)
|
||||
@ -561,4 +594,50 @@ class PeerConnectionOut implements Runnable
|
||||
m.piece = port;
|
||||
addMessage(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unused
|
||||
* @since 0.9.21
|
||||
*/
|
||||
void sendSuggest(int piece) {
|
||||
Message m = new Message();
|
||||
m.type = Message.SUGGEST;
|
||||
m.piece = piece;
|
||||
addMessage(m);
|
||||
}
|
||||
|
||||
/** @since 0.9.21 */
|
||||
private void sendHaveAll() {
|
||||
Message m = new Message();
|
||||
m.type = Message.HAVE_ALL;
|
||||
addMessage(m);
|
||||
}
|
||||
|
||||
/** @since 0.9.21 */
|
||||
private void sendHaveNone() {
|
||||
Message m = new Message();
|
||||
m.type = Message.HAVE_NONE;
|
||||
addMessage(m);
|
||||
}
|
||||
|
||||
/** @since 0.9.21 */
|
||||
void sendReject(int piece, int begin, int length) {
|
||||
Message m = new Message();
|
||||
m.type = Message.REJECT;
|
||||
m.piece = piece;
|
||||
m.begin = begin;
|
||||
m.length = length;
|
||||
addMessage(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unused
|
||||
* @since 0.9.21
|
||||
*/
|
||||
void sendAllowedFast(int piece) {
|
||||
Message m = new Message();
|
||||
m.type = Message.ALLOWED_FAST;
|
||||
m.piece = piece;
|
||||
addMessage(m);
|
||||
}
|
||||
}
|
||||
|
@ -25,14 +25,15 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
@ -52,7 +53,7 @@ import org.klomp.snark.dht.DHT;
|
||||
*/
|
||||
class PeerCoordinator implements PeerListener
|
||||
{
|
||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerCoordinator.class);
|
||||
private final Log _log;
|
||||
|
||||
/**
|
||||
* External use by PeerMonitorTask only.
|
||||
@ -69,7 +70,7 @@ class PeerCoordinator implements PeerListener
|
||||
|
||||
// package local for access by CheckDownLoadersTask
|
||||
final static long CHECK_PERIOD = 40*1000; // 40 seconds
|
||||
final static int MAX_UPLOADERS = 6;
|
||||
final static int MAX_UPLOADERS = 8;
|
||||
public static final long MAX_INACTIVE = 8*60*1000;
|
||||
|
||||
/**
|
||||
@ -87,8 +88,8 @@ class PeerCoordinator implements PeerListener
|
||||
// final static int MAX_DOWNLOADERS = MAX_CONNECTIONS;
|
||||
// int downloaders = 0;
|
||||
|
||||
private long uploaded;
|
||||
private long downloaded;
|
||||
private final AtomicLong uploaded = new AtomicLong();
|
||||
private final AtomicLong downloaded = new AtomicLong();
|
||||
final static int RATE_DEPTH = 3; // make following arrays RATE_DEPTH long
|
||||
private final long uploaded_old[] = {-1,-1,-1};
|
||||
private final long downloaded_old[] = {-1,-1,-1};
|
||||
@ -98,7 +99,7 @@ class PeerCoordinator implements PeerListener
|
||||
* This is a Queue, not a Set, because PeerCheckerTask keeps things in order for choking/unchoking.
|
||||
* External use by PeerMonitorTask only.
|
||||
*/
|
||||
final Queue<Peer> peers;
|
||||
final Deque<Peer> peers;
|
||||
|
||||
/**
|
||||
* Peers we heard about via PEX
|
||||
@ -144,6 +145,7 @@ class PeerCoordinator implements PeerListener
|
||||
{
|
||||
_util = util;
|
||||
_random = util.getContext().random();
|
||||
_log = util.getContext().logManager().getLog(PeerCoordinator.class);
|
||||
this.id = id;
|
||||
this.infohash = infohash;
|
||||
this.metainfo = metainfo;
|
||||
@ -154,7 +156,7 @@ class PeerCoordinator implements PeerListener
|
||||
wantedPieces = new ArrayList<Piece>();
|
||||
setWantedPieces();
|
||||
partialPieces = new ArrayList<PartialPiece>(getMaxConnections() + 1);
|
||||
peers = new LinkedBlockingQueue<Peer>();
|
||||
peers = new LinkedBlockingDeque<Peer>();
|
||||
magnetState = new MagnetState(infohash, metainfo);
|
||||
pexPeers = new ConcurrentHashSet<PeerID>();
|
||||
|
||||
@ -278,7 +280,15 @@ class PeerCoordinator implements PeerListener
|
||||
*/
|
||||
public long getUploaded()
|
||||
{
|
||||
return uploaded;
|
||||
return uploaded.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial total of uploaded bytes of all peers (from a saved status)
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public void setUploaded(long up) {
|
||||
uploaded.set(up);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -286,7 +296,7 @@ class PeerCoordinator implements PeerListener
|
||||
*/
|
||||
public long getDownloaded()
|
||||
{
|
||||
return downloaded;
|
||||
return downloaded.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -312,16 +322,22 @@ class PeerCoordinator implements PeerListener
|
||||
*/
|
||||
public long getDownloadRate()
|
||||
{
|
||||
if (halted)
|
||||
return 0;
|
||||
return getRate(downloaded_old);
|
||||
}
|
||||
|
||||
public long getUploadRate()
|
||||
{
|
||||
if (halted)
|
||||
return 0;
|
||||
return getRate(uploaded_old);
|
||||
}
|
||||
|
||||
public long getCurrentUploadRate()
|
||||
{
|
||||
if (halted)
|
||||
return 0;
|
||||
// no need to synchronize, only one value
|
||||
long r = uploaded_old[0];
|
||||
if (r <= 0)
|
||||
@ -387,7 +403,7 @@ class PeerCoordinator implements PeerListener
|
||||
* Formerly used to
|
||||
* reduce max if huge pieces to keep from ooming when leeching
|
||||
* but now we don't
|
||||
* @return usually 16
|
||||
* @return usually I2PSnarkUtil.MAX_CONNECTIONS
|
||||
*/
|
||||
private int getMaxConnections() {
|
||||
if (metainfo == null)
|
||||
@ -514,7 +530,10 @@ class PeerCoordinator implements PeerListener
|
||||
// Can't add to beginning since we converted from a List to a Queue
|
||||
// We can do this in Java 6 with a Deque
|
||||
//peers.add(0, peer);
|
||||
peers.add(peer);
|
||||
if (_util.getContext().random().nextInt(4) == 0)
|
||||
peers.push(peer);
|
||||
else
|
||||
peers.add(peer);
|
||||
peerCount = peers.size();
|
||||
unchokePeer();
|
||||
|
||||
@ -585,11 +604,13 @@ class PeerCoordinator implements PeerListener
|
||||
bitfield = storage.getBitField();
|
||||
else
|
||||
bitfield = null;
|
||||
// if we aren't a seed but we don't want any more
|
||||
final boolean partialComplete = wantedBytes == 0 && bitfield != null && !bitfield.complete();
|
||||
Runnable r = new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
peer.runConnection(_util, listener, bitfield, magnetState);
|
||||
peer.runConnection(_util, listener, bitfield, magnetState, partialComplete);
|
||||
}
|
||||
};
|
||||
String threadName = "Snark peer " + peer.toString();
|
||||
@ -901,6 +922,7 @@ class PeerCoordinator implements PeerListener
|
||||
* Returns a byte array containing the requested piece or null of
|
||||
* the piece is unknown.
|
||||
*
|
||||
* @return bytes or null for errors such as not having the piece yet
|
||||
* @throws RuntimeException on IOE getting the data
|
||||
*/
|
||||
public ByteArray gotRequest(Peer peer, int piece, int off, int len)
|
||||
@ -932,7 +954,7 @@ class PeerCoordinator implements PeerListener
|
||||
*/
|
||||
public void uploaded(Peer peer, int size)
|
||||
{
|
||||
uploaded += size;
|
||||
uploaded.addAndGet(size);
|
||||
|
||||
//if (listener != null)
|
||||
// listener.peerChange(this, peer);
|
||||
@ -943,7 +965,7 @@ class PeerCoordinator implements PeerListener
|
||||
*/
|
||||
public void downloaded(Peer peer, int size)
|
||||
{
|
||||
downloaded += size;
|
||||
downloaded.addAndGet(size);
|
||||
|
||||
//if (listener != null)
|
||||
// listener.peerChange(this, peer);
|
||||
@ -964,8 +986,9 @@ class PeerCoordinator implements PeerListener
|
||||
}
|
||||
int piece = pp.getPiece();
|
||||
|
||||
synchronized(wantedPieces)
|
||||
{
|
||||
// try/catch outside the synch to avoid deadlock in the catch
|
||||
try {
|
||||
synchronized(wantedPieces) {
|
||||
Piece p = new Piece(piece);
|
||||
if (!wantedPieces.contains(p))
|
||||
{
|
||||
@ -981,8 +1004,7 @@ class PeerCoordinator implements PeerListener
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// try/catch moved outside of synch
|
||||
// this takes forever if complete, as it rechecks
|
||||
if (storage.putPiece(pp))
|
||||
{
|
||||
@ -991,26 +1013,38 @@ class PeerCoordinator implements PeerListener
|
||||
}
|
||||
else
|
||||
{
|
||||
// so we will try again
|
||||
markUnrequested(peer, piece);
|
||||
// just in case
|
||||
removePartialPiece(piece);
|
||||
// Oops. We didn't actually download this then... :(
|
||||
downloaded -= metainfo.getPieceLength(piece);
|
||||
_log.warn("Got BAD piece " + piece + "/" + metainfo.getPieces() + " from " + peer + " for " + metainfo.getName());
|
||||
downloaded.addAndGet(0 - metainfo.getPieceLength(piece));
|
||||
// Mark this peer as not having the piece. PeerState will update its bitfield.
|
||||
for (Piece pc : wantedPieces) {
|
||||
if (pc.getId() == piece) {
|
||||
pc.removePeer(peer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Got BAD piece " + piece + "/" + metainfo.getPieces() + " from " + peer + " for " + metainfo.getName());
|
||||
return false; // No need to announce BAD piece to peers.
|
||||
}
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
|
||||
wantedPieces.remove(p);
|
||||
wantedBytes -= metainfo.getPieceLength(p.getId());
|
||||
} // synch
|
||||
} catch (IOException ioe) {
|
||||
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());
|
||||
}
|
||||
// deadlock was here
|
||||
snark.stopTorrent();
|
||||
throw new RuntimeException(msg, ioe);
|
||||
}
|
||||
wantedPieces.remove(p);
|
||||
wantedBytes -= metainfo.getPieceLength(p.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// just in case
|
||||
removePartialPiece(piece);
|
||||
@ -1122,8 +1156,9 @@ class PeerCoordinator implements PeerListener
|
||||
*
|
||||
* Also mark the piece unrequested if this peer was the only one.
|
||||
*
|
||||
* @param peer partials, must include the zero-offset (empty) ones too
|
||||
* No dup pieces, piece.setDownloaded() must be set
|
||||
* @param peer partials, must include the zero-offset (empty) ones too.
|
||||
* No dup pieces, piece.setDownloaded() must be set.
|
||||
* len field in Requests is ignored.
|
||||
* @since 0.8.2
|
||||
*/
|
||||
public void savePartialPieces(Peer peer, List<Request> partials)
|
||||
@ -1202,22 +1237,23 @@ class PeerCoordinator implements PeerListener
|
||||
boolean skipped = false;
|
||||
for(Piece piece : wantedPieces) {
|
||||
if (piece.getId() == savedPiece) {
|
||||
if (peer.isCompleted() && piece.getPeerCount() > 1) {
|
||||
if (peer.isCompleted() && piece.getPeerCount() > 1 &&
|
||||
wantedPieces.size() > 2*END_GAME_THRESHOLD) {
|
||||
// Try to preserve rarest-first
|
||||
// by not requesting a partial piece that non-seeders also have
|
||||
// by not requesting a partial piece that at least two non-seeders also have
|
||||
// from a seeder
|
||||
boolean nonSeeds = false;
|
||||
int nonSeeds = 0;
|
||||
for (Peer pr : peers) {
|
||||
PeerState state = pr.state;
|
||||
if (state == null) continue;
|
||||
BitField bf = state.bitfield;
|
||||
if (bf == null) continue;
|
||||
if (bf.get(savedPiece) && !pr.isCompleted()) {
|
||||
nonSeeds = true;
|
||||
break;
|
||||
if (++nonSeeds > 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nonSeeds) {
|
||||
if (nonSeeds > 1) {
|
||||
skipped = true;
|
||||
break;
|
||||
}
|
||||
@ -1452,8 +1488,8 @@ class PeerCoordinator implements PeerListener
|
||||
public int allowedUploaders()
|
||||
{
|
||||
if (listener != null && listener.overUploadLimit(uploaders)) {
|
||||
// if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Over limit, uploaders was: " + uploaders);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Over limit, uploaders was: " + uploaders);
|
||||
return uploaders - 1;
|
||||
} else if (uploaders < MAX_UPLOADERS)
|
||||
return uploaders + 1;
|
||||
|
@ -42,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<PeerID>
|
||||
public class PeerID implements Comparable<PeerID>
|
||||
{
|
||||
private byte[] id;
|
||||
private Destination address;
|
||||
@ -196,6 +196,7 @@ class PeerID implements Comparable<PeerID>
|
||||
* Compares port, address and id.
|
||||
* @deprecated unused? and will NPE now that address can be null?
|
||||
*/
|
||||
@Deprecated
|
||||
public int compareTo(PeerID pid)
|
||||
{
|
||||
int result = port - pid.port;
|
||||
|
@ -30,6 +30,7 @@ import net.i2p.data.DataHelper;
|
||||
*
|
||||
* @deprecated unused, for command line client only, commented out in Snark.java
|
||||
*/
|
||||
@Deprecated
|
||||
class PeerMonitorTask implements Runnable
|
||||
{
|
||||
final static long MONITOR_PERIOD = 10 * 1000; // Ten seconds.
|
||||
@ -37,8 +38,8 @@ class PeerMonitorTask implements Runnable
|
||||
|
||||
private final PeerCoordinator coordinator;
|
||||
|
||||
private long lastDownloaded = 0;
|
||||
private long lastUploaded = 0;
|
||||
//private long lastDownloaded = 0;
|
||||
//private long lastUploaded = 0;
|
||||
|
||||
PeerMonitorTask(PeerCoordinator coordinator)
|
||||
{
|
||||
|
@ -21,6 +21,7 @@
|
||||
package org.klomp.snark;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@ -36,7 +37,10 @@ class PeerState implements DataLoader
|
||||
private final Peer peer;
|
||||
/** Fixme, used by Peer.disconnect() to get to the coordinator */
|
||||
final PeerListener listener;
|
||||
/** Null before we have it. locking: this */
|
||||
private MetaInfo metainfo;
|
||||
/** Null unless needed. Contains -1 for all. locking: this */
|
||||
private List<Integer> havesBeforeMetaInfo;
|
||||
|
||||
// Interesting and choking describes whether we are interested in or
|
||||
// are choking the other side.
|
||||
@ -48,7 +52,7 @@ class PeerState implements DataLoader
|
||||
volatile boolean interested;
|
||||
volatile boolean choked = true;
|
||||
|
||||
/** the pieces the peer has */
|
||||
/** the pieces the peer has. locking: this */
|
||||
BitField bitfield;
|
||||
|
||||
// Package local for use by Peer.
|
||||
@ -65,6 +69,7 @@ class PeerState implements DataLoader
|
||||
private final static int MAX_PIPELINE_BYTES = 128*1024; // this is for inbound requests
|
||||
public final static int PARTSIZE = 16*1024; // outbound request
|
||||
private final static int MAX_PARTSIZE = 64*1024; // Don't let anybody request more than this
|
||||
private static final Integer PIECE_ALL = Integer.valueOf(-1);
|
||||
|
||||
/**
|
||||
* @param metainfo null if in magnet mode
|
||||
@ -130,55 +135,112 @@ class PeerState implements DataLoader
|
||||
{
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(peer + " rcv have(" + piece + ")");
|
||||
// FIXME we will lose these until we get the metainfo
|
||||
if (metainfo == null)
|
||||
return;
|
||||
// Sanity check
|
||||
if (piece < 0 || piece >= metainfo.getPieces())
|
||||
{
|
||||
// XXX disconnect?
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
if (piece < 0) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Got strange 'have: " + piece + "' message from " + peer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized(this) {
|
||||
if (metainfo == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Got HAVE " + piece + " before metainfo from " + peer);
|
||||
if (bitfield != null) {
|
||||
if (piece < bitfield.size())
|
||||
bitfield.set(piece);
|
||||
} else {
|
||||
// note reception for later
|
||||
if (havesBeforeMetaInfo == null) {
|
||||
havesBeforeMetaInfo = new ArrayList<Integer>(8);
|
||||
} else if (havesBeforeMetaInfo.size() > 1000) {
|
||||
// don't blow up
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Got too many haves before metainfo from " + peer);
|
||||
return;
|
||||
}
|
||||
havesBeforeMetaInfo.add(Integer.valueOf(piece));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (piece >= metainfo.getPieces()) {
|
||||
// XXX disconnect?
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Got strange 'have: " + piece + "' message from " + peer);
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized(this)
|
||||
{
|
||||
// Can happen if the other side never send a bitfield message.
|
||||
if (bitfield == null)
|
||||
bitfield = new BitField(metainfo.getPieces());
|
||||
|
||||
bitfield = new BitField(metainfo.getPieces());
|
||||
bitfield.set(piece);
|
||||
}
|
||||
}
|
||||
|
||||
if (listener.gotHave(peer, piece))
|
||||
setInteresting(true);
|
||||
}
|
||||
|
||||
void bitfieldMessage(byte[] bitmap)
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(peer + " rcv bitfield");
|
||||
void bitfieldMessage(byte[] bitmap) {
|
||||
bitfieldMessage(bitmap, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bitmap null to use the isAll param
|
||||
* @param isAll only if bitmap == null: true for have_all, false for have_none
|
||||
* @since 0.9.21
|
||||
*/
|
||||
private void bitfieldMessage(byte[] bitmap, boolean isAll) {
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
if (bitmap != null)
|
||||
_log.debug(peer + " rcv bitfield bytes: " + bitmap.length);
|
||||
else if (isAll)
|
||||
_log.debug(peer + " rcv bitfield HAVE_ALL");
|
||||
else
|
||||
_log.debug(peer + " rcv bitfield HAVE_NONE");
|
||||
}
|
||||
|
||||
synchronized(this) {
|
||||
if (bitfield != null)
|
||||
{
|
||||
// XXX - Be liberal in what you except?
|
||||
// XXX - Be liberal in what you accept?
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Got unexpected bitfield message from " + peer);
|
||||
return;
|
||||
}
|
||||
|
||||
// XXX - Check for weird bitfield and disconnect?
|
||||
// FIXME will have to regenerate the bitfield after we know exactly
|
||||
// Will have to regenerate the bitfield after we know exactly
|
||||
// how many pieces there are, as we don't know how many spare bits there are.
|
||||
if (metainfo == null)
|
||||
bitfield = new BitField(bitmap, bitmap.length * 8);
|
||||
else
|
||||
bitfield = new BitField(bitmap, metainfo.getPieces());
|
||||
}
|
||||
if (metainfo == null)
|
||||
return;
|
||||
// This happens in setMetaInfo() below.
|
||||
if (metainfo == null) {
|
||||
if (bitmap != null) {
|
||||
bitfield = new BitField(bitmap, bitmap.length * 8);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("have_x w/o metainfo: " + isAll);
|
||||
if (isAll) {
|
||||
// note reception for later
|
||||
if (havesBeforeMetaInfo == null)
|
||||
havesBeforeMetaInfo = new ArrayList<Integer>(1);
|
||||
else
|
||||
havesBeforeMetaInfo.clear();
|
||||
havesBeforeMetaInfo.add(PIECE_ALL);
|
||||
} // else HAVE_NONE, ignore
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (bitmap != null) {
|
||||
bitfield = new BitField(bitmap, metainfo.getPieces());
|
||||
} else {
|
||||
bitfield = new BitField(metainfo.getPieces());
|
||||
if (isAll)
|
||||
bitfield.setAll();
|
||||
}
|
||||
}
|
||||
} // synch
|
||||
|
||||
boolean interest = listener.gotBitField(peer, bitfield);
|
||||
setInteresting(interest);
|
||||
if (bitfield.complete() && !interest) {
|
||||
@ -198,14 +260,21 @@ class PeerState implements DataLoader
|
||||
+ piece + ", " + begin + ", " + length + ") ");
|
||||
if (metainfo == null)
|
||||
return;
|
||||
if (choking)
|
||||
{
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Request received, but choking " + peer);
|
||||
if (choking) {
|
||||
if (peer.supportsFast()) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Request received, sending reject to choked " + peer);
|
||||
out.sendReject(piece, begin, length);
|
||||
} else {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Request received, but choking " + peer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
// There is no check here that we actually have the piece;
|
||||
// this will be caught in loadData() below
|
||||
if (piece < 0
|
||||
|| piece >= metainfo.getPieces()
|
||||
|| begin < 0
|
||||
@ -219,6 +288,8 @@ class PeerState implements DataLoader
|
||||
+ ", " + begin
|
||||
+ ", " + length
|
||||
+ "' message from " + peer);
|
||||
if (peer.supportsFast())
|
||||
out.sendReject(piece, begin, length);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -227,8 +298,14 @@ class PeerState implements DataLoader
|
||||
// Todo: limit number of requests also? (robert 64 x 4KB)
|
||||
if (out.queuedBytes() + length > MAX_PIPELINE_BYTES)
|
||||
{
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Discarding request over pipeline limit from " + peer);
|
||||
if (peer.supportsFast()) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Rejecting request over pipeline limit from " + peer);
|
||||
out.sendReject(piece, begin, length);
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Discarding request over pipeline limit from " + peer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -243,7 +320,8 @@ class PeerState implements DataLoader
|
||||
/**
|
||||
* This is the callback that PeerConnectionOut calls
|
||||
*
|
||||
* @return bytes or null for errors
|
||||
* @return bytes or null for errors such as not having the piece yet
|
||||
* @throws RuntimeException on IOE getting the data
|
||||
* @since 0.8.2
|
||||
*/
|
||||
public ByteArray loadData(int piece, int begin, int length) {
|
||||
@ -253,6 +331,8 @@ class PeerState implements DataLoader
|
||||
// XXX - Protocol error-> diconnect?
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Got request for unknown piece: " + piece);
|
||||
if (peer.supportsFast())
|
||||
out.sendReject(piece, begin, length);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -265,6 +345,8 @@ class PeerState implements DataLoader
|
||||
+ ", " + begin
|
||||
+ ", " + length
|
||||
+ "' message from " + peer);
|
||||
if (peer.supportsFast())
|
||||
out.sendReject(piece, begin, length);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -322,6 +404,11 @@ class PeerState implements DataLoader
|
||||
{
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Got BAD " + req.getPiece() + " from " + peer);
|
||||
synchronized(this) {
|
||||
// so we don't ask again
|
||||
if (bitfield != null)
|
||||
bitfield.clear(req.getPiece());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -455,7 +542,12 @@ class PeerState implements DataLoader
|
||||
for (Integer p : pcs) {
|
||||
Request req = getLowestOutstandingRequest(p.intValue());
|
||||
if (req != null) {
|
||||
req.getPartialPiece().setDownloaded(req.off);
|
||||
PartialPiece pp = req.getPartialPiece();
|
||||
synchronized(pp) {
|
||||
int dl = pp.getDownloaded();
|
||||
if (req.off != dl)
|
||||
req = new Request(pp, dl, 1);
|
||||
}
|
||||
rv.add(req);
|
||||
}
|
||||
}
|
||||
@ -508,22 +600,43 @@ class PeerState implements DataLoader
|
||||
* @param meta non-null
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public void setMetaInfo(MetaInfo meta) {
|
||||
public synchronized void setMetaInfo(MetaInfo meta) {
|
||||
if (metainfo != null)
|
||||
return;
|
||||
BitField oldBF = bitfield;
|
||||
if (oldBF != null) {
|
||||
if (oldBF.size() != meta.getPieces())
|
||||
if (bitfield != null) {
|
||||
if (bitfield.size() != meta.getPieces())
|
||||
// fix bitfield, it was too big by 1-7 bits
|
||||
bitfield = new BitField(oldBF.getFieldBytes(), meta.getPieces());
|
||||
bitfield = new BitField(bitfield.getFieldBytes(), meta.getPieces());
|
||||
// else no extra
|
||||
} else if (havesBeforeMetaInfo != null) {
|
||||
// initialize it now
|
||||
bitfield = new BitField(meta.getPieces());
|
||||
} else {
|
||||
// it will be initialized later
|
||||
//bitfield = new BitField(meta.getPieces());
|
||||
}
|
||||
metainfo = meta;
|
||||
if (bitfield != null && bitfield.count() > 0)
|
||||
setInteresting(true);
|
||||
if (bitfield != null) {
|
||||
if (havesBeforeMetaInfo != null) {
|
||||
// set all 'haves' we got before the metainfo in the bitfield
|
||||
for (Integer i : havesBeforeMetaInfo) {
|
||||
if (i.equals(PIECE_ALL)) {
|
||||
bitfield.setAll();
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("set have_all after rcv metainfo");
|
||||
break;
|
||||
}
|
||||
int piece = i.intValue();
|
||||
if (piece >= 0 && piece < meta.getPieces())
|
||||
bitfield.set(piece);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("set have " + piece + " after rcv metainfo");
|
||||
}
|
||||
havesBeforeMetaInfo = null;
|
||||
}
|
||||
if (bitfield.count() > 0)
|
||||
setInteresting(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -536,6 +649,89 @@ class PeerState implements DataLoader
|
||||
listener.gotPort(peer, port, port + 1);
|
||||
}
|
||||
|
||||
/////////// fast message handlers /////////
|
||||
|
||||
/**
|
||||
* BEP 6
|
||||
* Treated as "have" for now
|
||||
* @since 0.9.21
|
||||
*/
|
||||
void suggestMessage(int piece) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Handling suggest as have(" + piece + ") from " + peer);
|
||||
haveMessage(piece);
|
||||
}
|
||||
|
||||
/**
|
||||
* BEP 6
|
||||
* @param isAll true for have_all, false for have_none
|
||||
* @since 0.9.21
|
||||
*/
|
||||
void haveMessage(boolean isAll) {
|
||||
bitfieldMessage(null, isAll);
|
||||
}
|
||||
|
||||
/**
|
||||
* BEP 6
|
||||
* If the peer rejects lower chunks but not higher ones, thus creating holes,
|
||||
* we won't figure it out and the piece will fail, since we don't currently
|
||||
* keep a chunk bitmap in PartialPiece.
|
||||
* As long as the peer rejects all the chunks, or rejects only the last chunks,
|
||||
* no holes are created and we will be fine. The reject messages may be in any order,
|
||||
* just don't make a hole when it's over.
|
||||
*
|
||||
* @since 0.9.21
|
||||
*/
|
||||
void rejectMessage(int piece, int begin, int length) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Got reject(" + piece + ',' + begin + ',' + length + ") from " + peer);
|
||||
out.cancelRequest(piece, begin, length);
|
||||
synchronized(this) {
|
||||
Request deletedRequest = null;
|
||||
// for this piece only
|
||||
boolean haveMoreRequests = false;
|
||||
for (Iterator<Request> iter = outstandingRequests.iterator(); iter.hasNext(); ) {
|
||||
Request req = iter.next();
|
||||
if (req.getPiece() == piece) {
|
||||
if (req.off == begin && req.len == length) {
|
||||
iter.remove();
|
||||
deletedRequest = req;
|
||||
} else {
|
||||
haveMoreRequests = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deletedRequest != null && !haveMoreRequests) {
|
||||
// We must return the piece to the coordinator
|
||||
// Create a new fake request so we can set the offset correctly
|
||||
PartialPiece pp = deletedRequest.getPartialPiece();
|
||||
int downloaded = pp.getDownloaded();
|
||||
Request req;
|
||||
if (deletedRequest.off == downloaded)
|
||||
req = deletedRequest;
|
||||
else
|
||||
req = new Request(pp, downloaded, 1);
|
||||
List<Request> pcs = Collections.singletonList(req);
|
||||
listener.savePartialPieces(this.peer, pcs);
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Returned to coord. w/ offset " + pp.getDownloaded() + " due to reject(" + piece + ',' + begin + ',' + length + ") from " + peer);
|
||||
}
|
||||
if (lastRequest != null && lastRequest.getPiece() == piece &&
|
||||
lastRequest.off == begin && lastRequest.len == length)
|
||||
lastRequest = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BEP 6
|
||||
* Ignored for now
|
||||
* @since 0.9.21
|
||||
*/
|
||||
void allowedFastMessage(int piece) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Ignoring allowed_fast(" + piece + ") from " + peer);
|
||||
}
|
||||
|
||||
void unknownMessage(int type, byte[] bs)
|
||||
{
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
@ -543,6 +739,8 @@ class PeerState implements DataLoader
|
||||
+ " length: " + bs.length);
|
||||
}
|
||||
|
||||
/////////// end message handlers /////////
|
||||
|
||||
/**
|
||||
* We now have this piece.
|
||||
* Tell the peer and cancel any requests for the piece.
|
||||
@ -601,6 +799,7 @@ class PeerState implements DataLoader
|
||||
* @deprecated deadlocks
|
||||
* @since 0.8.1
|
||||
*/
|
||||
@Deprecated
|
||||
synchronized boolean isRequesting(int piece) {
|
||||
if (pendingRequest != null && pendingRequest.getPiece() == piece)
|
||||
return true;
|
||||
|
@ -43,13 +43,13 @@ class Request
|
||||
*/
|
||||
Request(PartialPiece piece, int off, int len)
|
||||
{
|
||||
// Sanity check
|
||||
if (off < 0 || len <= 0 || off + len > piece.getLength())
|
||||
throw new IndexOutOfBoundsException("Illegal Request " + toString());
|
||||
|
||||
this.piece = piece;
|
||||
this.off = off;
|
||||
this.len = len;
|
||||
|
||||
// Sanity check
|
||||
if (off < 0 || len <= 0 || off + len > piece.getLength())
|
||||
throw new IndexOutOfBoundsException("Illegal Request " + toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,13 +27,13 @@ import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFile;
|
||||
|
||||
/**
|
||||
* Main Snark program startup class.
|
||||
@ -69,8 +69,6 @@ public class Snark
|
||||
"Commands: 'info', 'list', 'quit'.";
|
||||
****/
|
||||
|
||||
// String indicating main activity
|
||||
String activity = "Not started";
|
||||
|
||||
/****
|
||||
private static class OOMListener implements I2PThread.OOMEventListener {
|
||||
@ -223,11 +221,11 @@ public class Snark
|
||||
private PeerCoordinator coordinator;
|
||||
private ConnectionAcceptor acceptor;
|
||||
private TrackerClient trackerclient;
|
||||
private String rootDataDir = ".";
|
||||
private final File rootDataDir;
|
||||
private final CompleteListener completeListener;
|
||||
private volatile boolean stopped;
|
||||
private volatile boolean starting;
|
||||
private byte[] id;
|
||||
private final byte[] id;
|
||||
private final byte[] infoHash;
|
||||
private String additionalTrackerURL;
|
||||
protected final I2PSnarkUtil _util;
|
||||
@ -236,15 +234,29 @@ public class Snark
|
||||
private volatile String trackerProblems;
|
||||
private volatile int trackerSeenPeers;
|
||||
private volatile boolean _autoStoppable;
|
||||
// String indicating main activity
|
||||
private volatile String activity = "Not started";
|
||||
private final long savedUploaded;
|
||||
|
||||
|
||||
/** from main() via parseArguments() single torrent */
|
||||
/**
|
||||
* from main() via parseArguments() single torrent
|
||||
*
|
||||
* @deprecated unused
|
||||
*/
|
||||
/****
|
||||
Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
|
||||
StorageListener slistener, CoordinatorListener clistener) {
|
||||
this(util, torrent, ip, user_port, slistener, clistener, null, null, null, true, ".");
|
||||
}
|
||||
****/
|
||||
|
||||
/** single torrent - via router */
|
||||
/**
|
||||
* single torrent - via router
|
||||
*
|
||||
* @deprecated unused
|
||||
*/
|
||||
/****
|
||||
public Snark(I2PAppContext ctx, Properties opts, String torrent,
|
||||
StorageListener slistener, boolean start, String rootDir) {
|
||||
this(new I2PSnarkUtil(ctx), torrent, null, -1, slistener, null, null, null, null, false, rootDir);
|
||||
@ -274,12 +286,32 @@ public class Snark
|
||||
if (start)
|
||||
this.startTorrent();
|
||||
}
|
||||
****/
|
||||
|
||||
/** multitorrent */
|
||||
/**
|
||||
* multitorrent
|
||||
* @throws RuntimeException via fatal()
|
||||
*/
|
||||
public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
|
||||
StorageListener slistener, CoordinatorListener clistener,
|
||||
CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet,
|
||||
ConnectionAcceptor connectionAcceptor, boolean start, String rootDir)
|
||||
{
|
||||
this(util, torrent, ip, user_port, slistener, clistener, complistener,
|
||||
peerCoordinatorSet, connectionAcceptor, start, rootDir, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* multitorrent
|
||||
*
|
||||
* @param baseFile if null, use rootDir/torrentName; if non-null, use it instead
|
||||
* @throws RuntimeException via fatal()
|
||||
* @since 0.9.11
|
||||
*/
|
||||
public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
|
||||
StorageListener slistener, CoordinatorListener clistener,
|
||||
CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet,
|
||||
ConnectionAcceptor connectionAcceptor, boolean start, String rootDir, File baseFile)
|
||||
{
|
||||
if (slistener == null)
|
||||
slistener = this;
|
||||
@ -291,7 +323,7 @@ public class Snark
|
||||
acceptor = connectionAcceptor;
|
||||
|
||||
this.torrent = torrent;
|
||||
this.rootDataDir = rootDir;
|
||||
this.rootDataDir = new File(rootDir);
|
||||
|
||||
stopped = true;
|
||||
activity = "Network setup";
|
||||
@ -317,7 +349,6 @@ public class Snark
|
||||
*/
|
||||
|
||||
// Figure out what the torrent argument represents.
|
||||
meta = null;
|
||||
File f = null;
|
||||
InputStream in = null;
|
||||
byte[] x_infoHash = null;
|
||||
@ -395,13 +426,22 @@ public class Snark
|
||||
try
|
||||
{
|
||||
activity = "Checking storage";
|
||||
storage = new Storage(_util, meta, slistener);
|
||||
boolean shouldPreserve = completeListener != null && completeListener.getSavedPreserveNamesSetting(this);
|
||||
if (baseFile == null) {
|
||||
String base = meta.getName();
|
||||
if (!shouldPreserve)
|
||||
base = Storage.filterName(base);
|
||||
if (_util.getFilesPublic())
|
||||
baseFile = new File(rootDataDir, base);
|
||||
else
|
||||
baseFile = new SecureFile(rootDataDir, base);
|
||||
}
|
||||
storage = new Storage(_util, baseFile, meta, slistener, shouldPreserve);
|
||||
if (completeListener != null) {
|
||||
storage.check(rootDataDir,
|
||||
completeListener.getSavedTorrentTime(this),
|
||||
storage.check(completeListener.getSavedTorrentTime(this),
|
||||
completeListener.getSavedTorrentBitField(this));
|
||||
} else {
|
||||
storage.check(rootDataDir);
|
||||
storage.check();
|
||||
}
|
||||
// have to figure out when to reopen
|
||||
// if (!start)
|
||||
@ -429,6 +469,7 @@ public class Snark
|
||||
trackerclient = new TrackerClient(meta, coordinator);
|
||||
*/
|
||||
|
||||
savedUploaded = (completeListener != null) ? completeListener.getSavedUploaded(this) : 0;
|
||||
if (start)
|
||||
startTorrent();
|
||||
}
|
||||
@ -439,6 +480,7 @@ public class Snark
|
||||
* @param torrent a fake name for now (not a file name)
|
||||
* @param ih 20-byte info hash
|
||||
* @param trackerURL may be null
|
||||
* @throws RuntimeException via fatal()
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public Snark(I2PSnarkUtil util, String torrent, byte[] ih, String trackerURL,
|
||||
@ -453,7 +495,8 @@ public class Snark
|
||||
this.torrent = torrent;
|
||||
this.infoHash = ih;
|
||||
this.additionalTrackerURL = trackerURL;
|
||||
this.rootDataDir = rootDir;
|
||||
this.rootDataDir = rootDir != null ? new File(rootDir) : null; // null only for FetchAndAdd extension
|
||||
savedUploaded = 0;
|
||||
stopped = true;
|
||||
id = generateID();
|
||||
|
||||
@ -478,24 +521,21 @@ public class Snark
|
||||
|
||||
// Create a new ID and fill it with something random. First nine
|
||||
// zeros bytes, then three bytes filled with snark and then
|
||||
// sixteen random bytes.
|
||||
// eight random bytes.
|
||||
byte snark = (((3 + 7 + 10) * (1000 - 8)) / 992) - 17;
|
||||
byte[] rv = new byte[20];
|
||||
Random random = I2PAppContext.getGlobalContext().random();
|
||||
int i;
|
||||
for (i = 0; i < 9; i++)
|
||||
rv[i] = 0;
|
||||
rv[i++] = snark;
|
||||
rv[i++] = snark;
|
||||
rv[i++] = snark;
|
||||
while (i < 20)
|
||||
rv[i++] = (byte)random.nextInt(256);
|
||||
rv[9] = snark;
|
||||
rv[10] = snark;
|
||||
rv[11] = snark;
|
||||
I2PAppContext.getGlobalContext().random().nextBytes(rv, 12, 8);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start up contacting peers and querying the tracker.
|
||||
* Blocks if tunnel is not yet open.
|
||||
*
|
||||
* @throws RuntimeException via fatal()
|
||||
*/
|
||||
public synchronized void startTorrent() {
|
||||
starting = true;
|
||||
@ -522,6 +562,7 @@ public class Snark
|
||||
_log.info("Starting PeerCoordinator, ConnectionAcceptor, and TrackerClient");
|
||||
activity = "Collecting pieces";
|
||||
coordinator = new PeerCoordinator(_util, id, infoHash, meta, storage, this, this);
|
||||
coordinator.setUploaded(savedUploaded);
|
||||
if (_peerCoordinatorSet != null) {
|
||||
// multitorrent
|
||||
_peerCoordinatorSet.add(coordinator);
|
||||
@ -548,7 +589,7 @@ public class Snark
|
||||
} else if (trackerclient.halted()) {
|
||||
if (storage != null) {
|
||||
try {
|
||||
storage.reopen(rootDataDir);
|
||||
storage.reopen();
|
||||
} catch (IOException ioe) {
|
||||
try { storage.close(); } catch (IOException ioee) {
|
||||
ioee.printStackTrace();
|
||||
@ -576,7 +617,6 @@ public class Snark
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public synchronized void stopTorrent(boolean fast) {
|
||||
stopped = true;
|
||||
TrackerClient tc = trackerclient;
|
||||
if (tc != null)
|
||||
tc.halt(fast);
|
||||
@ -584,17 +624,28 @@ public class Snark
|
||||
if (pc != null)
|
||||
pc.halt();
|
||||
Storage st = storage;
|
||||
if (!fast)
|
||||
// HACK: Needed a way to distinguish between user-stop and
|
||||
// shutdown-stop. stopTorrent(true) is in stopAllTorrents().
|
||||
// (#766)
|
||||
stopped = true;
|
||||
if (st != null) {
|
||||
boolean changed = storage.isChanged();
|
||||
// TODO: Cache the config-in-mem to compare vs config-on-disk
|
||||
// (needed for auto-save to not double-save in some cases)
|
||||
//boolean changed = storage.isChanged() || getUploaded() != savedUploaded;
|
||||
boolean changed = true;
|
||||
if (changed && completeListener != null)
|
||||
completeListener.updateStatus(this);
|
||||
try {
|
||||
storage.close();
|
||||
} catch (IOException ioe) {
|
||||
System.out.println("Error closing " + torrent);
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
if (changed && completeListener != null)
|
||||
completeListener.updateStatus(this);
|
||||
}
|
||||
if (fast)
|
||||
// HACK: See above if(!fast)
|
||||
stopped = true;
|
||||
if (pc != null && _peerCoordinatorSet != null)
|
||||
_peerCoordinatorSet.remove(pc);
|
||||
if (_peerCoordinatorSet == null)
|
||||
@ -694,6 +745,18 @@ public class Snark
|
||||
return storage != null && storage.isChecking();
|
||||
}
|
||||
|
||||
/**
|
||||
* If checking is in progress, return completion 0.0 ... 1.0,
|
||||
* else return 1.0.
|
||||
* @since 0.9.23
|
||||
*/
|
||||
public double getCheckingProgress() {
|
||||
if (storage != null && storage.isChecking())
|
||||
return storage.getCheckingProgress();
|
||||
else
|
||||
return 1.0d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disk allocation (ballooning) in progress.
|
||||
* @since 0.9.3
|
||||
@ -739,7 +802,7 @@ public class Snark
|
||||
PeerCoordinator coord = coordinator;
|
||||
if (coord != null)
|
||||
return coord.getUploaded();
|
||||
return 0;
|
||||
return savedUploaded;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -763,6 +826,7 @@ public class Snark
|
||||
}
|
||||
|
||||
/**
|
||||
* Not HTML escaped.
|
||||
* @return String returned from tracker, or null if no error
|
||||
* @since 0.8.4
|
||||
*/
|
||||
@ -835,7 +899,7 @@ public class Snark
|
||||
}
|
||||
|
||||
/**
|
||||
* Bytes still wanted. DOES account for skipped files.
|
||||
* Bytes still wanted. DOES account for (i.e. does not include) skipped files.
|
||||
* FIXME -1 when not running.
|
||||
* @return exact value. or -1 if no storage yet or when not running.
|
||||
* @since 0.9.1
|
||||
@ -848,7 +912,31 @@ public class Snark
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not account for skipped files.
|
||||
* Bytes not received and set to skipped.
|
||||
* This is not the same as the total of all skipped files,
|
||||
* since pieces may span multiple files.
|
||||
*
|
||||
* @return exact value. or 0 if no storage yet.
|
||||
* @since 0.9.24
|
||||
*/
|
||||
public long getSkippedLength() {
|
||||
PeerCoordinator coord = coordinator;
|
||||
if (coord != null) {
|
||||
// fast way
|
||||
long r = getRemainingLength();
|
||||
if (r <= 0)
|
||||
return 0;
|
||||
long n = coord.getNeededLength();
|
||||
return r - n;
|
||||
} else if (storage != null) {
|
||||
// slow way
|
||||
return storage.getSkippedLength();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not account (i.e. includes) for skipped files.
|
||||
* @return number of pieces still needed (magnet mode or not), or -1 if unknown
|
||||
* @since 0.8.4
|
||||
*/
|
||||
@ -919,6 +1007,7 @@ public class Snark
|
||||
* non-valid argument list. The given listeners will be
|
||||
* passed to all components that take one.
|
||||
*/
|
||||
/****
|
||||
private static Snark parseArguments(String[] args,
|
||||
StorageListener slistener,
|
||||
CoordinatorListener clistener)
|
||||
@ -933,6 +1022,7 @@ public class Snark
|
||||
int i = 0;
|
||||
while (i < args.length)
|
||||
{
|
||||
****/
|
||||
/*
|
||||
if (args[i].equals("--debug"))
|
||||
{
|
||||
@ -954,7 +1044,9 @@ public class Snark
|
||||
catch (NumberFormatException nfe) { }
|
||||
}
|
||||
}
|
||||
else */ if (args[i].equals("--port"))
|
||||
else */
|
||||
/****
|
||||
if (args[i].equals("--port"))
|
||||
{
|
||||
if (args.length - 1 < i + 1)
|
||||
usage("--port needs port number to listen on");
|
||||
@ -1060,6 +1152,7 @@ public class Snark
|
||||
System.out.println
|
||||
(" \tor (with --share) a file to share.");
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* Aborts program abnormally.
|
||||
@ -1103,9 +1196,15 @@ public class Snark
|
||||
*/
|
||||
public void gotMetaInfo(PeerCoordinator coordinator, MetaInfo metainfo) {
|
||||
try {
|
||||
String base = Storage.filterName(metainfo.getName());
|
||||
File baseFile;
|
||||
if (_util.getFilesPublic())
|
||||
baseFile = new File(rootDataDir, base);
|
||||
else
|
||||
baseFile = new SecureFile(rootDataDir, base);
|
||||
// The following two may throw IOE...
|
||||
storage = new Storage(_util, metainfo, this);
|
||||
storage.check(rootDataDir);
|
||||
storage = new Storage(_util, baseFile, metainfo, this, false);
|
||||
storage.check();
|
||||
// ... so don't set meta until here
|
||||
meta = metainfo;
|
||||
if (completeListener != null) {
|
||||
@ -1152,8 +1251,8 @@ public class Snark
|
||||
// System.out.println(); // We have all the disk space we need.
|
||||
}
|
||||
|
||||
private boolean allChecked = false;
|
||||
private boolean checking = false;
|
||||
private boolean allChecked;
|
||||
private boolean checking;
|
||||
//private boolean prechecking = true;
|
||||
|
||||
public void storageChecked(Storage storage, int num, boolean checked)
|
||||
@ -1201,7 +1300,8 @@ public class Snark
|
||||
|
||||
public void setWantedPieces(Storage storage)
|
||||
{
|
||||
coordinator.setWantedPieces();
|
||||
if (coordinator != null)
|
||||
coordinator.setWantedPieces();
|
||||
}
|
||||
|
||||
///////////// End StorageListener methods
|
||||
@ -1210,7 +1310,7 @@ public class Snark
|
||||
/** SnarkSnutdown callback unused */
|
||||
public void shutdown()
|
||||
{
|
||||
// Should not be necessary since all non-deamon threads should
|
||||
// Should not be necessary since all non-daemon threads should
|
||||
// have died. But in reality this does not always happen.
|
||||
//System.exit(0);
|
||||
}
|
||||
@ -1229,7 +1329,8 @@ public class Snark
|
||||
* coordinatorListener
|
||||
*/
|
||||
final static int MIN_TOTAL_UPLOADERS = 4;
|
||||
final static int MAX_TOTAL_UPLOADERS = 10;
|
||||
final static int MAX_TOTAL_UPLOADERS = 20;
|
||||
|
||||
public boolean overUploadLimit(int uploaders) {
|
||||
if (_peerCoordinatorSet == null || uploaders <= 0)
|
||||
return false;
|
||||
@ -1239,7 +1340,8 @@ public class Snark
|
||||
totalUploaders += c.uploaders;
|
||||
}
|
||||
int limit = _util.getMaxUploaders();
|
||||
// debug("Total uploaders: " + totalUploaders + " Limit: " + limit, Snark.DEBUG);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Total uploaders: " + totalUploaders + " Limit: " + limit);
|
||||
return totalUploaders > limit;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,6 +28,7 @@ import net.i2p.util.I2PAppThread;
|
||||
* Makes sure everything ends correctly when shutting down.
|
||||
* @deprecated unused
|
||||
*/
|
||||
@Deprecated
|
||||
public class SnarkShutdown extends I2PAppThread
|
||||
{
|
||||
private final Storage storage;
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
package org.klomp.snark;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -32,10 +33,14 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import gnu.getopt.Getopt;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.SHA1;
|
||||
import net.i2p.data.ByteArray;
|
||||
@ -48,10 +53,11 @@ import net.i2p.util.SystemVersion;
|
||||
/**
|
||||
* Maintains pieces on disk. Can be used to store and retrieve pieces.
|
||||
*/
|
||||
public class Storage
|
||||
public class Storage implements Closeable
|
||||
{
|
||||
private final MetaInfo metainfo;
|
||||
private final List<TorrentFile> _torrentFiles;
|
||||
private final File _base;
|
||||
private final StorageListener listener;
|
||||
private final I2PSnarkUtil _util;
|
||||
private final Log _log;
|
||||
@ -63,35 +69,42 @@ public class Storage
|
||||
private final int piece_size;
|
||||
private final int pieces;
|
||||
private final long total_length;
|
||||
private final boolean _preserveFileNames;
|
||||
private boolean changed;
|
||||
private volatile boolean _isChecking;
|
||||
private final AtomicInteger _allocateCount = new AtomicInteger();
|
||||
private final AtomicInteger _checkProgress = new AtomicInteger();
|
||||
|
||||
/** The default piece size. */
|
||||
private static final int DEFAULT_PIECE_SIZE = 256*1024;
|
||||
/** bigger than this will be rejected */
|
||||
public static final int MAX_PIECE_SIZE = 4*1024*1024;
|
||||
public static final int MAX_PIECE_SIZE = 16*1024*1024;
|
||||
/** The maximum number of pieces in a torrent. */
|
||||
public static final int MAX_PIECES = 10*1024;
|
||||
public static final int MAX_PIECES = 32*1024;
|
||||
public static final long MAX_TOTAL_SIZE = MAX_PIECE_SIZE * (long) MAX_PIECES;
|
||||
|
||||
private static final Map<String, String> _filterNameCache = new ConcurrentHashMap<String, String>();
|
||||
|
||||
private static final boolean _isWindows = SystemVersion.isWindows();
|
||||
private static final boolean _isARM = SystemVersion.isARM();
|
||||
|
||||
private static final int BUFSIZE = PeerState.PARTSIZE;
|
||||
private static final ByteCache _cache = ByteCache.getInstance(16, BUFSIZE);
|
||||
|
||||
/**
|
||||
* Creates a new storage based on the supplied MetaInfo. This will
|
||||
* Creates a new storage based on the supplied MetaInfo.
|
||||
*
|
||||
* Does not check storage. Caller MUST call check(), which will
|
||||
* try to create and/or check all needed files in the MetaInfo.
|
||||
*
|
||||
* Does not check storage. Caller MUST call check()
|
||||
* @param baseFile the torrent data file or dir
|
||||
* @param preserveFileNames if true, do not remap names to a 'safe' charset
|
||||
*/
|
||||
public Storage(I2PSnarkUtil util, MetaInfo metainfo, StorageListener listener)
|
||||
public Storage(I2PSnarkUtil util, File baseFile, MetaInfo metainfo, StorageListener listener, boolean preserveFileNames)
|
||||
{
|
||||
_util = util;
|
||||
_log = util.getContext().logManager().getLog(Storage.class);
|
||||
_base = baseFile;
|
||||
this.metainfo = metainfo;
|
||||
this.listener = listener;
|
||||
needed = metainfo.getPieces();
|
||||
@ -102,6 +115,7 @@ public class Storage
|
||||
List<List<String>> files = metainfo.getFiles();
|
||||
int sz = files != null ? files.size() : 1;
|
||||
_torrentFiles = new ArrayList<TorrentFile>(sz);
|
||||
_preserveFileNames = preserveFileNames;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,16 +127,20 @@ public class Storage
|
||||
*
|
||||
* @param announce may be null
|
||||
* @param listener may be null
|
||||
* @param created_by may be null
|
||||
* @throws IOException when creating and/or checking files fails.
|
||||
*/
|
||||
public Storage(I2PSnarkUtil util, File baseFile, String announce,
|
||||
List<List<String>> announce_list,
|
||||
String created_by,
|
||||
boolean privateTorrent, StorageListener listener)
|
||||
throws IOException
|
||||
{
|
||||
_util = util;
|
||||
_base = baseFile;
|
||||
_log = util.getContext().logManager().getLog(Storage.class);
|
||||
this.listener = listener;
|
||||
_preserveFileNames = true;
|
||||
// Create names, rafs and lengths arrays.
|
||||
_torrentFiles = getFiles(baseFile);
|
||||
|
||||
@ -148,7 +166,7 @@ public class Storage
|
||||
else
|
||||
pc_size = DEFAULT_PIECE_SIZE;
|
||||
int pcs = (int) ((total - 1)/pc_size) + 1;
|
||||
while (pcs > (MAX_PIECES * 2 / 3) && pc_size < MAX_PIECE_SIZE)
|
||||
while (pcs > (MAX_PIECES / 3) && pc_size < MAX_PIECE_SIZE)
|
||||
{
|
||||
pc_size *= 2;
|
||||
pcs = (int) ((total - 1)/pc_size) +1;
|
||||
@ -183,7 +201,7 @@ public class Storage
|
||||
byte[] piece_hashes = fast_digestCreate();
|
||||
metainfo = new MetaInfo(announce, baseFile.getName(), null, files,
|
||||
lengthsList, piece_size, piece_hashes, total, privateTorrent,
|
||||
announce_list);
|
||||
announce_list, created_by);
|
||||
|
||||
}
|
||||
|
||||
@ -295,6 +313,18 @@ public class Storage
|
||||
return _isChecking;
|
||||
}
|
||||
|
||||
/**
|
||||
* If checking is in progress, return completion 0.0 ... 1.0,
|
||||
* else return 1.0.
|
||||
* @since 0.9.23
|
||||
*/
|
||||
public double getCheckingProgress() {
|
||||
if (_isChecking)
|
||||
return _checkProgress.get() / (double) pieces;
|
||||
else
|
||||
return 1.0d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disk allocation (ballooning) in progress.
|
||||
* Always false on Windows.
|
||||
@ -305,39 +335,48 @@ public class Storage
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file canonical path (non-directory)
|
||||
* Get index to pass to remaining(), getPriority(), setPriority()
|
||||
*
|
||||
* @param file non-canonical path (non-directory)
|
||||
* @return internal index of file; -1 if unknown file
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public int indexOf(File file) {
|
||||
for (int i = 0; i < _torrentFiles.size(); i++) {
|
||||
File f = _torrentFiles.get(i).RAFfile;
|
||||
if (f.equals(file))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileIndex as obtained from indexOf
|
||||
* @return number of bytes remaining; -1 if unknown file
|
||||
* @since 0.7.14
|
||||
*/
|
||||
public long remaining(String file) {
|
||||
/****
|
||||
public long remaining(int fileIndex) {
|
||||
if (fileIndex < 0 || fileIndex >= _torrentFiles.size())
|
||||
return -1;
|
||||
if (complete())
|
||||
return 0;
|
||||
long bytes = 0;
|
||||
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) {
|
||||
try {
|
||||
canonical = f.getCanonicalPath();
|
||||
} catch (IOException ioe) {
|
||||
f = null;
|
||||
}
|
||||
}
|
||||
if (f != null && canonical.equals(file)) {
|
||||
if (complete())
|
||||
return 0;
|
||||
int psz = piece_size;
|
||||
for (int i = 0; i < _torrentFiles.size(); i++) {
|
||||
TorrentFile tf = _torrentFiles.get(i);
|
||||
if (i == fileIndex) {
|
||||
long start = bytes;
|
||||
long end = start + tf.length;
|
||||
int pc = (int) (bytes / psz);
|
||||
int pc = (int) (bytes / piece_size);
|
||||
long rv = 0;
|
||||
if (!bitfield.get(pc))
|
||||
rv = Math.min(psz - (start % psz), tf.length);
|
||||
for (int j = pc + 1; (((long)j) * psz) < end && j < pieces; j++) {
|
||||
rv = Math.min(piece_size - (start % piece_size), tf.length);
|
||||
for (int j = pc + 1; (((long)j) * piece_size) < end && j < pieces; j++) {
|
||||
if (!bitfield.get(j)) {
|
||||
if (((long)(j+1))*psz < end)
|
||||
rv += psz;
|
||||
if (((long)(j+1))*piece_size < end)
|
||||
rv += piece_size;
|
||||
else
|
||||
rv += end - (((long)j) * psz);
|
||||
rv += end - (((long)j) * piece_size);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
@ -346,51 +385,66 @@ public class Storage
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* @param file canonical path (non-directory)
|
||||
* For efficiency, calculate remaining bytes for all files at once
|
||||
*
|
||||
* @return number of bytes remaining for each file, use indexOf() to get index for a file
|
||||
* @since 0.9.23
|
||||
*/
|
||||
public long[] remaining() {
|
||||
long[] rv = new long[_torrentFiles.size()];
|
||||
if (complete())
|
||||
return rv;
|
||||
long bytes = 0;
|
||||
for (int i = 0; i < _torrentFiles.size(); i++) {
|
||||
TorrentFile tf = _torrentFiles.get(i);
|
||||
long start = bytes;
|
||||
long end = start + tf.length;
|
||||
int pc = (int) (bytes / piece_size);
|
||||
long rvi = 0;
|
||||
if (!bitfield.get(pc))
|
||||
rvi = Math.min(piece_size - (start % piece_size), tf.length);
|
||||
for (int j = pc + 1; (((long)j) * piece_size) < end && j < pieces; j++) {
|
||||
if (!bitfield.get(j)) {
|
||||
if (((long)(j+1))*piece_size < end)
|
||||
rvi += piece_size;
|
||||
else
|
||||
rvi += end - (((long)j) * piece_size);
|
||||
}
|
||||
}
|
||||
rv[i] = rvi;
|
||||
bytes += tf.length;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileIndex as obtained from indexOf
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public int getPriority(String file) {
|
||||
public int getPriority(int fileIndex) {
|
||||
if (complete() || metainfo.getFiles() == null)
|
||||
return 0;
|
||||
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 tf.priority;
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
if (fileIndex < 0 || fileIndex >= _torrentFiles.size())
|
||||
return 0;
|
||||
return _torrentFiles.get(fileIndex).priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must call Snark.updatePiecePriorities()
|
||||
* (which calls getPiecePriorities()) after calling this.
|
||||
* @param file canonical path (non-directory)
|
||||
* @param fileIndex as obtained from indexOf
|
||||
* @param pri default 0; <0 to disable
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public void setPriority(String file, int pri) {
|
||||
public void setPriority(int fileIndex, int pri) {
|
||||
if (complete() || metainfo.getFiles() == null)
|
||||
return;
|
||||
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)) {
|
||||
tf.priority = pri;
|
||||
return;
|
||||
}
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
if (fileIndex < 0 || fileIndex >= _torrentFiles.size())
|
||||
return;
|
||||
_torrentFiles.get(fileIndex).priority = pri;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -448,9 +502,8 @@ public class Storage
|
||||
int file = 0;
|
||||
long pcEnd = -1;
|
||||
long fileEnd = _torrentFiles.get(0).length - 1;
|
||||
int psz = piece_size;
|
||||
for (int i = 0; i < rv.length; i++) {
|
||||
pcEnd += psz;
|
||||
pcEnd += piece_size;
|
||||
int pri = _torrentFiles.get(file).priority;
|
||||
while (fileEnd <= pcEnd && file < _torrentFiles.size() - 1) {
|
||||
file++;
|
||||
@ -465,6 +518,31 @@ public class Storage
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call setPriority() for all changed files first,
|
||||
* then call this.
|
||||
* The length of all the pieces that are not yet downloaded,
|
||||
* and are set to skipped.
|
||||
* This is not the same as the total of all skipped files,
|
||||
* since pieces may span multiple files.
|
||||
*
|
||||
* @return 0 on error, if complete, or if only one file
|
||||
* @since 0.9.24
|
||||
*/
|
||||
public long getSkippedLength() {
|
||||
int[] pri = getPiecePriorities();
|
||||
if (pri == null)
|
||||
return 0;
|
||||
long rv = 0;
|
||||
final int end = pri.length - 1;
|
||||
for (int i = 0; i <= end; i++) {
|
||||
if (pri[i] <= -9 && !bitfield.get(i)) {
|
||||
rv += (i != end) ? piece_size : metainfo.getPieceLength(i);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* The BitField that tells which pieces this storage contains.
|
||||
* Do not change this since this is the current state of the storage.
|
||||
@ -483,31 +561,37 @@ public class Storage
|
||||
* @since 0.7.14
|
||||
*/
|
||||
public String getBaseName() {
|
||||
return filterName(metainfo.getName());
|
||||
return optFilterName(metainfo.getName());
|
||||
}
|
||||
|
||||
/** @since 0.9.15 */
|
||||
public boolean getPreserveFileNames() {
|
||||
return _preserveFileNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates (and/or checks) all files from the metainfo file list.
|
||||
* Only call this once, and only after the constructor with the metainfo.
|
||||
* Use recheck() to check again later.
|
||||
*
|
||||
* @throws IllegalStateException if called more than once
|
||||
*/
|
||||
public void check(String rootDir) throws IOException
|
||||
public void check() throws IOException
|
||||
{
|
||||
check(rootDir, 0, null);
|
||||
check(0, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Use recheck() to check again later.
|
||||
*
|
||||
* @throws IllegalStateException if called more than once
|
||||
*/
|
||||
public void check(String rootDir, long savedTime, BitField savedBitField) throws IOException
|
||||
public void check(long savedTime, BitField savedBitField) throws IOException
|
||||
{
|
||||
File base;
|
||||
boolean areFilesPublic = _util.getFilesPublic();
|
||||
if (areFilesPublic)
|
||||
base = new File(rootDir, filterName(metainfo.getName()));
|
||||
else
|
||||
base = new SecureFile(rootDir, filterName(metainfo.getName()));
|
||||
boolean useSavedBitField = savedTime > 0 && savedBitField != null;
|
||||
|
||||
if (!_torrentFiles.isEmpty())
|
||||
@ -517,16 +601,18 @@ public class Storage
|
||||
{
|
||||
// Create base as file.
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Creating/Checking file: " + base);
|
||||
if (!base.createNewFile() && !base.exists())
|
||||
throw new IOException("Could not create file " + base);
|
||||
_log.info("Creating/Checking file: " + _base);
|
||||
// createNewFile() can throw a "Permission denied" IOE even if the file exists???
|
||||
// so do it second
|
||||
if (!_base.exists() && !_base.createNewFile())
|
||||
throw new IOException("Could not create file " + _base);
|
||||
|
||||
_torrentFiles.add(new TorrentFile(base, base, metainfo.getTotalLength()));
|
||||
_torrentFiles.add(new TorrentFile(_base, _base, metainfo.getTotalLength()));
|
||||
if (useSavedBitField) {
|
||||
long lm = base.lastModified();
|
||||
long lm = _base.lastModified();
|
||||
if (lm <= 0 || lm > savedTime)
|
||||
useSavedBitField = false;
|
||||
else if (base.length() != metainfo.getTotalLength())
|
||||
else if (_base.length() != metainfo.getTotalLength())
|
||||
useSavedBitField = false;
|
||||
}
|
||||
}
|
||||
@ -534,9 +620,9 @@ public class Storage
|
||||
{
|
||||
// Create base as dir.
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Creating/Checking directory: " + base);
|
||||
if (!base.mkdir() && !base.isDirectory())
|
||||
throw new IOException("Could not create directory " + base);
|
||||
_log.info("Creating/Checking directory: " + _base);
|
||||
if (!_base.mkdir() && !_base.isDirectory())
|
||||
throw new IOException("Could not create directory " + _base);
|
||||
|
||||
List<Long> ls = metainfo.getLengths();
|
||||
int size = files.size();
|
||||
@ -544,7 +630,7 @@ public class Storage
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
List<String> path = files.get(i);
|
||||
File f = createFileFromNames(base, path, areFilesPublic);
|
||||
File f = createFileFromNames(_base, path, areFilesPublic);
|
||||
// dup file name check after filtering
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (f.equals(_torrentFiles.get(j).RAFfile)) {
|
||||
@ -560,12 +646,12 @@ public class Storage
|
||||
else
|
||||
lastPath = '_' + lastPath;
|
||||
path.set(last, lastPath);
|
||||
f = createFileFromNames(base, path, areFilesPublic);
|
||||
f = createFileFromNames(_base, path, areFilesPublic);
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
long len = ls.get(i).longValue();
|
||||
_torrentFiles.add(new TorrentFile(base, f, len));
|
||||
_torrentFiles.add(new TorrentFile(_base, f, len));
|
||||
total += len;
|
||||
if (useSavedBitField) {
|
||||
long lm = f.lastModified();
|
||||
@ -609,10 +695,9 @@ public class Storage
|
||||
* Doesn't really reopen the file descriptors for a restart.
|
||||
* Just does an existence check but no length check or data reverification
|
||||
*
|
||||
* @param rootDir ignored
|
||||
* @throws IOE on fail
|
||||
*/
|
||||
public void reopen(String rootDir) throws IOException
|
||||
public void reopen() throws IOException
|
||||
{
|
||||
if (_torrentFiles.isEmpty())
|
||||
throw new IOException("Storage not checked yet");
|
||||
@ -635,6 +720,19 @@ public class Storage
|
||||
0x2028, 0x2029
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter the name, but only if configured to do so.
|
||||
* We will do so on torrents received from others, but not
|
||||
* on those we created ourselves, so we do not lose track of files.
|
||||
*
|
||||
* @since 0.9.15
|
||||
*/
|
||||
private String optFilterName(String name) {
|
||||
if (_preserveFileNames)
|
||||
return name;
|
||||
return filterName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes 'suspicious' characters from the given file name.
|
||||
* http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx
|
||||
@ -675,7 +773,7 @@ public class Storage
|
||||
}
|
||||
rv = repl;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
} catch (RuntimeException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
@ -688,14 +786,16 @@ public class Storage
|
||||
* Note that filtering each path element individually may lead to
|
||||
* things going in the wrong place if there are duplicates
|
||||
* in intermediate path elements after filtering.
|
||||
*
|
||||
* @param names path elements
|
||||
*/
|
||||
private static File createFileFromNames(File base, List<String> names, boolean areFilesPublic) throws IOException
|
||||
private File createFileFromNames(File base, List<String> names, boolean areFilesPublic) throws IOException
|
||||
{
|
||||
File f = null;
|
||||
Iterator<String> it = names.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
String name = filterName(it.next());
|
||||
String name = optFilterName(it.next());
|
||||
if (it.hasNext())
|
||||
{
|
||||
// Another dir in the hierarchy.
|
||||
@ -714,22 +814,82 @@ public class Storage
|
||||
f = new File(base, name);
|
||||
else
|
||||
f = new SecureFile(base, name);
|
||||
if (!f.createNewFile() && !f.exists())
|
||||
// createNewFile() can throw a "Permission denied" IOE even if the file exists???
|
||||
// so do it second
|
||||
if (!f.exists() && !f.createNewFile())
|
||||
throw new IOException("Could not create file " + f);
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
public static File getFileFromNames(File base, List<String> names)
|
||||
{
|
||||
Iterator<String> it = names.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
String name = filterName(it.next());
|
||||
base = new File(base, name);
|
||||
/**
|
||||
* The base file or directory.
|
||||
* @return the File
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public File getBase() {
|
||||
return _base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not include directories. Unsorted.
|
||||
* @return a new List
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public List<File> getFiles() {
|
||||
List<File> rv = new ArrayList<File>(_torrentFiles.size());
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
rv.add(tf.RAFfile);
|
||||
}
|
||||
return base;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not include directories.
|
||||
* @since 0.9.23
|
||||
*/
|
||||
public int getFileCount() {
|
||||
return _torrentFiles.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes the base for a multi-file torrent.
|
||||
* Sorted bottom-up for easy deletion.
|
||||
* Slow. Use for deletion only.
|
||||
* @return a new Set or null for a single-file torrent
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public SortedSet<File> getDirectories() {
|
||||
if (!_base.isDirectory())
|
||||
return null;
|
||||
SortedSet<File> rv = new TreeSet<File>(Collections.reverseOrder());
|
||||
rv.add(_base);
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
File f = tf.RAFfile;
|
||||
do {
|
||||
f = f.getParentFile();
|
||||
} while (f != null && rv.add(f));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking. Holds lock.
|
||||
* Recommend running only when stopped.
|
||||
* Caller should thread.
|
||||
* Calls listener.setWantedPieces() on completion if anything changed.
|
||||
*
|
||||
* @return true if anything changed, false otherwise
|
||||
* @since 0.9.23
|
||||
*/
|
||||
public boolean recheck() throws IOException {
|
||||
int previousNeeded = needed;
|
||||
checkCreateFiles(true);
|
||||
boolean changed = previousNeeded != needed;
|
||||
if (listener != null && changed)
|
||||
listener.setWantedPieces(this);
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -756,6 +916,7 @@ public class Storage
|
||||
|
||||
private void locked_checkCreateFiles(boolean recheck) throws IOException
|
||||
{
|
||||
_checkProgress.set(0);
|
||||
// Whether we are resuming or not,
|
||||
// if any of the files already exists we assume we are resuming.
|
||||
boolean resume = false;
|
||||
@ -772,13 +933,16 @@ 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()
|
||||
long lengthProgress = 0;
|
||||
for (TorrentFile tf : _torrentFiles)
|
||||
{
|
||||
long length = tf.RAFfile.length();
|
||||
lengthProgress += tf.length;
|
||||
if(tf.RAFfile.exists() && length == tf.length)
|
||||
{
|
||||
if (listener != null)
|
||||
listener.storageAllocated(this, length);
|
||||
_checkProgress.set(0);
|
||||
resume = true; // XXX Could dynamicly check
|
||||
}
|
||||
else if (length == 0) {
|
||||
@ -790,6 +954,8 @@ public class Storage
|
||||
tf.closeRAF();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
if (!resume)
|
||||
_checkProgress.set((int) (pieces * lengthProgress / total_length));
|
||||
} else {
|
||||
String msg = "File '" + tf.name + "' exists, but has wrong length (expected " +
|
||||
tf.length + " but found " + length + ") - repairing corruption";
|
||||
@ -798,6 +964,7 @@ public class Storage
|
||||
_log.error(msg);
|
||||
changed = true;
|
||||
resume = true;
|
||||
_checkProgress.set(0);
|
||||
_probablyComplete = false; // to force RW
|
||||
synchronized(tf) {
|
||||
RandomAccessFile raf = tf.checkRAF();
|
||||
@ -818,17 +985,16 @@ public class Storage
|
||||
long pieceEnd = 0;
|
||||
for (int i = 0; i < pieces; i++)
|
||||
{
|
||||
_checkProgress.set(i);
|
||||
int length = getUncheckedPiece(i, piece);
|
||||
boolean correctHash = metainfo.checkPiece(i, piece, 0, length);
|
||||
// close as we go so we don't run out of file descriptors
|
||||
pieceEnd += length;
|
||||
while (fileEnd <= pieceEnd) {
|
||||
TorrentFile tf = _torrentFiles.get(file);
|
||||
synchronized(tf) {
|
||||
try {
|
||||
tf.closeRAF();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
try {
|
||||
tf.closeRAF();
|
||||
} catch (IOException ioe) {}
|
||||
if (++file >= _torrentFiles.size())
|
||||
break;
|
||||
fileEnd += _torrentFiles.get(file).length;
|
||||
@ -844,6 +1010,7 @@ public class Storage
|
||||
}
|
||||
}
|
||||
|
||||
_checkProgress.set(pieces);
|
||||
_probablyComplete = complete();
|
||||
// close all the files so we don't end up with a zillion open ones;
|
||||
// we will reopen as needed
|
||||
@ -900,9 +1067,7 @@ public class Storage
|
||||
for (TorrentFile tf : _torrentFiles)
|
||||
{
|
||||
try {
|
||||
synchronized(tf) {
|
||||
tf.closeRAF();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error closing " + tf, ioe);
|
||||
// gobble gobble
|
||||
@ -1127,17 +1292,15 @@ public class Storage
|
||||
return length;
|
||||
}
|
||||
|
||||
private static final long RAFCloseDelay = 4*60*1000;
|
||||
private static final long RAF_CLOSE_DELAY = 4*60*1000;
|
||||
|
||||
/**
|
||||
* Close unused RAFs - call periodically
|
||||
*/
|
||||
public void cleanRAFs() {
|
||||
long cutoff = System.currentTimeMillis() - RAFCloseDelay;
|
||||
long cutoff = System.currentTimeMillis() - RAF_CLOSE_DELAY;
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
synchronized(tf) {
|
||||
tf.closeRAF(cutoff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1263,7 +1426,9 @@ public class Storage
|
||||
// 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)
|
||||
// Also don't ballon on ARM, as a proxy for solid state disk, where fragmentation doesn't matter too much.
|
||||
// Actual detection of SSD is almost impossible.
|
||||
if (!_isWindows && !_isARM)
|
||||
isSparse = true;
|
||||
}
|
||||
|
||||
@ -1320,18 +1485,44 @@ public class Storage
|
||||
* @since 0.9.4
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 1 || args.length > 2) {
|
||||
System.err.println("Usage: Storage file-or-dir [announceURL]");
|
||||
boolean error = false;
|
||||
String created_by = null;
|
||||
String announce = null;
|
||||
Getopt g = new Getopt("Storage", args, "a:c:");
|
||||
try {
|
||||
int c;
|
||||
while ((c = g.getopt()) != -1) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
announce = g.getOptarg();
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
created_by = g.getOptarg();
|
||||
break;
|
||||
|
||||
case '?':
|
||||
case ':':
|
||||
default:
|
||||
error = true;
|
||||
break;
|
||||
} // switch
|
||||
} // while
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
error = true;
|
||||
}
|
||||
if (error || args.length - g.getOptind() != 1) {
|
||||
System.err.println("Usage: Storage [-a announceURL] [-c created-by] file-or-dir");
|
||||
System.exit(1);
|
||||
}
|
||||
File base = new File(args[0]);
|
||||
String announce = args.length == 2 ? args[1] : null;
|
||||
File base = new File(args[g.getOptind()]);
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
I2PSnarkUtil util = new I2PSnarkUtil(ctx);
|
||||
File file = null;
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
Storage storage = new Storage(util, base, announce, null, false, null);
|
||||
Storage storage = new Storage(util, base, announce, null, created_by, false, null);
|
||||
MetaInfo meta = storage.getMetaInfo();
|
||||
file = new File(storage.getBaseName() + ".torrent");
|
||||
out = new FileOutputStream(file);
|
||||
|
@ -23,8 +23,8 @@ package org.klomp.snark;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -36,7 +36,9 @@ import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.ConvertToHash;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
@ -71,8 +73,9 @@ public class TrackerClient implements Runnable {
|
||||
private static final String COMPLETED_EVENT = "completed";
|
||||
private static final String STOPPED_EVENT = "stopped";
|
||||
private static final String NOT_REGISTERED = "torrent not registered"; //bytemonsoon
|
||||
/** this is our equivalent to router.utorrent.com for bootstrap */
|
||||
private static final String DEFAULT_BACKUP_TRACKER = "http://tracker.welterde.i2p/a";
|
||||
private static final String NOT_REGISTERED_2 = "torrent not found"; // diftracker
|
||||
private static final String NOT_REGISTERED_3 = "torrent unauthorised"; // vuze
|
||||
private static final String ERROR_GOT_HTML = "received html"; // fake return
|
||||
|
||||
private final static int SLEEP = 5; // 5 minutes.
|
||||
private final static int DELAY_MIN = 2000; // 2 secs.
|
||||
@ -82,9 +85,13 @@ public class TrackerClient implements Runnable {
|
||||
private final static int MAX_CONSEC_FAILS = 5; // slow down after this
|
||||
private final static int LONG_SLEEP = 30*60*1000; // sleep a while after lots of fails
|
||||
private final static long MIN_TRACKER_ANNOUNCE_INTERVAL = 15*60*1000;
|
||||
private final static long MIN_DHT_ANNOUNCE_INTERVAL = 10*60*1000;
|
||||
private final static long MIN_DHT_ANNOUNCE_INTERVAL = 39*60*1000;
|
||||
/** No guidance in BEP 5; standard practice is K (=8) */
|
||||
private static final int DHT_ANNOUNCE_PEERS = 4;
|
||||
public static final int PORT = 6881;
|
||||
private static final int MAX_TRACKERS = 12;
|
||||
// tracker.welterde.i2p
|
||||
private static final Hash DSA_ONLY_TRACKER = ConvertToHash.getHash("cfmqlafjfmgkzbt4r3jsfyhgsr5abgxryl6fnz3d3y5a365di5aa.b32.i2p");
|
||||
|
||||
private final I2PSnarkUtil _util;
|
||||
private final MetaInfo meta;
|
||||
@ -107,6 +114,8 @@ public class TrackerClient implements Runnable {
|
||||
// these 2 used in loop()
|
||||
private volatile boolean runStarted;
|
||||
private volatile int consecutiveFails;
|
||||
// if we don't want anything else.
|
||||
// Not necessarily seeding, as we may have skipped some files.
|
||||
private boolean completed;
|
||||
private volatile boolean _fastUnannounce;
|
||||
private long lastDHTAnnounce;
|
||||
@ -151,6 +160,7 @@ public class TrackerClient implements Runnable {
|
||||
consecutiveFails = 0;
|
||||
runStarted = false;
|
||||
_fastUnannounce = false;
|
||||
snark.setTrackerProblems(null);
|
||||
_thread = new I2PAppThread(this, _threadName + " #" + (++_runCount), true);
|
||||
_thread.start();
|
||||
started = true;
|
||||
@ -337,7 +347,9 @@ public class TrackerClient implements Runnable {
|
||||
_log.debug("Backup announce: [" + url + "] for infoHash: " + infoHash);
|
||||
}
|
||||
if (backupTrackers.isEmpty()) {
|
||||
backupTrackers.add(new TCTracker(DEFAULT_BACKUP_TRACKER, false));
|
||||
backupTrackers.add(new TCTracker(SnarkManager.DEFAULT_BACKUP_TRACKER, false));
|
||||
} else if (trackers.size() > 1) {
|
||||
Collections.shuffle(backupTrackers, _util.getContext().random());
|
||||
}
|
||||
}
|
||||
this.completed = coordinator.getLeft() == 0;
|
||||
@ -355,12 +367,21 @@ public class TrackerClient implements Runnable {
|
||||
if (h == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Bad announce URL: [" + ann + ']');
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
// comment this out if tracker.welterde.i2p upgrades
|
||||
if (h.equals(DSA_ONLY_TRACKER)) {
|
||||
Destination dest = _util.getMyDestination();
|
||||
if (dest != null && dest.getSigType() != SigType.DSA_SHA1) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Skipping incompatible tracker: " + ann);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (existing.size() >= MAX_TRACKERS) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Not using announce URL, we have enough: [" + ann + ']');
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
boolean rv = existing.add(h);
|
||||
if (!rv) {
|
||||
@ -389,7 +410,7 @@ public class TrackerClient implements Runnable {
|
||||
// Local DHT tracker announce
|
||||
DHT dht = _util.getDHT();
|
||||
if (dht != null && (meta == null || !meta.isPrivate()))
|
||||
dht.announce(snark.getInfoHash());
|
||||
dht.announce(snark.getInfoHash(), coordinator.completed());
|
||||
|
||||
int oldSeenPeers = snark.getTrackerSeenPeers();
|
||||
int maxSeenPeers = 0;
|
||||
@ -521,9 +542,9 @@ public class TrackerClient implements Runnable {
|
||||
!snark.isChecking() &&
|
||||
info.getSeedCount() > 100 &&
|
||||
coordinator.getPeerCount() <= 0 &&
|
||||
_util.getContext().clock().now() > _startedOn + 2*60*60*1000 &&
|
||||
_util.getContext().clock().now() > _startedOn + 30*60*1000 &&
|
||||
snark.getTotalLength() > 0 &&
|
||||
uploaded >= snark.getTotalLength() * 3 / 2) {
|
||||
uploaded >= snark.getTotalLength() / 2) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Auto stopping " + snark.getBaseName());
|
||||
snark.setAutoStoppable(false);
|
||||
@ -537,7 +558,8 @@ public class TrackerClient implements Runnable {
|
||||
DHT dht = _util.getDHT();
|
||||
if (dht != null) {
|
||||
for (Peer peer : peers) {
|
||||
dht.announce(snark.getInfoHash(), peer.getPeerID().getDestHash());
|
||||
dht.announce(snark.getInfoHash(), peer.getPeerID().getDestHash(),
|
||||
false); // TODO actual seed/leech status
|
||||
}
|
||||
}
|
||||
|
||||
@ -570,13 +592,21 @@ public class TrackerClient implements Runnable {
|
||||
// don't show secondary tracker problems to the user
|
||||
if (tr.isPrimary)
|
||||
snark.setTrackerProblems(tr.trackerProblems);
|
||||
if (tr.trackerProblems.toLowerCase(Locale.US).startsWith(NOT_REGISTERED)) {
|
||||
String tplc = tr.trackerProblems.toLowerCase(Locale.US);
|
||||
if (tplc.startsWith(NOT_REGISTERED) || tplc.startsWith(NOT_REGISTERED_2) ||
|
||||
tplc.startsWith(NOT_REGISTERED_3) || tplc.startsWith(ERROR_GOT_HTML)) {
|
||||
// Give a guy some time to register it if using opentrackers too
|
||||
//if (trckrs.size() == 1) {
|
||||
// stop = true;
|
||||
// snark.stopTorrent();
|
||||
//} else { // hopefully each on the opentrackers list is really open
|
||||
if (tr.registerFails++ > MAX_REGISTER_FAILS)
|
||||
if (tr.registerFails++ > MAX_REGISTER_FAILS ||
|
||||
!completed || // no use retrying if we aren't seeding
|
||||
tplc.startsWith(ERROR_GOT_HTML) || // fake msg from doRequest()
|
||||
(!tr.isPrimary && tr.registerFails > MAX_REGISTER_FAILS / 2))
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Not longer announcing to " + tr.announce + " : " +
|
||||
tr.trackerProblems + " after " + tr.registerFails + " failures");
|
||||
tr.stop = true;
|
||||
//
|
||||
}
|
||||
@ -650,7 +680,9 @@ public class TrackerClient implements Runnable {
|
||||
numwant = 1;
|
||||
else
|
||||
numwant = _util.getMaxConnections();
|
||||
Collection<Hash> hashes = dht.getPeersAndAnnounce(snark.getInfoHash(), numwant, 5*60*1000, 1, 3*60*1000);
|
||||
Collection<Hash> hashes = dht.getPeersAndAnnounce(snark.getInfoHash(), numwant,
|
||||
5*60*1000, DHT_ANNOUNCE_PEERS, 3*60*1000,
|
||||
coordinator.completed(), numwant <= 1);
|
||||
if (!hashes.isEmpty()) {
|
||||
runStarted = true;
|
||||
lastDHTAnnounce = _util.getContext().clock().now();
|
||||
@ -787,10 +819,15 @@ public class TrackerClient implements Runnable {
|
||||
tr.lastRequestTime = System.currentTimeMillis();
|
||||
// Don't wait for a response to stopped when shutting down
|
||||
boolean fast = _fastUnannounce && event.equals(STOPPED_EVENT);
|
||||
byte[] fetched = _util.get(s, true, fast ? -1 : 0, small ? 128 : 1024, small ? 1024 : 8*1024);
|
||||
if (fetched == null) {
|
||||
throw new IOException("Error fetching " + s);
|
||||
}
|
||||
byte[] fetched = _util.get(s, true, fast ? -1 : 0, small ? 128 : 1024, small ? 1024 : 32*1024);
|
||||
if (fetched == null)
|
||||
throw new IOException("Error fetching");
|
||||
if (fetched.length == 0)
|
||||
throw new IOException("No data");
|
||||
// The HTML check only works if we didn't exceed the maxium fetch size specified in get(),
|
||||
// otherwise we already threw an IOE.
|
||||
if (fetched[0] == '<')
|
||||
throw new IOException(ERROR_GOT_HTML);
|
||||
|
||||
InputStream in = new ByteArrayInputStream(fetched);
|
||||
|
||||
@ -838,20 +875,21 @@ public class TrackerClient implements Runnable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ann an announce URL
|
||||
* @param ann an announce URL, may be null, returns false if null
|
||||
* @return true for i2p hosts only
|
||||
* @since 0.7.12
|
||||
*/
|
||||
public static boolean isValidAnnounce(String ann) {
|
||||
URL url;
|
||||
if (ann == null)
|
||||
return false;
|
||||
URI url;
|
||||
try {
|
||||
url = new URL(ann);
|
||||
} catch (MalformedURLException mue) {
|
||||
return false;
|
||||
url = new URI(ann);
|
||||
} catch (URISyntaxException use) {
|
||||
return false;
|
||||
}
|
||||
return url.getProtocol().equals("http") &&
|
||||
(url.getHost().endsWith(".i2p") || url.getHost().equals("i2p")) &&
|
||||
url.getPort() < 0;
|
||||
return "http".equals(url.getScheme()) && url.getHost() != null &&
|
||||
(url.getHost().endsWith(".i2p") || url.getHost().equals("i2p"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -860,15 +898,17 @@ public class TrackerClient implements Runnable {
|
||||
* @since 0.9.5
|
||||
*/
|
||||
private static Hash getHostHash(String ann) {
|
||||
URL url;
|
||||
URI url;
|
||||
try {
|
||||
url = new URL(ann);
|
||||
} catch (MalformedURLException mue) {
|
||||
url = new URI(ann);
|
||||
} catch (URISyntaxException use) {
|
||||
return null;
|
||||
}
|
||||
if (url.getPort() >= 0 || !url.getProtocol().equals("http"))
|
||||
if (!"http".equals(url.getScheme()))
|
||||
return null;
|
||||
String host = url.getHost();
|
||||
if (host == null)
|
||||
return null;
|
||||
if (host.endsWith(".i2p"))
|
||||
return ConvertToHash.getHash(host);
|
||||
if (host.equals("i2p")) {
|
||||
@ -876,7 +916,7 @@ public class TrackerClient implements Runnable {
|
||||
if (path == null || path.length() < 517 ||
|
||||
!path.startsWith("/"))
|
||||
return null;
|
||||
String[] parts = path.substring(1).split("/?&;", 2);
|
||||
String[] parts = DataHelper.split(path.substring(1), "[/\\?&;]", 2);
|
||||
return ConvertToHash.getHash(parts[0]);
|
||||
}
|
||||
return null;
|
||||
|
@ -62,7 +62,7 @@ class TrackerInfo
|
||||
private TrackerInfo(Map<String, BEValue> m, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
|
||||
throws IOException
|
||||
{
|
||||
BEValue reason = (BEValue)m.get("failure reason");
|
||||
BEValue reason = m.get("failure reason");
|
||||
if (reason != null)
|
||||
{
|
||||
failure_reason = reason.getString();
|
||||
@ -72,13 +72,13 @@ class TrackerInfo
|
||||
else
|
||||
{
|
||||
failure_reason = null;
|
||||
BEValue beInterval = (BEValue)m.get("interval");
|
||||
BEValue beInterval = m.get("interval");
|
||||
if (beInterval == null)
|
||||
throw new InvalidBEncodingException("No interval given");
|
||||
else
|
||||
interval = beInterval.getInt();
|
||||
|
||||
BEValue bePeers = (BEValue)m.get("peers");
|
||||
BEValue bePeers = m.get("peers");
|
||||
if (bePeers == null) {
|
||||
peers = Collections.emptySet();
|
||||
} else {
|
||||
@ -93,14 +93,14 @@ class TrackerInfo
|
||||
peers = p;
|
||||
}
|
||||
|
||||
BEValue bev = (BEValue)m.get("complete");
|
||||
BEValue bev = m.get("complete");
|
||||
if (bev != null) try {
|
||||
complete = bev.getInt();
|
||||
if (complete < 0)
|
||||
complete = 0;
|
||||
} catch (InvalidBEncodingException ibe) {}
|
||||
|
||||
bev = (BEValue)m.get("incomplete");
|
||||
bev = m.get("incomplete");
|
||||
if (bev != null) try {
|
||||
incomplete = bev.getInt();
|
||||
if (incomplete < 0)
|
||||
@ -196,6 +196,9 @@ class TrackerInfo
|
||||
return complete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not HTML escaped.
|
||||
*/
|
||||
public String getFailureReason()
|
||||
{
|
||||
return failure_reason;
|
||||
|
@ -6,6 +6,7 @@ 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;
|
||||
@ -109,7 +110,7 @@ class UpdateRunner implements UpdateTask, CompleteListener {
|
||||
_umgr.notifyAttemptFailed(this, "No tracker, no DHT, no OT", null);
|
||||
continue;
|
||||
}
|
||||
_snark = _smgr.addMagnet(name, ih, trackerURL, true, true, this);
|
||||
_snark = _smgr.addMagnet(name, ih, trackerURL, true, true, null, this);
|
||||
if (_snark != null) {
|
||||
updateStatus("<b>" + _smgr.util().getString("Updating from {0}", linkify(updateURL)) + "</b>");
|
||||
new Timeout();
|
||||
@ -290,12 +291,21 @@ class UpdateRunner implements UpdateTask, CompleteListener {
|
||||
return _smgr.getSavedTorrentBitField(snark);
|
||||
}
|
||||
|
||||
public boolean getSavedPreserveNamesSetting(Snark snark) {
|
||||
return _smgr.getSavedPreserveNamesSetting(snark);
|
||||
}
|
||||
|
||||
public long getSavedUploaded(Snark snark) {
|
||||
return _smgr.getSavedUploaded(snark);
|
||||
}
|
||||
|
||||
//////// 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>";
|
||||
String durl = url.length() <= 28 ? DataHelper.escapeHTML(url) :
|
||||
DataHelper.escapeHTML(url.substring(0, 25)) + "…";
|
||||
// TODO urlEncode instead
|
||||
return "<a target=\"_blank\" href=\"" + DataHelper.escapeHTML(url) + "\"/>" + durl + "</a>";
|
||||
}
|
||||
|
||||
private void updateStatus(String s) {
|
||||
|
@ -21,6 +21,7 @@
|
||||
package org.klomp.snark.bencode;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
@ -232,10 +233,8 @@ public class BDecoder
|
||||
|
||||
if (c == '-')
|
||||
{
|
||||
c = read();
|
||||
if (c == '0')
|
||||
throw new InvalidBEncodingException("Negative zero not allowed");
|
||||
chars.append((char)c);
|
||||
c = read();
|
||||
}
|
||||
|
||||
if (c < '1' || c > '9')
|
||||
@ -376,4 +375,21 @@ public class BDecoder
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* prints out the decoded data
|
||||
* @since 0.9.14
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 1) {
|
||||
System.err.println("Usage: BDecoder file.torrent");
|
||||
System.exit(1);
|
||||
}
|
||||
try {
|
||||
BEValue bev = bdecode(new FileInputStream(args[0]));
|
||||
System.out.println(bev.toString());
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* Holds different types that a bencoded byte array can represent.
|
||||
@ -208,7 +209,7 @@ public class BEValue
|
||||
} else if (bin) {
|
||||
buf.append(bs.length).append(" bytes: ").append(Base64.encode(bs));
|
||||
} else {
|
||||
buf.append('"').append(new String(bs)).append('"');
|
||||
buf.append('"').append(DataHelper.getUTF8(bs)).append('"');
|
||||
}
|
||||
valueString = buf.toString();
|
||||
} else
|
||||
|
@ -36,7 +36,7 @@ public interface DHT {
|
||||
public void ping(Destination dest, int port);
|
||||
|
||||
/**
|
||||
* Get peers for a torrent, and announce to the closest node we find.
|
||||
* Get peers for a torrent, and announce to the closest annMax nodes we find.
|
||||
* Blocking!
|
||||
* Caller should run in a thread.
|
||||
*
|
||||
@ -45,9 +45,13 @@ public interface DHT {
|
||||
* @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
|
||||
* @param isSeed true if seed, false if leech
|
||||
* @param noSeeds true if we do not want seeds in the result
|
||||
* @return possibly empty (never null)
|
||||
*/
|
||||
public Collection<Hash> getPeersAndAnnounce(byte[] ih, int max, long maxWait, int annMax, long annMaxWait);
|
||||
public Collection<Hash> getPeersAndAnnounce(byte[] ih, int max, long maxWait,
|
||||
int annMax, long annMaxWait,
|
||||
boolean isSeed, boolean noSeeds);
|
||||
|
||||
/**
|
||||
* Announce to ourselves.
|
||||
@ -55,16 +59,16 @@ public interface DHT {
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
*/
|
||||
public void announce(byte[] ih);
|
||||
public void announce(byte[] ih, boolean isSeed);
|
||||
|
||||
/**
|
||||
* Announce somebody else we know about.
|
||||
* Announce somebody else we know about to ourselves.
|
||||
* Non-blocking.
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param peerHash the peer's Hash
|
||||
*/
|
||||
public void announce(byte[] ih, byte[] peerHash);
|
||||
public void announce(byte[] ih, byte[] peerHash, boolean isSeed);
|
||||
|
||||
/**
|
||||
* Remove reference to ourselves in the local tracker.
|
||||
@ -84,9 +88,10 @@ public interface DHT {
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param maxWait the maximum total time to wait (ms) or 0 to do all in parallel and return immediately.
|
||||
* @param isSeed true if seed, false if leech
|
||||
* @return the number of successful announces, not counting ourselves.
|
||||
*/
|
||||
public int announce(byte[] ih, int max, long maxWait);
|
||||
public int announce(byte[] ih, int max, long maxWait, boolean isSeed);
|
||||
|
||||
/**
|
||||
* Stop everything.
|
||||
|
@ -34,7 +34,8 @@ class DHTTracker {
|
||||
|
||||
/** stagger with other cleaners */
|
||||
private static final long CLEAN_TIME = 199*1000;
|
||||
private static final long MAX_EXPIRE_TIME = 45*60*1000;
|
||||
/** no guidance in BEP 5; Vuze is 8h */
|
||||
private static final long MAX_EXPIRE_TIME = 3*60*60*1000L;
|
||||
private static final long MIN_EXPIRE_TIME = 15*60*1000;
|
||||
private static final long DELTA_EXPIRE_TIME = 3*60*1000;
|
||||
private static final int MAX_PEERS = 2000;
|
||||
@ -59,7 +60,7 @@ class DHTTracker {
|
||||
_isRunning = false;
|
||||
}
|
||||
|
||||
void announce(InfoHash ih, Hash hash) {
|
||||
void announce(InfoHash ih, Hash hash, boolean isSeed) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Announce " + hash + " for " + ih);
|
||||
Peers peers = _torrents.get(ih);
|
||||
@ -78,6 +79,9 @@ class DHTTracker {
|
||||
if (peer2 != null)
|
||||
peer = peer2;
|
||||
peer.setLastSeen(_context.clock().now());
|
||||
// don't let false trump true, as not all sources know the seed status
|
||||
if (isSeed)
|
||||
peer.setSeed(true);
|
||||
} else {
|
||||
// We could update setLastSeen if he is already
|
||||
// in there, but that would tend to keep
|
||||
@ -93,26 +97,43 @@ class DHTTracker {
|
||||
Peers peers = _torrents.get(ih);
|
||||
if (peers == null)
|
||||
return;
|
||||
Peer peer = new Peer(hash.getData());
|
||||
peers.remove(peer);
|
||||
peers.remove(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Caller's responsibility to remove himself from the list
|
||||
*
|
||||
* @param noSeeds true if we do not want seeds in the result
|
||||
* @return list or empty list (never null)
|
||||
*/
|
||||
List<Hash> getPeers(InfoHash ih, int max) {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
List<Hash> getPeers(InfoHash ih, int max, boolean noSeeds) {
|
||||
Peers peers = _torrents.get(ih);
|
||||
if (peers == null)
|
||||
if (peers == null || max <= 0)
|
||||
return Collections.emptyList();
|
||||
|
||||
int size = peers.size();
|
||||
List<Hash> rv = new ArrayList<Hash>(peers.values());
|
||||
if (max < size) {
|
||||
Collections.shuffle(rv, _context.random());
|
||||
List<Peer> rv = new ArrayList<Peer>(peers.values());
|
||||
int size = rv.size();
|
||||
if (max < size)
|
||||
Collections.shuffle(rv, _context.random());
|
||||
if (noSeeds) {
|
||||
int i = 0;
|
||||
for (Iterator<Peer> iter = rv.iterator(); iter.hasNext(); ) {
|
||||
if (iter.next().isSeed())
|
||||
iter.remove();
|
||||
else if (++i >= max)
|
||||
break;
|
||||
}
|
||||
if (max < rv.size())
|
||||
rv = rv.subList(0, max);
|
||||
} else {
|
||||
if (max < size)
|
||||
rv = rv.subList(0, max);
|
||||
}
|
||||
return rv;
|
||||
// a Peer is a Hash
|
||||
List rv1 = rv;
|
||||
List<Hash> rv2 = rv1;
|
||||
return rv2;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -10,6 +10,7 @@ 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;
|
||||
@ -39,6 +40,8 @@ import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
import org.klomp.snark.I2PSnarkUtil;
|
||||
import org.klomp.snark.SnarkManager;
|
||||
import org.klomp.snark.TrackerClient;
|
||||
import org.klomp.snark.bencode.BDecoder;
|
||||
import org.klomp.snark.bencode.BEncoder;
|
||||
@ -126,8 +129,10 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
|
||||
/** Max number of nodes to return. BEP 5 says 8 */
|
||||
private static final int K = 8;
|
||||
/** Max number of peers to return. BEP 5 doesn't say. We'll use the same as I2PSnarkUtil.MAX_CONNECTIONS */
|
||||
private static final int MAX_WANT = 16;
|
||||
/** Max number of peers to return. BEP 5 doesn't say.
|
||||
* We'll use more than I2PSnarkUtil.MAX_CONNECTIONS since lots could be old.
|
||||
*/
|
||||
private static final int MAX_WANT = I2PSnarkUtil.MAX_CONNECTIONS * 3 / 2;
|
||||
|
||||
/** overloads error codes which start with 201 */
|
||||
private static final int REPLY_NONE = 0;
|
||||
@ -151,7 +156,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private static final long CLEAN_TIME = 63*1000;
|
||||
private static final long EXPLORE_TIME = 877*1000;
|
||||
private static final long BLACKLIST_CLEAN_TIME = 17*60*1000;
|
||||
private static final String DHT_FILE_SUFFIX = ".dht.dat";
|
||||
public static final String DHT_FILE_SUFFIX = ".dht.dat";
|
||||
|
||||
private static final int SEND_CRYPTO_TAGS = 8;
|
||||
private static final int LOW_CRYPTO_TAGS = 4;
|
||||
@ -184,8 +189,14 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
_myNID = new NID(_myID);
|
||||
}
|
||||
_myNodeInfo = new NodeInfo(_myNID, session.getMyDestination(), _qPort);
|
||||
_dhtFile = new File(ctx.getConfigDir(), baseName + DHT_FILE_SUFFIX);
|
||||
_backupDhtFile = baseName.equals("i2psnark") ? null : new File(ctx.getConfigDir(), "i2psnark" + DHT_FILE_SUFFIX);
|
||||
File conf = new File(ctx.getConfigDir(), baseName + ".config" + SnarkManager.CONFIG_DIR_SUFFIX);
|
||||
_dhtFile = new File(conf, "i2psnark" + DHT_FILE_SUFFIX);
|
||||
if (baseName.equals("i2psnark")) {
|
||||
_backupDhtFile = null;
|
||||
} else {
|
||||
File bconf = new File(ctx.getConfigDir(), "i2psnark.config" + SnarkManager.CONFIG_DIR_SUFFIX);
|
||||
_backupDhtFile = new File(bconf, "i2psnark" + DHT_FILE_SUFFIX);
|
||||
}
|
||||
_knownNodes = new DHTNodes(ctx, _myNID);
|
||||
|
||||
start();
|
||||
@ -235,6 +246,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* @param maxWait how long to wait for each to reply (not total) must be > 0
|
||||
* @param parallel how many outstanding at once (unimplemented, always 1)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void explore(NID target, int maxNodes, long maxWait, int parallel) {
|
||||
List<NodeInfo> nodes = _knownNodes.findClosest(target, maxNodes);
|
||||
if (nodes.isEmpty()) {
|
||||
@ -305,7 +317,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peers for a torrent, and announce to the closest node we find.
|
||||
* Get peers for a torrent, and announce to the closest annMax nodes we find.
|
||||
* This is an iterative lookup in the DHT.
|
||||
* Blocking!
|
||||
* Caller should run in a thread.
|
||||
@ -315,12 +327,17 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* @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
|
||||
* @param isSeed true if seed, false if leech
|
||||
* @param noSeeds true if we do not want seeds in the result
|
||||
* @return possibly empty (never null)
|
||||
*/
|
||||
public Collection<Hash> getPeersAndAnnounce(byte[] ih, int max, long maxWait, int annMax, long annMaxWait) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public Collection<Hash> getPeersAndAnnounce(byte[] ih, int max, long maxWait,
|
||||
int annMax, long annMaxWait,
|
||||
boolean isSeed, boolean noSeeds) {
|
||||
// check local tracker first
|
||||
InfoHash iHash = new InfoHash(ih);
|
||||
Collection<Hash> rv = _tracker.getPeers(iHash, max);
|
||||
Collection<Hash> rv = _tracker.getPeers(iHash, max, noSeeds);
|
||||
rv.remove(_myNodeInfo.getHash());
|
||||
if (rv.size() >= max)
|
||||
return rv;
|
||||
@ -356,7 +373,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Try " + i + ": " + nInfo);
|
||||
|
||||
ReplyWaiter waiter = sendGetPeers(nInfo, iHash);
|
||||
ReplyWaiter waiter = sendGetPeers(nInfo, iHash, noSeeds);
|
||||
if (waiter == null)
|
||||
continue;
|
||||
synchronized(waiter) {
|
||||
@ -415,7 +432,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
// now announce
|
||||
if (!heardFrom.isEmpty()) {
|
||||
announce(ih);
|
||||
announce(ih, isSeed);
|
||||
// announce to the closest we've heard from
|
||||
int annCnt = 0;
|
||||
long start = _context.clock().now();
|
||||
@ -424,7 +441,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
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))
|
||||
if (announce(ih, annTo, toWait, isSeed))
|
||||
annCnt++;
|
||||
if (annMaxWait > 0) {
|
||||
annMaxWait -= _context.clock().now() - start;
|
||||
@ -437,7 +454,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
// 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);
|
||||
announce(ih, annMax, annMaxWait, isSeed);
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
_log.info("Finished get Peers, returning " + rv.size());
|
||||
@ -454,21 +471,21 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
*/
|
||||
public void announce(byte[] ih) {
|
||||
public void announce(byte[] ih, boolean isSeed) {
|
||||
InfoHash iHash = new InfoHash(ih);
|
||||
_tracker.announce(iHash, _myNodeInfo.getHash());
|
||||
_tracker.announce(iHash, _myNodeInfo.getHash(), isSeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Announce somebody else we know about.
|
||||
* Announce somebody else we know about to ourselves.
|
||||
* Non-blocking.
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param peerHash the peer's Hash
|
||||
*/
|
||||
public void announce(byte[] ih, byte[] peerHash) {
|
||||
public void announce(byte[] ih, byte[] peerHash, boolean isSeed) {
|
||||
InfoHash iHash = new InfoHash(ih);
|
||||
_tracker.announce(iHash, new Hash(peerHash));
|
||||
_tracker.announce(iHash, new Hash(peerHash), isSeed);
|
||||
// Do NOT do this, corrupts the Hash cache and the Peer ID
|
||||
//_tracker.announce(iHash, Hash.create(peerHash));
|
||||
}
|
||||
@ -500,10 +517,11 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param max maximum number of peers to announce to
|
||||
* @param maxWait the maximum total time to wait (ms) or 0 to do all in parallel and return immediately.
|
||||
* @param isSeed true if seed, false if leech
|
||||
* @return the number of successful announces, not counting ourselves.
|
||||
*/
|
||||
public int announce(byte[] ih, int max, long maxWait) {
|
||||
announce(ih);
|
||||
public int announce(byte[] ih, int max, long maxWait, boolean isSeed) {
|
||||
announce(ih, isSeed);
|
||||
int rv = 0;
|
||||
long start = _context.clock().now();
|
||||
InfoHash iHash = new InfoHash(ih);
|
||||
@ -513,7 +531,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
for (NodeInfo nInfo : nodes) {
|
||||
if (!_isRunning)
|
||||
break;
|
||||
if (announce(ih, nInfo, Math.min(maxWait, 60*1000)))
|
||||
if (announce(ih, nInfo, Math.min(maxWait, 60*1000), isSeed))
|
||||
rv++;
|
||||
maxWait -= _context.clock().now() - start;
|
||||
if (maxWait < 1000)
|
||||
@ -531,9 +549,10 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param nInfo the peer to announce to
|
||||
* @param maxWait the maximum time to wait (ms) or 0 to return immediately.
|
||||
* @param isSeed true if seed, false if leech
|
||||
* @return success
|
||||
*/
|
||||
private boolean announce(byte[] ih, NodeInfo nInfo, long maxWait) {
|
||||
private boolean announce(byte[] ih, NodeInfo nInfo, long maxWait, boolean isSeed) {
|
||||
InfoHash iHash = new InfoHash(ih);
|
||||
// it isn't clear from BEP 5 if a token is bound to a single infohash?
|
||||
// for now, just bind to the NID
|
||||
@ -549,7 +568,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
return false;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("No token for announce to " + nInfo + ", sending get_peers first");
|
||||
ReplyWaiter waiter = sendGetPeers(nInfo, iHash);
|
||||
ReplyWaiter waiter = sendGetPeers(nInfo, iHash, false);
|
||||
if (waiter == null)
|
||||
return false;
|
||||
long start = _context.clock().now();
|
||||
@ -580,7 +599,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
|
||||
// send and wait on rcv msg lock unless maxWait <= 0
|
||||
ReplyWaiter waiter = sendAnnouncePeer(nInfo, iHash, token);
|
||||
ReplyWaiter waiter = sendAnnouncePeer(nInfo, iHash, token, isSeed);
|
||||
if (waiter == null)
|
||||
return false;
|
||||
if (maxWait <= 0)
|
||||
@ -722,15 +741,18 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* Blocking if we have to look up the dest for the nodeinfo
|
||||
*
|
||||
* @param nInfo who to send it to
|
||||
* @param noSeeds true if we do not want seeds in the result
|
||||
* @return null on error
|
||||
*/
|
||||
private ReplyWaiter sendGetPeers(NodeInfo nInfo, InfoHash ih) {
|
||||
private ReplyWaiter sendGetPeers(NodeInfo nInfo, InfoHash ih, boolean noSeeds) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending get peers of " + ih + " to: " + nInfo);
|
||||
_log.info("Sending get peers of " + ih + " to: " + nInfo + " noseeds? " + noSeeds);
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("q", "get_peers");
|
||||
Map<String, Object> args = new HashMap<String, Object>();
|
||||
args.put("info_hash", ih.getData());
|
||||
if (noSeeds)
|
||||
args.put("noseed", Integer.valueOf(1));
|
||||
map.put("a", args);
|
||||
ReplyWaiter rv = sendQuery(nInfo, map, true);
|
||||
// save the InfoHash so we can get it later
|
||||
@ -743,11 +765,12 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* Non-blocking, will fail if we don't have the dest for the nodeinfo
|
||||
*
|
||||
* @param nInfo who to send it to
|
||||
* @param isSeed true if seed, false if leech
|
||||
* @return null on error
|
||||
*/
|
||||
private ReplyWaiter sendAnnouncePeer(NodeInfo nInfo, InfoHash ih, Token token) {
|
||||
private ReplyWaiter sendAnnouncePeer(NodeInfo nInfo, InfoHash ih, Token token, boolean isSeed) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending announce of " + ih + " to: " + nInfo);
|
||||
_log.info("Sending announce of " + ih + " to: " + nInfo + " seed? " + isSeed);
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("q", "announce_peer");
|
||||
Map<String, Object> args = new HashMap<String, Object>();
|
||||
@ -755,6 +778,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
// port ignored
|
||||
args.put("port", Integer.valueOf(TrackerClient.PORT));
|
||||
args.put("token", token.getData());
|
||||
args.put("seed", Integer.valueOf(isSeed ? 1 : 0));
|
||||
map.put("a", args);
|
||||
// an announce need not be signed, we have a token
|
||||
ReplyWaiter rv = sendQuery(nInfo, map, false);
|
||||
@ -814,16 +838,20 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
// All errors use the response port.
|
||||
|
||||
/**
|
||||
* Unused
|
||||
*
|
||||
* @param nInfo who to send it to
|
||||
* @return success
|
||||
*/
|
||||
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<String, Object>();
|
||||
Map<String, Object> resps = new HashMap<String, Object>();
|
||||
map.put("r", resps);
|
||||
return sendResponse(nInfo, msgID, map);
|
||||
Map<String, Object> map = new HashMap<String, Object>(4);
|
||||
List<Object> error = new ArrayList<Object>(2);
|
||||
error.add(Integer.valueOf(err));
|
||||
error.add(msg);
|
||||
map.put("e", error);
|
||||
return sendError(nInfo, msgID, map);
|
||||
}
|
||||
|
||||
// Low-level send methods
|
||||
@ -835,6 +863,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* @param repliable true for all but announce
|
||||
* @return null on error
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private ReplyWaiter sendQuery(NodeInfo nInfo, Map<String, Object> map, boolean repliable) {
|
||||
if (nInfo.equals(_myNodeInfo))
|
||||
throw new IllegalArgumentException("wtf don't send to ourselves");
|
||||
@ -884,6 +913,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* @param toPort the query port, we will increment here
|
||||
* @return success
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean sendResponse(NodeInfo nInfo, MsgID msgID, Map<String, Object> map) {
|
||||
if (nInfo.equals(_myNodeInfo))
|
||||
throw new IllegalArgumentException("wtf don't send to ourselves");
|
||||
@ -910,7 +940,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param toPort the query port, we will increment here
|
||||
* Unused
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
private boolean sendError(NodeInfo nInfo, MsgID msgID, Map<String, Object> map) {
|
||||
@ -1113,14 +1144,22 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
} else if (method.equals("get_peers")) {
|
||||
byte[] hash = args.get("info_hash").getBytes();
|
||||
InfoHash ih = new InfoHash(hash);
|
||||
receiveGetPeers(msgID, nInfo, ih);
|
||||
boolean noSeeds = false;
|
||||
BEValue nos = args.get("noseed");
|
||||
if (nos != null)
|
||||
noSeeds = nos.getInt() == 1;
|
||||
receiveGetPeers(msgID, nInfo, ih, noSeeds);
|
||||
} else if (method.equals("announce_peer")) {
|
||||
byte[] hash = args.get("info_hash").getBytes();
|
||||
InfoHash ih = new InfoHash(hash);
|
||||
// this is the "TCP" port, we don't care
|
||||
//int port = args.get("port").getInt();
|
||||
byte[] token = args.get("token").getBytes();
|
||||
receiveAnnouncePeer(msgID, ih, token);
|
||||
boolean isSeed = false;
|
||||
BEValue iss = args.get("seed");
|
||||
if (iss != null)
|
||||
isSeed = iss.getInt() == 1;
|
||||
receiveAnnouncePeer(msgID, ih, token, isSeed);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unknown query method rcvd: " + method);
|
||||
@ -1233,18 +1272,22 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
/**
|
||||
* Handle and respond to the query
|
||||
*/
|
||||
private void receiveGetPeers(MsgID msgID, NodeInfo nInfo, InfoHash ih) throws InvalidBEncodingException {
|
||||
private void receiveGetPeers(MsgID msgID, NodeInfo nInfo,
|
||||
InfoHash ih, boolean noSeeds) throws InvalidBEncodingException {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Rcvd get_peers from: " + nInfo + " for: " + ih);
|
||||
_log.info("Rcvd get_peers from: " + nInfo + " for: " + ih + " noseeds? " + noSeeds);
|
||||
// generate and save random token
|
||||
Token token = new Token(_context);
|
||||
_outgoingTokens.put(token, nInfo);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Stored new OB token: " + token + " for: " + nInfo);
|
||||
|
||||
List<Hash> peers = _tracker.getPeers(ih, MAX_WANT);
|
||||
List<Hash> peers = _tracker.getPeers(ih, MAX_WANT, noSeeds);
|
||||
// Check this before removing him, so we don't needlessly send nodes
|
||||
// if he's the only one on the torrent.
|
||||
boolean noPeers = peers.isEmpty();
|
||||
peers.remove(nInfo.getHash()); // him
|
||||
if (peers.isEmpty()) {
|
||||
if (noPeers) {
|
||||
// similar to find node, but with token
|
||||
// get closest from DHT
|
||||
List<NodeInfo> nodes = _knownNodes.findClosest(ih, K);
|
||||
@ -1256,9 +1299,14 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
sendNodes(nInfo, msgID, token, nodeArray);
|
||||
} else {
|
||||
List<byte[]> hashes = new ArrayList<byte[]>(peers.size());
|
||||
for (Hash peer : peers) {
|
||||
hashes.add(peer.getData());
|
||||
List<byte[]> hashes;
|
||||
if (peers.isEmpty()) {
|
||||
hashes = Collections.emptyList();
|
||||
} else {
|
||||
hashes = new ArrayList<byte[]>(peers.size());
|
||||
for (Hash peer : peers) {
|
||||
hashes.add(peer.getData());
|
||||
}
|
||||
}
|
||||
sendPeers(nInfo, msgID, token, hashes);
|
||||
}
|
||||
@ -1269,7 +1317,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* We have no node info here, it came on response port, we have to get it from the token.
|
||||
* So we can't verify that it came from the same peer, as BEP 5 specifies.
|
||||
*/
|
||||
private void receiveAnnouncePeer(MsgID msgID, InfoHash ih, byte[] tok) throws InvalidBEncodingException {
|
||||
private void receiveAnnouncePeer(MsgID msgID, InfoHash ih,
|
||||
byte[] tok, boolean isSeed) throws InvalidBEncodingException {
|
||||
Token token = new Token(tok);
|
||||
NodeInfo nInfo = _outgoingTokens.get(token);
|
||||
if (nInfo == null) {
|
||||
@ -1280,9 +1329,9 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
return;
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Rcvd announce from: " + nInfo + " for: " + ih);
|
||||
_log.info("Rcvd announce from: " + nInfo + " for: " + ih + " seed? " + isSeed);
|
||||
|
||||
_tracker.announce(ih, nInfo.getHash());
|
||||
_tracker.announce(ih, nInfo.getHash(), isSeed);
|
||||
// the reply for an announce is the same as the reply for a ping
|
||||
sendPong(nInfo, msgID);
|
||||
}
|
||||
@ -1341,7 +1390,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* @throws NPE, IllegalArgumentException, and others too
|
||||
*/
|
||||
private List<NodeInfo> receiveNodes(NodeInfo nInfo, byte[] ids) throws InvalidBEncodingException {
|
||||
int max = Math.min(K, ids.length / NodeInfo.LENGTH);
|
||||
// Azureus sends 20
|
||||
int max = Math.min(3 * K, ids.length / NodeInfo.LENGTH);
|
||||
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);
|
||||
@ -1365,7 +1415,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private List<Hash> receivePeers(NodeInfo nInfo, List<BEValue> peers) throws InvalidBEncodingException {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Rcvd peers from: " + nInfo);
|
||||
int max = Math.min(MAX_WANT, peers.size());
|
||||
int max = Math.min(MAX_WANT * 2, peers.size());
|
||||
List<Hash> rv = new ArrayList<Hash>(max);
|
||||
for (BEValue bev : peers) {
|
||||
byte[] b = bev.getBytes();
|
||||
@ -1399,6 +1449,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
// Errors.....
|
||||
|
||||
/**
|
||||
* @param error 1st item is error code, 2nd is message string
|
||||
* @throws NPE, and others too
|
||||
*/
|
||||
private void receiveError(ReplyWaiter waiter, List<BEValue> error) throws InvalidBEncodingException {
|
||||
|
@ -102,7 +102,7 @@ class NodeInfo extends SimpleDataStructure {
|
||||
*/
|
||||
public NodeInfo(String s) throws DataFormatException {
|
||||
super();
|
||||
String[] parts = s.split(":", 4);
|
||||
String[] parts = DataHelper.split(s, ":", 4);
|
||||
if (parts.length != 4)
|
||||
throw new DataFormatException("Bad format");
|
||||
byte[] nid = Base64.decode(parts[0]);
|
||||
@ -225,7 +225,7 @@ class NodeInfo extends SimpleDataStructure {
|
||||
NodeInfo ni = (NodeInfo) o;
|
||||
// assume dest matches, ignore it
|
||||
return this.hash.equals(ni.hash) && nID.equals(ni.nID) && port == ni.port;
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package org.klomp.snark.dht;
|
||||
* From zzzot, modded and relicensed to GPLv2
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
|
||||
import net.i2p.crypto.SHA1Hash;
|
||||
@ -15,7 +16,7 @@ import net.i2p.data.DataHelper;
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
class NodeInfoComparator implements Comparator<NodeInfo> {
|
||||
class NodeInfoComparator implements Comparator<NodeInfo>, Serializable {
|
||||
private final byte[] _base;
|
||||
|
||||
public NodeInfoComparator(SHA1Hash h) {
|
||||
|
@ -14,7 +14,9 @@ import net.i2p.data.Hash;
|
||||
*/
|
||||
class Peer extends Hash {
|
||||
|
||||
private long lastSeen;
|
||||
private volatile long lastSeen;
|
||||
// todo we could pack this into the upper bit of lastSeen
|
||||
private volatile boolean isSeed;
|
||||
|
||||
public Peer(byte[] data) {
|
||||
super(data);
|
||||
@ -27,4 +29,14 @@ class Peer extends Hash {
|
||||
public void setLastSeen(long now) {
|
||||
lastSeen = now;
|
||||
}
|
||||
|
||||
/** @since 0.9.14 */
|
||||
public boolean isSeed() {
|
||||
return isSeed;
|
||||
}
|
||||
|
||||
/** @since 0.9.14 */
|
||||
public void setSeed(boolean isSeed) {
|
||||
this.isSeed = isSeed;
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +87,8 @@ abstract class PersistDHT {
|
||||
out.println(ni.toPersistentString());
|
||||
count++;
|
||||
}
|
||||
if (out.checkError())
|
||||
throw new IOException("Failed write to " + file);
|
||||
} catch (IOException ioe) {
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("Error writing the DHT File", ioe);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user