forked from I2P_Developers/i2p.i2p
Compare commits
452 Commits
i2p-0.9.29
...
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 | |||
![]() |
50450ecba6 | ||
![]() |
44c0d9357b | ||
![]() |
42ba5fa509 | ||
![]() |
24b2bb8e3f | ||
![]() |
026a9cf026 | ||
![]() |
87c8031e5a | ||
![]() |
bff3f0436c | ||
![]() |
bfe8c65628 | ||
![]() |
a22bf6b4a4 | ||
![]() |
c4e7b1a799 | ||
![]() |
232d0b5e77 | ||
![]() |
82095d9786 | ||
![]() |
f68967c5f2 | ||
![]() |
9898a4a930 | ||
![]() |
092881acc4 | ||
![]() |
b1ed295e26 | ||
1a28a2af23 | |||
c66559ff84 | |||
![]() |
6308038a08 | ||
![]() |
e141abb5de | ||
![]() |
94c6496f9d | ||
![]() |
d360ba4817 | ||
![]() |
cca3f57373 | ||
![]() |
57e51ab8c5 | ||
![]() |
93511c0e61 | ||
![]() |
0a0757a69f | ||
![]() |
14bacc272e | ||
![]() |
2bc5630832 | ||
![]() |
ecb9726ba4 | ||
![]() |
894186e975 | ||
![]() |
053ebd7079 | ||
![]() |
fabc57b57a | ||
![]() |
04b9b202e5 | ||
![]() |
acec99ff43 | ||
![]() |
e81a01d3e8 | ||
![]() |
7101fb0997 | ||
![]() |
6b7ac9fba6 | ||
![]() |
0badcf63aa | ||
![]() |
bd69499f8f | ||
![]() |
23f835781c | ||
994b51b5b1 | |||
![]() |
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 |
32
.tx/config
32
.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
|
||||
@@ -401,10 +416,15 @@ 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]
|
||||
|
@@ -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
|
||||
|
87
README.md
87
README.md
@@ -1,51 +1,62 @@
|
||||
Prerequisites to build from source:
|
||||
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.
|
||||
# I2P
|
||||
|
||||
To build:
|
||||
On x86 systems do:
|
||||
ant pkg
|
||||
This is the source code for the reference Java implementation of I2P.
|
||||
|
||||
On non-x86, use one of the following instead:
|
||||
ant installer-linux
|
||||
ant installer-freebsd
|
||||
ant installer-osx
|
||||
Latest release: https://geti2p.net/download
|
||||
|
||||
Run 'ant' with no arguments to see other build options.
|
||||
See INSTALL.txt or https://geti2p.net/download for installation instructions.
|
||||
## Installing
|
||||
|
||||
Documentation:
|
||||
https://geti2p.net/how
|
||||
API: http://docs.i2p-projekt.de/javadoc/
|
||||
or run 'ant javadoc' then start at build/javadoc/index.html
|
||||
See INSTALL.txt or https://geti2p.net/download for installation instructions.
|
||||
|
||||
Latest release:
|
||||
https://geti2p.net/download
|
||||
## Documentation
|
||||
|
||||
To get development branch from source control:
|
||||
https://geti2p.net/newdevelopers
|
||||
https://geti2p.net/how
|
||||
|
||||
FAQ:
|
||||
https://geti2p.net/faq
|
||||
FAQ: https://geti2p.net/faq
|
||||
|
||||
Need help?
|
||||
IRC irc.freenode.net #i2p
|
||||
http://forum.i2p/
|
||||
API: http://docs.i2p-projekt.de/javadoc/
|
||||
or run 'ant javadoc' then start at build/javadoc/index.html
|
||||
|
||||
Bug reports:
|
||||
https://trac.i2p2.de/report/1
|
||||
## Building packages from source
|
||||
|
||||
Contact information, security issues, press inquiries:
|
||||
https://geti2p.net/en/contact
|
||||
To get development branch from source control: https://geti2p.net/newdevelopers
|
||||
|
||||
Twitter:
|
||||
@i2p, @geti2p
|
||||
### Prerequisites
|
||||
|
||||
Licenses:
|
||||
See LICENSE.txt
|
||||
- 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
|
||||
|
@@ -45,7 +45,7 @@ public class MUXlisten implements Runnable {
|
||||
private ThreadGroup tg;
|
||||
private final String N;
|
||||
private ServerSocket listener;
|
||||
private final int backlog = 50; // should this be more? less?
|
||||
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;
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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"
|
||||
|
@@ -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: 2017-01-30 03:06+0000\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"
|
||||
|
@@ -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,7 +8,8 @@
|
||||
# Boxoa590, 2013
|
||||
# ducki2p <ducki2p@gmail.com>, 2011
|
||||
# foo <foo@bar>, 2009
|
||||
# French language coordinator <french.translation@rbox.me>, 2017
|
||||
# French language coordinator <french.coordinator@rbox.me>, 2017
|
||||
# French language coordinator <french.coordinator@rbox.me>, 2017
|
||||
# Boxoa590, 2013
|
||||
# Towinet, 2016
|
||||
msgid ""
|
||||
@@ -16,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: 2017-01-17 14:10+0000\n"
|
||||
"Last-Translator: French language coordinator <french.translation@rbox.me>\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"
|
||||
@@ -43,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
|
||||
@@ -78,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
|
||||
@@ -97,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
|
||||
|
@@ -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-12-09 22:59+0000\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"
|
||||
|
@@ -6,14 +6,14 @@
|
||||
# Translators:
|
||||
# 123hund123 <M8R-ra4r1r@mailinator.com>, 2011
|
||||
# Anders Nilsson <anders@devode.se>, 2016
|
||||
# Jonatan Nyberg <jonatan@autistici.org>, 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-12-19 13:35+0000\n"
|
||||
"Last-Translator: Anders Nilsson <anders@devode.se>\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"
|
||||
@@ -39,7 +39,7 @@ 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
|
||||
|
@@ -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" />
|
||||
|
@@ -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;
|
||||
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -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.
|
||||
*
|
||||
@@ -237,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
|
||||
@@ -471,6 +480,9 @@ public class Snark
|
||||
*/
|
||||
|
||||
savedUploaded = (completeListener != null) ? completeListener.getSavedUploaded(this) : 0;
|
||||
if (completeListener != null)
|
||||
_comments = completeListener.getSavedComments(this);
|
||||
|
||||
if (start)
|
||||
startTorrent();
|
||||
}
|
||||
@@ -542,6 +554,7 @@ public class Snark
|
||||
starting = true;
|
||||
try {
|
||||
x_startTorrent();
|
||||
_startedTime = _util.getContext().clock().now();
|
||||
} finally {
|
||||
starting = false;
|
||||
}
|
||||
@@ -633,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)
|
||||
@@ -1218,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1285,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)
|
||||
@@ -1295,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)
|
||||
@@ -1364,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;
|
||||
@@ -45,13 +47,15 @@ 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.
|
||||
@@ -108,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
|
||||
@@ -124,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";
|
||||
@@ -131,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;
|
||||
@@ -168,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 */
|
||||
@@ -188,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 {
|
||||
@@ -246,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);
|
||||
@@ -315,6 +341,12 @@ 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");
|
||||
}
|
||||
@@ -322,6 +354,46 @@ public class SnarkManager implements CompleteListener {
|
||||
/** @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; }
|
||||
|
||||
@@ -332,7 +404,7 @@ public class SnarkManager implements CompleteListener {
|
||||
* Escapes '<' and '>' before queueing
|
||||
*/
|
||||
public void addMessage(String message) {
|
||||
addMessageNoEscape(message.replace("<", "<").replace(">", ">"));
|
||||
addMessageNoEscape(message.replace("&", "&").replace("<", "<").replace(">", ">"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -384,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);
|
||||
@@ -440,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,
|
||||
@@ -591,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
|
||||
@@ -612,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);
|
||||
@@ -667,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
|
||||
@@ -720,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;
|
||||
}
|
||||
@@ -734,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);
|
||||
@@ -768,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 {
|
||||
@@ -782,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) {
|
||||
@@ -849,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) {}
|
||||
@@ -903,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);
|
||||
@@ -966,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)) {
|
||||
@@ -1075,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)
|
||||
@@ -1305,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());
|
||||
@@ -1526,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
|
||||
@@ -1542,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
|
||||
@@ -1745,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
|
||||
@@ -1753,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1776,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());
|
||||
@@ -1806,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) {
|
||||
@@ -1865,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())
|
||||
@@ -2097,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)
|
||||
@@ -2471,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) {
|
||||
@@ -2485,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) {
|
||||
@@ -2532,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();
|
||||
}
|
||||
@@ -2555,8 +2820,13 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2588,6 +2858,15 @@ public class SnarkManager implements CompleteListener {
|
||||
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()) {
|
||||
@@ -2595,17 +2874,18 @@ 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) {
|
||||
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);
|
||||
}
|
||||
_util.disconnect();
|
||||
} else {
|
||||
_util.disconnect();
|
||||
_stopping = false;
|
||||
|
@@ -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
|
||||
|
@@ -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) {
|
||||
|
@@ -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>
|
@@ -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"));
|
||||
}
|
||||
|
||||
/** */
|
||||
|
@@ -140,7 +140,7 @@ class DHTTracker {
|
||||
* Debug info, HTML formatted
|
||||
*/
|
||||
public void renderStatusHTML(StringBuilder buf) {
|
||||
buf.append("DHT tracker: ").append(_torrentCount).append(" torrents ")
|
||||
buf.append("<b>DHT tracker:</b> ").append(_torrentCount).append(" torrents ")
|
||||
.append(_peerCount).append(" peers ")
|
||||
.append(DataHelper.formatDuration(_expireTime)).append(" expiration<br>");
|
||||
}
|
||||
|
@@ -682,17 +682,17 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
public String renderStatusHTML() {
|
||||
long uptime = Math.max(1000, _context.clock().now() - _started);
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
buf.append("<br><b>DHT DEBUG</b><br>TX: ").append(_txPkts.get()).append(" pkts / ")
|
||||
buf.append("<br><hr class=\"debug\"><b>DHT DEBUG</b><br><hr class=\"debug\"><hr><b>TX:</b> ").append(_txPkts.get()).append(" pkts / ")
|
||||
.append(DataHelper.formatSize2(_txBytes.get())).append("B / ")
|
||||
.append(DataHelper.formatSize2(_txBytes.get() * 1000 / uptime)).append("Bps<br>" +
|
||||
"RX: ").append(_rxPkts.get()).append(" pkts / ")
|
||||
"<b>RX:</b> ").append(_rxPkts.get()).append(" pkts / ")
|
||||
.append(DataHelper.formatSize2(_rxBytes.get())).append("B / ")
|
||||
.append(DataHelper.formatSize2(_rxBytes.get() * 1000 / uptime)).append("Bps<br>" +
|
||||
"DHT Peers: ").append( _knownNodes.size()).append("<br>" +
|
||||
"Blacklisted: ").append(_blacklist.size()).append("<br>" +
|
||||
"Sent tokens: ").append(_outgoingTokens.size()).append("<br>" +
|
||||
"Rcvd tokens: ").append(_incomingTokens.size()).append("<br>" +
|
||||
"Pending queries: ").append(_sentQueries.size()).append("<br>");
|
||||
"<b>DHT Peers:</b> ").append( _knownNodes.size()).append("<br>" +
|
||||
"<b>Blacklisted:</b> ").append(_blacklist.size()).append("<br>" +
|
||||
"<b>Sent tokens:</b> ").append(_outgoingTokens.size()).append("<br>" +
|
||||
"<b>Rcvd tokens:</b> ").append(_incomingTokens.size()).append("<br>" +
|
||||
"<b>Pending queries:</b> ").append(_sentQueries.size()).append("<br><br><hr class=\"debug\">");
|
||||
_tracker.renderStatusHTML(buf);
|
||||
_knownNodes.renderStatusHTML(buf);
|
||||
return buf.toString();
|
||||
@@ -846,6 +846,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* @param nInfo who to send it to
|
||||
* @return success
|
||||
*/
|
||||
/****
|
||||
private boolean sendError(NodeInfo nInfo, MsgID msgID, int err, String msg) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending error " + msg + " to: " + nInfo);
|
||||
@@ -856,6 +857,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
map.put("e", error);
|
||||
return sendError(nInfo, msgID, map);
|
||||
}
|
||||
****/
|
||||
|
||||
// Low-level send methods
|
||||
|
||||
@@ -1422,6 +1424,11 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
List<Hash> rv = new ArrayList<Hash>(max);
|
||||
for (BEValue bev : peers) {
|
||||
byte[] b = bev.getBytes();
|
||||
if (b.length != Hash.HASH_LENGTH) {
|
||||
if (_log.shouldWarn())
|
||||
_log.info("Bad peers entry from: " + nInfo);
|
||||
continue;
|
||||
}
|
||||
//Hash h = new Hash(b);
|
||||
Hash h = Hash.create(b);
|
||||
rv.add(h);
|
||||
|
@@ -106,11 +106,11 @@ class NodeInfo extends SimpleDataStructure {
|
||||
if (parts.length != 4)
|
||||
throw new DataFormatException("Bad format");
|
||||
byte[] nid = Base64.decode(parts[0]);
|
||||
if (nid == null)
|
||||
if (nid == null || nid.length != NID.HASH_LENGTH)
|
||||
throw new DataFormatException("Bad NID");
|
||||
nID = new NID(nid);
|
||||
byte[] h = Base64.decode(parts[1]);
|
||||
if (h == null)
|
||||
if (h == null || h.length != Hash.HASH_LENGTH)
|
||||
throw new DataFormatException("Bad hash");
|
||||
//hash = new Hash(h);
|
||||
hash = Hash.create(h);
|
||||
|
@@ -24,41 +24,40 @@ public class ConfigUIHelper {
|
||||
* added to the top-level build.xml for the updater.
|
||||
* As of 0.9.12, ISO 639-2 three-letter codes are supported also.
|
||||
*
|
||||
* Note: we don't currently _x the language strings,
|
||||
* we'll just rely on the JVM's translations for now.
|
||||
* Country flag unused.
|
||||
*/
|
||||
private static final String langs[][] = {
|
||||
{ "ar", "lang_ar", "Arabic", null },
|
||||
{ "cs", "cz", "Czech", null },
|
||||
//{ "da", "dk", "Danish", null },
|
||||
{ "de", "de", "German", null },
|
||||
//{ "et", "ee", "Estonian", null },
|
||||
//{ "el", "gr", "Greek", null },
|
||||
{ "ar", "lang_ar", "Arabic ﻉﺮﺒﻳﺓ", null },
|
||||
{ "cs", "cz", "Čeština", null },
|
||||
{ "zh", "cn", "Chinese 中文", null },
|
||||
//{ "zh_TW", "tw", "Chinese 中文", "Taiwan" },
|
||||
//{ "da", "dk", "Dansk", null },
|
||||
{ "de", "de", "Deutsch", null },
|
||||
//{ "et", "ee", "Eesti", null },
|
||||
{ "en", "us", "English", null },
|
||||
{ "es", "es", "Spanish", null },
|
||||
{ "fi", "fi", "Finnish", null },
|
||||
{ "fr", "fr", "French", null },
|
||||
//{ "gl", "lang_gl", "Galician", null },
|
||||
{ "hu", "hu", "Hungarian", null },
|
||||
{ "it", "it", "Italian", null },
|
||||
{ "ja", "jp", "Japanese", null },
|
||||
{ "ko", "kr", "Korean", null },
|
||||
{ "es", "es", "Español", null },
|
||||
{ "fr", "fr", "Français", null },
|
||||
//{ "gl", "lang_gl", "Galego", null },
|
||||
//{ "el", "gr", "Greek Ελληνικά", null },
|
||||
{ "in", "id", "bahasa Indonesia", null },
|
||||
{ "it", "it", "Italiano", null },
|
||||
{ "ja", "jp", "Japanese 日本語", null },
|
||||
{ "ko", "kr", "Korean 한국어", null },
|
||||
//{ "mg", "mg", "Malagasy", null },
|
||||
{ "nl", "nl", "Dutch", null },
|
||||
{ "nb", "no", "Norwegian Bokmaal", null },
|
||||
{ "pl", "pl", "Polish", null },
|
||||
{ "pt", "pt", "Portuguese", null },
|
||||
{ "pt_BR", "br", "Portuguese", "Brazil" },
|
||||
{ "ro", "ro", "Romanian", null },
|
||||
{ "ru", "ru", "Russian", null },
|
||||
{ "sk", "sk", "Slovak", null },
|
||||
{ "sv", "se", "Swedish", null },
|
||||
{ "tr", "tr", "Turkish", null },
|
||||
{ "uk", "ua", "Ukrainian", null },
|
||||
{ "vi", "vn", "Vietnamese", null },
|
||||
{ "zh", "cn", "Chinese", null },
|
||||
//{ "zh_TW", "tw", "Chinese", "Taiwan" },
|
||||
{ "hu", "hu", "Magyar", null },
|
||||
{ "nl", "nl", "Nederlands", null },
|
||||
{ "nb", "no", "Norsk (bokmål)", null },
|
||||
{ "pl", "pl", "Polski", null },
|
||||
{ "pt", "pt", "Português", null },
|
||||
{ "pt_BR", "br", "Português", "Brazil" },
|
||||
{ "ro", "ro", "Română", null },
|
||||
{ "ru", "ru", "Russian Русский", null },
|
||||
{ "sk", "sk", "Slovenčina", null },
|
||||
{ "fi", "fi", "Suomi", null },
|
||||
{ "sv", "se", "Svenska", null },
|
||||
{ "tr", "tr", "Türkçe", null },
|
||||
{ "uk", "ua", "Ukrainian Українська", null },
|
||||
{ "vi", "vn", "Vietnamese Tiếng Việt", null },
|
||||
{ "xx", "a1", "Debug: Find untagged strings", null },
|
||||
};
|
||||
|
||||
@@ -107,16 +106,11 @@ public class ConfigUIHelper {
|
||||
buf.append("value=\"").append(lang).append("\">");
|
||||
int under = lang.indexOf('_');
|
||||
String slang = (under > 0) ? lang.substring(0, under) : lang;
|
||||
// we don't actually have translations for these, see above
|
||||
buf.append(Translate.getDisplayLanguage(slang, langs[i][2], ctx, BUNDLE_NAME));
|
||||
buf.append(langs[i][2]);
|
||||
String name = langs[i][3];
|
||||
if (name != null) {
|
||||
String cou = (under > 0) ? lang.substring(under + 1) : lang;
|
||||
Locale cur = new Locale(current);
|
||||
Locale loc = new Locale(slang, cou);
|
||||
buf.append(" (")
|
||||
//.append(Translate.getString(name, ctx, COUNTRY_BUNDLE_NAME))
|
||||
.append(loc.getDisplayCountry(cur))
|
||||
.append(name)
|
||||
.append(')');
|
||||
}
|
||||
buf.append("</option>\n");
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -22,15 +22,24 @@
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
|
||||
<Call name="addConnector">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
|
||||
<New class="org.eclipse.jetty.server.ServerConnector">
|
||||
<Arg><Ref id="Server" /></Arg>
|
||||
<Arg type="int">1</Arg> <!-- number of acceptors -->
|
||||
<Arg type="int">0</Arg> <!-- default number of selectors -->
|
||||
<Arg>
|
||||
<Array type="org.eclipse.jetty.server.ConnectionFactory"> <!-- varargs so we need an array -->
|
||||
<Item>
|
||||
<New class="org.eclipse.jetty.server.HttpConnectionFactory">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.HttpConfiguration" />
|
||||
</Arg>
|
||||
</New>
|
||||
</Item>
|
||||
</Array>
|
||||
</Arg>
|
||||
<Set name="host">127.0.0.1</Set>
|
||||
<Set name="port">8002</Set>
|
||||
<Set name="maxIdleTime">600000</Set>
|
||||
<Set name="Acceptors">1</Set>
|
||||
<Set name="statsOn">false</Set>
|
||||
<Set name="lowResourcesConnections">5000</Set>
|
||||
<Set name="lowResourcesMaxIdleTime">5000</Set>
|
||||
<Set name="useDirectBuffers">false</Set>
|
||||
<Set name="idleTimeout">600000</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
@@ -96,7 +105,7 @@
|
||||
<Ref id="DeploymentManager">
|
||||
<Call name="addAppProvider">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.deploy.providers.ContextProvider">
|
||||
<New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
|
||||
<Set name="monitoredDirName">./contexts</Set>
|
||||
<Set name="scanInterval">0</Set>
|
||||
</New>
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1683
apps/i2psnark/locale/messages_in.po
Normal file
1683
apps/i2psnark/locale/messages_in.po
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user