forked from I2P_Developers/i2p.i2p
Compare commits
559 Commits
i2p-0.9.28
...
i2p-0.9.32
Author | SHA1 | Date | |
---|---|---|---|
e281620063 | |||
7a6db4eda2 | |||
97ad909378 | |||
718fff945e | |||
622272758e | |||
19b84aff9f | |||
![]() |
5d71fb0b44 | ||
![]() |
73ab3e834c | ||
![]() |
8901e3c34c | ||
![]() |
3b1a1fa027 | ||
![]() |
91977aabae | ||
![]() |
a715d2b6d7 | ||
![]() |
31a52d2acc | ||
![]() |
dc6ab70a69 | ||
670e56ded2 | |||
![]() |
20a58a24bb | ||
![]() |
bf42cc1594 | ||
![]() |
e44fb48ff0 | ||
![]() |
1c6ca5011d | ||
![]() |
cdd89df98d | ||
![]() |
ac833ec474 | ||
![]() |
da9c06e663 | ||
![]() |
df95e29f4e | ||
![]() |
47d354711e | ||
![]() |
b54a5c592e | ||
![]() |
5ecae1a951 | ||
![]() |
0e5b46e7d8 | ||
![]() |
97267a4d5c | ||
9ae9abd888 | |||
3f4df2317a | |||
c7a3e271db | |||
37e0b2ceb1 | |||
e3751b36ec | |||
cd21cda42a | |||
5fb6b2c79a | |||
c4a932b187 | |||
30b9f063d3 | |||
de487411b6 | |||
![]() |
bcd7b7e6ca | ||
![]() |
8f4f7b7b53 | ||
![]() |
10cfa6eeab | ||
![]() |
9e2bd4aacd | ||
![]() |
1a493094bb | ||
![]() |
3d24cc9e92 | ||
![]() |
7346ce1c89 | ||
![]() |
d5e97d2392 | ||
2cd9b34427 | |||
94738c1396 | |||
8d89d1eedb | |||
8fd354eea6 | |||
![]() |
f611f4c965 | ||
![]() |
40642c805c | ||
![]() |
2bb0ca97d6 | ||
![]() |
d828ed4342 | ||
![]() |
a06c3fe043 | ||
d77cd04061 | |||
1efc1677a3 | |||
2071cf8596 | |||
51d4827657 | |||
![]() |
6615586a4e | ||
3d385228f0 | |||
24414845d0 | |||
2c76b87fba | |||
bbe3a71d2c | |||
9d1097b467 | |||
8beb75be50 | |||
![]() |
80c55c150f | ||
![]() |
82806099a7 | ||
903f46f3a8 | |||
d99a699839 | |||
![]() |
9f15ae6e56 | ||
![]() |
26bc83c51b | ||
![]() |
5dd5a99de5 | ||
![]() |
ac75327a78 | ||
![]() |
2f48af517e | ||
![]() |
e284675e5a | ||
![]() |
674f523cba | ||
![]() |
2bfe27d217 | ||
![]() |
fe2387e4f0 | ||
![]() |
30eb089623 | ||
![]() |
ee1112cca7 | ||
![]() |
a39fb3d7c2 | ||
![]() |
0feb16d57b | ||
![]() |
b8f5c956e6 | ||
![]() |
f9b8a5ec11 | ||
![]() |
32968af39c | ||
![]() |
49ed17032c | ||
70393fd2d9 | |||
d4a139f215 | |||
0b85bffeff | |||
5ddb4edd14 | |||
![]() |
d2da262374 | ||
![]() |
3c19bb4ec5 | ||
![]() |
123c0de700 | ||
![]() |
19fcb9e5b9 | ||
![]() |
281534b0b0 | ||
![]() |
4fff74793b | ||
7d62c43209 | |||
00388675f1 | |||
4ddca951f2 | |||
2abaa4a39b | |||
402a2af898 | |||
![]() |
4e8ed909e3 | ||
![]() |
5c670fc57f | ||
![]() |
e83d31f749 | ||
![]() |
d8831151fe | ||
![]() |
87d6c302e6 | ||
![]() |
afec920bee | ||
![]() |
873b0b0f19 | ||
![]() |
96c73e7fcb | ||
![]() |
56f6a68ef3 | ||
![]() |
7cd7f01db9 | ||
![]() |
1650cbe8de | ||
![]() |
dc46f00a35 | ||
![]() |
d2edce845a | ||
![]() |
bd84ba2d5c | ||
![]() |
bd74b22b02 | ||
c768b8f8d7 | |||
![]() |
56c0fbeebe | ||
![]() |
5bc9d0af52 | ||
![]() |
12cbbfbac7 | ||
![]() |
4aa4ff738a | ||
![]() |
3d535a2282 | ||
![]() |
36030ae180 | ||
![]() |
e21479a164 | ||
32efa2e509 | |||
![]() |
8216b68ee1 | ||
![]() |
3e4318cca6 | ||
![]() |
cf7a6041b3 | ||
25c709afe2 | |||
58b38b283f | |||
931d7a95bd | |||
![]() |
d771cc6e37 | ||
![]() |
e21a57a1fc | ||
![]() |
1ede0c8b2f | ||
![]() |
57f3b89eed | ||
![]() |
9eedb5c732 | ||
![]() |
4964b1165f | ||
![]() |
bd1ccba960 | ||
![]() |
3334d9f737 | ||
![]() |
135920a9bb | ||
![]() |
c0742b5cf3 | ||
![]() |
163023ecc2 | ||
![]() |
791bc9a84f | ||
![]() |
c91f942194 | ||
![]() |
66385c06ae | ||
![]() |
6fe55529d6 | ||
4c6af7cdca | |||
![]() |
5e8730a616 | ||
![]() |
6acca28969 | ||
![]() |
017f66a342 | ||
![]() |
ff400c9448 | ||
![]() |
b28cc94a4a | ||
![]() |
4da95af45a | ||
![]() |
a9bf1e2969 | ||
![]() |
d50c12dc7d | ||
5361571c15 | |||
13d9322d81 | |||
d35f260f15 | |||
7338b79ef1 | |||
![]() |
edda87d53e | ||
de0a5c7094 | |||
![]() |
008f421377 | ||
![]() |
6821e13820 | ||
![]() |
fa73b481e6 | ||
![]() |
63581d32de | ||
![]() |
0db637ed5a | ||
![]() |
62b3e19217 | ||
![]() |
2b82312b77 | ||
81b8b18581 | |||
769cb40b79 | |||
81026c2871 | |||
dc33ad101a | |||
![]() |
b7baeddc7c | ||
![]() |
e6b968e48c | ||
![]() |
96bdfb3a63 | ||
![]() |
7002c683ab | ||
![]() |
ae7c2f6e5f | ||
b8c97a1d7a | |||
a5551c1be3 | |||
7d129215cf | |||
777d2fc0f0 | |||
7f45e97335 | |||
![]() |
feee22e389 | ||
![]() |
208372ed9e | ||
![]() |
6fd66c9396 | ||
![]() |
e8c28ad42c | ||
![]() |
5caefb5ea9 | ||
ac788d973f | |||
e973185be1 | |||
040af114e1 | |||
![]() |
6f60d642af | ||
![]() |
8a5967f592 | ||
![]() |
a929b5c683 | ||
![]() |
e32d4b15e8 | ||
![]() |
67b3c46758 | ||
![]() |
7de815bd90 | ||
![]() |
0aae97162e | ||
![]() |
ac31a1319b | ||
![]() |
e453ea48e1 | ||
![]() |
8f04aa70cb | ||
![]() |
d7d13d1a4f | ||
![]() |
6b056ce6e0 | ||
b340f4a17c | |||
93cb2a0513 | |||
610af51e95 | |||
efd7d651cc | |||
d139892ae0 | |||
45f3695f9d | |||
3e354f2f1a | |||
b0514cc564 | |||
3718c73341 | |||
cd76457128 | |||
f3d931d090 | |||
ffb3e27c0f | |||
8d6ca6431d | |||
920b14212f | |||
1e9c68859e | |||
![]() |
e390e67aee | ||
d27d060322 | |||
baa314d71f | |||
d98c3d8f71 | |||
5e497f790d | |||
3dbbc2943f | |||
925caccb57 | |||
a21e027403 | |||
4dec3657b0 | |||
06f78d7cfe | |||
6901914640 | |||
75db7fa196 | |||
f8ea882f99 | |||
dd0153e29a | |||
9dd146680d | |||
5fa6583a4e | |||
![]() |
48661361fa | ||
![]() |
4747172059 | ||
![]() |
f541dbe042 | ||
![]() |
a3e146a814 | ||
![]() |
fa6fc84bf8 | ||
![]() |
a0911a43dd | ||
![]() |
b6c6a5d696 | ||
![]() |
5353fd986e | ||
![]() |
3f40af54d1 | ||
![]() |
9e8d4fbe21 | ||
![]() |
cb3326fa42 | ||
![]() |
123473439c | ||
![]() |
c12312f523 | ||
dd8f7638c5 | |||
4edc4077f8 | |||
0d3b1fa490 | |||
828b0954a3 | |||
ec82912085 | |||
021067dab9 | |||
bfc04178a6 | |||
f390831835 | |||
5eefb8b222 | |||
c06e320f99 | |||
b810b79a44 | |||
3cc0122af4 | |||
48fb12ebeb | |||
82554b943e | |||
04e0cfe020 | |||
ed590cd6f5 | |||
c1991241e4 | |||
b503f03bd6 | |||
cff8eafc43 | |||
a0b2f5738a | |||
b335ded1d2 | |||
0a2097a238 | |||
1ab67de677 | |||
47277063e4 | |||
ca6ce37a0b | |||
a00f11d5b7 | |||
13ad5d72ae | |||
b9a7a76174 | |||
a8ae2ce955 | |||
de5661ef1b | |||
e4da3b0f3b | |||
95b6bd36e5 | |||
05aef9bd59 | |||
fda673038f | |||
24801553d6 | |||
43c3a14745 | |||
4b722c9b7f | |||
1150b4cd73 | |||
e7cfb2d6fe | |||
ad810de747 | |||
b57d7c699f | |||
ba825e61d5 | |||
aea20a552e | |||
0703a29562 | |||
db0381d6b1 | |||
a9be26d2bc | |||
db5ab0b9b8 | |||
072d990279 | |||
6b4f665f34 | |||
48a055d462 | |||
9146f3c7e1 | |||
1fe9acefe2 | |||
359b4570e4 | |||
1c720a0fda | |||
d1fdf14911 | |||
6fbeef5f8c | |||
6ef6106336 | |||
23ee40d990 | |||
4e236fc5ab | |||
d573910b7a | |||
da00b95599 | |||
e20310d257 | |||
![]() |
74151b06ef | ||
![]() |
de7a4ba55c | ||
![]() |
5ddeeec9ea | ||
![]() |
1f7042a68e | ||
![]() |
a65722f3e8 | ||
![]() |
964dacd907 | ||
![]() |
25b03979d5 | ||
![]() |
6151480ee6 | ||
![]() |
97fa5d9921 | ||
![]() |
83c9dc7437 | ||
![]() |
4bf0147198 | ||
![]() |
d7e5aaf919 | ||
![]() |
10a021f5fc | ||
![]() |
20df511401 | ||
![]() |
73708041e8 | ||
b71aafbb28 | |||
319ce9b065 | |||
87cff00b99 | |||
f38ee48ca3 | |||
ec6c24429a | |||
15915afeb8 | |||
b72085bcf3 | |||
5f96067c22 | |||
61e4e2a052 | |||
f7cdf221eb | |||
69ad95710a | |||
920572d3bc | |||
d3abbe86e9 | |||
3631efa567 | |||
a6e62afc1f | |||
8bfb3649dd | |||
45abf6575c | |||
7fb82da7ff | |||
e995a4cf92 | |||
2de25ca453 | |||
8d928eadba | |||
e03c25e397 | |||
b695293070 | |||
c09bfa0a26 | |||
f420a99b6f | |||
33b1851317 | |||
3c735eaf96 | |||
3f452c5acb | |||
380f55a0e8 | |||
8a89b3da29 | |||
f2ae1bfe09 | |||
17b781c1b4 | |||
68fb3f42a8 | |||
fc8c193f37 | |||
c949d776be | |||
e29e3e2fd3 | |||
e01c443fa2 | |||
![]() |
50450ecba6 | ||
![]() |
44c0d9357b | ||
![]() |
42ba5fa509 | ||
![]() |
24b2bb8e3f | ||
![]() |
026a9cf026 | ||
![]() |
87c8031e5a | ||
![]() |
bff3f0436c | ||
![]() |
bfe8c65628 | ||
![]() |
a22bf6b4a4 | ||
![]() |
c4e7b1a799 | ||
![]() |
232d0b5e77 | ||
![]() |
82095d9786 | ||
![]() |
f68967c5f2 | ||
![]() |
9898a4a930 | ||
![]() |
092881acc4 | ||
![]() |
b1ed295e26 | ||
2d8f0c2956 | |||
f0241d4a1c | |||
a11bd7cbe7 | |||
36ec4de9c7 | |||
843b66d61e | |||
afb87cd75c | |||
aa098ac800 | |||
bbbbfe2417 | |||
9ab55ec368 | |||
a85546534e | |||
83c786a5fd | |||
50d735b126 | |||
da893452ea | |||
6306799a4b | |||
82d812c25c | |||
890ad257e1 | |||
7cb5dab67f | |||
1cf6030646 | |||
36fd93da0d | |||
5483306d21 | |||
1f228a3f85 | |||
12cc7b3a3b | |||
61fd242938 | |||
78da3b9785 | |||
e0e06d73eb | |||
112fa503e6 | |||
9dab3b0dfe | |||
82064760d5 | |||
bd1354f95b | |||
fb74f41692 | |||
5db89d8743 | |||
b970912cc1 | |||
d196047382 | |||
eefb36cb99 | |||
dc5bfb224d | |||
e461e8fb4f | |||
8bb114e9c4 | |||
f77fc52ca7 | |||
1a28a2af23 | |||
d9c6360e0a | |||
8137661751 | |||
c66559ff84 | |||
e47c628891 | |||
7b0a5bcd5a | |||
6e7dedba81 | |||
83e356140d | |||
b66c7802e7 | |||
7311cf33b7 | |||
60c93f1e1c | |||
a36083ab18 | |||
![]() |
6308038a08 | ||
![]() |
e141abb5de | ||
![]() |
94c6496f9d | ||
![]() |
d360ba4817 | ||
![]() |
cca3f57373 | ||
![]() |
57e51ab8c5 | ||
![]() |
93511c0e61 | ||
7cae467b59 | |||
2df64cd098 | |||
![]() |
0a0757a69f | ||
![]() |
14bacc272e | ||
![]() |
2bc5630832 | ||
![]() |
ecb9726ba4 | ||
![]() |
894186e975 | ||
![]() |
053ebd7079 | ||
![]() |
fabc57b57a | ||
![]() |
04b9b202e5 | ||
![]() |
acec99ff43 | ||
![]() |
e81a01d3e8 | ||
![]() |
7101fb0997 | ||
![]() |
6b7ac9fba6 | ||
![]() |
0badcf63aa | ||
![]() |
bd69499f8f | ||
![]() |
23f835781c | ||
![]() |
5dd6f2d06b | ||
![]() |
7f9a211242 | ||
ca440a50fe | |||
994b51b5b1 | |||
fc0ddb0193 | |||
f0e6baad27 | |||
6ad4cb0928 | |||
91163948b5 | |||
e16cf2ce51 | |||
307a03f0ed | |||
b63d44b226 | |||
57be0df858 | |||
6843950bdc | |||
134cbd46e4 | |||
![]() |
91007735a1 | ||
![]() |
14ca463499 | ||
a7492269fb | |||
074c630ac6 | |||
f902a63144 | |||
ffcd2d4517 | |||
dd400bb14b | |||
63a2a6da08 | |||
3846e08e68 | |||
e625e67b5d | |||
0819857b86 | |||
fbbfd8acf0 | |||
95fb2df609 | |||
c1baee64d3 | |||
ce47d4ea68 | |||
14a839ebba | |||
636badcec2 | |||
6093f26fb6 | |||
4615fce28e | |||
328f544de1 | |||
0c76201bd9 | |||
0a1cd20585 | |||
b78b3cb942 | |||
62b5b49b02 | |||
7734d3dd65 | |||
27724a809f | |||
1d6fc40d59 | |||
176ecaa9f1 | |||
42efed578a | |||
![]() |
f7db737c1f | ||
![]() |
6512a9eb11 | ||
![]() |
3e96a24fb8 | ||
![]() |
01902de200 | ||
![]() |
e8e42a0b9d | ||
![]() |
5368da2632 | ||
![]() |
ddf332e779 | ||
![]() |
e01fe689d0 | ||
![]() |
c586970128 | ||
![]() |
f1d56a488a | ||
![]() |
3471950f6f | ||
![]() |
e34eaa351e | ||
![]() |
6d4f8fd471 | ||
![]() |
b38f3b56f8 | ||
![]() |
e86092b3e0 | ||
![]() |
ed3120956c | ||
![]() |
e16760d4d0 | ||
![]() |
147a7e4468 | ||
![]() |
6b7db094a9 | ||
![]() |
9d3dec8577 | ||
![]() |
797ee21024 | ||
![]() |
e485752546 | ||
![]() |
c9728fe2ef | ||
![]() |
206718f1e8 | ||
![]() |
31b480a68e | ||
![]() |
1a335321b2 | ||
![]() |
96fc02de5c | ||
![]() |
804011d18d | ||
![]() |
f95100a1ff | ||
![]() |
bbc2454f36 | ||
![]() |
62437acf66 | ||
![]() |
9cbcf5baa7 | ||
![]() |
63260aac59 | ||
![]() |
b316315c61 | ||
![]() |
61be475044 | ||
![]() |
29befb44cc | ||
![]() |
6bfd39d5c0 | ||
![]() |
8e391444b4 | ||
![]() |
a945cabf11 | ||
![]() |
4634debe21 | ||
![]() |
d0456098e2 | ||
![]() |
a3c9887fe5 | ||
![]() |
327f36d022 | ||
![]() |
8eeb902467 | ||
![]() |
61e54cf52e | ||
![]() |
50291233a1 | ||
![]() |
edb3ffcf45 | ||
![]() |
e34f0443bc | ||
![]() |
cb0147a798 | ||
![]() |
a7f789f069 | ||
![]() |
66544f41f2 | ||
![]() |
82c55e6008 | ||
![]() |
1ad69e7127 | ||
![]() |
6470f2d983 | ||
![]() |
709ccf6c22 | ||
![]() |
03887826e8 | ||
![]() |
44a27008dd | ||
![]() |
33229a31ee | ||
![]() |
653047146a | ||
![]() |
423167fdd3 | ||
![]() |
4547d5887d | ||
![]() |
3790129b30 | ||
![]() |
aa95750e1f | ||
![]() |
ac6cd7e4c6 | ||
b0c3c11bd9 |
47
.tx/config
47
.tx/config
@@ -9,6 +9,8 @@ trans.es = apps/i2ptunnel/locale/messages_es.po
|
||||
trans.fi = apps/i2ptunnel/locale/messages_fi.po
|
||||
trans.fr = apps/i2ptunnel/locale/messages_fr.po
|
||||
trans.hu = apps/i2ptunnel/locale/messages_hu.po
|
||||
;; Java converts id to in
|
||||
trans.id = apps/i2ptunnel/locale/messages_in.po
|
||||
trans.it = apps/i2ptunnel/locale/messages_it.po
|
||||
trans.ja = apps/i2ptunnel/locale/messages_ja.po
|
||||
trans.ko = apps/i2ptunnel/locale/messages_ko.po
|
||||
@@ -69,6 +71,8 @@ trans.et_EE = apps/routerconsole/locale/messages_et.po
|
||||
trans.fi = apps/routerconsole/locale/messages_fi.po
|
||||
trans.fr = apps/routerconsole/locale/messages_fr.po
|
||||
trans.hu = apps/routerconsole/locale/messages_hu.po
|
||||
;; Java converts id to in
|
||||
trans.id = apps/routerconsole/locale/messages_in.po
|
||||
trans.it = apps/routerconsole/locale/messages_it.po
|
||||
trans.ja = apps/routerconsole/locale/messages_ja.po
|
||||
trans.ko = apps/routerconsole/locale/messages_ko.po
|
||||
@@ -113,6 +117,7 @@ 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.sr = apps/routerconsole/locale-news/messages_sr.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
|
||||
@@ -134,6 +139,8 @@ trans.fi = apps/routerconsole/locale-countries/messages_fi.po
|
||||
trans.fr = apps/routerconsole/locale-countries/messages_fr.po
|
||||
trans.gl = apps/routerconsole/locale-countries/messages_gl.po
|
||||
trans.hu = apps/routerconsole/locale-countries/messages_hu.po
|
||||
;; Java converts id to in
|
||||
trans.id = apps/routerconsole/locale-countries/messages_in.po
|
||||
trans.it = apps/routerconsole/locale-countries/messages_it.po
|
||||
trans.ja = apps/routerconsole/locale-countries/messages_ja.po
|
||||
trans.ko = apps/routerconsole/locale-countries/messages_ko.po
|
||||
@@ -164,6 +171,8 @@ trans.es = apps/i2psnark/locale/messages_es.po
|
||||
trans.fi = apps/i2psnark/locale/messages_fi.po
|
||||
trans.fr = apps/i2psnark/locale/messages_fr.po
|
||||
trans.hu = apps/i2psnark/locale/messages_hu.po
|
||||
;; Java converts id to in
|
||||
trans.id = apps/i2psnark/locale/messages_in.po
|
||||
trans.it = apps/i2psnark/locale/messages_it.po
|
||||
trans.ja = apps/i2psnark/locale/messages_ja.po
|
||||
trans.ko = apps/i2psnark/locale/messages_ko.po
|
||||
@@ -194,6 +203,8 @@ trans.fi = apps/susidns/locale/messages_fi.po
|
||||
trans.fr = apps/susidns/locale/messages_fr.po
|
||||
trans.gl = apps/susidns/locale/messages_gl.po
|
||||
trans.hu = apps/susidns/locale/messages_hu.po
|
||||
;; Java converts id to in
|
||||
trans.id = apps/susidns/locale/messages_in.po
|
||||
trans.it = apps/susidns/locale/messages_it.po
|
||||
trans.ja = apps/susidns/locale/messages_ja.po
|
||||
trans.ko = apps/susidns/locale/messages_ko.po
|
||||
@@ -250,9 +261,11 @@ trans.zh_CN = apps/desktopgui/locale/messages_zh.po
|
||||
[I2P.susimail]
|
||||
source_file = apps/susimail/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/susimail/locale/messages_ar.po
|
||||
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.el = apps/susimail/locale/messages_el.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
|
||||
@@ -264,12 +277,14 @@ trans.it = apps/susimail/locale/messages_it.po
|
||||
trans.ja = apps/susimail/locale/messages_ja.po
|
||||
trans.ko = apps/susimail/locale/messages_ko.po
|
||||
trans.mg = apps/susimail/locale/messages_mg.po
|
||||
trans.nb = apps/susimail/locale/messages_nb.po
|
||||
trans.nl = apps/susimail/locale/messages_nl.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.sk = apps/susimail/locale/messages_sk.po
|
||||
trans.sq = apps/susimail/locale/messages_sq.po
|
||||
trans.sv_SE = apps/susimail/locale/messages_sv.po
|
||||
trans.tr_TR = apps/susimail/locale/messages_tr.po
|
||||
@@ -287,11 +302,13 @@ 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.gl = debian/po/gl.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.nb = debian/po/nb.po
|
||||
trans.nl = debian/po/nl.po
|
||||
trans.pl = debian/po/pl.po
|
||||
trans.pt = debian/po/pt.po
|
||||
@@ -309,12 +326,10 @@ trans.zh_TW = debian/po/zh_TW.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.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.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
|
||||
@@ -329,8 +344,7 @@ 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.uk_UA = installer/resources/locale/po/messages_uk.po
|
||||
trans.zh_CN = installer/resources/locale/po/messages_zh.po
|
||||
|
||||
[I2P.getopt]
|
||||
@@ -384,6 +398,7 @@ trans.ko = apps/ministreaming/locale/messages_ko.po
|
||||
trans.nb = apps/ministreaming/locale/messages_nb.po
|
||||
trans.nl = apps/ministreaming/locale/messages_nl.po
|
||||
trans.pl = apps/ministreaming/locale/messages_pl.po
|
||||
trans.pt = apps/ministreaming/locale/messages_pt.po
|
||||
trans.pt_BR = apps/ministreaming/locale/messages_pt_BR.po
|
||||
trans.ro = apps/ministreaming/locale/messages_ro.po
|
||||
trans.ru_RU = apps/ministreaming/locale/messages_ru.po
|
||||
@@ -392,6 +407,26 @@ trans.tr_TR = apps/ministreaming/locale/messages_tr.po
|
||||
trans.uk_UA = apps/ministreaming/locale/messages_uk.po
|
||||
trans.zh_CN = apps/ministreaming/locale/messages_zh.po
|
||||
|
||||
[I2P.manpages]
|
||||
type = PO
|
||||
source_file = installer/resources/locale-man/man.pot
|
||||
source_lang = en
|
||||
; after adding languages here, add to debian/*.manpages also
|
||||
trans.de = installer/resources/locale-man/man_de.po
|
||||
trans.es = installer/resources/locale-man/man_es.po
|
||||
trans.fi = installer/resources/locale-man/man_fi.po
|
||||
trans.fr = installer/resources/locale-man/man_fr.po
|
||||
trans.id = installer/resources/locale-man/man_id.po
|
||||
trans.it = installer/resources/locale-man/man_it.po
|
||||
trans.ko = installer/resources/locale-man/man_ko.po
|
||||
trans.nl = installer/resources/locale-man/man_nl.po
|
||||
trans.pl = installer/resources/locale-man/man_pl.po
|
||||
trans.pt = installer/resources/locale-man/man_pt.po
|
||||
trans.pt_BR = installer/resources/locale-man/man_pt_BR.po
|
||||
trans.ru_RU = installer/resources/locale-man/man_ru.po
|
||||
trans.sv_SE = installer/resources/locale-man/man_sv.po
|
||||
trans.zh_CN = installer/resources/locale-man/man_zh.po
|
||||
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
|
14
Docker.entrypoint.sh
Normal file
14
Docker.entrypoint.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
export JAVA_HOME=/opt/jdk/jre
|
||||
|
||||
# Ensure user rights
|
||||
chown -R i2p:nobody /opt/i2p
|
||||
chmod -R u+rwx /opt/i2p
|
||||
|
||||
gosu i2p /opt/i2p/i2psvc /opt/i2p/wrapper.config wrapper.pidfile=/var/tmp/i2p.pid \
|
||||
wrapper.name=i2p \
|
||||
wrapper.displayname="I2P Service" \
|
||||
wrapper.statusfile=/var/tmp/i2p.status \
|
||||
wrapper.java.statusfile=/var/tmp/i2p.java.status \
|
||||
wrapper.logfile=/var/tmp/wrapper.log
|
7
Docker.expt
Normal file
7
Docker.expt
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/expect
|
||||
set timeout 15;
|
||||
spawn java -jar /tmp/i2pinstall.jar -console
|
||||
expect {
|
||||
-re ".*press 1 to continue, 2 to quit, 3 to redisplay" {send "1\r"; exp_continue;}
|
||||
-re "Select target path *" {send "/opt/i2p\r"; exp_continue;}
|
||||
}
|
62
Dockerfile
Normal file
62
Dockerfile
Normal file
@@ -0,0 +1,62 @@
|
||||
FROM meeh/java8server:latest
|
||||
# Docker image based on Alpine with Java.
|
||||
|
||||
# We use Oracle Java to run I2P, but uses the openjdk to build it.
|
||||
|
||||
|
||||
MAINTAINER Mikal Villa <mikal@sigterm.no>
|
||||
|
||||
ENV GIT_BRANCH="master"
|
||||
ENV I2P_PREFIX="/opt/i2p"
|
||||
ENV PATH=${I2P_PREFIX}/bin:$PATH
|
||||
ENV JAVA_HOME=/usr/lib/jvm/default-jvm
|
||||
|
||||
ENV GOSU_VERSION=1.7
|
||||
ENV GOSU_SHASUM="34049cfc713e8b74b90d6de49690fa601dc040021980812b2f1f691534be8a50 /usr/local/bin/gosu"
|
||||
|
||||
RUN mkdir /user && adduser -S -h /user i2p && chown -R i2p:nobody /user
|
||||
|
||||
# Adding files first, since Docker.expt is required for installation
|
||||
ADD Docker.expt /tmp/Docker.expt
|
||||
ADD Docker.entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Required for wget https
|
||||
RUN apk add --no-cache openssl
|
||||
# Gosu is a replacement for su/sudo in docker and not a backdoor :) See https://github.com/tianon/gosu
|
||||
RUN wget -O /usr/local/bin/gosu https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64 \
|
||||
&& echo "${GOSU_SHASUM}" | sha256sum -c && chmod +x /usr/local/bin/gosu
|
||||
|
||||
#
|
||||
# Each RUN is a layer, adding the dependencies and building i2pd in one layer takes around 8-900Mb, so to keep the
|
||||
# image under 200mb we need to remove all the build dependencies in the same "RUN" / layer.
|
||||
#
|
||||
|
||||
# The main layer
|
||||
RUN apk --no-cache add build-base git gettext tar bzip2 apache-ant openjdk8 expect \
|
||||
&& mkdir -p /usr/src/build \
|
||||
&& cd /usr/src/build \
|
||||
&& git clone -b ${GIT_BRANCH} https://github.com/i2p/i2p.i2p.git \
|
||||
&& cd /usr/src/build/i2p.i2p \
|
||||
&& echo "noExe=true" >> build.properties \
|
||||
&& ant installer-linux \
|
||||
&& cp i2pinstall*.jar /tmp/i2pinstall.jar \
|
||||
&& mkdir -p /opt \
|
||||
&& chown i2p:root /opt \
|
||||
&& chmod u+rw /opt \
|
||||
&& gosu i2p expect -f /tmp/Docker.expt \
|
||||
&& cd ${I2P_PREFIX} \
|
||||
&& rm -fr man docs *.bat *.command *.app /tmp/i2pinstall.jar /tmp/Docker.expt \
|
||||
&& rm -fr /usr/src/build \
|
||||
&& apk --purge del build-base apache-ant expect tcl expat git openjdk8 openjdk8-jre openjdk8-jre-base openjdk8-jre-lib bzip2 tar \
|
||||
binutils-libs binutils pkgconfig libcurl libc-dev musl-dev g++ make fortify-headers pkgconf giflib libssh2 libxdmcp libxcb \
|
||||
libx11 pcre alsa-lib libxi libxrender libxml2 readline bash openssl \
|
||||
&& rm -fr /usr/lib/jvm/default-jre \
|
||||
&& ln -sf /opt/jdk/jre /usr/lib/jvm/default-jre \
|
||||
&& chmod a+x /entrypoint.sh
|
||||
|
||||
|
||||
|
||||
EXPOSE 7654 7656 7657 7658 4444 6668 8998 7659 7660 4445 15000-20000
|
||||
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
|
@@ -11,7 +11,7 @@ you may use:
|
||||
lynx http://localhost:7657/
|
||||
to configure the router.
|
||||
|
||||
If you're having trouble, swing by http://forum.i2p/, check the
|
||||
If you're having trouble, check the
|
||||
website at https://geti2p.net/, or get on irc://irc.freenode.net/#i2p
|
||||
|
||||
I2P will create and store files and configuration data in the user directory
|
||||
|
@@ -58,7 +58,7 @@ Public domain except as listed below:
|
||||
|
||||
PRNG:
|
||||
Copyright (C) 2001, 2002, Free Software Foundation, Inc.
|
||||
See licenses/LICENSE-LGPLv2.1.txt
|
||||
See licenses/LICENSE-LGPLv2.1.txt or /usr/share/common-licenses/LGPL-2.1
|
||||
|
||||
HashCash code:
|
||||
Copyright 2006 Gregory Rubin grrubin@gmail.com
|
||||
@@ -212,12 +212,11 @@ Applications:
|
||||
Zxing 3.3.0:
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
Jetty 8.1.21.v20160908:
|
||||
Jetty 9.2.21.v20170120:
|
||||
See licenses/ABOUT-Jetty.html
|
||||
See licenses/NOTICE-Jetty.html
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
See licenses/LICENSE-ECLIPSE-1.0.html
|
||||
See licenses/NOTICE-Commons-Logging.txt
|
||||
|
||||
JRobin 1.6.0-1:
|
||||
Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
@@ -245,6 +244,7 @@ Applications:
|
||||
- All other flag icons: public domain, courtesy mjames@gmail.com http://www.famfamfam.com/
|
||||
Silk icons: See licenses/LICENSE-SilkIcons.txt
|
||||
FatCow icons: See licenses/LICENSE-FatCowIcons.txt
|
||||
Fugue Icons: See licenses/LICENSE-FugueIcons.txt
|
||||
|
||||
GeoIP Data:
|
||||
This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com/
|
||||
@@ -283,7 +283,7 @@ Applications:
|
||||
Bundles systray4j-2.4.1:
|
||||
See licenses/LICENSE-LGPLv2.1.txt
|
||||
|
||||
Tomcat 6.0.48:
|
||||
Tomcat 8.0.33:
|
||||
Copyright 1999-2016 The Apache Software Foundation
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
See licenses/NOTICE-Tomcat.txt
|
||||
|
62
README.md
Normal file
62
README.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# I2P
|
||||
|
||||
This is the source code for the reference Java implementation of I2P.
|
||||
|
||||
Latest release: https://geti2p.net/download
|
||||
|
||||
## Installing
|
||||
|
||||
See INSTALL.txt or https://geti2p.net/download for installation instructions.
|
||||
|
||||
## Documentation
|
||||
|
||||
https://geti2p.net/how
|
||||
|
||||
FAQ: https://geti2p.net/faq
|
||||
|
||||
API: http://docs.i2p-projekt.de/javadoc/
|
||||
or run 'ant javadoc' then start at build/javadoc/index.html
|
||||
|
||||
## Building packages from source
|
||||
|
||||
To get development branch from source control: https://geti2p.net/newdevelopers
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- 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.
|
||||
|
||||
### Build process
|
||||
|
||||
On x86 systems do:
|
||||
|
||||
ant pkg
|
||||
|
||||
On non-x86, use one of the following instead:
|
||||
|
||||
ant installer-linux
|
||||
ant installer-freebsd
|
||||
ant installer-osx
|
||||
|
||||
Run 'ant' with no arguments to see other build options.
|
||||
|
||||
## Contact info
|
||||
|
||||
Need help? See the IRC channel #i2p on irc.freenode.net
|
||||
|
||||
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
|
||||
|
@@ -35,7 +35,6 @@ FAQ:
|
||||
|
||||
Need help?
|
||||
IRC irc.freenode.net #i2p
|
||||
http://forum.i2p/
|
||||
|
||||
Bug reports:
|
||||
https://trac.i2p2.de/report/1
|
||||
|
@@ -155,7 +155,6 @@ public class BOB implements Runnable, ClientApp {
|
||||
*
|
||||
* @param mgr may be null
|
||||
* @param args non-null
|
||||
* @throws Exception on bad args
|
||||
* @since 0.9.10
|
||||
*/
|
||||
public BOB(I2PAppContext context, ClientAppManager mgr, String[] args) {
|
||||
@@ -364,25 +363,30 @@ public class BOB implements Runnable, ClientApp {
|
||||
// We could order them to stop, but that could cause nasty issues in the locks.
|
||||
visitAllThreads();
|
||||
database.getReadLock();
|
||||
int all = database.getcount();
|
||||
database.releaseReadLock();
|
||||
NamedDB nickinfo;
|
||||
for (i = 0; i < all; i++) {
|
||||
database.getReadLock();
|
||||
nickinfo = (NamedDB) database.getnext(i);
|
||||
nickinfo.getReadLock();
|
||||
if (nickinfo.get(P_RUNNING).equals(Boolean.TRUE) && nickinfo.get(P_STOPPING).equals(Boolean.FALSE) && nickinfo.get(P_STARTING).equals(Boolean.FALSE)) {
|
||||
nickinfo.releaseReadLock();
|
||||
database.releaseReadLock();
|
||||
database.getWriteLock();
|
||||
nickinfo.getWriteLock();
|
||||
nickinfo.add(P_STOPPING, Boolean.valueOf(true));
|
||||
nickinfo.releaseWriteLock();
|
||||
database.releaseWriteLock();
|
||||
} else {
|
||||
nickinfo.releaseReadLock();
|
||||
database.releaseReadLock();
|
||||
try {
|
||||
for (Object ndb : database.values()) {
|
||||
nickinfo = (NamedDB) ndb;
|
||||
nickinfo.getReadLock();
|
||||
boolean released = false;
|
||||
try {
|
||||
if (nickinfo.get(P_RUNNING).equals(Boolean.TRUE) && nickinfo.get(P_STOPPING).equals(Boolean.FALSE) && nickinfo.get(P_STARTING).equals(Boolean.FALSE)) {
|
||||
nickinfo.releaseReadLock();
|
||||
released = true;
|
||||
nickinfo.getWriteLock();
|
||||
try {
|
||||
nickinfo.add(P_STOPPING, Boolean.TRUE);
|
||||
} finally {
|
||||
nickinfo.releaseWriteLock();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (!released)
|
||||
nickinfo.releaseReadLock();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
changeState(STOPPED);
|
||||
_log.info("BOB is now stopped.");
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -25,16 +25,16 @@ import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
* Process I2P->TCP
|
||||
* Process I2P->TCP
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class I2PtoTCP implements Runnable {
|
||||
|
||||
private I2PSocket I2P;
|
||||
private NamedDB info, database;
|
||||
private final NamedDB info, database;
|
||||
private Socket sock;
|
||||
private AtomicBoolean lives;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@@ -56,8 +56,8 @@ public class I2PtoTCP implements Runnable {
|
||||
}
|
||||
|
||||
private void runlock() {
|
||||
database.releaseReadLock();
|
||||
info.releaseReadLock();
|
||||
database.releaseReadLock();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,23 +78,15 @@ public class I2PtoTCP implements Runnable {
|
||||
die:
|
||||
{
|
||||
try {
|
||||
try {
|
||||
rlock();
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
}
|
||||
rlock();
|
||||
try {
|
||||
host = info.get("OUTHOST").toString();
|
||||
port = Integer.parseInt(info.get("OUTPORT").toString());
|
||||
tell = info.get("QUIET").equals(Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
runlock();
|
||||
break die;
|
||||
}
|
||||
try {
|
||||
} finally {
|
||||
runlock();
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
}
|
||||
sock = new Socket(host, port);
|
||||
sock.setKeepAlive(true);
|
||||
|
@@ -38,18 +38,18 @@ import net.i2p.util.Log;
|
||||
*/
|
||||
public class MUXlisten implements Runnable {
|
||||
|
||||
private NamedDB database, info;
|
||||
private Logger _log;
|
||||
private I2PSocketManager socketManager;
|
||||
private ByteArrayInputStream prikey;
|
||||
private final NamedDB database, info;
|
||||
private final Logger _log;
|
||||
private final I2PSocketManager socketManager;
|
||||
private final ByteArrayInputStream prikey;
|
||||
private ThreadGroup tg;
|
||||
private String N;
|
||||
private ServerSocket listener = null;
|
||||
private int backlog = 50; // should this be more? less?
|
||||
boolean go_out;
|
||||
boolean come_in;
|
||||
private AtomicBoolean lock;
|
||||
private AtomicBoolean lives;
|
||||
private final String N;
|
||||
private ServerSocket listener;
|
||||
private static final int backlog = 50; // should this be more? less?
|
||||
private final boolean go_out;
|
||||
private final boolean come_in;
|
||||
private final AtomicBoolean lock;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor Will fail if INPORT is occupied.
|
||||
@@ -61,43 +61,39 @@ public class MUXlisten implements Runnable {
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
MUXlisten(AtomicBoolean lock, NamedDB database, NamedDB info, Logger _log) throws I2PException, IOException, RuntimeException {
|
||||
int port = 0;
|
||||
InetAddress host = null;
|
||||
this.lock = lock;
|
||||
this.tg = null;
|
||||
this.database = database;
|
||||
this.info = info;
|
||||
this._log = _log;
|
||||
lives = new AtomicBoolean(false);
|
||||
try {
|
||||
int port = 0;
|
||||
InetAddress host = null;
|
||||
this.lock = lock;
|
||||
this.tg = null;
|
||||
this.database = database;
|
||||
this.info = info;
|
||||
this._log = _log;
|
||||
lives = new AtomicBoolean(false);
|
||||
|
||||
this.database.getWriteLock();
|
||||
this.info.getWriteLock();
|
||||
this.info.add("STARTING", Boolean.valueOf(true));
|
||||
this.info.releaseWriteLock();
|
||||
this.database.releaseWriteLock();
|
||||
this.database.getReadLock();
|
||||
this.info.getReadLock();
|
||||
|
||||
N = this.info.get("NICKNAME").toString();
|
||||
prikey = new ByteArrayInputStream((byte[]) info.get("KEYS"));
|
||||
// Make a new copy so that anything else won't muck with our database.
|
||||
Properties R = (Properties) info.get("PROPERTIES");
|
||||
Properties Q = new Properties();
|
||||
Lifted.copyProperties(R, Q);
|
||||
this.database.releaseReadLock();
|
||||
this.info.releaseReadLock();
|
||||
|
||||
this.database.getReadLock();
|
||||
this.info.getReadLock();
|
||||
this.go_out = info.exists("OUTPORT");
|
||||
this.come_in = info.exists("INPORT");
|
||||
if (this.come_in) {
|
||||
port = Integer.parseInt(info.get("INPORT").toString());
|
||||
host = InetAddress.getByName(info.get("INHOST").toString());
|
||||
wlock();
|
||||
try {
|
||||
this.info.add("STARTING", Boolean.TRUE);
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
Properties Q = new Properties();
|
||||
rlock();
|
||||
try {
|
||||
N = this.info.get("NICKNAME").toString();
|
||||
prikey = new ByteArrayInputStream((byte[]) info.get("KEYS"));
|
||||
// Make a new copy so that anything else won't muck with our database.
|
||||
Properties R = (Properties) info.get("PROPERTIES");
|
||||
Lifted.copyProperties(R, Q);
|
||||
|
||||
this.go_out = info.exists("OUTPORT");
|
||||
this.come_in = info.exists("INPORT");
|
||||
if (this.come_in) {
|
||||
port = Integer.parseInt(info.get("INPORT").toString());
|
||||
host = InetAddress.getByName(info.get("INHOST").toString());
|
||||
}
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
this.database.releaseReadLock();
|
||||
this.info.releaseReadLock();
|
||||
|
||||
String i2cpHost = Q.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
|
||||
int i2cpPort = 7654;
|
||||
@@ -115,48 +111,51 @@ public class MUXlisten implements Runnable {
|
||||
prikey, i2cpHost, i2cpPort, Q);
|
||||
} catch (IOException e) {
|
||||
// Something went bad.
|
||||
this.database.getWriteLock();
|
||||
this.info.getWriteLock();
|
||||
this.info.add("STARTING", Boolean.valueOf(false));
|
||||
this.info.releaseWriteLock();
|
||||
this.database.releaseWriteLock();
|
||||
throw new IOException(e.toString());
|
||||
wlock();
|
||||
try {
|
||||
this.info.add("STARTING", Boolean.FALSE);
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
// Something went bad.
|
||||
this.database.getWriteLock();
|
||||
this.info.getWriteLock();
|
||||
this.info.add("STARTING", Boolean.valueOf(false));
|
||||
this.info.releaseWriteLock();
|
||||
this.database.releaseWriteLock();
|
||||
throw new RuntimeException(e);
|
||||
wlock();
|
||||
try {
|
||||
this.info.add("STARTING", Boolean.FALSE);
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
// Something else went bad.
|
||||
this.database.getWriteLock();
|
||||
this.info.getWriteLock();
|
||||
this.info.add("STARTING", Boolean.valueOf(false));
|
||||
this.info.releaseWriteLock();
|
||||
this.database.releaseWriteLock();
|
||||
wlock();
|
||||
try {
|
||||
this.info.add("STARTING", Boolean.FALSE);
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void rlock() throws Exception {
|
||||
private void rlock() {
|
||||
database.getReadLock();
|
||||
info.getReadLock();
|
||||
}
|
||||
|
||||
private void runlock() throws Exception {
|
||||
database.releaseReadLock();
|
||||
private void runlock() {
|
||||
info.releaseReadLock();
|
||||
database.releaseReadLock();
|
||||
}
|
||||
|
||||
private void wlock() throws Exception {
|
||||
private void wlock() {
|
||||
database.getWriteLock();
|
||||
info.getWriteLock();
|
||||
}
|
||||
|
||||
private void wunlock() throws Exception {
|
||||
private void wunlock() {
|
||||
info.releaseWriteLock();
|
||||
database.releaseWriteLock();
|
||||
}
|
||||
@@ -170,24 +169,19 @@ public class MUXlisten implements Runnable {
|
||||
Thread t = null;
|
||||
Thread q = null;
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
info.add("RUNNING", Boolean.valueOf(true));
|
||||
info.add("RUNNING", Boolean.TRUE);
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
wunlock();
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
} finally {
|
||||
wunlock();
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
return;
|
||||
}
|
||||
lives.set(true);
|
||||
lock.set(false);
|
||||
@@ -214,21 +208,17 @@ public class MUXlisten implements Runnable {
|
||||
q.start();
|
||||
}
|
||||
|
||||
wlock();
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
info.add("STARTING", Boolean.valueOf(false));
|
||||
info.add("STARTING", Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
wunlock();
|
||||
break quit;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
break quit;
|
||||
}
|
||||
try {
|
||||
} finally {
|
||||
wunlock();
|
||||
} catch (Exception e) {
|
||||
break quit;
|
||||
}
|
||||
boolean spin = true;
|
||||
while (spin && lives.get()) {
|
||||
@@ -237,21 +227,17 @@ public class MUXlisten implements Runnable {
|
||||
} catch (InterruptedException e) {
|
||||
break quit;
|
||||
}
|
||||
rlock();
|
||||
try {
|
||||
rlock();
|
||||
try {
|
||||
spin = info.get("STOPPING").equals(Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
runlock();
|
||||
break quit;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
break quit;
|
||||
}
|
||||
try {
|
||||
} finally {
|
||||
runlock();
|
||||
} catch (Exception e) {
|
||||
break quit;
|
||||
}
|
||||
}
|
||||
} // die
|
||||
@@ -271,16 +257,16 @@ public class MUXlisten implements Runnable {
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
info.add("STARTING", Boolean.valueOf(false));
|
||||
info.add("STOPPING", Boolean.valueOf(true));
|
||||
info.add("RUNNING", Boolean.valueOf(false));
|
||||
info.add("STARTING", Boolean.FALSE);
|
||||
info.add("STOPPING", Boolean.TRUE);
|
||||
info.add("RUNNING", Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
wunlock();
|
||||
return;
|
||||
}
|
||||
wunlock();
|
||||
} catch (Exception e) {
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
// Start cleanup.
|
||||
while (!lock.compareAndSet(false, true)) {
|
||||
@@ -322,15 +308,15 @@ public class MUXlisten implements Runnable {
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
info.add("STARTING", Boolean.valueOf(false));
|
||||
info.add("STOPPING", Boolean.valueOf(false));
|
||||
info.add("RUNNING", Boolean.valueOf(false));
|
||||
info.add("STARTING", Boolean.FALSE);
|
||||
info.add("STOPPING", Boolean.FALSE);
|
||||
info.add("RUNNING", Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
wunlock();
|
||||
return;
|
||||
}
|
||||
wunlock();
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
|
@@ -15,6 +15,12 @@
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
|
||||
/**
|
||||
* Internal database to relate nicknames to options to values
|
||||
*
|
||||
@@ -22,129 +28,62 @@ package net.i2p.BOB;
|
||||
*/
|
||||
public class NamedDB {
|
||||
|
||||
private volatile Object[][] data;
|
||||
private int index, writersWaiting, readers;
|
||||
private final Map<String, Object> data;
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false);
|
||||
|
||||
/**
|
||||
* make initial NULL object
|
||||
*
|
||||
*/
|
||||
public NamedDB() {
|
||||
this.data = new Object[1][2];
|
||||
this.data = new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
synchronized public void getReadLock() {
|
||||
while ((writersWaiting != 0)) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
readers++;
|
||||
public void getReadLock() {
|
||||
lock.readLock().lock();
|
||||
}
|
||||
|
||||
synchronized public void releaseReadLock() {
|
||||
readers--;
|
||||
notifyAll();
|
||||
public void releaseReadLock() {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
|
||||
synchronized public void getWriteLock() {
|
||||
writersWaiting++;
|
||||
while (readers != 0 && writersWaiting != 1) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
public void getWriteLock() {
|
||||
lock.writeLock().lock();
|
||||
}
|
||||
|
||||
synchronized public void releaseWriteLock() {
|
||||
writersWaiting--;
|
||||
notifyAll();
|
||||
public void releaseWriteLock() {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find objects in the array, returns its index or throws exception
|
||||
* @param key
|
||||
* @return an objects index
|
||||
* @throws ArrayIndexOutOfBoundsException when key does not exist
|
||||
*/
|
||||
public int idx(Object key) throws ArrayIndexOutOfBoundsException {
|
||||
for (int i = 0; i < index; i++) {
|
||||
if (key.equals(data[i][0])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new ArrayIndexOutOfBoundsException("Can't locate key for index");
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an object from array if it exists
|
||||
* Delete an object if it exists
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
public void kill(Object key) {
|
||||
|
||||
int i, j, k, l;
|
||||
Object[][] olddata;
|
||||
int didsomething = 0;
|
||||
|
||||
try {
|
||||
k = idx(key);
|
||||
} catch (ArrayIndexOutOfBoundsException b) {
|
||||
return;
|
||||
}
|
||||
olddata = new Object[index + 2][2];
|
||||
// copy to olddata, skipping 'k'
|
||||
for (i = 0, l = 0; l < index; i++, l++) {
|
||||
if (i == k) {
|
||||
l++;
|
||||
didsomething++;
|
||||
}
|
||||
for (j = 0; j < 2; j++) {
|
||||
olddata[i][j] = data[l][j];
|
||||
}
|
||||
}
|
||||
index -= didsomething;
|
||||
data = olddata;
|
||||
public void kill(String key) {
|
||||
data.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add object to the array, deletes the old one if it exists
|
||||
* Add object, deletes the old one if it exists
|
||||
*
|
||||
* @param key
|
||||
* @param val
|
||||
*/
|
||||
public void add(Object key, Object val) {
|
||||
Object[][] olddata;
|
||||
int i, j;
|
||||
i = 0;
|
||||
kill(key);
|
||||
|
||||
olddata = new Object[index + 2][2];
|
||||
// copy to olddata
|
||||
for (i = 0; i < index; i++) {
|
||||
for (j = 0; j < 2; j++) {
|
||||
olddata[i][j] = data[i][j];
|
||||
}
|
||||
}
|
||||
data = olddata;
|
||||
data[index++] = new Object[]{key, val};
|
||||
public void add(String key, Object val) {
|
||||
data.put(key, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object, and return it, throws RuntimeException
|
||||
* Get the object, and return it, throws RuntimeException if not found
|
||||
*
|
||||
* @param key
|
||||
* @return Object
|
||||
* @throws java.lang.RuntimeException
|
||||
* @param key non-null
|
||||
* @return Object non-null
|
||||
* @throws java.lang.RuntimeException if not found
|
||||
*/
|
||||
public Object get(Object key) throws RuntimeException {
|
||||
for (int i = 0; i < index; i++) {
|
||||
if (key.equals(data[i][0])) {
|
||||
return data[i][1];
|
||||
}
|
||||
}
|
||||
public Object get(String key) throws RuntimeException {
|
||||
Object rv = data.get(key);
|
||||
if (rv != null)
|
||||
return rv;
|
||||
throw new RuntimeException("Key not found");
|
||||
}
|
||||
|
||||
@@ -154,33 +93,14 @@ public class NamedDB {
|
||||
* @param key
|
||||
* @return true if an object exists, else returns false
|
||||
*/
|
||||
public boolean exists(Object key) {
|
||||
for (int i = 0; i < index; i++) {
|
||||
if (key.equals(data[i][0])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
public boolean exists(String key) {
|
||||
return data.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param i index
|
||||
* @return an indexed Object
|
||||
* @throws java.lang.RuntimeException
|
||||
* @since 0.9.29 replaces getcount() and getnext(int)
|
||||
*/
|
||||
public Object getnext(int i) throws RuntimeException {
|
||||
if (i < index && i > -1) {
|
||||
return data[i][1];
|
||||
}
|
||||
throw new RuntimeException("No more data");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the count of how many objects
|
||||
*/
|
||||
public int getcount() {
|
||||
return index;
|
||||
public Collection<Object> values() {
|
||||
return data.values();
|
||||
}
|
||||
}
|
||||
|
@@ -27,9 +27,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
*/
|
||||
public class TCPio implements Runnable {
|
||||
|
||||
private InputStream Ain;
|
||||
private OutputStream Aout;
|
||||
private AtomicBoolean lives;
|
||||
private final InputStream Ain;
|
||||
private final OutputStream Aout;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@@ -35,29 +35,26 @@ import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
*
|
||||
* Process TCP->I2P
|
||||
* Process TCP->I2P
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class TCPtoI2P implements Runnable {
|
||||
|
||||
private I2PSocket I2P;
|
||||
private NamedDB info, database;
|
||||
private Socket sock;
|
||||
private I2PSocketManager socketManager;
|
||||
private AtomicBoolean lives;
|
||||
private final Socket sock;
|
||||
private final I2PSocketManager socketManager;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param i2p
|
||||
* @param socket
|
||||
* param info
|
||||
* param database
|
||||
* @param info unused
|
||||
* @param database unused
|
||||
*/
|
||||
TCPtoI2P(I2PSocketManager i2p, Socket socket, NamedDB info, NamedDB database, AtomicBoolean lives) {
|
||||
this.sock = socket;
|
||||
this.info = info;
|
||||
this.database = database;
|
||||
this.socketManager = i2p;
|
||||
this.lives = lives;
|
||||
}
|
||||
@@ -106,16 +103,6 @@ public class TCPtoI2P implements Runnable {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private void rlock() {
|
||||
database.getReadLock();
|
||||
info.getReadLock();
|
||||
}
|
||||
|
||||
private void runlock() {
|
||||
info.releaseReadLock();
|
||||
database.releaseReadLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* TCP stream to I2P stream thread starter
|
||||
*
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="addressbook" default="war" basedir=".">
|
||||
<project name="addressbook" default="all" basedir=".">
|
||||
|
||||
<property name="src" value="java/src"/>
|
||||
<property name="build" value="build"/>
|
||||
@@ -8,6 +8,8 @@
|
||||
<property name="war" value="addressbook.war"/>
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="javac.version" value="1.7" />
|
||||
|
||||
<target name="all" depends="jar, emptyWar"/>
|
||||
|
||||
<target name="init">
|
||||
<mkdir dir="${build}"/>
|
||||
@@ -36,24 +38,10 @@
|
||||
</depend>
|
||||
</target>
|
||||
|
||||
<target name="dependServlet" if="depend.available">
|
||||
<depend
|
||||
cache="../../build"
|
||||
srcdir="${src}"
|
||||
destdir="${build}" >
|
||||
<!-- Depend on classes instead of jars where available -->
|
||||
<classpath>
|
||||
<pathelement location="../../core/java/build/obj" />
|
||||
<pathelement location="../jetty/jettylib/javax.servlet.jar" />
|
||||
</classpath>
|
||||
</depend>
|
||||
</target>
|
||||
|
||||
<target name="compile" depends="init, depend">
|
||||
<target name="compile" depends="init, depend, warUpToDate">
|
||||
<javac debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
|
||||
includeAntRuntime="false"
|
||||
srcdir="${src}" destdir="${build}"
|
||||
excludes="net/i2p/addressbook/Servlet.java">
|
||||
srcdir="${src}" destdir="${build}">
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
<classpath>
|
||||
<pathelement location="../../core/java/build/i2p.jar" />
|
||||
@@ -61,20 +49,6 @@
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="compileServlet" depends="init, dependServlet, compile">
|
||||
<javac debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
|
||||
includeAntRuntime="false"
|
||||
srcdir="${src}" destdir="${build}"
|
||||
includes="net/i2p/addressbook/Servlet.java">
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
<classpath>
|
||||
<pathelement location="../../core/java/build/i2p.jar" />
|
||||
<pathelement location="../jetty/jettylib/javax.servlet.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<!-- unused for now (except for Android), as we oddly ship addressbook as a .war -->
|
||||
<target name="jar" depends="compile, changes">
|
||||
<!-- set if unset -->
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
@@ -91,29 +65,14 @@
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="war" depends="compileServlet, changes, warUpToDate" unless="war.uptodate">
|
||||
<mkdir dir="${dist}/tmp"/>
|
||||
<mkdir dir="${dist}/tmp/WEB-INF"/>
|
||||
<mkdir dir="${dist}/tmp/WEB-INF/classes"/>
|
||||
<copy todir="${dist}/tmp/WEB-INF/classes">
|
||||
<fileset dir="${build}"/>
|
||||
</copy>
|
||||
<!-- set if unset -->
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
<war basedir="${dist}/tmp" webxml="web.xml" destfile="${dist}/${war}">
|
||||
<manifest>
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
<attribute name="Build-Date" value="${build.timestamp}" />
|
||||
<attribute name="Base-Revision" value="${workspace.version}" />
|
||||
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" />
|
||||
<attribute name="X-Compile-Source-JDK" value="${javac.version}" />
|
||||
<attribute name="X-Compile-Target-JDK" value="${javac.version}" />
|
||||
</manifest>
|
||||
</war>
|
||||
<delete dir="${dist}/tmp"/>
|
||||
</target>
|
||||
|
||||
<target name="emptyWar" depends="init">
|
||||
<jar destfile="${dist}/${war}" >
|
||||
<manifest>
|
||||
<attribute name="Note" value="Intentionally empty" />
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="warUpToDate">
|
||||
<uptodate property="war.uptodate" targetfile="${dist}/${war}">
|
||||
|
@@ -43,15 +43,16 @@ import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
* Main class of addressbook. Performs updates, and runs the main loop.
|
||||
* As of 0.9.30, package private, run with DaemonThread.
|
||||
*
|
||||
* @author Ragnarok
|
||||
*
|
||||
*/
|
||||
public class Daemon {
|
||||
class Daemon {
|
||||
public static final String VERSION = "2.0.4";
|
||||
private static final Daemon _instance = new Daemon();
|
||||
private volatile boolean _running;
|
||||
private static final boolean DEBUG = false;
|
||||
// If you change this, change in SusiDNS SubscriptionBean also
|
||||
private static final String DEFAULT_SUB = "http://i2p-projekt.i2p/hosts.txt";
|
||||
/** @since 0.9.12 */
|
||||
static final String OLD_DEFAULT_SUB = "http://www.i2p2.i2p/hosts.txt";
|
||||
@@ -787,14 +788,15 @@ public class Daemon {
|
||||
* others are ignored.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Daemon daemon = new Daemon();
|
||||
if (args != null && args.length > 0 && args[0].equals("test"))
|
||||
_instance.test(args);
|
||||
daemon.test(args);
|
||||
else
|
||||
_instance.run(args);
|
||||
daemon.run(args);
|
||||
}
|
||||
|
||||
/** @since 0.9.26 */
|
||||
private static void test(String[] args) {
|
||||
public static void test(String[] args) {
|
||||
Properties ctxProps = new Properties();
|
||||
String PROP_FORCE = "i2p.naming.blockfile.writeInAppContext";
|
||||
ctxProps.setProperty(PROP_FORCE, "true");
|
||||
@@ -875,14 +877,14 @@ public class Daemon {
|
||||
* Call this to get the addressbook to reread its config and
|
||||
* refetch its subscriptions.
|
||||
*/
|
||||
public static void wakeup() {
|
||||
synchronized (_instance) {
|
||||
_instance.notifyAll();
|
||||
public void wakeup() {
|
||||
synchronized (this) {
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public static void stop() {
|
||||
_instance._running = false;
|
||||
public void stop() {
|
||||
_running = false;
|
||||
wakeup();
|
||||
}
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@ import net.i2p.util.I2PAppThread;
|
||||
public class DaemonThread extends I2PAppThread implements NamingServiceUpdater {
|
||||
|
||||
private final String[] args;
|
||||
private final Daemon daemon;
|
||||
|
||||
/**
|
||||
* Construct a DaemonThread with the command line arguments args.
|
||||
@@ -44,6 +45,7 @@ public class DaemonThread extends I2PAppThread implements NamingServiceUpdater {
|
||||
*/
|
||||
public DaemonThread(String[] args) {
|
||||
this.args = args;
|
||||
daemon = new Daemon();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -56,18 +58,28 @@ public class DaemonThread extends I2PAppThread implements NamingServiceUpdater {
|
||||
//} catch (InterruptedException exp) {
|
||||
//}
|
||||
I2PAppContext.getGlobalContext().namingService().registerUpdater(this);
|
||||
Daemon.main(this.args);
|
||||
I2PAppContext.getGlobalContext().namingService().unregisterUpdater(this);
|
||||
try {
|
||||
if (args != null && args.length > 0 && args[0].equals("test"))
|
||||
daemon.test(args);
|
||||
else
|
||||
daemon.run(args);
|
||||
} finally {
|
||||
I2PAppContext.getGlobalContext().namingService().unregisterUpdater(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void halt() {
|
||||
Daemon.stop();
|
||||
daemon.stop();
|
||||
interrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* The NamingServiceUpdater interface
|
||||
* @param options ignored
|
||||
* The NamingServiceUpdater interface.
|
||||
* While this may be called directly, the recommended way
|
||||
* is to call I2PAppContext.namingService().requestUpdate(Properties)
|
||||
* which will call this.
|
||||
*
|
||||
* @param options ignored, may be null
|
||||
* @since 0.8.7
|
||||
*/
|
||||
public void update(Properties options) {
|
||||
|
@@ -236,21 +236,28 @@ class HostTxtParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage: HostTxtParser validate example.i2p=b64dest[#!key1=val1#key2=val2]
|
||||
* Usage: HostTxtParser [-q] validate example.i2p=b64dest[#!key1=val1#key2=val2]
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
boolean quiet = false;
|
||||
if (args.length > 0 && args[0].equals("-q")) {
|
||||
quiet = true;
|
||||
args = java.util.Arrays.copyOfRange(args, 1, args.length);
|
||||
}
|
||||
if (args.length != 2 || !args[0].equals("validate")) {
|
||||
System.err.println("Usage: HostTxtParser validate example.i2p=b64dest[#!key1=val1#key2=val2]");
|
||||
System.exit(1);
|
||||
}
|
||||
HostTxtEntry e = parse(args[1].trim(), false);
|
||||
if (e == null) {
|
||||
System.err.println("Bad format");
|
||||
System.exit(1);
|
||||
if (!quiet)
|
||||
System.err.println("Bad format");
|
||||
System.exit(2);
|
||||
}
|
||||
if (!e.hasValidSig()) {
|
||||
System.err.println("Bad signature");
|
||||
System.exit(1);
|
||||
if (!quiet)
|
||||
System.err.println("Bad signature");
|
||||
System.exit(3);
|
||||
}
|
||||
Properties p = e.getProps();
|
||||
if (p != null) {
|
||||
@@ -259,12 +266,14 @@ class HostTxtParser {
|
||||
p.containsKey(HostTxtEntry.PROP_OLDNAME) ||
|
||||
p.containsKey(HostTxtEntry.PROP_OLDSIG)) {
|
||||
if (!e.hasValidSig()) {
|
||||
System.err.println("Bad inner signature");
|
||||
System.exit(1);
|
||||
if (!quiet)
|
||||
System.err.println("Bad inner signature");
|
||||
System.exit(4);
|
||||
}
|
||||
}
|
||||
}
|
||||
System.err.println("Good signature for " + e.getName());
|
||||
if (!quiet)
|
||||
System.err.println("Good signature for " + e.getName());
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
|
@@ -5,7 +5,7 @@
|
||||
* It probably won't make your computer catch on fire, or eat
|
||||
* your children, but it might. Use at your own risk.
|
||||
*/
|
||||
package net.i2p.client.naming;
|
||||
package net.i2p.router.naming;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -15,6 +15,7 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@@ -29,6 +30,11 @@ import java.util.StringTokenizer;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.naming.DummyNamingService;
|
||||
import net.i2p.client.naming.HostsTxtNamingService;
|
||||
import net.i2p.client.naming.NamingService;
|
||||
import net.i2p.client.naming.NamingServiceListener;
|
||||
import net.i2p.client.naming.SingleFileNamingService;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
@@ -94,7 +100,7 @@ import net.metanotion.util.skiplist.SkipList;
|
||||
*
|
||||
* All host names are converted to lower case.
|
||||
*
|
||||
* @since 0.8.7
|
||||
* @since 0.8.7, moved from core to addressbook in 0.9.31
|
||||
*/
|
||||
public class BlockfileNamingService extends DummyNamingService {
|
||||
|
||||
@@ -418,10 +424,10 @@ public class BlockfileNamingService extends DummyNamingService {
|
||||
// version 3 -> version 4
|
||||
// support multiple destinations per hostname
|
||||
if (VersionComparator.comp(_version, "4") < 0) {
|
||||
// Upgrade of 4K entry DB on RPi 2 is over 2 1/2 minutes, disable for now
|
||||
if (SystemVersion.isAndroid() || SystemVersion.isARM()) {
|
||||
// Upgrade of 4K entry DB on RPi 2 is over 2 1/2 minutes, probably worse on Android, disable for now
|
||||
if (SystemVersion.isAndroid()) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Deferring upgrade to version 4 on Android/ARM");
|
||||
_log.warn("Deferring upgrade to version 4 on Android");
|
||||
return true;
|
||||
}
|
||||
SkipList<String, Properties> hdr = _bf.getIndex(INFO_SKIPLIST, _stringSerializer, _infoSerializer);
|
||||
@@ -1291,6 +1297,132 @@ public class BlockfileNamingService extends DummyNamingService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export in a hosts.txt format.
|
||||
* Output is sorted.
|
||||
* Caller must close writer.
|
||||
*
|
||||
* @param options If non-null and contains the key "list", get
|
||||
* from that list (default "hosts.txt", NOT all lists)
|
||||
* Key "search": return only those matching substring
|
||||
* Key "startsWith": return only those starting with
|
||||
* ("[0-9]" allowed)
|
||||
* Key "beginWith": start here in the iteration
|
||||
* @since 0.9.30 override NamingService to add stored authentication strings
|
||||
*/
|
||||
@Override
|
||||
public void export(Writer out, Properties options) throws IOException {
|
||||
String listname = FALLBACK_LIST;
|
||||
String search = null;
|
||||
String startsWith = null;
|
||||
String beginWith = null;
|
||||
if (options != null) {
|
||||
String ln = options.getProperty("list");
|
||||
if (ln != null)
|
||||
listname = ln;
|
||||
search = options.getProperty("search");
|
||||
startsWith = options.getProperty("startsWith");
|
||||
beginWith = options.getProperty("beginWith");
|
||||
if (beginWith == null && startsWith != null) {
|
||||
if (startsWith.equals("[0-9]"))
|
||||
beginWith = "0";
|
||||
else
|
||||
beginWith = startsWith;
|
||||
}
|
||||
}
|
||||
out.write("# Address book: ");
|
||||
out.write(getName());
|
||||
out.write(" (" + listname + ')');
|
||||
final String nl = System.getProperty("line.separator", "\n");
|
||||
out.write(nl);
|
||||
out.write("# Exported: ");
|
||||
out.write((new Date()).toString());
|
||||
out.write(nl);
|
||||
synchronized(_bf) {
|
||||
if (_isClosed)
|
||||
return;
|
||||
try {
|
||||
SkipList<String, DestEntry> sl = _bf.getIndex(listname, _stringSerializer, _destSerializer);
|
||||
if (sl == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No skiplist found for lookup in " + listname);
|
||||
return;
|
||||
}
|
||||
if (beginWith == null && search == null) {
|
||||
int sz = sl.size();
|
||||
if (sz <= 0) {
|
||||
out.write("# No entries");
|
||||
out.write(nl);
|
||||
return;
|
||||
}
|
||||
if (sz > 1) {
|
||||
// actually not right due to multidest
|
||||
out.write("# " + sz + " entries");
|
||||
out.write(nl);
|
||||
}
|
||||
}
|
||||
SkipIterator<String, DestEntry> iter;
|
||||
if (beginWith != null)
|
||||
iter = sl.find(beginWith);
|
||||
else
|
||||
iter = sl.iterator();
|
||||
int cnt = 0;
|
||||
while (iter.hasNext()) {
|
||||
String key = iter.nextKey();
|
||||
if (startsWith != null) {
|
||||
if (startsWith.equals("[0-9]")) {
|
||||
if (key.charAt(0) > '9')
|
||||
break;
|
||||
} else if (!key.startsWith(startsWith)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
DestEntry de = iter.next();
|
||||
if (!validate(key, de, listname))
|
||||
continue;
|
||||
if (search != null && key.indexOf(search) < 0)
|
||||
continue;
|
||||
int dsz = de.destList != null ? de.destList.size() : 1;
|
||||
// new non-DSA dest is put first, so put in reverse
|
||||
// order so importers will see the older dest first
|
||||
for (int i = dsz - 1; i >= 0; i--) {
|
||||
Properties p;
|
||||
Destination d;
|
||||
if (i == 0) {
|
||||
p = de.props;
|
||||
d = de.dest;
|
||||
} else {
|
||||
p = de.propsList.get(i);
|
||||
d = de.destList.get(i);
|
||||
}
|
||||
out.write(key);
|
||||
out.write('=');
|
||||
out.write(d.toBase64());
|
||||
if (p != null)
|
||||
SingleFileNamingService.writeOptions(p, out);
|
||||
out.write(nl);
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
if (beginWith != null || search != null) {
|
||||
if (cnt <= 0) {
|
||||
out.write("# No entries");
|
||||
out.write(nl);
|
||||
return;
|
||||
}
|
||||
if (cnt > 1) {
|
||||
out.write("# " + cnt + " entries");
|
||||
out.write(nl);
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException re) {
|
||||
throw new IOException("DB lookup error", re);
|
||||
} finally {
|
||||
deleteInvalid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options If non-null and contains the key "list", get
|
||||
* from that list (default "hosts.txt", NOT all lists)
|
||||
@@ -1578,6 +1710,11 @@ public class BlockfileNamingService extends DummyNamingService {
|
||||
}
|
||||
storedOptions.remove(i);
|
||||
removeReverseEntry(hostname, d);
|
||||
if (options != null) {
|
||||
String list = options.getProperty("list");
|
||||
if (list != null)
|
||||
storedOptions.get(0).setProperty("list", list);
|
||||
}
|
||||
return put(hostname, newDests, storedOptions, false);
|
||||
}
|
||||
}
|
@@ -337,7 +337,7 @@ public class BlockFile implements Closeable {
|
||||
/**
|
||||
* Go to any page but the superblock.
|
||||
* Page 1 is the superblock, must use file.seek(0) to get there.
|
||||
* @param page >= 2
|
||||
* @param page >= 2
|
||||
*/
|
||||
public static void pageSeek(RandomAccessInterface file, int page) throws IOException {
|
||||
if (page < METAINDEX_PAGE)
|
@@ -221,6 +221,7 @@ public class BSkipList<K extends Comparable<? super K>, V> extends SkipList<K, V
|
||||
}
|
||||
****/
|
||||
|
||||
/** find */
|
||||
@Override
|
||||
public SkipIterator<K, V> find(K key) {
|
||||
if (!this.fileOnly)
|
@@ -1,4 +1,4 @@
|
||||
package net.i2p.client.naming;
|
||||
package net.i2p.router.naming;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
@@ -1,38 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE web-app
|
||||
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
|
||||
"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>
|
||||
<init-param>
|
||||
<param-name>home</param-name>
|
||||
<param-value>./addressbook</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>addressbook</servlet-name>
|
||||
<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,7 +11,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"Language-Team: Arabic (http://www.transifex.com/otf/I2P/language/ar/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -48,7 +48,7 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "ألغي"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
@@ -78,16 +78,16 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr ""
|
||||
msgstr "توقف في مدة {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
msgstr "إغلاق وشيك"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "الشبكة"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
|
@@ -5,13 +5,14 @@
|
||||
#
|
||||
# Translators:
|
||||
# Gabriel Radev <gabosss@gmail.com>, 2015
|
||||
# Vitaly Zdorovenko <stenliterziev@gmail.com>, 2017
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"PO-Revision-Date: 2017-09-20 20:31+0000\n"
|
||||
"Last-Translator: Vitaly Zdorovenko <stenliterziev@gmail.com>\n"
|
||||
"Language-Team: Bulgarian (http://www.transifex.com/otf/I2P/language/bg/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -47,7 +48,7 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "Деактивиране"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
@@ -62,7 +63,7 @@ msgstr "Спиране на I2P"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "Рестартирайте Незабавно"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
@@ -77,7 +78,7 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr ""
|
||||
msgstr "Изключване в {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
|
@@ -9,7 +9,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"Language-Team: Catalan (http://www.transifex.com/otf/I2P/language/ca/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -85,7 +85,7 @@ msgstr ""
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "Xarxa"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
|
@@ -9,7 +9,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"Language-Team: Czech (http://www.transifex.com/otf/I2P/language/cs/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -85,7 +85,7 @@ msgstr ""
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "Síť"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
|
@@ -11,7 +11,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"Language-Team: Danish (http://www.transifex.com/otf/I2P/language/da/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -48,7 +48,7 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "Deaktivere"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
@@ -78,16 +78,16 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr ""
|
||||
msgstr "Lukker ned om {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
msgstr "Lukker ned om et øjeblik"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "Netværk"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
|
@@ -13,7 +13,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-06-08 09:04+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Lars Schimmer <echelon@i2pmail.org>\n"
|
||||
"Language-Team: German (http://www.transifex.com/otf/I2P/language/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@@ -4,14 +4,15 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Elektra M. <safiragon@yahoo.gr>, 2017
|
||||
# lixtetrax <lixtetrax@grhack.net>, 2012
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"PO-Revision-Date: 2017-09-20 19:55+0000\n"
|
||||
"Last-Translator: Elektra M. <safiragon@yahoo.gr>\n"
|
||||
"Language-Team: Greek (http://www.transifex.com/otf/I2P/language/el/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -42,12 +43,12 @@ msgstr "Έναρξη φυλλομετρητή Ι2Ρ"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr ""
|
||||
msgstr "Ρύθμιση της γραμμής εργασιών του I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "Απενεργοποίηση"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
@@ -62,33 +63,33 @@ msgstr "Τερματισμός Ι2Ρ"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "Άμεση επανεκκίνηση του Ι2Ρ"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "Άμεση διακοπή του I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr ""
|
||||
msgstr "Ακύρωση τερματισμού λειτουργίας του I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr ""
|
||||
msgstr "Σβήσιμο σε {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
msgstr "Επίκειται σβήσιμο"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "Δίκτυο "
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr ""
|
||||
msgstr "I2P: Κάντε δεξί κλικ για το μενού"
|
||||
|
@@ -13,7 +13,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-27 21:54+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: strel\n"
|
||||
"Language-Team: Spanish (http://www.transifex.com/otf/I2P/language/es/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@@ -4,14 +4,15 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Amir H. Firouzian, 2017
|
||||
# NoProfile, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Amir H. Firouzian\n"
|
||||
"Language-Team: Persian (http://www.transifex.com/otf/I2P/language/fa/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -27,7 +28,7 @@ msgstr "شروع I2P"
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "I2P is starting!"
|
||||
msgstr ""
|
||||
msgstr "I2P راه اندازی شد!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
@@ -57,7 +58,7 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr ""
|
||||
msgstr "توقف I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
|
@@ -11,7 +11,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-06-01 18:50+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: outolumo <outolumo@gmail.com>\n"
|
||||
"Language-Team: Finnish (http://www.transifex.com/otf/I2P/language/fi/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@@ -8,6 +8,8 @@
|
||||
# Boxoa590, 2013
|
||||
# ducki2p <ducki2p@gmail.com>, 2011
|
||||
# foo <foo@bar>, 2009
|
||||
# French language coordinator <french.coordinator@rbox.me>, 2017
|
||||
# French language coordinator <french.coordinator@rbox.me>, 2017
|
||||
# Boxoa590, 2013
|
||||
# Towinet, 2016
|
||||
msgid ""
|
||||
@@ -15,8 +17,8 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-26 15:55+0000\n"
|
||||
"Last-Translator: Towinet\n"
|
||||
"PO-Revision-Date: 2017-10-25 17:51+0000\n"
|
||||
"Last-Translator: French language coordinator <french.coordinator@rbox.me>\n"
|
||||
"Language-Team: French (http://www.transifex.com/otf/I2P/language/fr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -42,12 +44,12 @@ msgstr "Démarrage"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Lancer le navigateur I2P"
|
||||
msgstr "Lancer le navigateur d’I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Configurer la zone de notification d'I2P"
|
||||
msgstr "Configurer la zone de notification d’I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
@@ -77,16 +79,16 @@ msgstr "Arrêter I2P immédiatement"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Annuler l'arrêt d'I2P"
|
||||
msgstr "Annuler la fermeture d’I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Arrêt dans {0}"
|
||||
msgstr "Fermeture dans {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Arrêt imminent"
|
||||
msgstr "La fermeture est imminente"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
@@ -96,4 +98,4 @@ msgstr "Réseau"
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P : clic droit pour menu"
|
||||
msgstr "I2P : clic droit pour obtenir le menu"
|
||||
|
@@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-10-22 05:24+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Uberius Crypto <uberius@anonymail.tech>\n"
|
||||
"Language-Team: Galician (http://www.transifex.com/otf/I2P/language/gl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@@ -9,7 +9,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"Language-Team: Hungarian (http://www.transifex.com/otf/I2P/language/hu/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -46,7 +46,7 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "Kikapcsol"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
@@ -76,16 +76,16 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr ""
|
||||
msgstr "Kikapcsolás: {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
msgstr "Kikapcsolás hamarosan"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "Hálózat"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
|
@@ -5,13 +5,14 @@
|
||||
#
|
||||
# Translators:
|
||||
# Khairul Agasta <khairuldroids@gmail.com>, 2014
|
||||
# Robert Dafis <robertdafis@gmail.com>, 2017
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"PO-Revision-Date: 2017-07-27 00:14+0000\n"
|
||||
"Last-Translator: Robert Dafis <robertdafis@gmail.com>\n"
|
||||
"Language-Team: Indonesian (http://www.transifex.com/otf/I2P/language/id/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -42,12 +43,12 @@ msgstr "Luncurkan Peramban I2P"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr ""
|
||||
msgstr "Ubah pengaturan I2P System Tray"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "Matikan"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
@@ -62,33 +63,33 @@ msgstr "Hentikan I2P"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "Ulang kembali I2P sekarang"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "Hentikan I2P sekarang"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr ""
|
||||
msgstr "Batal tutup I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr ""
|
||||
msgstr "Mematikan I2P dalam {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
msgstr "I2P sedang dalam proses dimatikan"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "Jaringan"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr ""
|
||||
msgstr "I2P: klik kanan untuk menampilkan menu"
|
||||
|
@@ -6,14 +6,16 @@
|
||||
# Translators:
|
||||
# Leelium <bovas85@gmail.com>, 2012
|
||||
# mkkid <jokjok@hotmail.it>, 2011
|
||||
# Sebastiano Pistore <olatusrooc@virgilio.it>, 2016
|
||||
# Leelium <bovas85@gmail.com>, 2012
|
||||
# mkkid <jokjok@hotmail.it>, 2011
|
||||
# Sebastiano Pistore <SebastianoPistore.info@protonmail.ch>, 2016-2017
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-26 09:32+0000\n"
|
||||
"Last-Translator: Sebastiano Pistore <olatusrooc@virgilio.it>\n"
|
||||
"PO-Revision-Date: 2017-07-30 07:04+0000\n"
|
||||
"Last-Translator: Sebastiano Pistore <SebastianoPistore.info@protonmail.ch>\n"
|
||||
"Language-Team: Italian (http://www.transifex.com/otf/I2P/language/it/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -34,17 +36,17 @@ msgstr "Avvio di I2P in corso!"
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "Starting"
|
||||
msgstr "Avvio"
|
||||
msgstr "Avvio in corso"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Avvia il Browser I2P"
|
||||
msgstr "Lancia il browser I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Configura l'icona di I2P nella Barra dei notifica"
|
||||
msgstr "Configura icona di I2P nella Barra di notifica"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
@@ -59,7 +61,7 @@ msgstr "Riavvia I2P"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "Ferma I2P"
|
||||
msgstr "Arresta I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
@@ -74,16 +76,16 @@ msgstr "Arresta subito I2P"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Annulla l'arresto di I2P"
|
||||
msgstr "Annulla arresto di I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Spegnimento in {0}"
|
||||
msgstr "Arresto in {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Spegnimento imminente"
|
||||
msgstr "Arresto imminente"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
@@ -93,4 +95,4 @@ msgstr "Rete"
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Fai un click destro per aprire il menu"
|
||||
msgstr "I2P: Fai click destro per aprire il menu"
|
||||
|
@@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"Language-Team: Japanese (http://www.transifex.com/otf/I2P/language/ja/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -47,7 +47,7 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "無効"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
@@ -77,16 +77,16 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr ""
|
||||
msgstr "{0} でシャットダウン"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
msgstr "即時シャットダウン"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "ネットワーク"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
|
@@ -11,7 +11,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-07-31 00:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: HelloKS <kqwe1859@gmail.com>\n"
|
||||
"Language-Team: Korean (http://www.transifex.com/otf/I2P/language/ko/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"Language-Team: Malagasy (http://www.transifex.com/otf/I2P/language/mg/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@@ -5,13 +5,14 @@
|
||||
#
|
||||
# Translators:
|
||||
# Allan Nordhøy <epost@anotheragency.no>, 2014
|
||||
# Allan Nordhøy <epost@anotheragency.no>, 2017
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"PO-Revision-Date: 2017-09-19 14:52+0000\n"
|
||||
"Last-Translator: Allan Nordhøy <epost@anotheragency.no>\n"
|
||||
"Language-Team: Norwegian Bokmål (http://www.transifex.com/otf/I2P/language/nb/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -42,12 +43,12 @@ msgstr "Kjør I2P-nettleser"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr ""
|
||||
msgstr "Sett opp I2P-systemkruv"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "Skru av"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
@@ -62,33 +63,33 @@ msgstr "Stopp I2P"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "Umiddelbar omstart av I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "Umiddelbar stopp av I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr ""
|
||||
msgstr "Avbryt nedstenging av I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr ""
|
||||
msgstr "Skrur av om {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
msgstr "Skru av med en gang"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "Nettverk"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr ""
|
||||
msgstr "Høyreklikk-meny i I2P"
|
||||
|
@@ -12,7 +12,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-06-19 14:16+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Martijn de Boer\n"
|
||||
"Language-Team: Dutch (http://www.transifex.com/otf/I2P/language/nl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@@ -6,20 +6,20 @@
|
||||
# Translators:
|
||||
# PolishAnon <b790979@klzlk.com>, 2011
|
||||
# polacco <polacco@i2pmail.org>, 2015
|
||||
# Verdulo :-) <cybertomek@openmailbox.org>, 2016
|
||||
# Verdulo :-), 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 19:49+0000\n"
|
||||
"Last-Translator: Verdulo :-) <cybertomek@openmailbox.org>\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Verdulo :-)\n"
|
||||
"Language-Team: Polish (http://www.transifex.com/otf/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=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
|
@@ -4,7 +4,7 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Maria Manuela Silva, 2016
|
||||
# Manuela Silva <manuela.silva@sky.com>, 2016
|
||||
# wicked, 2013
|
||||
# wicked, 2012
|
||||
msgid ""
|
||||
@@ -12,8 +12,8 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-31 15:25+0000\n"
|
||||
"Last-Translator: Maria Manuela Silva\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Manuela Silva <manuela.silva@sky.com>\n"
|
||||
"Language-Team: Portuguese (http://www.transifex.com/otf/I2P/language/pt/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
@@ -13,7 +13,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-06-13 01:45+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Rafael Ferrari\n"
|
||||
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/otf/I2P/language/pt_BR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@@ -11,7 +11,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-09-16 18:17+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Predator <Predator@windowslive.com>\n"
|
||||
"Language-Team: Romanian (http://www.transifex.com/otf/I2P/language/ro/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@@ -14,7 +14,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-07-27 14:27+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: c4sp3r\n"
|
||||
"Language-Team: Russian (Russia) (http://www.transifex.com/otf/I2P/language/ru_RU/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@@ -11,7 +11,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"Language-Team: Slovak (http://www.transifex.com/otf/I2P/language/sk/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -48,7 +48,7 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "Zakázať"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
@@ -87,7 +87,7 @@ msgstr ""
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "Sieť"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
|
@@ -4,14 +4,15 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Besnik <besnik@programeshqip.org>, 2016
|
||||
# Shpetim <shpetim@privacysolutions.no>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Besnik <besnik@programeshqip.org>\n"
|
||||
"Language-Team: Albanian (http://www.transifex.com/otf/I2P/language/sq/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -22,73 +23,73 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
msgid "Start I2P"
|
||||
msgstr "Starto I2P'në"
|
||||
msgstr "Nise I2P-në"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P po starton!"
|
||||
msgstr "I2P po niset!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "Starting"
|
||||
msgstr "Po startoj"
|
||||
msgstr "Po niset"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Nis Browser'in e I2P'së"
|
||||
msgstr "Nis Shfletuesin I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr ""
|
||||
msgstr "Formësoni Panel Sistemi I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "Çaktivizoje"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "Ristarto I2P¨në"
|
||||
msgstr "Rinise I2P-në"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "Ndale I2P'në"
|
||||
msgstr "Ndale I2P-në"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "Rinise I2P-në Menjëherë"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "Ndale I2P-në Menjëherë"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr ""
|
||||
msgstr "Anuloje Mbylljen e I2P-së"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr ""
|
||||
msgstr "Mbylle për {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
msgstr "Mbyllje shumë shpejt"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "Rrjet"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr ""
|
||||
msgstr "I2P: Djathtas-klikoni për menu"
|
||||
|
@@ -5,14 +5,15 @@
|
||||
#
|
||||
# Translators:
|
||||
# 123hund123 <M8R-ra4r1r@mailinator.com>, 2011
|
||||
# Jonatan Nyberg <jonatan@autistici.org>, 2016
|
||||
# Anders Nilsson <anders@devode.se>, 2016
|
||||
# Jony, 2016-2017
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-06-14 13:22+0000\n"
|
||||
"Last-Translator: Jonatan Nyberg <jonatan@autistici.org>\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Jony\n"
|
||||
"Language-Team: Swedish (Sweden) (http://www.transifex.com/otf/I2P/language/sv_SE/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -38,12 +39,12 @@ msgstr "Startar"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Öppna I2P browser"
|
||||
msgstr "Starta I2P-webbläsare"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Konfigurera I2P systemfältet"
|
||||
msgstr "Konfigurera I2P-meddelandefältet"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
@@ -68,28 +69,28 @@ msgstr "Starta om I2P omedelbart"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "Stoppa I2P omedelbart"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr ""
|
||||
msgstr "Avbryt I2P-avstängning"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr ""
|
||||
msgstr "Stänger av om {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
msgstr "Avstängning nära"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "Nätverk"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr ""
|
||||
msgstr "I2P: Högerklicka för meny"
|
||||
|
@@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-26 05:38+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Kaya Zeren <kayazeren@gmail.com>\n"
|
||||
"Language-Team: Turkish (Turkey) (http://www.transifex.com/otf/I2P/language/tr_TR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@@ -8,13 +8,14 @@
|
||||
# Denis Lysenko <gribua@gmail.com>, 2011
|
||||
# LinuxChata, 2014
|
||||
# madjong <madjong@i2pmail.org>, 2014
|
||||
# Maxym Mykhalchuk, 2017
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-07-14 18:50+0000\n"
|
||||
"Last-Translator: w008 <alex.on.www@gmail.com>\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Maxym Mykhalchuk\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"
|
||||
@@ -45,12 +46,12 @@ msgstr "Запустити I2P Браузер"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr ""
|
||||
msgstr "Налаштувати системний трей I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "Вимкнути"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
@@ -65,26 +66,26 @@ msgstr "Зупинити I2P"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "Негайно перезапустити I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "Негайно зупинити I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr ""
|
||||
msgstr "Скасувати зупинку I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr ""
|
||||
msgstr "Зупинка за {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
msgstr "Зупинка неминуча"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
@@ -94,4 +95,4 @@ msgstr "Мережа"
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr ""
|
||||
msgstr "I2P: Клацніть правою кнопкою миші щоб відкрити меню"
|
||||
|
@@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 12:34+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"Language-Team: Vietnamese (http://www.transifex.com/otf/I2P/language/vi/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -47,7 +47,7 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "Tắt"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
@@ -86,7 +86,7 @@ msgstr ""
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "Mạng lưới"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
|
@@ -6,14 +6,14 @@
|
||||
# Translators:
|
||||
# ducki2p <ducki2p@gmail.com>, 2011
|
||||
# walking <walking@i2pmail.org>, 2011
|
||||
# Y.F Yang <yfdyh000@gmail.com>, 2016
|
||||
# YF <yfdyh000@gmail.com>, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2016-05-25 14:00+0000\n"
|
||||
"Last-Translator: Y.F Yang <yfdyh000@gmail.com>\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: YF <yfdyh000@gmail.com>\n"
|
||||
"Language-Team: Chinese (China) (http://www.transifex.com/otf/I2P/language/zh_CN/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
@@ -88,8 +88,8 @@
|
||||
<attribute name="X-Compile-Target-JDK" value="${javac.version}" />
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
</target>
|
||||
|
||||
<target name="jarUpToDate">
|
||||
<uptodate property="jar.uptodate" targetfile="build/i2psnark.jar" >
|
||||
<srcfiles dir= "build/obj" includes="**/*.class" excludes="**/web/* **/messages_*.class" />
|
||||
@@ -137,13 +137,13 @@
|
||||
</manifest>
|
||||
</war>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="warUpToDate">
|
||||
<uptodate property="war.uptodate" targetfile="../i2psnark.war" >
|
||||
<srcfiles dir= "." includes="build/obj/org/klomp/snark/web/*.class ../resources/**/* ../web.xml" />
|
||||
</uptodate>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="bundle" depends="compile" unless="no.bundle">
|
||||
<mkdir dir="build/messages-src" />
|
||||
<!-- Update the messages_*.po files.
|
||||
@@ -155,7 +155,7 @@
|
||||
<exec executable="sh" osfamily="mac" failifexecutionfails="true" failonerror="${require.gettext}" >
|
||||
<arg value="./bundle-messages.sh" />
|
||||
</exec>
|
||||
<!-- multi-lang is optional -->
|
||||
<!-- multi-lang is optional -->
|
||||
<exec executable="sh" osfamily="windows" failifexecutionfails="false" >
|
||||
<arg value="./bundle-messages.sh" />
|
||||
</exec>
|
||||
@@ -242,51 +242,66 @@
|
||||
<copy todir="build/standalone-resources/.resources/themes/snark" >
|
||||
<fileset dir="../../../installer/resources/themes/snark/" />
|
||||
</copy>
|
||||
|
||||
<replace dir="build/standalone-resources/.resources/themes/snark"
|
||||
summary="true"
|
||||
token="url('/themes/"
|
||||
value="url('/i2psnark/.resources/themes/" >
|
||||
token="url(/themes/console/classic/images/"
|
||||
value="url(/i2psnark/.resources/themes/snark/classic/images/" >
|
||||
<include name="**/*.css" />
|
||||
</replace>
|
||||
<replace dir="build/standalone-resources/.resources/themes/snark"
|
||||
summary="true"
|
||||
token="url('../../console/images/"
|
||||
value="url('/i2psnark/.resources/themes/snark/ubergine/images/" >
|
||||
token="url(/themes/console/dark/images/"
|
||||
value="url(/i2psnark/.resources/themes/snark/dark/images/" >
|
||||
<include name="**/*.css" />
|
||||
</replace>
|
||||
<replace dir="build/standalone-resources/.resources/themes/snark"
|
||||
summary="true"
|
||||
token="url('../../console/dark/images/"
|
||||
value="url('/i2psnark/.resources/themes/snark/ubergine/images/" >
|
||||
token="url(../../console/light/images/"
|
||||
value="url(/i2psnark/.resources/themes/snark/light/images/" >
|
||||
<include name="**/*.css" />
|
||||
</replace>
|
||||
<replace dir="build/standalone-resources/.resources/themes/snark"
|
||||
summary="true"
|
||||
token="url('../../console/light/images/"
|
||||
value="url('/i2psnark/.resources/themes/snark/ubergine/images/" >
|
||||
token="url(/themes/console/light/images/"
|
||||
value="url(/i2psnark/.resources/themes/snark/light/images/" >
|
||||
<include name="**/*.css" />
|
||||
</replace>
|
||||
<replace dir="build/standalone-resources/.resources/themes/snark"
|
||||
summary="true"
|
||||
token="url('images/"
|
||||
value="url('/i2psnark/.resources/themes/snark/ubergine/images/" >
|
||||
token="url(/themes/console/images/transparent.gif"
|
||||
value="url(/i2psnark/.resources/themes/snark/ubergine/images/transparent.gif" >
|
||||
<include name="**/*.css" />
|
||||
</replace>
|
||||
<copy todir="build/standalone-resources/.resources/themes/snark/ubergine/images" >
|
||||
<!-- we really don't need all of these -->
|
||||
<fileset dir="../../../installer/resources/themes/console/images/" />
|
||||
</copy>
|
||||
<copy file="../../../installer/resources/themes/console/dark/images/transparent.gif"
|
||||
<replace dir="build/standalone-resources/.resources/themes/snark"
|
||||
summary="true"
|
||||
token="url(/themes/console/images/info/"
|
||||
value="url(/i2psnark/.resources/themes/snark/ubergine/images/" >
|
||||
<include name="**/*.css" />
|
||||
</replace>
|
||||
|
||||
<!-- Rather than pulling in all the console theme images, let's just specify the ones we need -->
|
||||
<copy file="../../../installer/resources/themes/console/images/transparent.gif"
|
||||
todir="build/standalone-resources/.resources/themes/snark/ubergine/images" />
|
||||
<copy file="../../../installer/resources/themes/console/dark/images/header.png"
|
||||
todir="build/standalone-resources/.resources/themes/snark/dark/images" />
|
||||
<copy file="../../../installer/resources/themes/console/light/images/header.png"
|
||||
todir="build/standalone-resources/.resources/themes/snark/light/images" />
|
||||
<copy file="../../../installer/resources/themes/console/dark/images/camotile2.png"
|
||||
todir="build/standalone-resources/.resources/themes/snark/dark/images" />
|
||||
<copy file="../../../installer/resources/themes/console/images/info/errortriangle.png"
|
||||
todir="build/standalone-resources/.resources/themes/snark/ubergine/images" />
|
||||
<copy file="../../../installer/resources/themes/console/classic/images/bg0.png"
|
||||
todir="build/standalone-resources/.resources/themes/snark/classic/images" />
|
||||
|
||||
<mkdir dir="build/standalone-resources/.resources/js" />
|
||||
<copy file="../../routerconsole/jsp/js/ajax.js" todir="build/standalone-resources/.resources/js" />
|
||||
|
||||
<zip destfile="../i2psnark.war" update="true" duplicate="preserve" >
|
||||
<fileset dir="build/standalone-resources" />
|
||||
</zip>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="standalone_prep" depends="standalone_jar, standalone_war">
|
||||
<delete dir="./dist" />
|
||||
<mkdir dir="./dist" />
|
||||
|
@@ -48,7 +48,7 @@ public class BitField
|
||||
* as set by the given byte array. This will make a copy of the array.
|
||||
* Extra bytes will be ignored.
|
||||
*
|
||||
* @exception ArrayOutOfBoundsException if give byte array is not large
|
||||
* @throws IndexOutOfBoundsException if give byte array is not large
|
||||
* enough.
|
||||
*/
|
||||
public BitField(byte[] bitfield, int size)
|
||||
@@ -90,7 +90,7 @@ public class BitField
|
||||
/**
|
||||
* Sets the given bit to true.
|
||||
*
|
||||
* @exception IndexOutOfBoundsException if bit is smaller then zero
|
||||
* @throws IndexOutOfBoundsException if bit is smaller then zero
|
||||
* bigger then size (inclusive).
|
||||
*/
|
||||
public void set(int bit)
|
||||
@@ -110,7 +110,7 @@ public class BitField
|
||||
/**
|
||||
* Sets the given bit to false.
|
||||
*
|
||||
* @exception IndexOutOfBoundsException if bit is smaller then zero
|
||||
* @throws IndexOutOfBoundsException if bit is smaller then zero
|
||||
* bigger then size (inclusive).
|
||||
* @since 0.9.22
|
||||
*/
|
||||
@@ -141,7 +141,7 @@ public class BitField
|
||||
/**
|
||||
* Return true if the bit is set or false if it is not.
|
||||
*
|
||||
* @exception IndexOutOfBoundsException if bit is smaller then zero
|
||||
* @throws IndexOutOfBoundsException if bit is smaller then zero
|
||||
* bigger then size (inclusive).
|
||||
*/
|
||||
public boolean get(int bit)
|
||||
|
@@ -21,6 +21,9 @@
|
||||
|
||||
package org.klomp.snark;
|
||||
|
||||
import org.klomp.snark.comments.CommentSet;
|
||||
|
||||
|
||||
/**
|
||||
* Callback for Snark events.
|
||||
* @since 0.9.4 moved from Snark.java
|
||||
@@ -65,4 +68,14 @@ public interface CompleteListener {
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public long getSavedUploaded(Snark snark);
|
||||
|
||||
/**
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public CommentSet getSavedComments(Snark snark);
|
||||
|
||||
/**
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public void locked_saveComments(Snark snark, CommentSet comments);
|
||||
}
|
||||
|
@@ -14,6 +14,8 @@ import net.i2p.util.Log;
|
||||
import org.klomp.snark.bencode.BDecoder;
|
||||
import org.klomp.snark.bencode.BEncoder;
|
||||
import org.klomp.snark.bencode.BEValue;
|
||||
import org.klomp.snark.comments.Comment;
|
||||
import org.klomp.snark.comments.CommentSet;
|
||||
|
||||
/**
|
||||
* REF: BEP 10 Extension Protocol
|
||||
@@ -31,6 +33,10 @@ 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";
|
||||
/** @since 0.9.31 */
|
||||
public static final int ID_COMMENT = 4;
|
||||
/** @since 0.9.31 */
|
||||
public static final String TYPE_COMMENT = "ut_comment";
|
||||
/** 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;
|
||||
@@ -40,9 +46,10 @@ abstract class ExtensionHandler {
|
||||
* @param metasize -1 if unknown
|
||||
* @param pexAndMetadata advertise these capabilities
|
||||
* @param dht advertise DHT capability
|
||||
* @param comment advertise ut_comment capability
|
||||
* @return bencoded outgoing handshake message
|
||||
*/
|
||||
public static byte[] getHandshake(int metasize, boolean pexAndMetadata, boolean dht, boolean uploadOnly) {
|
||||
public static byte[] getHandshake(int metasize, boolean pexAndMetadata, boolean dht, boolean uploadOnly, boolean comment) {
|
||||
Map<String, Object> handshake = new HashMap<String, Object>();
|
||||
Map<String, Integer> m = new HashMap<String, Integer>();
|
||||
if (pexAndMetadata) {
|
||||
@@ -54,6 +61,9 @@ abstract class ExtensionHandler {
|
||||
if (dht) {
|
||||
m.put(TYPE_DHT, Integer.valueOf(ID_DHT));
|
||||
}
|
||||
if (comment) {
|
||||
m.put(TYPE_COMMENT, Integer.valueOf(ID_COMMENT));
|
||||
}
|
||||
// include the map even if empty so the far-end doesn't NPE
|
||||
handshake.put("m", m);
|
||||
handshake.put("p", Integer.valueOf(TrackerClient.PORT));
|
||||
@@ -77,6 +87,8 @@ abstract class ExtensionHandler {
|
||||
handlePEX(peer, listener, bs, log);
|
||||
else if (id == ID_DHT)
|
||||
handleDHT(peer, listener, bs, log);
|
||||
else if (id == ID_COMMENT)
|
||||
handleComment(peer, listener, bs, log);
|
||||
else if (log.shouldLog(Log.INFO))
|
||||
log.info("Unknown extension msg " + id + " from " + peer);
|
||||
}
|
||||
@@ -430,4 +442,114 @@ abstract class ExtensionHandler {
|
||||
// log.info("DHT msg exception to " + peer, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle comment request and response
|
||||
*
|
||||
* Ref: https://blinkenlights.ch/ccms/software/bittorrent.html
|
||||
* Ref: https://github.com/adrian-bl/bitflu/blob/3cb7fe887dbdea8132e4fa36fbbf5f26cf992db3/plugins/Bitflu/20_DownloadBitTorrent.pm#L3403
|
||||
* @since 0.9.31
|
||||
*/
|
||||
private static void handleComment(Peer peer, PeerListener listener, byte[] bs, Log log) {
|
||||
if (log.shouldLog(Log.DEBUG))
|
||||
log.debug("Got comment msg from " + peer);
|
||||
try {
|
||||
InputStream is = new ByteArrayInputStream(bs);
|
||||
BDecoder dec = new BDecoder(is);
|
||||
BEValue bev = dec.bdecodeMap();
|
||||
Map<String, BEValue> map = bev.getMap();
|
||||
int type = map.get("msg_type").getInt();
|
||||
if (type == 0) {
|
||||
// request
|
||||
int num = 20;
|
||||
BEValue b = map.get("num");
|
||||
if (b != null)
|
||||
num = b.getInt();
|
||||
listener.gotCommentReq(peer, num);
|
||||
} else if (type == 1) {
|
||||
// response
|
||||
List<BEValue> list = map.get("comments").getList();
|
||||
if (list.isEmpty())
|
||||
return;
|
||||
List<Comment> comments = new ArrayList<Comment>(list.size());
|
||||
long now = I2PAppContext.getGlobalContext().clock().now();
|
||||
for (BEValue li : list) {
|
||||
Map<String, BEValue> m = li.getMap();
|
||||
String owner = m.get("owner").getString();
|
||||
String text = m.get("text").getString();
|
||||
// 0-5 range for rating is enforced by Comment constructor
|
||||
int rating = m.get("like").getInt();
|
||||
long time = now - (Math.max(0, m.get("timestamp").getInt()) * 1000L);
|
||||
Comment c = new Comment(text, owner, rating, time, false);
|
||||
comments.add(c);
|
||||
}
|
||||
listener.gotComments(peer, comments);
|
||||
} else {
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("Unknown comment msg type " + type + " from " + peer);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("Comment msg exception from " + peer, e);
|
||||
//peer.disconnect(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static final byte[] COMMENTS_FILTER = new byte[64];
|
||||
|
||||
/**
|
||||
* Send comment request
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public static void sendCommentReq(Peer peer, int num) {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("msg_type", Integer.valueOf(0));
|
||||
map.put("num", Integer.valueOf(num));
|
||||
map.put("filter", COMMENTS_FILTER);
|
||||
byte[] payload = BEncoder.bencode(map);
|
||||
try {
|
||||
int hisMsgCode = peer.getHandshakeMap().get("m").getMap().get(TYPE_COMMENT).getInt();
|
||||
peer.sendExtension(hisMsgCode, payload);
|
||||
} catch (Exception e) {
|
||||
// NPE, no caps
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send comments
|
||||
* Caller must sync on comments
|
||||
* @param num max to send
|
||||
* @param comments non-null
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public static void locked_sendComments(Peer peer, int num, CommentSet comments) {
|
||||
int toSend = Math.min(num, comments.size());
|
||||
if (toSend <= 0)
|
||||
return;
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("msg_type", Integer.valueOf(1));
|
||||
List<Object> lc = new ArrayList<Object>(toSend);
|
||||
long now = I2PAppContext.getGlobalContext().clock().now();
|
||||
int i = 0;
|
||||
for (Comment c : comments) {
|
||||
if (i++ >= toSend)
|
||||
break;
|
||||
Map<String, Object> mc = new HashMap<String, Object>();
|
||||
String s = c.getName();
|
||||
mc.put("owner", s != null ? s : "");
|
||||
s = c.getText();
|
||||
mc.put("text", s != null ? s : "");
|
||||
mc.put("like", Integer.valueOf(c.getRating()));
|
||||
mc.put("timestamp", Long.valueOf((now - c.getTime()) / 1000L));
|
||||
lc.add(mc);
|
||||
}
|
||||
map.put("comments", lc);
|
||||
byte[] payload = BEncoder.bencode(map);
|
||||
try {
|
||||
int hisMsgCode = peer.getHandshakeMap().get("m").getMap().get(TYPE_COMMENT).getInt();
|
||||
peer.sendExtension(hisMsgCode, payload);
|
||||
} catch (Exception e) {
|
||||
// NPE, no caps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -65,15 +65,20 @@ public class I2PSnarkUtil {
|
||||
private int _maxConnections;
|
||||
private final File _tmpDir;
|
||||
private int _startupDelay;
|
||||
private boolean _collapsePanels;
|
||||
private boolean _shouldUseOT;
|
||||
private boolean _shouldUseDHT;
|
||||
private boolean _enableRatings, _enableComments;
|
||||
private String _commentsName;
|
||||
private boolean _areFilesPublic;
|
||||
private List<String> _openTrackers;
|
||||
private DHT _dht;
|
||||
private long _startedTime;
|
||||
|
||||
private static final int EEPGET_CONNECT_TIMEOUT = 45*1000;
|
||||
private static final int EEPGET_CONNECT_TIMEOUT_SHORT = 5*1000;
|
||||
public static final int DEFAULT_STARTUP_DELAY = 3;
|
||||
public static final boolean DEFAULT_COLLAPSE_PANELS = true;
|
||||
public static final boolean DEFAULT_USE_OPENTRACKERS = true;
|
||||
public static final int MAX_CONNECTIONS = 24; // per torrent
|
||||
public static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond";
|
||||
@@ -103,6 +108,9 @@ public class I2PSnarkUtil {
|
||||
_shouldUseOT = DEFAULT_USE_OPENTRACKERS;
|
||||
_openTrackers = Collections.emptyList();
|
||||
_shouldUseDHT = DEFAULT_USE_DHT;
|
||||
_collapsePanels = DEFAULT_COLLAPSE_PANELS;
|
||||
_enableRatings = _enableComments = true;
|
||||
_commentsName = "";
|
||||
// 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
|
||||
@@ -260,6 +268,8 @@ public class I2PSnarkUtil {
|
||||
if (opts.getProperty(I2PClient.PROP_SIGTYPE) == null)
|
||||
opts.setProperty(I2PClient.PROP_SIGTYPE, "EdDSA_SHA512_Ed25519");
|
||||
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
|
||||
if (_manager != null)
|
||||
_startedTime = _context.clock().now();
|
||||
_connecting = false;
|
||||
}
|
||||
if (_shouldUseDHT && _manager != null && _dht == null)
|
||||
@@ -295,6 +305,7 @@ public class I2PSnarkUtil {
|
||||
_dht.stop();
|
||||
_dht = null;
|
||||
}
|
||||
_startedTime = 0;
|
||||
I2PSocketManager mgr = _manager;
|
||||
// FIXME this can cause race NPEs elsewhere
|
||||
_manager = null;
|
||||
@@ -310,6 +321,16 @@ public class I2PSnarkUtil {
|
||||
_tmpDir.mkdirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* When did we connect to the network?
|
||||
* For RPC
|
||||
* @return 0 if not connected
|
||||
* @since 0.9.30
|
||||
*/
|
||||
public long getStartedTime() {
|
||||
return _startedTime;
|
||||
}
|
||||
|
||||
/** connect to the given destination */
|
||||
I2PSocket connect(PeerID peer) throws IOException {
|
||||
I2PSocketManager mgr = _manager;
|
||||
@@ -357,12 +378,12 @@ public class I2PSnarkUtil {
|
||||
public File get(String url, boolean rewrite) { return get(url, rewrite, 0); }
|
||||
|
||||
/**
|
||||
* @param retries if < 0, set timeout to a few seconds
|
||||
* @param retries if < 0, set timeout to a few seconds
|
||||
*/
|
||||
public File get(String url, int retries) { return get(url, true, retries); }
|
||||
|
||||
/**
|
||||
* @param retries if < 0, set timeout to a few seconds
|
||||
* @param retries if < 0, set timeout to a few seconds
|
||||
*/
|
||||
public File get(String url, boolean rewrite, int retries) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -413,7 +434,7 @@ public class I2PSnarkUtil {
|
||||
|
||||
/**
|
||||
* Fetch to memory
|
||||
* @param retries if < 0, set timeout to a few seconds
|
||||
* @param retries if < 0, set timeout to a few seconds
|
||||
* @param initialSize buffer size
|
||||
* @param maxSize fails if greater
|
||||
* @return null on error
|
||||
@@ -631,6 +652,54 @@ public class I2PSnarkUtil {
|
||||
return _shouldUseDHT;
|
||||
}
|
||||
|
||||
/** @since 0.9.31 */
|
||||
public void setRatingsEnabled(boolean yes) {
|
||||
_enableRatings = yes;
|
||||
}
|
||||
|
||||
/** @since 0.9.31 */
|
||||
public boolean ratingsEnabled() {
|
||||
return _enableRatings;
|
||||
}
|
||||
|
||||
/** @since 0.9.31 */
|
||||
public void setCommentsEnabled(boolean yes) {
|
||||
_enableComments = yes;
|
||||
}
|
||||
|
||||
/** @since 0.9.31 */
|
||||
public boolean commentsEnabled() {
|
||||
return _enableComments;
|
||||
}
|
||||
|
||||
/** @since 0.9.31 */
|
||||
public void setCommentsName(String name) {
|
||||
_commentsName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-null, "" if none
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public String getCommentsName() {
|
||||
return _commentsName == null ? "" : _commentsName;
|
||||
}
|
||||
|
||||
/** @since 0.9.31 */
|
||||
public boolean utCommentsEnabled() {
|
||||
return _enableRatings || _enableComments;
|
||||
}
|
||||
|
||||
/** @since 0.9.32 */
|
||||
public boolean collapsePanels() {
|
||||
return _collapsePanels;
|
||||
}
|
||||
|
||||
/** @since 0.9.32 */
|
||||
public void setCollapsePanels(boolean yes) {
|
||||
_collapsePanels = yes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like DataHelper.toHexString but ensures no loss of leading zero bytes
|
||||
* @since 0.8.4
|
||||
@@ -659,7 +728,7 @@ public class I2PSnarkUtil {
|
||||
*
|
||||
* @param s string to be translated containing {0}
|
||||
* The {0} will be replaced by the parameter.
|
||||
* Single quotes must be doubled, i.e. ' -> '' in the string.
|
||||
* Single quotes must be doubled, i.e. ' -> '' in the string.
|
||||
* @param o parameter, not translated.
|
||||
* To translate parameter also, use _t("foo {0} bar", _t("baz"))
|
||||
* Do not double the single quotes in the parameter.
|
||||
|
@@ -161,7 +161,7 @@ class MagnetState {
|
||||
|
||||
/**
|
||||
* @return true if this was the last piece
|
||||
* @throws NPE, IllegalArgumentException, IOException, ...
|
||||
* @throws NullPointerException IllegalArgumentException, IOException, ...
|
||||
*/
|
||||
public boolean saveChunk(int chunk, byte[] data, int off, int length) throws Exception {
|
||||
if (!isInitialized)
|
||||
@@ -185,7 +185,7 @@ class MagnetState {
|
||||
|
||||
/**
|
||||
* @return true if this was the last piece
|
||||
* @throws NPE, IllegalArgumentException, IOException, ...
|
||||
* @throws NullPointerException IllegalArgumentException, IOException, ...
|
||||
*/
|
||||
private MetaInfo buildMetaInfo() throws Exception {
|
||||
// top map has nothing in it but the info map (no announce)
|
||||
|
@@ -52,26 +52,26 @@ class Message
|
||||
|
||||
// Not all fields are used for every message.
|
||||
// KEEP_ALIVE doesn't have a real wire representation
|
||||
byte type;
|
||||
final 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;
|
||||
final int piece;
|
||||
|
||||
// Used for REQUEST, PIECE and CANCEL messages.
|
||||
// Also REJECT
|
||||
int begin;
|
||||
int length;
|
||||
final int begin;
|
||||
final int length;
|
||||
|
||||
// Used for PIECE and BITFIELD and EXTENSION messages
|
||||
byte[] data;
|
||||
int off;
|
||||
int len;
|
||||
final int off;
|
||||
final int len;
|
||||
|
||||
// Used to do deferred fetch of data
|
||||
DataLoader dataLoader;
|
||||
private final DataLoader dataLoader;
|
||||
|
||||
// now unused
|
||||
//SimpleTimer.TimedEvent expireEvent;
|
||||
@@ -79,6 +79,79 @@ class Message
|
||||
private static final int BUFSIZE = PeerState.PARTSIZE;
|
||||
private static final ByteCache _cache = ByteCache.getInstance(16, BUFSIZE);
|
||||
|
||||
/**
|
||||
* For types KEEP_ALIVE, CHOKE, UNCHOKE, INTERESTED, UNINTERESTED, HAVE_ALL, HAVE_NONE
|
||||
* @since 0.9.32
|
||||
*/
|
||||
Message(byte type) {
|
||||
this(type, 0, 0, 0, null, 0, 0, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* For types HAVE, PORT, SUGGEST, ALLOWED_FAST
|
||||
* @since 0.9.32
|
||||
*/
|
||||
Message(byte type, int piece) {
|
||||
this(type, piece, 0, 0, null, 0, 0, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* For types REQUEST, REJECT, CANCEL
|
||||
* @since 0.9.32
|
||||
*/
|
||||
Message(byte type, int piece, int begin, int length) {
|
||||
this(type, piece, begin, length, null, 0, 0, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* For type BITFIELD
|
||||
* @since 0.9.32
|
||||
*/
|
||||
Message(byte[] data) {
|
||||
this(BITFIELD, 0, 0, 0, data, 0, data.length, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* For type EXTENSION
|
||||
* @since 0.9.32
|
||||
*/
|
||||
Message(int id, byte[] data) {
|
||||
this(EXTENSION, id, 0, 0, data, 0, data.length, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* For type PIECE with deferred data
|
||||
* @since 0.9.32
|
||||
*/
|
||||
Message(int piece, int begin, int length, DataLoader loader) {
|
||||
this(PIECE, piece, begin, length, null, 0, length, loader);
|
||||
}
|
||||
|
||||
/**
|
||||
* For type PIECE with data
|
||||
* We don't use this anymore.
|
||||
* @since 0.9.32
|
||||
*/
|
||||
/****
|
||||
Message(int piece, int begin, int length, byte[] data) {
|
||||
this(PIECE, piece, begin, length, data, 0, length, null);
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* @since 0.9.32
|
||||
*/
|
||||
private Message(byte type, int piece, int begin, int length, byte[] data, int off, int len, DataLoader loader) {
|
||||
this.type = type;
|
||||
this.piece = piece;
|
||||
this.begin = begin;
|
||||
this.length = length;
|
||||
this.data = data;
|
||||
this.off = off;
|
||||
this.len = len;
|
||||
dataLoader = loader;
|
||||
}
|
||||
|
||||
/** Utility method for sending a message through a DataStream. */
|
||||
void sendMessage(DataOutputStream dos) throws IOException
|
||||
{
|
||||
@@ -121,10 +194,10 @@ class Message
|
||||
datalen += 4;
|
||||
|
||||
// msg type is 1 byte
|
||||
if (type == EXTENSION)
|
||||
else if (type == EXTENSION)
|
||||
datalen += 1;
|
||||
|
||||
if (type == PORT)
|
||||
else if (type == PORT)
|
||||
datalen += 2;
|
||||
|
||||
// add length of data for piece or bitfield array.
|
||||
@@ -150,10 +223,10 @@ class Message
|
||||
type == REJECT)
|
||||
dos.writeInt(length);
|
||||
|
||||
if (type == EXTENSION)
|
||||
else if (type == EXTENSION)
|
||||
dos.writeByte((byte) piece & 0xff);
|
||||
|
||||
if (type == PORT)
|
||||
else if (type == PORT)
|
||||
dos.writeShort(piece & 0xffff);
|
||||
|
||||
// Send actual data
|
||||
|
@@ -464,7 +464,7 @@ public class MetaInfo
|
||||
* Return the length of a piece. All pieces are of equal length
|
||||
* except for the last one (<code>getPieces()-1</code>).
|
||||
*
|
||||
* @exception IndexOutOfBoundsException when piece is equal to or
|
||||
* @throws IndexOutOfBoundsException when piece is equal to or
|
||||
* greater then the number of pieces in the torrent.
|
||||
*/
|
||||
public int getPieceLength(int piece)
|
||||
|
@@ -89,6 +89,8 @@ public class Peer implements Comparable<Peer>
|
||||
*/
|
||||
//private static final long OPTION_AZMP = 0x1000000000000000l;
|
||||
private long options;
|
||||
private final boolean _isIncoming;
|
||||
private int _totalCommentsSent;
|
||||
|
||||
/**
|
||||
* Outgoing connection.
|
||||
@@ -103,6 +105,7 @@ public class Peer implements Comparable<Peer>
|
||||
this.infohash = infohash;
|
||||
this.metainfo = metainfo;
|
||||
_id = __id.incrementAndGet();
|
||||
_isIncoming = false;
|
||||
//_log.debug("Creating a new peer with " + peerID.toString(), new Exception("creating"));
|
||||
}
|
||||
|
||||
@@ -115,7 +118,7 @@ public class Peer implements Comparable<Peer>
|
||||
* the connect() method.
|
||||
*
|
||||
* @param metainfo null if in magnet mode
|
||||
* @exception IOException when an error occurred during the handshake.
|
||||
* @throws IOException when an error occurred during the handshake.
|
||||
*/
|
||||
public Peer(final I2PSocket sock, InputStream in, OutputStream out, byte[] my_id, byte[] infohash, MetaInfo metainfo)
|
||||
throws IOException
|
||||
@@ -130,6 +133,16 @@ public class Peer implements Comparable<Peer>
|
||||
_id = __id.incrementAndGet();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Creating a new peer " + peerID.toString(), new Exception("creating " + _id));
|
||||
_isIncoming = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this an incoming connection?
|
||||
* For RPC
|
||||
* @since 0.9.30
|
||||
*/
|
||||
public boolean isIncoming() {
|
||||
return _isIncoming;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,7 +173,7 @@ public class Peer implements Comparable<Peer>
|
||||
if (state != null) {
|
||||
String r = state.getRequests();
|
||||
if (r != null)
|
||||
return sock.toString() + "<br>Requests: " + r;
|
||||
return sock.toString() + "<br><b>Requests:</b> <span class=\"debugRequests\">" + r + "</span>";
|
||||
}
|
||||
return sock.toString();
|
||||
}
|
||||
@@ -278,7 +291,8 @@ 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, uploadOnly));
|
||||
boolean comment = util.utCommentsEnabled();
|
||||
out.sendExtension(0, ExtensionHandler.getHandshake(metasize, pexAndMetadata, dht, uploadOnly, comment));
|
||||
}
|
||||
|
||||
// Send our bitmap
|
||||
@@ -734,4 +748,14 @@ public class Peer implements Comparable<Peer>
|
||||
{
|
||||
return PeerCoordinator.getRate(downloaded_old);
|
||||
}
|
||||
|
||||
/** @since 0.9.31 */
|
||||
int getTotalCommentsSent() {
|
||||
return _totalCommentsSent;
|
||||
}
|
||||
|
||||
/** @since 0.9.31 */
|
||||
void setTotalCommentsSent(int count) {
|
||||
_totalCommentsSent = count;
|
||||
}
|
||||
}
|
||||
|
@@ -81,7 +81,10 @@ class PeerCheckerTask implements Runnable
|
||||
" interested: " + coordinator.getInterestedUploaders() +
|
||||
" limit: " + uploadLimit + " overBW? " + overBWLimit);
|
||||
DHT dht = _util.getDHT();
|
||||
boolean fetchComments = _util.utCommentsEnabled();
|
||||
int i = 0;
|
||||
for (Peer peer : peerList) {
|
||||
i++;
|
||||
|
||||
// Remove dying peers
|
||||
if (!peer.isConnected())
|
||||
@@ -226,9 +229,12 @@ class PeerCheckerTask implements Runnable
|
||||
}
|
||||
}
|
||||
peer.retransmitRequests();
|
||||
// send PEX
|
||||
if ((_runCount % 17) == 0 && !peer.isCompleted())
|
||||
// send PEX, about every 12 minutes
|
||||
if (((_runCount + i) % 17) == 0 && !peer.isCompleted())
|
||||
coordinator.sendPeers(peer);
|
||||
// send Comment Request, about every 30 minutes
|
||||
if (fetchComments && ((_runCount + i) % 47) == 0)
|
||||
coordinator.sendCommentReq(peer);
|
||||
// cheap failsafe for seeds connected to seeds, stop pinging and hopefully
|
||||
// the inactive checker (above) will eventually disconnect it
|
||||
if (coordinator.getNeededLength() > 0 || !peer.isCompleted())
|
||||
@@ -238,7 +244,7 @@ class PeerCheckerTask implements Runnable
|
||||
dht.announce(coordinator.getInfoHash(), peer.getPeerID().getDestHash(),
|
||||
peer.isCompleted());
|
||||
}
|
||||
}
|
||||
} // for peer
|
||||
|
||||
// Resync actual uploaders value
|
||||
// (can shift a bit by disconnecting peers)
|
||||
|
@@ -49,12 +49,11 @@ class PeerConnectionIn implements Runnable
|
||||
this.peer = peer;
|
||||
this.din = din;
|
||||
lastRcvd = System.currentTimeMillis();
|
||||
quit = false;
|
||||
}
|
||||
|
||||
void disconnect()
|
||||
{
|
||||
if (quit == true)
|
||||
if (quit)
|
||||
return;
|
||||
|
||||
quit = true;
|
||||
|
@@ -126,11 +126,7 @@ class PeerConnectionOut implements Runnable
|
||||
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;
|
||||
Message r = new Message(Message.REJECT, nm.piece, nm.begin, nm.length);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Send " + peer + ": " + r);
|
||||
r.sendMessage(dout);
|
||||
@@ -294,11 +290,7 @@ class PeerConnectionOut implements Runnable
|
||||
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;
|
||||
Message r = new Message(Message.REJECT, m.piece, m.begin, m.length);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Send " + peer + ": " + r);
|
||||
try {
|
||||
@@ -314,13 +306,12 @@ class PeerConnectionOut implements Runnable
|
||||
|
||||
void sendAlive()
|
||||
{
|
||||
Message m = new Message();
|
||||
m.type = Message.KEEP_ALIVE;
|
||||
// addMessage(m);
|
||||
synchronized(sendQueue)
|
||||
{
|
||||
if(sendQueue.isEmpty())
|
||||
if(sendQueue.isEmpty()) {
|
||||
Message m = new Message(Message.KEEP_ALIVE);
|
||||
sendQueue.offer(m);
|
||||
}
|
||||
sendQueue.notifyAll();
|
||||
}
|
||||
}
|
||||
@@ -335,11 +326,7 @@ class PeerConnectionOut implements Runnable
|
||||
: Message.CHOKE;
|
||||
if (!removeMessage(inverseType))
|
||||
{
|
||||
Message m = new Message();
|
||||
if (choke)
|
||||
m.type = Message.CHOKE;
|
||||
else
|
||||
m.type = Message.UNCHOKE;
|
||||
Message m = new Message(choke ? Message.CHOKE : Message.UNCHOKE);
|
||||
addMessage(m);
|
||||
}
|
||||
}
|
||||
@@ -353,11 +340,7 @@ class PeerConnectionOut implements Runnable
|
||||
: Message.INTERESTED;
|
||||
if (!removeMessage(inverseType))
|
||||
{
|
||||
Message m = new Message();
|
||||
if (interest)
|
||||
m.type = Message.INTERESTED;
|
||||
else
|
||||
m.type = Message.UNINTERESTED;
|
||||
Message m = new Message(interest ? Message.INTERESTED : Message.UNINTERESTED);
|
||||
addMessage(m);
|
||||
}
|
||||
}
|
||||
@@ -365,9 +348,7 @@ class PeerConnectionOut implements Runnable
|
||||
|
||||
void sendHave(int piece)
|
||||
{
|
||||
Message m = new Message();
|
||||
m.type = Message.HAVE;
|
||||
m.piece = piece;
|
||||
Message m = new Message(Message.HAVE, piece);
|
||||
addMessage(m);
|
||||
}
|
||||
|
||||
@@ -379,11 +360,7 @@ class PeerConnectionOut implements Runnable
|
||||
} 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;
|
||||
Message m = new Message(bitfield.getFieldBytes());
|
||||
addMessage(m);
|
||||
}
|
||||
}
|
||||
@@ -434,11 +411,7 @@ class PeerConnectionOut implements Runnable
|
||||
}
|
||||
}
|
||||
}
|
||||
Message m = new Message();
|
||||
m.type = Message.REQUEST;
|
||||
m.piece = req.getPiece();
|
||||
m.begin = req.off;
|
||||
m.length = req.len;
|
||||
Message m = new Message(Message.REQUEST, req.getPiece(), req.off, req.len);
|
||||
addMessage(m);
|
||||
req.sendTime = System.currentTimeMillis();
|
||||
}
|
||||
@@ -482,14 +455,7 @@ class PeerConnectionOut implements Runnable
|
||||
|
||||
// queue a fake message... set everything up,
|
||||
// except save the PeerState instead of the bytes.
|
||||
Message m = new Message();
|
||||
m.type = Message.PIECE;
|
||||
m.piece = piece;
|
||||
m.begin = begin;
|
||||
m.length = length;
|
||||
m.dataLoader = loader;
|
||||
m.off = 0;
|
||||
m.len = length;
|
||||
Message m = new Message(piece, begin, length, loader);
|
||||
addMessage(m);
|
||||
}
|
||||
|
||||
@@ -498,21 +464,17 @@ class PeerConnectionOut implements Runnable
|
||||
* Also add a timeout.
|
||||
* We don't use this anymore.
|
||||
*/
|
||||
/****
|
||||
void sendPiece(int piece, int begin, int length, byte[] bytes)
|
||||
{
|
||||
Message m = new Message();
|
||||
m.type = Message.PIECE;
|
||||
m.piece = piece;
|
||||
m.begin = begin;
|
||||
m.length = length;
|
||||
m.data = bytes;
|
||||
m.off = 0;
|
||||
m.len = length;
|
||||
Message m = new Message(piece, begin, length, bytes);
|
||||
// since we have the data already loaded, queue a timeout to remove it
|
||||
// no longer prefetched
|
||||
addMessage(m);
|
||||
}
|
||||
****/
|
||||
|
||||
/** send cancel */
|
||||
void sendCancel(Request req)
|
||||
{
|
||||
// See if it is still in our send queue
|
||||
@@ -531,11 +493,7 @@ class PeerConnectionOut implements Runnable
|
||||
}
|
||||
|
||||
// Always send, just to be sure it it is really canceled.
|
||||
Message m = new Message();
|
||||
m.type = Message.CANCEL;
|
||||
m.piece = req.getPiece();
|
||||
m.begin = req.off;
|
||||
m.length = req.len;
|
||||
Message m = new Message(Message.CANCEL, req.getPiece(), req.off, req.len);
|
||||
addMessage(m);
|
||||
}
|
||||
|
||||
@@ -578,20 +536,13 @@ class PeerConnectionOut implements Runnable
|
||||
|
||||
/** @since 0.8.2 */
|
||||
void sendExtension(int id, byte[] bytes) {
|
||||
Message m = new Message();
|
||||
m.type = Message.EXTENSION;
|
||||
m.piece = id;
|
||||
m.data = bytes;
|
||||
m.off = 0;
|
||||
m.len = bytes.length;
|
||||
Message m = new Message(id, bytes);
|
||||
addMessage(m);
|
||||
}
|
||||
|
||||
/** @since 0.8.4 */
|
||||
void sendPort(int port) {
|
||||
Message m = new Message();
|
||||
m.type = Message.PORT;
|
||||
m.piece = port;
|
||||
Message m = new Message(Message.PORT, port);
|
||||
addMessage(m);
|
||||
}
|
||||
|
||||
@@ -599,34 +550,28 @@ class PeerConnectionOut implements Runnable
|
||||
* Unused
|
||||
* @since 0.9.21
|
||||
*/
|
||||
/****
|
||||
void sendSuggest(int piece) {
|
||||
Message m = new Message();
|
||||
m.type = Message.SUGGEST;
|
||||
m.piece = piece;
|
||||
Message m = new Message(Message.SUGGEST, piece);
|
||||
addMessage(m);
|
||||
}
|
||||
****/
|
||||
|
||||
/** @since 0.9.21 */
|
||||
private void sendHaveAll() {
|
||||
Message m = new Message();
|
||||
m.type = Message.HAVE_ALL;
|
||||
Message m = new Message(Message.HAVE_ALL);
|
||||
addMessage(m);
|
||||
}
|
||||
|
||||
/** @since 0.9.21 */
|
||||
private void sendHaveNone() {
|
||||
Message m = new Message();
|
||||
m.type = Message.HAVE_NONE;
|
||||
Message m = new Message(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;
|
||||
Message m = new Message(Message.REJECT, piece, begin, length);
|
||||
addMessage(m);
|
||||
}
|
||||
|
||||
@@ -634,10 +579,10 @@ class PeerConnectionOut implements Runnable
|
||||
* Unused
|
||||
* @since 0.9.21
|
||||
*/
|
||||
/****
|
||||
void sendAllowedFast(int piece) {
|
||||
Message m = new Message();
|
||||
m.type = Message.ALLOWED_FAST;
|
||||
m.piece = piece;
|
||||
Message m = new Message(Message.ALLOWED_FAST, piece);
|
||||
addMessage(m);
|
||||
}
|
||||
****/
|
||||
}
|
||||
|
@@ -47,6 +47,8 @@ import net.i2p.util.SimpleTimer2;
|
||||
|
||||
import org.klomp.snark.bencode.BEValue;
|
||||
import org.klomp.snark.bencode.InvalidBEncodingException;
|
||||
import org.klomp.snark.comments.Comment;
|
||||
import org.klomp.snark.comments.CommentSet;
|
||||
import org.klomp.snark.dht.DHT;
|
||||
|
||||
/**
|
||||
@@ -385,7 +387,7 @@ class PeerCoordinator implements PeerListener
|
||||
|
||||
/**
|
||||
* Inbound.
|
||||
* Not halted, peers < max.
|
||||
* Not halted, peers < max.
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public boolean needPeers()
|
||||
@@ -395,7 +397,7 @@ class PeerCoordinator implements PeerListener
|
||||
|
||||
/**
|
||||
* Outbound.
|
||||
* Not halted, peers < max, and need pieces.
|
||||
* Not halted, peers < max, and need pieces.
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public boolean needOutboundPeers() {
|
||||
@@ -1386,6 +1388,8 @@ class PeerCoordinator implements PeerListener
|
||||
} else if (id == ExtensionHandler.ID_HANDSHAKE) {
|
||||
sendPeers(peer);
|
||||
sendDHT(peer);
|
||||
if (_util.utCommentsEnabled())
|
||||
sendCommentReq(peer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1434,6 +1438,35 @@ class PeerCoordinator implements PeerListener
|
||||
} catch (InvalidBEncodingException ibee) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a commment request message to the peer, if he supports it.
|
||||
* @since 0.9.31
|
||||
*/
|
||||
void sendCommentReq(Peer peer) {
|
||||
Map<String, BEValue> handshake = peer.getHandshakeMap();
|
||||
if (handshake == null)
|
||||
return;
|
||||
BEValue bev = handshake.get("m");
|
||||
if (bev == null)
|
||||
return;
|
||||
// TODO if peer hasn't been connected very long, don't bother
|
||||
// unless forced at handshake time (see above)
|
||||
try {
|
||||
if (bev.getMap().get(ExtensionHandler.TYPE_COMMENT) != null) {
|
||||
int sz = 0;
|
||||
CommentSet comments = snark.getComments();
|
||||
if (comments != null) {
|
||||
synchronized(comments) {
|
||||
sz = comments.size();
|
||||
}
|
||||
}
|
||||
if (sz >= CommentSet.MAX_SIZE)
|
||||
return;
|
||||
ExtensionHandler.sendCommentReq(peer, CommentSet.MAX_SIZE - sz);
|
||||
}
|
||||
} catch (InvalidBEncodingException ibee) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the storage after transition out of magnet mode
|
||||
* Snark calls this after we call gotMetaInfo()
|
||||
@@ -1485,6 +1518,44 @@ class PeerCoordinator implements PeerListener
|
||||
// rather than running another thread here.
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when comments are requested via ut_comment
|
||||
*
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public void gotCommentReq(Peer peer, int num) {
|
||||
/* TODO cache per-torrent setting, use it instead */
|
||||
if (!_util.utCommentsEnabled())
|
||||
return;
|
||||
CommentSet comments = snark.getComments();
|
||||
if (comments != null) {
|
||||
int lastSent = peer.getTotalCommentsSent();
|
||||
int sz;
|
||||
synchronized(comments) {
|
||||
sz = comments.size();
|
||||
// only send if we have more than last time
|
||||
if (sz <= lastSent)
|
||||
return;
|
||||
ExtensionHandler.locked_sendComments(peer, num, comments);
|
||||
}
|
||||
peer.setTotalCommentsSent(sz);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when comments are received via ut_comment
|
||||
*
|
||||
* @param comments non-null
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public void gotComments(Peer peer, List<Comment> comments) {
|
||||
/* TODO cache per-torrent setting, use it instead */
|
||||
if (!_util.utCommentsEnabled())
|
||||
return;
|
||||
if (!comments.isEmpty())
|
||||
snark.addComments(comments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by TrackerClient
|
||||
* @return the Set itself, modifiable, not a copy, caller should clear()
|
||||
|
@@ -24,6 +24,8 @@ import java.util.List;
|
||||
|
||||
import net.i2p.data.ByteArray;
|
||||
|
||||
import org.klomp.snark.comments.Comment;
|
||||
|
||||
/**
|
||||
* Listener for Peer events.
|
||||
*/
|
||||
@@ -215,4 +217,18 @@ interface PeerListener
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public I2PSnarkUtil getUtil();
|
||||
|
||||
/**
|
||||
* Called when comments are requested via ut_comment
|
||||
*
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public void gotCommentReq(Peer peer, int num);
|
||||
|
||||
/**
|
||||
* Called when comments are received via ut_comment
|
||||
*
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public void gotComments(Peer peer, List<Comment> comments);
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
@@ -35,6 +36,10 @@ import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFile;
|
||||
|
||||
import org.klomp.snark.comments.Comment;
|
||||
import org.klomp.snark.comments.CommentSet;
|
||||
|
||||
|
||||
/**
|
||||
* Main Snark program startup class.
|
||||
*
|
||||
@@ -212,6 +217,7 @@ public class Snark
|
||||
|
||||
***********/
|
||||
|
||||
/** max connections */
|
||||
public static final String PROP_MAX_CONNECTIONS = "i2psnark.maxConnections";
|
||||
|
||||
/** most of these used to be public, use accessors below instead */
|
||||
@@ -236,8 +242,12 @@ public class Snark
|
||||
private volatile boolean _autoStoppable;
|
||||
// String indicating main activity
|
||||
private volatile String activity = "Not started";
|
||||
private final long savedUploaded;
|
||||
|
||||
private long savedUploaded;
|
||||
private long _startedTime;
|
||||
private CommentSet _comments;
|
||||
private final Object _commentLock = new Object();
|
||||
private static final AtomicInteger __RPCID = new AtomicInteger();
|
||||
private final int _rpcID = __RPCID.incrementAndGet();
|
||||
|
||||
/**
|
||||
* from main() via parseArguments() single torrent
|
||||
@@ -470,6 +480,9 @@ public class Snark
|
||||
*/
|
||||
|
||||
savedUploaded = (completeListener != null) ? completeListener.getSavedUploaded(this) : 0;
|
||||
if (completeListener != null)
|
||||
_comments = completeListener.getSavedComments(this);
|
||||
|
||||
if (start)
|
||||
startTorrent();
|
||||
}
|
||||
@@ -541,6 +554,7 @@ public class Snark
|
||||
starting = true;
|
||||
try {
|
||||
x_startTorrent();
|
||||
_startedTime = _util.getContext().clock().now();
|
||||
} finally {
|
||||
starting = false;
|
||||
}
|
||||
@@ -632,16 +646,28 @@ public class Snark
|
||||
if (st != null) {
|
||||
// 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);
|
||||
long nowUploaded = getUploaded();
|
||||
boolean changed = storage.isChanged() || nowUploaded != savedUploaded;
|
||||
try {
|
||||
storage.close();
|
||||
} catch (IOException ioe) {
|
||||
System.out.println("Error closing " + torrent);
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
savedUploaded = nowUploaded;
|
||||
if (changed && completeListener != null)
|
||||
completeListener.updateStatus(this);
|
||||
// TODO should save comments at shutdown even if never started...
|
||||
if (completeListener != null) {
|
||||
synchronized(_commentLock) {
|
||||
if (_comments != null) {
|
||||
synchronized(_comments) {
|
||||
if (_comments.isModified())
|
||||
completeListener.locked_saveComments(this, _comments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fast)
|
||||
// HACK: See above if(!fast)
|
||||
@@ -1217,8 +1243,13 @@ public class Snark
|
||||
} catch (IOException ioe) {
|
||||
if (storage != null) {
|
||||
try { storage.close(); } catch (IOException ioee) {}
|
||||
// clear storage, we have a mess if we have non-null storage and null metainfo,
|
||||
// as on restart, Storage.reopen() will throw an ioe
|
||||
storage = null;
|
||||
}
|
||||
fatal("Could not check or create storage", ioe);
|
||||
// TODO we're still in an inconsistent state, won't work if restarted
|
||||
// (PeerState "disconnecting seed that connects to seeds"
|
||||
fatal("Could not create data files", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1284,8 +1315,12 @@ public class Snark
|
||||
|
||||
allChecked = true;
|
||||
checking = false;
|
||||
if (storage.isChanged() && completeListener != null)
|
||||
if (storage.isChanged() && completeListener != null) {
|
||||
completeListener.updateStatus(this);
|
||||
// this saved the status, so reset the variables
|
||||
storage.clearChanged();
|
||||
savedUploaded = getUploaded();
|
||||
}
|
||||
}
|
||||
|
||||
public void storageCompleted(Storage storage)
|
||||
@@ -1294,8 +1329,12 @@ public class Snark
|
||||
_log.info("Completely received " + torrent);
|
||||
//storage.close();
|
||||
//System.out.println("Completely received: " + torrent);
|
||||
if (completeListener != null)
|
||||
if (completeListener != null) {
|
||||
completeListener.torrentComplete(this);
|
||||
// this saved the status, so reset the variables
|
||||
savedUploaded = getUploaded();
|
||||
storage.clearChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void setWantedPieces(Storage storage)
|
||||
@@ -1363,4 +1402,57 @@ public class Snark
|
||||
long limit = 1024l * _util.getMaxUpBW();
|
||||
return total > limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* A unique ID for this torrent, useful for RPC
|
||||
* @return positive value unless you wrap around
|
||||
* @since 0.9.30
|
||||
*/
|
||||
public int getRPCID() {
|
||||
return _rpcID;
|
||||
}
|
||||
|
||||
/**
|
||||
* When did we start this torrent
|
||||
* For RPC
|
||||
* @return 0 if not started before. Not cleared when stopped.
|
||||
* @since 0.9.30
|
||||
*/
|
||||
public long getStartedTime() {
|
||||
return _startedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current comment set for this torrent.
|
||||
* Not a copy.
|
||||
* Caller MUST synch on the returned object for all operations.
|
||||
*
|
||||
* @return may be null if none
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public CommentSet getComments() {
|
||||
synchronized(_commentLock) {
|
||||
return _comments;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the current comment set for this torrent,
|
||||
* creating it if it didn't previously exist.
|
||||
*
|
||||
* @return true if the set changed
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public boolean addComments(List<Comment> comments) {
|
||||
synchronized(_commentLock) {
|
||||
if (_comments == null) {
|
||||
_comments = new CommentSet(comments);
|
||||
return true;
|
||||
} else {
|
||||
synchronized(_comments) {
|
||||
return _comments.addAll(comments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -27,7 +27,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientApp;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.app.ClientAppState;
|
||||
import net.i2p.crypto.SHA1Hash;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.Base64;
|
||||
@@ -42,15 +44,18 @@ import net.i2p.util.SecureDirectory;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
import net.i2p.util.SystemVersion;
|
||||
import net.i2p.util.Translate;
|
||||
|
||||
import org.klomp.snark.comments.Comment;
|
||||
import org.klomp.snark.comments.CommentSet;
|
||||
import org.klomp.snark.dht.DHT;
|
||||
import org.klomp.snark.dht.KRPC;
|
||||
|
||||
/**
|
||||
* Manage multiple snarks
|
||||
*/
|
||||
public class SnarkManager implements CompleteListener {
|
||||
public class SnarkManager implements CompleteListener, ClientApp {
|
||||
|
||||
/**
|
||||
* Map of (canonical) filename of the .torrent file to Snark instance.
|
||||
@@ -107,9 +112,12 @@ public class SnarkManager implements CompleteListener {
|
||||
//private static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
|
||||
//private static final String PROP_META_PRIORITY_SUFFIX = ".priority";
|
||||
private static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet.";
|
||||
/** @since 0.9.31 */
|
||||
private static final String PROP_META_COMMENTS = "comments";
|
||||
|
||||
private static final String CONFIG_FILE_SUFFIX = ".config";
|
||||
private static final String CONFIG_FILE = "i2psnark" + CONFIG_FILE_SUFFIX;
|
||||
private static final String COMMENT_FILE_SUFFIX = ".comments.txt.gz";
|
||||
public static final String PROP_FILES_PUBLIC = "i2psnark.filesPublic";
|
||||
public static final String PROP_OLD_AUTO_START = "i2snark.autoStart"; // oops
|
||||
public static final String PROP_AUTO_START = "i2psnark.autoStart"; // convert in migration to new config file
|
||||
@@ -123,6 +131,8 @@ public class SnarkManager implements CompleteListener {
|
||||
public static final String RC_PROP_UNIVERSAL_THEMING = "routerconsole.universal.theme";
|
||||
public static final String PROP_THEME = "i2psnark.theme";
|
||||
public static final String DEFAULT_THEME = "ubergine";
|
||||
/** @since 0.9.32 */
|
||||
public static final String PROP_COLLAPSE_PANELS = "i2psnark.collapsePanels";
|
||||
private static final String PROP_USE_OPENTRACKERS = "i2psnark.useOpentrackers";
|
||||
public static final String PROP_OPENTRACKERS = "i2psnark.opentrackers";
|
||||
public static final String PROP_PRIVATETRACKERS = "i2psnark.privatetrackers";
|
||||
@@ -130,6 +140,12 @@ public class SnarkManager implements CompleteListener {
|
||||
private static final String PROP_SMART_SORT = "i2psnark.smartSort";
|
||||
private static final String PROP_LANG = "i2psnark.lang";
|
||||
private static final String PROP_COUNTRY = "i2psnark.country";
|
||||
/** @since 0.9.31 */
|
||||
private static final String PROP_RATINGS = "i2psnark.ratings";
|
||||
/** @since 0.9.31 */
|
||||
private static final String PROP_COMMENTS = "i2psnark.comments";
|
||||
/** @since 0.9.31 */
|
||||
private static final String PROP_COMMENTS_NAME = "i2psnark.commentsName";
|
||||
|
||||
public static final int MIN_UP_BW = 10;
|
||||
public static final int DEFAULT_MAX_UP_BW = 25;
|
||||
@@ -139,7 +155,7 @@ public class SnarkManager implements CompleteListener {
|
||||
public static final int DEFAULT_TUNNEL_QUANTITY = 3;
|
||||
public static final String CONFIG_DIR_SUFFIX = ".d";
|
||||
private static final String SUBDIR_PREFIX = "s";
|
||||
private static final String B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~";
|
||||
private static final String B64 = Base64.ALPHABET_I2P;
|
||||
|
||||
/**
|
||||
* "name", "announceURL=websiteURL" pairs
|
||||
@@ -167,6 +183,7 @@ public class SnarkManager implements CompleteListener {
|
||||
// The following is ECDSA_SHA256_P256
|
||||
,"TheBland", "http://s5ikrdyjwbcgxmqetxb3nyheizftms7euacuub2hic7defkh3xhq.b32.i2p/a=http://tracker.thebland.i2p/tracker/index.jsp"
|
||||
,"psi's open tracker", "http://uajd4nctepxpac4c4bdyrdw7qvja2a5u3x25otfhkptcjgd53ioq.b32.i2p/announce=http://uajd4nctepxpac4c4bdyrdw7qvja2a5u3x25otfhkptcjgd53ioq.b32.i2p/"
|
||||
,"C.Tracker", "http://ri5a27ioqd4vkik72fawbcryglkmwyy4726uu5j3eg6zqh2jswfq.b32.i2p/announce=http://tracker.crypthost.i2p/tracker/index.jsp",
|
||||
};
|
||||
|
||||
/** URL. This is our equivalent to router.utorrent.com for bootstrap */
|
||||
@@ -187,8 +204,11 @@ public class SnarkManager implements CompleteListener {
|
||||
"opentracker.psi.i2p", "vmow3h54yljn7zvzbqepdddt5fmygijujycod2q6yznpy2rrzuwa.b32.i2p",
|
||||
"tracker.killyourtv.i2p", "5mpvzxfbd4rtped3c7ln4ddw52e7i7t56s36ztky4ustxtxrjdpa.b32.i2p",
|
||||
"opendiftracker.i2p", "bikpeyxci4zuyy36eau5ycw665dplun4yxamn7vmsastejdqtfoq.b32.i2p",
|
||||
"tracker.crypthost.i2p", "ri5a27ioqd4vkik72fawbcryglkmwyy4726uu5j3eg6zqh2jswfq.b32.i2p",
|
||||
// psi go - unregistered
|
||||
"uajd4nctepxpac4c4bdyrdw7qvja2a5u3x25otfhkptcjgd53ioq.b32.i2p"
|
||||
"uajd4nctepxpac4c4bdyrdw7qvja2a5u3x25otfhkptcjgd53ioq.b32.i2p",
|
||||
// Vuze - unregistered
|
||||
"crs2nugpvoqygnpabqbopwyjqettwszth6ubr2fh7whstlos3a6q.b32.i2p"
|
||||
}));
|
||||
|
||||
static {
|
||||
@@ -245,6 +265,13 @@ public class SnarkManager implements CompleteListener {
|
||||
*/
|
||||
public void start() {
|
||||
_running = true;
|
||||
if ("i2psnark".equals(_contextName)) {
|
||||
// Register with the ClientAppManager so the rpc plugin can find us
|
||||
// only if default instance
|
||||
ClientAppManager cmgr = _context.clientAppManager();
|
||||
if (cmgr != null)
|
||||
cmgr.register(this);
|
||||
}
|
||||
_peerCoordinatorSet = new PeerCoordinatorSet();
|
||||
_connectionAcceptor = new ConnectionAcceptor(_util, _peerCoordinatorSet);
|
||||
_monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor", true);
|
||||
@@ -302,6 +329,8 @@ public class SnarkManager implements CompleteListener {
|
||||
* Runs inline.
|
||||
*/
|
||||
public void stop() {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Snark stop() begin", new Exception("I did it"));
|
||||
if (_umgr != null && _uhandler != null) {
|
||||
//_uhandler.shutdown();
|
||||
_umgr.unregister(_uhandler, UpdateType.ROUTER_SIGNED, UpdateMethod.TORRENT);
|
||||
@@ -312,11 +341,59 @@ public class SnarkManager implements CompleteListener {
|
||||
_connectionAcceptor.halt();
|
||||
_idleChecker.cancel();
|
||||
stopAllTorrents(true);
|
||||
if ("i2psnark".equals(_contextName)) {
|
||||
// only if default instance
|
||||
ClientAppManager cmgr = _context.clientAppManager();
|
||||
if (cmgr != null)
|
||||
cmgr.unregister(this);
|
||||
}
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Snark stop() end");
|
||||
}
|
||||
|
||||
/** @since 0.9.1 */
|
||||
public boolean isStopping() { return _stopping; }
|
||||
|
||||
/**
|
||||
* ClientApp method. Does nothing.
|
||||
* Doesn't matter, we are only registering.
|
||||
* @since 0.9.30
|
||||
*/
|
||||
public void startup() {}
|
||||
|
||||
/**
|
||||
* ClientApp method. Does nothing.
|
||||
* Doesn't matter, we are only registering.
|
||||
* @since 0.9.30
|
||||
*/
|
||||
public void shutdown(String[] args) {}
|
||||
|
||||
/**
|
||||
* ClientApp method.
|
||||
* Doesn't matter, we are only registering.
|
||||
* @return INITIALIZED always.
|
||||
* @since 0.9.30
|
||||
*/
|
||||
public ClientAppState getState() {
|
||||
return ClientAppState.INITIALIZED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ClientApp method.
|
||||
* @since 0.9.30
|
||||
*/
|
||||
public String getName() {
|
||||
return "i2psnark";
|
||||
}
|
||||
|
||||
/**
|
||||
* ClientApp method.
|
||||
* @since 0.9.30
|
||||
*/
|
||||
public String getDisplayName() {
|
||||
return "i2psnark: " + _contextPath;
|
||||
}
|
||||
|
||||
/** hook to I2PSnarkUtil for the servlet */
|
||||
public I2PSnarkUtil util() { return _util; }
|
||||
|
||||
@@ -324,15 +401,15 @@ public class SnarkManager implements CompleteListener {
|
||||
|
||||
/**
|
||||
* Use if it does not include a link.
|
||||
* Escapes '<' and '>' before queueing
|
||||
* Escapes '<' and '>' before queueing
|
||||
*/
|
||||
public void addMessage(String message) {
|
||||
addMessageNoEscape(message.replace("<", "<").replace(">", ">"));
|
||||
addMessageNoEscape(message.replace("&", "&").replace("<", "<").replace(">", ">"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Use if it includes a link.
|
||||
* Does not escape '<' and '>' before queueing
|
||||
* Does not escape '<' and '>' before queueing
|
||||
* @since 0.9.14.1
|
||||
*/
|
||||
public void addMessageNoEscape(String message) {
|
||||
@@ -379,6 +456,17 @@ public class SnarkManager implements CompleteListener {
|
||||
return Boolean.parseBoolean(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return default true
|
||||
* @since 0.9.32
|
||||
*/
|
||||
public boolean isCollapsePanelsEnabled() {
|
||||
String val = _config.getProperty(PROP_COLLAPSE_PANELS);
|
||||
if (val == null)
|
||||
return I2PSnarkUtil.DEFAULT_COLLAPSE_PANELS;
|
||||
return Boolean.parseBoolean(val);
|
||||
}
|
||||
|
||||
/****
|
||||
public String linkPrefix() {
|
||||
return _config.getProperty(PROP_LINK_PREFIX, DEFAULT_LINK_PREFIX + getDataDir().getAbsolutePath() + File.separatorChar);
|
||||
@@ -435,6 +523,14 @@ public class SnarkManager implements CompleteListener {
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* For RPC
|
||||
* @since 0.9.30
|
||||
*/
|
||||
public File getConfigDir() {
|
||||
return _configDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate the old flat config file to the new config dir
|
||||
* containing the config file minus the per-torrent entries,
|
||||
@@ -586,6 +682,53 @@ public class SnarkManager implements CompleteListener {
|
||||
return new File(subdir, hex + CONFIG_FILE_SUFFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* The conmment file for a torrent
|
||||
* @param confDir the config directory
|
||||
* @param ih 20-byte infohash
|
||||
* @since 0.9.31
|
||||
*/
|
||||
private static File commentFile(File confDir, byte[] ih) {
|
||||
String hex = I2PSnarkUtil.toHex(ih);
|
||||
File subdir = new SecureDirectory(confDir, SUBDIR_PREFIX + B64.charAt((ih[0] >> 2) & 0x3f));
|
||||
return new File(subdir, hex + COMMENT_FILE_SUFFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* The conmments for a torrent
|
||||
* @return null if none
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public CommentSet getSavedComments(Snark snark) {
|
||||
File com = commentFile(_configDir, snark.getInfoHash());
|
||||
if (com.exists()) {
|
||||
try {
|
||||
return new CommentSet(com);
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Comment load error", ioe);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the conmments for a torrent
|
||||
* Caller must synchronize on comments.
|
||||
*
|
||||
* @param comments non-null
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public void locked_saveComments(Snark snark, CommentSet comments) {
|
||||
File com = commentFile(_configDir, snark.getInfoHash());
|
||||
try {
|
||||
comments.save(com);
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Comment save error", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the info hash from a config file name
|
||||
* @return null for invalid name
|
||||
@@ -607,7 +750,7 @@ public class SnarkManager implements CompleteListener {
|
||||
return new SHA1Hash(ih);
|
||||
}
|
||||
|
||||
/** null to set initial defaults */
|
||||
/** @param filename null to set initial defaults */
|
||||
public void loadConfig(String filename) {
|
||||
synchronized(_configLock) {
|
||||
locked_loadConfig(filename);
|
||||
@@ -662,17 +805,32 @@ public class SnarkManager implements CompleteListener {
|
||||
// no, so we can switch default to true later
|
||||
//if (!_config.containsKey(PROP_USE_DHT))
|
||||
// _config.setProperty(PROP_USE_DHT, Boolean.toString(I2PSnarkUtil.DEFAULT_USE_DHT));
|
||||
if (!_config.containsKey(PROP_RATINGS))
|
||||
_config.setProperty(PROP_RATINGS, "true");
|
||||
if (!_config.containsKey(PROP_COMMENTS))
|
||||
_config.setProperty(PROP_COMMENTS, "true");
|
||||
if (!_config.containsKey(PROP_COMMENTS_NAME))
|
||||
_config.setProperty(PROP_COMMENTS_NAME, "");
|
||||
if (!_config.containsKey(PROP_COLLAPSE_PANELS))
|
||||
_config.setProperty(PROP_COLLAPSE_PANELS,
|
||||
Boolean.toString(I2PSnarkUtil.DEFAULT_COLLAPSE_PANELS));
|
||||
updateConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public boolean getUniversalTheming() {
|
||||
return _context.getBooleanProperty(RC_PROP_UNIVERSAL_THEMING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current theme.
|
||||
* @return String -- the current theme
|
||||
*/
|
||||
public String getTheme() {
|
||||
String theme = _config.getProperty(PROP_THEME);
|
||||
boolean universalTheming = _context.getBooleanProperty(RC_PROP_UNIVERSAL_THEMING);
|
||||
if (universalTheming) {
|
||||
if (getUniversalTheming()) {
|
||||
// Fetch routerconsole theme (or use our default if it doesn't exist)
|
||||
theme = _context.getProperty(RC_PROP_THEME, DEFAULT_THEME);
|
||||
// Ensure that theme exists
|
||||
@@ -715,7 +873,7 @@ public class SnarkManager implements CompleteListener {
|
||||
themes = new String[0];
|
||||
}
|
||||
} else {
|
||||
themes = new String[] { "light", "ubergine", "vanilla" };
|
||||
themes = new String[] { "classic", "dark", "light", "midnight", "ubergine", "vanilla" };
|
||||
}
|
||||
return themes;
|
||||
}
|
||||
@@ -729,7 +887,7 @@ public class SnarkManager implements CompleteListener {
|
||||
_util.setMaxUpBW(limits[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void updateConfig() {
|
||||
String i2cpHost = _config.getProperty(PROP_I2CP_HOST);
|
||||
int i2cpPort = getInt(PROP_I2CP_PORT, 7654);
|
||||
@@ -763,10 +921,22 @@ public class SnarkManager implements CompleteListener {
|
||||
// careful, so we can switch default to true later
|
||||
_util.setUseDHT(Boolean.parseBoolean(_config.getProperty(PROP_USE_DHT,
|
||||
Boolean.toString(I2PSnarkUtil.DEFAULT_USE_DHT))));
|
||||
getDataDir().mkdirs();
|
||||
_util.setRatingsEnabled(Boolean.parseBoolean(_config.getProperty(PROP_RATINGS, "true")));
|
||||
_util.setCommentsEnabled(Boolean.parseBoolean(_config.getProperty(PROP_COMMENTS, "true")));
|
||||
_util.setCommentsName(_config.getProperty(PROP_COMMENTS_NAME, ""));
|
||||
_util.setCollapsePanels(Boolean.parseBoolean(_config.getProperty(PROP_COLLAPSE_PANELS,
|
||||
Boolean.toString(I2PSnarkUtil.DEFAULT_COLLAPSE_PANELS))));
|
||||
File dd = getDataDir();
|
||||
if (dd.isDirectory()) {
|
||||
if (!dd.canWrite())
|
||||
addMessage(_t("No write permissions for data directory") + ": " + dd);
|
||||
} else {
|
||||
if (!dd.mkdirs())
|
||||
addMessage(_t("Data directory cannot be created") + ": " + dd);
|
||||
}
|
||||
initTrackerMap();
|
||||
}
|
||||
|
||||
|
||||
private int getInt(String prop, int defaultVal) {
|
||||
String p = _config.getProperty(prop);
|
||||
try {
|
||||
@@ -777,29 +947,29 @@ public class SnarkManager implements CompleteListener {
|
||||
}
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* all params may be null or need trimming
|
||||
*/
|
||||
public void updateConfig(String dataDir, boolean filesPublic, boolean autoStart, boolean smartSort, String refreshDelay,
|
||||
String startDelay, String pageSize, String seedPct, String eepHost,
|
||||
String startDelay, String pageSize, String seedPct, String eepHost,
|
||||
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
|
||||
String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme,
|
||||
String lang) {
|
||||
String lang, boolean enableRatings, boolean enableComments, String commentName, boolean collapsePanels) {
|
||||
synchronized(_configLock) {
|
||||
locked_updateConfig(dataDir, filesPublic, autoStart, smartSort,refreshDelay,
|
||||
startDelay, pageSize, seedPct, eepHost,
|
||||
eepPort, i2cpHost, i2cpPort, i2cpOpts,
|
||||
upLimit, upBW, useOpenTrackers, useDHT, theme,
|
||||
lang);
|
||||
locked_updateConfig(dataDir, filesPublic, autoStart, smartSort, refreshDelay,
|
||||
startDelay, pageSize, seedPct, eepHost,
|
||||
eepPort, i2cpHost, i2cpPort, i2cpOpts,
|
||||
upLimit, upBW, useOpenTrackers, useDHT, theme,
|
||||
lang, enableRatings, enableComments, commentName, collapsePanels);
|
||||
}
|
||||
}
|
||||
|
||||
private void locked_updateConfig(String dataDir, boolean filesPublic, boolean autoStart, boolean smartSort, String refreshDelay,
|
||||
String startDelay, String pageSize, String seedPct, String eepHost,
|
||||
String startDelay, String pageSize, String seedPct, String eepHost,
|
||||
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
|
||||
String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme,
|
||||
String lang) {
|
||||
String lang, boolean enableRatings, boolean enableComments, String commentName, boolean collapsePanels) {
|
||||
boolean changed = false;
|
||||
boolean interruptMonitor = false;
|
||||
//if (eepHost != null) {
|
||||
@@ -844,7 +1014,7 @@ public class SnarkManager implements CompleteListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (startDelay != null && _context.isRouterContext()) {
|
||||
int minutes = _util.getStartupDelay();
|
||||
try { minutes = Integer.parseInt(startDelay.trim()); } catch (NumberFormatException nfe) {}
|
||||
@@ -898,6 +1068,8 @@ public class SnarkManager implements CompleteListener {
|
||||
} else if (!dd.canRead()) {
|
||||
addMessage(_t("Unreadable") + ": " + dataDir);
|
||||
} else {
|
||||
if (!dd.canWrite())
|
||||
addMessage(_t("No write permissions for data directory") + ": " + dataDir);
|
||||
changed = true;
|
||||
interruptMonitor = true;
|
||||
_config.setProperty(PROP_DIR, dataDir);
|
||||
@@ -961,7 +1133,7 @@ public class SnarkManager implements CompleteListener {
|
||||
if (split > 0)
|
||||
oldOpts.put(pair.substring(0, split), pair.substring(split+1));
|
||||
}
|
||||
|
||||
|
||||
boolean reconnect = i2cpHost != null && i2cpHost.trim().length() > 0 && port > 0 &&
|
||||
(port != _util.getI2CPPort() || !oldI2CPHost.equals(i2cpHost));
|
||||
if (reconnect || !oldOpts.equals(opts)) {
|
||||
@@ -1070,13 +1242,53 @@ public class SnarkManager implements CompleteListener {
|
||||
_util.setUseDHT(useDHT);
|
||||
changed = true;
|
||||
}
|
||||
if (_util.ratingsEnabled() != enableRatings) {
|
||||
_config.setProperty(PROP_RATINGS, Boolean.toString(enableRatings));
|
||||
if (enableRatings)
|
||||
addMessage(_t("Enabled Ratings."));
|
||||
else
|
||||
addMessage(_t("Disabled Ratings."));
|
||||
_util.setRatingsEnabled(enableRatings);
|
||||
changed = true;
|
||||
}
|
||||
if (_util.commentsEnabled() != enableComments) {
|
||||
_config.setProperty(PROP_COMMENTS, Boolean.toString(enableComments));
|
||||
if (enableComments)
|
||||
addMessage(_t("Enabled Comments."));
|
||||
else
|
||||
addMessage(_t("Disabled Comments."));
|
||||
_util.setCommentsEnabled(enableComments);
|
||||
changed = true;
|
||||
}
|
||||
if (commentName == null) {
|
||||
commentName = "";
|
||||
} else {
|
||||
commentName = commentName.replaceAll("[\n\r<>#;]", "");
|
||||
if (commentName.length() > Comment.MAX_NAME_LEN)
|
||||
commentName = commentName.substring(0, Comment.MAX_NAME_LEN);
|
||||
}
|
||||
if (!_util.getCommentsName().equals(commentName)) {
|
||||
_config.setProperty(PROP_COMMENTS_NAME, commentName);
|
||||
addMessage(_t("Comments name set to {0}.", commentName));
|
||||
_util.setCommentsName(commentName);
|
||||
changed = true;
|
||||
}
|
||||
if (theme != null) {
|
||||
if(!theme.equals(_config.getProperty(PROP_THEME))) {
|
||||
_config.setProperty(PROP_THEME, theme);
|
||||
addMessage(_t("{0} theme loaded, return to main i2psnark page to view.", theme));
|
||||
addMessage(_t("{0} theme loaded.", theme));
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (_util.collapsePanels() != collapsePanels) {
|
||||
_config.setProperty(PROP_COLLAPSE_PANELS, Boolean.toString(collapsePanels));
|
||||
if (collapsePanels)
|
||||
addMessage(_t("Collapsible panels enabled."));
|
||||
else
|
||||
addMessage(_t("Collapsible panels disabled."));
|
||||
_util.setCollapsePanels(collapsePanels);
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
saveConfig();
|
||||
if (interruptMonitor)
|
||||
@@ -1300,7 +1512,7 @@ public class SnarkManager implements CompleteListener {
|
||||
fis.close();
|
||||
fis = null;
|
||||
} catch (IOException e) {}
|
||||
|
||||
|
||||
// This test may be a duplicate, but not if we were called
|
||||
// from the DirMonitor, which only checks for dup torrent file names.
|
||||
Snark snark = getTorrentByInfoHash(info.getInfoHash());
|
||||
@@ -1521,9 +1733,9 @@ public class SnarkManager implements CompleteListener {
|
||||
* Called from servlet. This is only for the 'create torrent' form.
|
||||
*
|
||||
* @param metainfo the metainfo for the torrent
|
||||
* @param bitfield the current completion status of the torrent
|
||||
* @param bitfield the current completion status of the torrent, or null
|
||||
* @param filename the absolute path to save the metainfo to, generally ending in ".torrent", which is also the name of the torrent
|
||||
* Must be a filesystem-safe name.
|
||||
* Must be a filesystem-safe name. If null, will generate a name from the metainfo.
|
||||
* @param baseFile may be null, if so look in rootDataDir
|
||||
* @throws RuntimeException via Snark.fatal()
|
||||
* @return success
|
||||
@@ -1537,10 +1749,18 @@ public class SnarkManager implements CompleteListener {
|
||||
if (snark != null) {
|
||||
addMessage(_t("Torrent with this info hash is already running: {0}", snark.getBaseName()));
|
||||
return false;
|
||||
} else {
|
||||
} else if (bitfield != null) {
|
||||
saveTorrentStatus(metainfo, bitfield, null, baseFile, true, 0, true); // no file priorities
|
||||
}
|
||||
// so addTorrent won't recheck
|
||||
if (filename == null) {
|
||||
File f = new File(getDataDir(), Storage.filterName(metainfo.getName()) + ".torrent");
|
||||
if (f.exists()) {
|
||||
addMessage(_t("Failed to copy torrent file to {0}", f.getAbsolutePath()));
|
||||
_log.error("Torrent file already exists: " + f);
|
||||
}
|
||||
filename = f.getAbsolutePath();
|
||||
}
|
||||
try {
|
||||
locked_writeMetaInfo(metainfo, filename, areFilesPublic());
|
||||
// hold the lock for a long time
|
||||
@@ -1740,6 +1960,31 @@ public class SnarkManager implements CompleteListener {
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get setting for comments enabled from the config file.
|
||||
* Caller must first check global I2PSnarkUtil.commentsEnabled()
|
||||
* Default true.
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public boolean getSavedCommentsEnabled(Snark snark) {
|
||||
boolean rv = true;
|
||||
Properties config = getConfig(snark);
|
||||
if (config != null) {
|
||||
String s = config.getProperty(PROP_META_COMMENTS);
|
||||
if (s != null)
|
||||
rv = Boolean.parseBoolean(s);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set setting for comments enabled in the config file.
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public void setSavedCommentsEnabled(Snark snark, boolean yes) {
|
||||
saveTorrentStatus(snark, Boolean.valueOf(yes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the completion status of a torrent and other data in the config file
|
||||
@@ -1748,13 +1993,24 @@ public class SnarkManager implements CompleteListener {
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public void saveTorrentStatus(Snark snark) {
|
||||
saveTorrentStatus(snark, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the completion status of a torrent and other data in the config file
|
||||
* for that torrent. Does nothing for magnets.
|
||||
*
|
||||
* @param comments null for no change
|
||||
* @since 0.9.31
|
||||
*/
|
||||
private void saveTorrentStatus(Snark snark, Boolean comments) {
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
Storage storage = snark.getStorage();
|
||||
if (meta == null || storage == null)
|
||||
return;
|
||||
saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(),
|
||||
storage.getBase(), storage.getPreserveFileNames(),
|
||||
snark.getUploaded(), snark.isStopped());
|
||||
snark.getUploaded(), snark.isStopped(), comments);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1771,13 +2027,24 @@ public class SnarkManager implements CompleteListener {
|
||||
*/
|
||||
private void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities,
|
||||
File base, boolean preserveNames, long uploaded, boolean stopped) {
|
||||
saveTorrentStatus(metainfo, bitfield, priorities, base, preserveNames, uploaded, stopped, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* @param comments null for no change
|
||||
* @since 0.9.31
|
||||
*/
|
||||
private void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities,
|
||||
File base, boolean preserveNames, long uploaded, boolean stopped,
|
||||
Boolean comments) {
|
||||
synchronized (_configLock) {
|
||||
locked_saveTorrentStatus(metainfo, bitfield, priorities, base, preserveNames, uploaded, stopped);
|
||||
locked_saveTorrentStatus(metainfo, bitfield, priorities, base, preserveNames, uploaded, stopped, comments);
|
||||
}
|
||||
}
|
||||
|
||||
private void locked_saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities,
|
||||
File base, boolean preserveNames, long uploaded, boolean stopped) {
|
||||
File base, boolean preserveNames, long uploaded, boolean stopped,
|
||||
Boolean comments) {
|
||||
byte[] ih = metainfo.getInfoHash();
|
||||
Properties config = getConfig(ih);
|
||||
String now = Long.toString(System.currentTimeMillis());
|
||||
@@ -1801,6 +2068,8 @@ public class SnarkManager implements CompleteListener {
|
||||
config.setProperty(PROP_META_RUNNING, Boolean.toString(running));
|
||||
if (base != null)
|
||||
config.setProperty(PROP_META_BASE, base.getAbsolutePath());
|
||||
if (comments != null)
|
||||
config.setProperty(PROP_META_COMMENTS, comments.toString());
|
||||
|
||||
// now the file priorities
|
||||
if (priorities != null) {
|
||||
@@ -1860,7 +2129,9 @@ public class SnarkManager implements CompleteListener {
|
||||
private void removeTorrentStatus(Snark snark) {
|
||||
byte[] ih = snark.getInfoHash();
|
||||
File conf = configFile(_configDir, ih);
|
||||
File comm = commentFile(_configDir, ih);
|
||||
synchronized (_configLock) {
|
||||
comm.delete();
|
||||
boolean ok = conf.delete();
|
||||
if (ok) {
|
||||
if (_log.shouldInfo())
|
||||
@@ -2092,7 +2363,7 @@ public class SnarkManager implements CompleteListener {
|
||||
// don't bother delaying if auto start is false
|
||||
long delay = (60L * 1000) * getStartupDelayMinutes();
|
||||
if (delay > 0 && shouldAutoStart()) {
|
||||
addMessage(_t("Adding torrents in {0}", DataHelper.formatDuration2(delay)));
|
||||
addMessageNoEscape(_t("Adding torrents in {0}", DataHelper.formatDuration2(delay)));
|
||||
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
|
||||
// Remove that first message
|
||||
if (_messages.size() == 1)
|
||||
@@ -2466,6 +2737,7 @@ public class SnarkManager implements CompleteListener {
|
||||
|
||||
/**
|
||||
* If not connected, thread it, otherwise inline
|
||||
* @throws RuntimeException via Snark.fatal()
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public void startTorrent(byte[] infoHash) {
|
||||
@@ -2480,6 +2752,7 @@ public class SnarkManager implements CompleteListener {
|
||||
|
||||
/**
|
||||
* If not connected, thread it, otherwise inline
|
||||
* @throws RuntimeException via Snark.fatal()
|
||||
* @since 0.9.23
|
||||
*/
|
||||
public void startTorrent(Snark snark) {
|
||||
@@ -2527,17 +2800,14 @@ public class SnarkManager implements CompleteListener {
|
||||
private final Snark snark;
|
||||
public ThreadedStarter(Snark s) { snark = s; }
|
||||
public void run() {
|
||||
try {
|
||||
run2();
|
||||
} catch (RuntimeException e) {
|
||||
_log.error("Error starting", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void run2() {
|
||||
if (snark != null) {
|
||||
if (snark.isStopped())
|
||||
snark.startTorrent();
|
||||
if (snark.isStopped()) {
|
||||
try {
|
||||
snark.startTorrent();
|
||||
} catch (RuntimeException re) {
|
||||
// Snark.fatal() will log and call fatal() here for user message before throwing
|
||||
}
|
||||
}
|
||||
} else {
|
||||
startAll();
|
||||
}
|
||||
@@ -2550,15 +2820,20 @@ public class SnarkManager implements CompleteListener {
|
||||
*/
|
||||
private void startAll() {
|
||||
for (Snark snark : _snarks.values()) {
|
||||
if (snark.isStopped())
|
||||
snark.startTorrent();
|
||||
if (snark.isStopped()) {
|
||||
try {
|
||||
snark.startTorrent();
|
||||
} catch (RuntimeException re) {
|
||||
// Snark.fatal() will log and call fatal() here for user message before throwing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all running torrents, and close the tunnel after a delay
|
||||
* to allow for announces.
|
||||
* If called at router shutdown via Jetty shutdown hook -> webapp destroy() -> stop(),
|
||||
* If called at router shutdown via Jetty shutdown hook -> webapp destroy() -> stop(),
|
||||
* the tunnel won't actually be closed as the SimpleTimer2 is already shutdown
|
||||
* or will be soon, so we delay a few seconds inline.
|
||||
* @param finalShutdown if true, sleep at the end if any torrents were running
|
||||
@@ -2580,7 +2855,18 @@ public class SnarkManager implements CompleteListener {
|
||||
stopTorrent(snark, false);
|
||||
// Throttle since every unannounce is now threaded.
|
||||
// How to do this without creating a ton of threads?
|
||||
try { Thread.sleep(20); } catch (InterruptedException ie) {}
|
||||
if (count % 8 == 0) {
|
||||
try { Thread.sleep(20); } catch (InterruptedException ie) {}
|
||||
}
|
||||
} else {
|
||||
CommentSet cs = snark.getComments();
|
||||
if (cs != null) {
|
||||
synchronized(cs) {
|
||||
if (cs.isModified()) {
|
||||
locked_saveComments(snark, cs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_util.connected()) {
|
||||
@@ -2588,12 +2874,17 @@ public class SnarkManager implements CompleteListener {
|
||||
DHT dht = _util.getDHT();
|
||||
if (dht != null)
|
||||
dht.stop();
|
||||
// Schedule this even for final shutdown, as there's a chance
|
||||
// that it's just this webapp that is stopping.
|
||||
_context.simpleTimer2().addEvent(new Disconnector(), 60*1000);
|
||||
addMessage(_t("Closing I2P tunnel after notifying trackers."));
|
||||
if (finalShutdown) {
|
||||
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
|
||||
long toWait = 5*1000;
|
||||
if (SystemVersion.isARM())
|
||||
toWait *= 2;
|
||||
try { Thread.sleep(toWait); } catch (InterruptedException ie) {}
|
||||
_util.disconnect();
|
||||
_stopping = false;
|
||||
} else {
|
||||
// Only schedule this if not a final shutdown
|
||||
_context.simpleTimer2().addEvent(new Disconnector(), 60*1000);
|
||||
}
|
||||
} else {
|
||||
_util.disconnect();
|
||||
|
@@ -305,6 +305,14 @@ public class Storage implements Closeable
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the storage changed variable
|
||||
* @since 0.9.30
|
||||
*/
|
||||
void clearChanged() {
|
||||
changed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* File checking in progress.
|
||||
* @since 0.9.3
|
||||
@@ -436,7 +444,7 @@ public class Storage implements Closeable
|
||||
* Must call Snark.updatePiecePriorities()
|
||||
* (which calls getPiecePriorities()) after calling this.
|
||||
* @param fileIndex as obtained from indexOf
|
||||
* @param pri default 0; <0 to disable
|
||||
* @param pri default 0; <0 to disable
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public void setPriority(int fileIndex, int pri) {
|
||||
@@ -695,7 +703,7 @@ public class Storage implements Closeable
|
||||
* Doesn't really reopen the file descriptors for a restart.
|
||||
* Just does an existence check but no length check or data reverification
|
||||
*
|
||||
* @throws IOE on fail
|
||||
* @throws IOException on fail
|
||||
*/
|
||||
public void reopen() throws IOException
|
||||
{
|
||||
@@ -1111,7 +1119,7 @@ public class Storage implements Closeable
|
||||
*
|
||||
* @return true if the piece was correct (sha metainfo hash
|
||||
* matches), otherwise false.
|
||||
* @exception IOException when some storage related error occurs.
|
||||
* @throws IOException when some storage related error occurs.
|
||||
*/
|
||||
public boolean putPiece(PartialPiece pp) throws IOException
|
||||
{
|
||||
|
@@ -11,6 +11,9 @@ import net.i2p.update.*;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
import org.klomp.snark.comments.CommentSet;
|
||||
|
||||
|
||||
/**
|
||||
* The downloader for router signed updates.
|
||||
*
|
||||
@@ -299,6 +302,16 @@ class UpdateRunner implements UpdateTask, CompleteListener {
|
||||
return _smgr.getSavedUploaded(snark);
|
||||
}
|
||||
|
||||
/** @since 0.9.31 */
|
||||
public CommentSet getSavedComments(Snark snark) {
|
||||
return _smgr.getSavedComments(snark);
|
||||
}
|
||||
|
||||
/** @since 0.9.31 */
|
||||
public void locked_saveComments(Snark snark, CommentSet comments) {
|
||||
_smgr.locked_saveComments(snark, comments);
|
||||
}
|
||||
|
||||
//////// end CompleteListener methods
|
||||
|
||||
private static String linkify(String url) {
|
||||
|
@@ -102,9 +102,9 @@ public class BDecoder
|
||||
* @return The first BEValue on the stream or null when the stream
|
||||
* has ended.
|
||||
*
|
||||
* @exception InvalidBEncoding when the stream doesn't start with a
|
||||
* @throws InvalidBEncodingException when the stream doesn't start with a
|
||||
* bencoded value or the stream isn't a bencoded stream at all.
|
||||
* @exception IOException when somthing bad happens with the stream
|
||||
* @throws IOException when somthing bad happens with the stream
|
||||
* to read from.
|
||||
*/
|
||||
public static BEValue bdecode(InputStream in) throws IOException
|
||||
|
@@ -23,13 +23,17 @@ package org.klomp.snark.bencode;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
public class BEncoder
|
||||
{
|
||||
|
||||
@@ -155,6 +159,12 @@ public class BEncoder
|
||||
out.write(bs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keys must be Strings or (supported as of 0.9.31) byte[]s
|
||||
* A mix in the same Map is not supported.
|
||||
*
|
||||
* @throws IllegalArgumentException if keys are not all Strings or all byte[]s
|
||||
*/
|
||||
public static byte[] bencode(Map<?, ?> m)
|
||||
{
|
||||
try
|
||||
@@ -169,31 +179,71 @@ public class BEncoder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keys must be Strings or (supported as of 0.9.31) byte[]s
|
||||
* A mix in the same Map is not supported.
|
||||
*
|
||||
* @throws IllegalArgumentException if keys are not all Strings or all byte[]s
|
||||
*/
|
||||
public static void bencode(Map<?, ?> m, OutputStream out)
|
||||
throws IOException, IllegalArgumentException
|
||||
{
|
||||
out.write('d');
|
||||
|
||||
// Keys must be sorted. XXX - But is this the correct order?
|
||||
Set<?> s = m.keySet();
|
||||
List<String> l = new ArrayList<String>(s.size());
|
||||
for (Object k : s) {
|
||||
// Keys must be Strings.
|
||||
if (String.class.isAssignableFrom(k.getClass()))
|
||||
l.add((String) k);
|
||||
else
|
||||
throw new IllegalArgumentException("Cannot bencode map: contains non-String key of type " + k.getClass());
|
||||
List<String> l = null;
|
||||
List<byte[]> b = null;
|
||||
try {
|
||||
for (Object k : s) {
|
||||
if (l != null) {
|
||||
l.add((String) k);
|
||||
} else if (b != null) {
|
||||
b.add((byte[]) k);
|
||||
} else if (String.class.isAssignableFrom(k.getClass())) {
|
||||
l = new ArrayList<String>(s.size());
|
||||
l.add((String) k);
|
||||
} else if (byte[].class.isAssignableFrom(k.getClass())) {
|
||||
b = new ArrayList<byte[]>(s.size());
|
||||
b.add((byte[]) k);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot bencode map: contains key of type " + k.getClass());
|
||||
}
|
||||
}
|
||||
} catch (ClassCastException cce) {
|
||||
throw new IllegalArgumentException("Cannot bencode map: mixed keys", cce);
|
||||
}
|
||||
Collections.sort(l);
|
||||
|
||||
Iterator<String> it = l.iterator();
|
||||
while(it.hasNext())
|
||||
{
|
||||
String key = it.next();
|
||||
bencode(key, out);
|
||||
bencode(m.get(key), out);
|
||||
}
|
||||
if (l != null) {
|
||||
// Keys must be sorted. XXX - This is not the correct order.
|
||||
// Spec says to sort by bytes, not lexically
|
||||
Collections.sort(l);
|
||||
for (String key : l) {
|
||||
bencode(key, out);
|
||||
bencode(m.get(key), out);
|
||||
}
|
||||
} else if (b != null) {
|
||||
// Works for arrays of equal lengths, otherwise is probably not
|
||||
// what the bittorrent spec intends.
|
||||
Collections.sort(b, new BAComparator());
|
||||
for (byte[] key : b) {
|
||||
bencode(key, out);
|
||||
bencode(m.get(key), out);
|
||||
}
|
||||
}
|
||||
|
||||
out.write('e');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorter arrays are less. See DataHelper.compareTo()
|
||||
* Works for arrays of equal lengths, otherwise is probably not
|
||||
* what the bittorrent spec intends.
|
||||
*
|
||||
* @since 0.9.31
|
||||
*/
|
||||
private static class BAComparator implements Comparator<byte[]>, Serializable {
|
||||
public int compare(byte[] l, byte[] r) {
|
||||
return DataHelper.compareTo(l, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
219
apps/i2psnark/java/src/org/klomp/snark/comments/Comment.java
Normal file
219
apps/i2psnark/java/src/org/klomp/snark/comments/Comment.java
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
*/
|
||||
package org.klomp.snark.comments;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* Store comments
|
||||
*
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public class Comment implements Comparable<Comment> {
|
||||
|
||||
private final String text, name;
|
||||
// seconds since 1/1/2005
|
||||
private final int time;
|
||||
private final byte rating;
|
||||
private final boolean byMe;
|
||||
private boolean hidden;
|
||||
private static final AtomicInteger _id = new AtomicInteger();
|
||||
private final int id = _id.incrementAndGet();
|
||||
|
||||
public static final int MAX_NAME_LEN = 32;
|
||||
// same as IRC, more or less
|
||||
private static final int MAX_TEXT_LEN = 512;
|
||||
private static final int BUCKET_SIZE = 10*60*1000;
|
||||
private static final long TIME_SHRINK = 1000L;
|
||||
// 1/1/2005
|
||||
private static final long TIME_OFFSET = 1104537600000L;
|
||||
|
||||
/**
|
||||
* My comment, now
|
||||
*
|
||||
* @param text may be null, will be truncated to max length, newlines replaced with spaces
|
||||
* @param name may be null, will be truncated to max length, newlines and commas removed
|
||||
* @param rating 0-5
|
||||
*/
|
||||
public Comment(String text, String name, int rating) {
|
||||
this(text, name, rating, I2PAppContext.getGlobalContext().clock().now(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param text may be null, will be truncated to max length, newlines replaced with spaces
|
||||
* @param name may be null, will be truncated to max length, newlines and commas removed
|
||||
* @param time java time (ms)
|
||||
* @param rating 0-5
|
||||
*/
|
||||
public Comment(String text, String name, int rating, long time, boolean isMine) {
|
||||
if (text != null) {
|
||||
text = text.trim();
|
||||
text = text.replaceAll("[\r\n]", " ");
|
||||
if (text.length() == 0)
|
||||
text = null;
|
||||
else if (text.length() > MAX_TEXT_LEN)
|
||||
text = text.substring(0, MAX_TEXT_LEN);
|
||||
}
|
||||
this.text = text;
|
||||
if (name != null) {
|
||||
name = name.trim();
|
||||
// comma because it's not last in the persistent string
|
||||
name = name.replaceAll("[,\r\n]", "");
|
||||
if (name.length() == 0)
|
||||
name = null;
|
||||
else if (name.length() > MAX_NAME_LEN)
|
||||
name = name.substring(0, MAX_NAME_LEN);
|
||||
}
|
||||
this.name = name;
|
||||
if (rating < 0)
|
||||
rating = 0;
|
||||
else if (rating > 5)
|
||||
rating = 5;
|
||||
this.rating = (byte) rating;
|
||||
if (time < TIME_OFFSET) {
|
||||
time = TIME_OFFSET;
|
||||
} else {
|
||||
long now = I2PAppContext.getGlobalContext().clock().now();
|
||||
if (time > now)
|
||||
time = now;
|
||||
}
|
||||
this.time = (int) ((time - TIME_OFFSET) / TIME_SHRINK);
|
||||
this.byMe = isMine;
|
||||
}
|
||||
|
||||
public String getText() { return text; }
|
||||
|
||||
public String getName() { return name; }
|
||||
|
||||
public int getRating() { return rating; }
|
||||
|
||||
/** java time (ms) */
|
||||
public long getTime() { return (time * TIME_SHRINK) + TIME_OFFSET; }
|
||||
|
||||
public boolean isMine() { return byMe; }
|
||||
|
||||
public boolean isHidden() { return hidden; }
|
||||
|
||||
void setHidden() { hidden = true; }
|
||||
|
||||
/**
|
||||
* A unique ID that may be used to delete this comment from
|
||||
* the CommentSet via remove(int). NOT persisted across restarts.
|
||||
*/
|
||||
public int getID() { return id; }
|
||||
|
||||
/**
|
||||
* reverse
|
||||
*/
|
||||
public int compareTo(Comment c) {
|
||||
if (time > c.time)
|
||||
return -1;
|
||||
if (time < c.time)
|
||||
return 1;
|
||||
// arbitrary sort below here
|
||||
if (rating != c.rating)
|
||||
return c.rating - rating;
|
||||
if (name != null || c.name != null) {
|
||||
if (name == null)
|
||||
return 1;
|
||||
if (c.name == null)
|
||||
return -1;
|
||||
int rv = name.compareTo(c.name);
|
||||
if (rv != 0)
|
||||
return rv;
|
||||
}
|
||||
if (text != null || c.text != null) {
|
||||
if (text == null)
|
||||
return 1;
|
||||
if (c.text == null)
|
||||
return -1;
|
||||
int rv = text.compareTo(c.text);
|
||||
if (rv != 0)
|
||||
return rv;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return time,rating,mine,hidden,name,text
|
||||
*/
|
||||
public String toPersistentString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(getTime());
|
||||
buf.append(',');
|
||||
buf.append(Byte.toString(rating));
|
||||
buf.append(',');
|
||||
buf.append(byMe ? "1" : "0");
|
||||
buf.append(',');
|
||||
buf.append(hidden ? "1" : "0");
|
||||
buf.append(',');
|
||||
if (name != null)
|
||||
buf.append(name);
|
||||
buf.append(',');
|
||||
if (text != null)
|
||||
buf.append(text);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null if can't be parsed
|
||||
*/
|
||||
public static Comment fromPersistentString(String s) {
|
||||
String[] ss = DataHelper.split(s, ",", 6);
|
||||
if (ss.length != 6)
|
||||
return null;
|
||||
try {
|
||||
long t = Long.parseLong(ss[0]);
|
||||
int r = Integer.parseInt(ss[1]);
|
||||
boolean m = !ss[2].equals("0");
|
||||
boolean h = !ss[3].equals("0");
|
||||
Comment rv = new Comment(ss[5], ss[4], r, t, m);
|
||||
if (h)
|
||||
rv.setHidden();
|
||||
return rv;
|
||||
} catch (NumberFormatException nfe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return time / (BUCKET_SIZE / (int) TIME_SHRINK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comments in the same 10-minute bucket and otherwise equal
|
||||
* are considered equal. This will result in duplicates
|
||||
* near the border.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null) return false;
|
||||
if (!(o instanceof Comment)) return false;
|
||||
Comment c = (Comment) o;
|
||||
return rating == c.rating &&
|
||||
eq(text, c.text) &&
|
||||
eq(name, c.name) &&
|
||||
hashCode() == c.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignores timestamp
|
||||
* @param c non-null
|
||||
*/
|
||||
public boolean equalsIgnoreTimestamp(Comment c) {
|
||||
return rating == c.rating &&
|
||||
eq(text, c.text) &&
|
||||
eq(name, c.name);
|
||||
}
|
||||
|
||||
private static boolean eq(String lhs, String rhs) {
|
||||
return (lhs == null && rhs == null) || (lhs != null && lhs.equals(rhs));
|
||||
}
|
||||
}
|
354
apps/i2psnark/java/src/org/klomp/snark/comments/CommentSet.java
Normal file
354
apps/i2psnark/java/src/org/klomp/snark/comments/CommentSet.java
Normal file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* Released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
*/
|
||||
package org.klomp.snark.comments;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
|
||||
/**
|
||||
* Store comments.
|
||||
*
|
||||
* Optimized for fast checking of duplicates, and retrieval of ratings.
|
||||
* Removes are not really removed, only marked as hidden, so
|
||||
* they don't reappear.
|
||||
* Duplicates are detected based on an approximate time range.
|
||||
* Max size of both elements and total text length is enforced.
|
||||
*
|
||||
* Supports persistence via save() and File constructor.
|
||||
*
|
||||
* NOT THREAD SAFE except for iterating AFTER the iterator() call.
|
||||
*
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public class CommentSet extends AbstractSet<Comment> {
|
||||
|
||||
private final HashMap<Integer, List<Comment>> map;
|
||||
private int size, realSize;
|
||||
private int myRating;
|
||||
private int totalRating;
|
||||
private int ratingSize;
|
||||
private int totalTextSize;
|
||||
private long latestCommentTime;
|
||||
private boolean modified;
|
||||
|
||||
public static final int MAX_SIZE = 256;
|
||||
|
||||
// Comment.java enforces max text length of 512, but
|
||||
// we don't want 256*512 in memory per-torrent, so
|
||||
// track and enforce separately.
|
||||
// Assume most comments are short or null.
|
||||
private static final int MAX_TOTAL_TEXT_LEN = MAX_SIZE * 16;
|
||||
|
||||
public CommentSet() {
|
||||
super();
|
||||
map = new HashMap<Integer, List<Comment>>(4);
|
||||
}
|
||||
|
||||
public CommentSet(Collection<Comment> coll) {
|
||||
super();
|
||||
map = new HashMap<Integer, List<Comment>>(coll.size());
|
||||
addAll(coll);
|
||||
}
|
||||
|
||||
/**
|
||||
* File must be gzipped.
|
||||
* Need not be sorted.
|
||||
* See Comment.toPersistentString() for format.
|
||||
*/
|
||||
public CommentSet(File file) throws IOException {
|
||||
this();
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
br = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(file)), "UTF-8"));
|
||||
String line = null;
|
||||
while ( (line = br.readLine()) != null) {
|
||||
Comment c = Comment.fromPersistentString(line);
|
||||
if (c != null)
|
||||
add(c);
|
||||
}
|
||||
} finally {
|
||||
if (br != null) try { br.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
modified = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* File will be gzipped.
|
||||
* Not sorted, includes hidden.
|
||||
* See Comment.toPersistentString() for format.
|
||||
* Sets isModified() to false.
|
||||
*/
|
||||
public void save(File file) throws IOException {
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
out = new PrintWriter(new OutputStreamWriter(new GZIPOutputStream(new SecureFileOutputStream(file)), "UTF-8"));
|
||||
for (List<Comment> l : map.values()) {
|
||||
for (Comment c : l) {
|
||||
out.println(c.toPersistentString());
|
||||
}
|
||||
}
|
||||
if (out.checkError())
|
||||
throw new IOException("Failed write to " + file);
|
||||
modified = false;
|
||||
} finally {
|
||||
if (out != null) out.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Max length for strings enforced in Comment.java.
|
||||
* Max total length for strings enforced here.
|
||||
* Enforces max size for set
|
||||
*/
|
||||
@Override
|
||||
public boolean add(Comment c) {
|
||||
if (realSize >= MAX_SIZE && !c.isMine())
|
||||
return false;
|
||||
String s = c.getText();
|
||||
if (s != null && totalTextSize + s.length() > MAX_TOTAL_TEXT_LEN)
|
||||
return false;
|
||||
// If isMine and no text and rating changed, don't bother
|
||||
if (c.isMine() && c.getText() == null && c.getRating() == myRating)
|
||||
return false;
|
||||
Integer hc = Integer.valueOf(c.hashCode());
|
||||
List<Comment> list = map.get(hc);
|
||||
if (list == null) {
|
||||
list = Collections.singletonList(c);
|
||||
map.put(hc, list);
|
||||
addStats(c);
|
||||
return true;
|
||||
}
|
||||
if (list.contains(c))
|
||||
return false;
|
||||
if (list.size() == 1) {
|
||||
// presume unmodifiable singletonList
|
||||
List<Comment> nlist = new ArrayList<Comment>(2);
|
||||
nlist.add(list.get(0));
|
||||
map.put(hc, nlist);
|
||||
list = nlist;
|
||||
}
|
||||
list.add(c);
|
||||
// If isMine and no text and comment changed, remove old ones
|
||||
if (c.isMine() && c.getText() == null)
|
||||
removeMyOldRatings(c.getID());
|
||||
addStats(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only hides the comment, doesn't really remove it.
|
||||
* @return true if present and not previously hidden
|
||||
*/
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
if (o == null || !(o instanceof Comment))
|
||||
return false;
|
||||
Comment c = (Comment) o;
|
||||
Integer hc = Integer.valueOf(c.hashCode());
|
||||
List<Comment> list = map.get(hc);
|
||||
if (list == null)
|
||||
return false;
|
||||
int i = list.indexOf(c);
|
||||
if (i >= 0) {
|
||||
Comment cc = list.get(i);
|
||||
if (!cc.isHidden()) {
|
||||
removeStats(cc);
|
||||
cc.setHidden();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the id as retrieved from Comment.getID().
|
||||
* Only hides the comment, doesn't really remove it.
|
||||
* This is for the UI.
|
||||
*
|
||||
* @return true if present and not previously hidden
|
||||
*/
|
||||
public boolean remove(int id) {
|
||||
// not the most efficient but should be rare.
|
||||
for (List<Comment> l : map.values()) {
|
||||
for (Comment c : l) {
|
||||
if (c.getID() == id) {
|
||||
return remove(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all ratings of mine with empty comments,
|
||||
* except the ID specified.
|
||||
*/
|
||||
private void removeMyOldRatings(int exceptID) {
|
||||
for (List<Comment> l : map.values()) {
|
||||
for (Comment c : l) {
|
||||
if (c.isMine() && c.getText() == null && c.getID() != exceptID && !c.isHidden()) {
|
||||
removeStats(c);
|
||||
c.setHidden();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** may be hidden */
|
||||
private void addStats(Comment c) {
|
||||
realSize++;
|
||||
if (!c.isHidden()) {
|
||||
size++;
|
||||
int r = c.getRating();
|
||||
if (r > 0) {
|
||||
if (c.isMine()) {
|
||||
myRating = r;
|
||||
} else {
|
||||
totalRating += r;
|
||||
ratingSize++;
|
||||
}
|
||||
}
|
||||
long time = c.getTime();
|
||||
if (time > latestCommentTime)
|
||||
latestCommentTime = time;
|
||||
}
|
||||
String t = c.getText();
|
||||
if (t != null)
|
||||
totalTextSize += t.length();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
/** call before setting hidden */
|
||||
private void removeStats(Comment c) {
|
||||
if (!c.isHidden()) {
|
||||
size--;
|
||||
int r = c.getRating();
|
||||
if (r > 0) {
|
||||
if (c.isMine()) {
|
||||
if (myRating == r)
|
||||
myRating = 0;
|
||||
} else {
|
||||
totalRating -= r;
|
||||
ratingSize--;
|
||||
}
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is not adjusted if the latest comment wasn't hidden but is then hidden.
|
||||
* @return the timestamp of the most recent non-hidden comment
|
||||
*/
|
||||
public long getLatestCommentTime() { return latestCommentTime; }
|
||||
|
||||
/**
|
||||
* @return true if modified since instantiation
|
||||
*/
|
||||
public boolean isModified() { return modified; }
|
||||
|
||||
/**
|
||||
* @return 0 if none, or 1-5
|
||||
*/
|
||||
public int getMyRating() { return myRating; }
|
||||
|
||||
/**
|
||||
* @return Number of ratings making up the average rating
|
||||
*/
|
||||
public int getRatingCount() { return ratingSize; }
|
||||
|
||||
/**
|
||||
* @return 0 if none, or 1-5
|
||||
*/
|
||||
public double getAverageRating() {
|
||||
if (ratingSize <= 0)
|
||||
return 0.0d;
|
||||
return totalRating / (double) ratingSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually clears everything, including hidden.
|
||||
* Resets ratings to zero.
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
if (realSize > 0) {
|
||||
modified = true;
|
||||
realSize = 0;
|
||||
map.clear();
|
||||
size = 0;
|
||||
myRating = 0;
|
||||
totalRating = 0;
|
||||
ratingSize = 0;
|
||||
totalTextSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* May be more than what the iterator returns,
|
||||
* we do additional deduping in the iterator.
|
||||
*
|
||||
* @return the non-hidden size
|
||||
*/
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be in reverse-sort order, i.e. newest-first.
|
||||
* The returned iterator is thread-safe after this call.
|
||||
* Changes after this call will not be reflected in the iterator.
|
||||
* iter.remove() has no effect on the underlying set.
|
||||
* Hidden comments not included.
|
||||
*
|
||||
* Returned values may be less than indicated in size()
|
||||
* due to additional deduping in the iterator.
|
||||
*/
|
||||
public Iterator<Comment> iterator() {
|
||||
if (size <= 0)
|
||||
return Collections.<Comment>emptyList().iterator();
|
||||
List<Comment> list = new ArrayList<Comment>(size);
|
||||
for (List<Comment> l : map.values()) {
|
||||
int hc = l.get(0).hashCode();
|
||||
List<Comment> prevList = map.get(Integer.valueOf(hc - 1));
|
||||
for (Comment c : l) {
|
||||
if (!c.isHidden()) {
|
||||
// additional deduping at boundary
|
||||
if (prevList != null) {
|
||||
boolean dup = false;
|
||||
for (Comment pc : prevList) {
|
||||
if (c.equalsIgnoreTimestamp(pc)) {
|
||||
dup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dup)
|
||||
continue;
|
||||
}
|
||||
list.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(list);
|
||||
return list.iterator();
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
Data structures to support ut_comment, since 0.9.31.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
@@ -42,7 +42,7 @@ public interface DHT {
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param max maximum number of peers to return
|
||||
* @param maxWait the maximum time to wait (ms) must be > 0
|
||||
* @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
|
||||
@@ -81,7 +81,7 @@ public interface DHT {
|
||||
|
||||
/**
|
||||
* Announce to the closest DHT peers.
|
||||
* Blocking unless maxWait <= 0
|
||||
* Blocking unless maxWait <= 0
|
||||
* Caller should run in a thread.
|
||||
* This also automatically announces ourself to our local tracker.
|
||||
* For best results do a getPeers() first so we have tokens.
|
||||
|
@@ -138,7 +138,7 @@ class DHTNodes {
|
||||
* @since 0.9.4
|
||||
*/
|
||||
public void renderStatusHTML(StringBuilder buf) {
|
||||
buf.append(_kad.toString().replace("\n", "<br>\n"));
|
||||
buf.append(_kad.toString().replace("\n", "<br><hr class=\"debug\">\n"));
|
||||
}
|
||||
|
||||
/** */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user