forked from I2P_Developers/i2p.i2p
Compare commits
632 Commits
i2p-1.5.0
...
programdat
Author | SHA1 | Date | |
---|---|---|---|
1c67f06ffc | |||
0ce816acd1 | |||
2fd01c8390 | |||
16eb1577f9 | |||
ecd58a833c | |||
b533ccaabd | |||
0f560139f3 | |||
b610b7a695 | |||
26f882edd5 | |||
f00e020f2b | |||
edd0dd0b0d | |||
031fa45eb8 | |||
a2eee5a673 | |||
45c160f27a | |||
7ba59b4338 | |||
554b17fe9a | |||
64632eed4a | |||
49299f3f28 | |||
00774590b0 | |||
52b640b582 | |||
911e69e3ae | |||
d809d6653d | |||
8df81fc0a1 | |||
ed76829562 | |||
4f4044c3f0 | |||
3a4bfc9c07 | |||
b25c207e9a | |||
fcae43547b | |||
b34b0cc399 | |||
f4875d12fa | |||
2f06e9bebf | |||
9b6dde008d | |||
6ddaa72a86 | |||
d064de1913 | |||
97013f8874 | |||
39954032d2 | |||
44f0acead5 | |||
6d024b49a9 | |||
8a379d5394 | |||
972adde7eb | |||
f3dd06ad1e | |||
55603782bb | |||
3d65f93277 | |||
e35d173982 | |||
ec6e9b1bfb | |||
ca6046f821 | |||
c81b4645a8 | |||
0687ef8364 | |||
8d149136fe | |||
2a83c8f6ee | |||
689016f48e | |||
146bbf67f8 | |||
b606c2084b | |||
c1efe6daab | |||
4bdb7d752f | |||
6d9aca9d9b | |||
e250531174 | |||
5948a7dec3 | |||
4e4718b3b1 | |||
d94ff6ad86 | |||
e4a8a6492d | |||
17695915fa | |||
69adf84363 | |||
af8272b970 | |||
5355ee7740 | |||
02cd83187b | |||
f5ca15c28d | |||
b7db6ae77c | |||
4aa6ee2308 | |||
21dc07bf5a | |||
4a6fe9b291 | |||
8aef6ce292 | |||
c9cf208de7 | |||
097fa34e91 | |||
c2ffcb8512 | |||
2432680ed3 | |||
8e985eb951 | |||
a18a5136b7 | |||
73c8ee826e | |||
2a4ab118de | |||
70596d0548 | |||
6ede06cfdf | |||
8818c78cbd | |||
9489b41cb7 | |||
9a9722dd3e | |||
ec3a88dc34 | |||
4e21fe74e5 | |||
9dc8ca4edb | |||
efc3b847ac | |||
3701cf9782 | |||
a171d71bbe | |||
a6ef362ad7 | |||
c2f3732469 | |||
454cd6bda6 | |||
740c7fad4d | |||
c875f1d105 | |||
43945dcbbf | |||
3c7659277d | |||
26cffabf86 | |||
897338b790 | |||
ac3fc6a0d8 | |||
20acd7cf61 | |||
08e95655c1 | |||
5d27ce3f8d | |||
1e95c123e5 | |||
a6536ea48a | |||
9aa9f7a714 | |||
723257d6ef | |||
c5323a8d36 | |||
67082463e4 | |||
b3072d9ef3 | |||
3c4956f0c3 | |||
d69a65605d | |||
2bf7d5ddea | |||
c2edd7e926 | |||
ed1e705d35 | |||
fa145ff7c5 | |||
7610b3842f | |||
d812f82e92 | |||
0a87559ba2 | |||
c248279a03 | |||
22efde37e3 | |||
c976b86b6e | |||
3ef06f88c9 | |||
2ec0894c35 | |||
d85d501291 | |||
e4996a2db6 | |||
ffc4520382 | |||
386b259666 | |||
ee5e3a97ed | |||
a70c988a07 | |||
788babeaa0 | |||
3befe90007 | |||
bfdf75f45b | |||
9de618d644 | |||
cdf778514c | |||
03f315fc1c | |||
379227592c | |||
3d9ded4ab1 | |||
29c827c076 | |||
5582814d7c | |||
43a5998cfd | |||
d1633938db | |||
5129665256 | |||
0d51b2d25f | |||
ba7b154a09 | |||
52bb2aea94 | |||
265804a750 | |||
74795a83d6 | |||
0fe437f649 | |||
a2ec81eaec | |||
df4be26878 | |||
06456fb318 | |||
d9c932ac2e | |||
d680cfd80c | |||
4a9f8240db | |||
266dc09384 | |||
4dd5e7737b | |||
af6722c57b | |||
2ff0a13990 | |||
cfdc2203fb | |||
2c53424210 | |||
8b0edde290 | |||
a4d3bf285d | |||
3acc4b754a | |||
ef08dfad55 | |||
b9099fbd99 | |||
c9a6950550 | |||
80f76e38b4 | |||
7ea3c430aa | |||
d467dc28b9 | |||
8ac78af293 | |||
e44eeaf5ae | |||
17cc514bb1 | |||
86996dde28 | |||
34789fdb30 | |||
a74f36358f | |||
a6ad525197 | |||
0826b1e228 | |||
5d3c29fb27 | |||
8256f61d56 | |||
ffc5ed8dc2 | |||
860eb22af7 | |||
1a050303fa | |||
ce0c84c5d9 | |||
b537bd18b0 | |||
de79f52c81 | |||
a5bc80da09 | |||
5f4ad87d10 | |||
cc56c4b506 | |||
13efc59916 | |||
515a9b8d0d | |||
52958b95d5 | |||
b36ed7de1a | |||
693aefe83a | |||
ed30da0f0f | |||
81255e19ae | |||
07dbab9f02 | |||
bf27bf140d | |||
3a906a6ce5 | |||
47e02f7930 | |||
75b808fcf6 | |||
5cbdb7d806 | |||
2c8428d85e | |||
8fe9d7ac06 | |||
db8a843e82 | |||
a730c1acea | |||
d6aaf3ee3a | |||
9e4063a161 | |||
008943f699 | |||
71a58cb1bf | |||
4a6f6f8647 | |||
32bba60bed | |||
0f3abf0c9e | |||
5c1b6ba703 | |||
5e6ec442a7 | |||
cdf5bec155 | |||
281bf68098 | |||
ce2b5639b5 | |||
4c10b414c4 | |||
4c15a0e1c4 | |||
203fbdaa9f | |||
9431b9cfdc | |||
2bec84dd88 | |||
cf7efcada8 | |||
dda25bf1ed | |||
04b1bdb453 | |||
e04d31eb04 | |||
706ed2ced2 | |||
bb7278c5f6 | |||
3edc235104 | |||
806046df64 | |||
345c64ebaf | |||
53eb6ba7e0 | |||
eaa526583d | |||
c0b8fe94a8 | |||
6e5049c1db | |||
949f619ecc | |||
f7c3e06db5 | |||
1a26e1789c | |||
e40bbc9f18 | |||
ec72f3cdf6 | |||
b19998c072 | |||
66e1c94bee | |||
0449589406 | |||
cc85df19aa | |||
8418bda5a5 | |||
75492514ca | |||
e43810f182 | |||
9d49dc7af7 | |||
680320dede | |||
b2d6a091d1 | |||
5d22a2152a | |||
acbf849b44 | |||
7cf9895908 | |||
d41c39a4d0 | |||
b1852c127b | |||
8c59c514b2 | |||
93493a6d15 | |||
03e5c6c13c | |||
826bf3c24f | |||
68273cd256 | |||
e130f85a65 | |||
9385ce7080 | |||
da190cc24f | |||
2a6dc58659 | |||
f4eda0551d | |||
0560b3221d | |||
631a6dd2b2 | |||
0154a87cbf | |||
4f8ad3b6cb | |||
458e980e2f | |||
e08d2f354a | |||
037e6940a9 | |||
b3055feff6 | |||
c520dcb0f6 | |||
fc88d672c5 | |||
c62884ef85 | |||
d9c629a6b1 | |||
70b7d1204b | |||
a90bbc3554 | |||
c2ec6cdeff | |||
f57abe84bd | |||
a1ee8220bb | |||
ce73b9e8da | |||
e6c3c097b5 | |||
8961009292 | |||
de27cb1a46 | |||
e8afbc5b92 | |||
587409daa7 | |||
af5019c8dd | |||
46bba0fe71 | |||
049456493f | |||
2f63762c80 | |||
ca0d9f5a26 | |||
aa620f5ed3 | |||
15bb157126 | |||
7e3e42ce42 | |||
981c5e3878 | |||
393ee71ad9 | |||
97736cef1c | |||
25af51faf9 | |||
a717dfb923 | |||
e594b9532c | |||
a7a5b06b5c | |||
eb72e97c03 | |||
25cdc988e1 | |||
3ce669575f | |||
63aa64f8bb | |||
a6f61d2bf6 | |||
9457271ce6 | |||
22512d3889 | |||
a7115263f0 | |||
c4b167b845 | |||
5e09245234 | |||
fee38c1c32 | |||
a1ced67bad | |||
87f7d470a6 | |||
32d60858da | |||
be4ad7eba9 | |||
7f8fa825ac | |||
1cd5926f6c | |||
9c31be66e6 | |||
0e89b07ae8 | |||
7b6e6270ba | |||
9d9310a726 | |||
0f26baf114 | |||
5ef93f11a9 | |||
ce53714ba1 | |||
50ce3c2856 | |||
21c1f89249 | |||
46ef49f2cf | |||
e53a59b4ac | |||
a13f2b9768 | |||
44c30e78fc | |||
ec63f41b27 | |||
c19944384e | |||
0c08a05bce | |||
7eda9c77af | |||
66045cebc2 | |||
2b93dbbf48 | |||
759f6968f6 | |||
e3db28542c | |||
e7f98e9243 | |||
4908f760d9 | |||
6bb3657de2 | |||
dc40755e7c | |||
140ab47354 | |||
da887f7c6c | |||
f4be99ecd0 | |||
b8407a261e | |||
969a8a5d8a | |||
b57d9f2f7e | |||
c9a97d889b | |||
51bdd9a283 | |||
04dd18615c | |||
265f5ee5df | |||
9c3f8602da | |||
74dedcf7f0 | |||
ae2b99b1c6 | |||
818ecc3563 | |||
5ccbeca676 | |||
a621f56c33 | |||
1513695768 | |||
63e202f8f0 | |||
80535875ad | |||
f0ad921fd2 | |||
63f3d88f78 | |||
9f7f1bbcab | |||
edc9d6fec5 | |||
a089156afc | |||
767a5043ab | |||
3b9c26fe8a | |||
961936f8d5 | |||
7ea31835c2 | |||
bf1f2e4635 | |||
198008472a | |||
91e9d95df7 | |||
1b5feda517 | |||
29f74ba72a | |||
d6684403a2 | |||
388aa233e0 | |||
ea92b79340 | |||
3ba754d723 | |||
d651d25de6 | |||
00ea32938e | |||
37002822b3 | |||
50d56ccbe8 | |||
48cb6f79ef | |||
a325e63426 | |||
309e306337 | |||
2ba56a5e17 | |||
c949ad5205 | |||
5621b4bf97 | |||
dbfe8d24a8 | |||
548c0994a7 | |||
d0ca1d38ca | |||
96560e8590 | |||
19712cfd95 | |||
3dcc954341 | |||
568b5e303f | |||
73d90ed5c4 | |||
b2fe36b0d3 | |||
632a1578a2 | |||
e28f4be46b | |||
73e34b3941 | |||
b4e2366458 | |||
1389e89f6d | |||
c3abe7b3d4 | |||
042c1e88aa | |||
899ce0f959 | |||
5dd8139aad | |||
13ee324d36 | |||
afa7278080 | |||
b6be2d7e65 | |||
78ba3d1f68 | |||
d930f0a64a | |||
c1dc3c8275 | |||
8bf87da4b1 | |||
8812e822f9 | |||
f17cd24dc8 | |||
b9f53069bb | |||
21f5f7c148 | |||
3057103875 | |||
2752015b6e | |||
f2e0aacbf0 | |||
3e8f8a2bd3 | |||
77e30e246d | |||
5e7a00ede4 | |||
ba55ec09ed | |||
0b058c0ffd | |||
61422d9f7f | |||
b63a2e41be | |||
606961c788 | |||
9573c6ed0f | |||
70bb63e8ab | |||
b96255a65b | |||
e15dae5c5f | |||
695cf8796d | |||
175f043819 | |||
662ea995c1 | |||
b8670e1e5b | |||
7f4441078d | |||
150248d8d7 | |||
aaa1da4c66 | |||
8167f5184d | |||
034a5acd37 | |||
7249f21602 | |||
d1192f74f2 | |||
13f910be70 | |||
2d42541b79 | |||
131da9bdb9 | |||
bc97e955e2 | |||
faa1bf117a | |||
aa386f3bdc | |||
f1170b948f | |||
59ab40779c | |||
85b9862b64 | |||
132d76a06b | |||
c4b4b2d4b2 | |||
db6914f555 | |||
bef729463d | |||
d0e72aca66 | |||
46ee8be9c6 | |||
cc3d2cf67b | |||
57c997730f | |||
0826f431ef | |||
c63cb378e8 | |||
242dc92397 | |||
26f34d6985 | |||
e002d3f558 | |||
3d5dd639e3 | |||
2bfedfbc74 | |||
70131c6b25 | |||
e946040ddd | |||
bab37e57fe | |||
70e06de846 | |||
11f60a7192 | |||
6282c365bb | |||
621ea49621 | |||
e51738d180 | |||
811442f9cb | |||
464a39b939 | |||
1a05083ed0 | |||
937b6120ff | |||
2a451cdb97 | |||
ccba4a197d | |||
feaff690a3 | |||
098ef9a0ff | |||
f317d29dd5 | |||
f17b568f19 | |||
5029516087 | |||
69699638ae | |||
e6c76fa5ae | |||
b8435f5e9e | |||
5995b0b7a7 | |||
80237a57bd | |||
c4cfe420a6 | |||
14c5d54f0e | |||
b1a4a8517e | |||
22ff40bc84 | |||
b5d7dffb08 | |||
a59cad0066 | |||
cbb6a6db65 | |||
730b9790d9 | |||
ebf6ca5b34 | |||
0422134a86 | |||
1a77352fa7 | |||
cc971eb34f | |||
fa0e59435e | |||
962cc31f31 | |||
87362fd68b | |||
51f6bef5dc | |||
a1ea48e2b6 | |||
e9aa3a55cc | |||
d03c690724 | |||
2a900a8c5b | |||
de995761db | |||
cfbdf8385d | |||
83b959c4a1 | |||
e66ec208a8 | |||
cf22186182 | |||
890a8927a5 | |||
dd439bc9be | |||
537a8bf19b | |||
bd0c696b84 | |||
5c56884d7f | |||
b53707074f | |||
6cb8d2eeb7 | |||
3895cd1068 | |||
5b2fbc4ec4 | |||
87654e2f4f | |||
9c29f8c8ff | |||
619c36d18d | |||
cf10a2d5b6 | |||
56fdc244d4 | |||
adc69c0d9a | |||
dd9a5548a8 | |||
ab88f86954 | |||
9466b225b6 | |||
ef1e2b02de | |||
ee68aec647 | |||
3dfeb92b63 | |||
ee5288ebb1 | |||
488acdfd98 | |||
ee2e7ec30d | |||
40466bc602 | |||
d8d6954ef0 | |||
0aa4550bbe | |||
ad82946fd3 | |||
77b48a48ab | |||
d948fa8db3 | |||
31393c2bef | |||
e3fc34ef1f | |||
d7fdd6d9dc | |||
5a3a7b843a | |||
e06f8961b4 | |||
9d1aa5b762 | |||
2e71a0b36a | |||
b072f40ed1 | |||
35d2f118ce | |||
3f7f315951 | |||
6ef4c74d97 | |||
0e4d684e7d | |||
e3be6b50ce | |||
dad2bed334 | |||
bbe66f0e18 | |||
27bf65c1a4 | |||
9c7b415d62 | |||
78e4572a8c | |||
4507ecd5f2 | |||
721d39c01d | |||
427fc1c1ca | |||
33f1b3be87 | |||
7e1c8c7983 | |||
aa6b27d829 | |||
999e2615c3 | |||
807b7d672f | |||
685a2f1e39 | |||
4217a05ae9 | |||
1e70849bde | |||
1ab3e9b310 | |||
fd2cf972bf | |||
d9eed6446e | |||
6b823e6381 | |||
917b7e615e | |||
af97381461 | |||
4975bb1482 | |||
83e2246195 | |||
3632070e3f | |||
0cb30a085c | |||
a7a59a2b1b | |||
bf7155b935 | |||
62fb294f54 | |||
b7e710b28f | |||
4a8534e4e6 | |||
aa4e2f5c95 | |||
fe4fbce7bd | |||
33374eacaa | |||
cea76ed9d5 | |||
f41db2685e | |||
95bf068b0a | |||
e2caa246f2 | |||
bed013d858 | |||
282460cb3f | |||
f015d1f490 | |||
f0758ee36f | |||
c77e9537ae | |||
b7de63e922 | |||
13ade14289 | |||
2b43e4e4b5 | |||
571986a78b | |||
d7c89be9a2 | |||
d466fd6799 | |||
116ec88f56 | |||
346372e002 | |||
f14b7d53a3 | |||
3355daa334 | |||
67fea26638 | |||
b1c367777d | |||
3917dc6d2f | |||
2d239edf34 | |||
1fbe084b74 | |||
7a37f09334 | |||
1ae05103e4 | |||
a66422fa3c | |||
dabc29f8a5 | |||
132da4a35a | |||
ea1eac2343 | |||
569e035bfd | |||
8b1b5d4eb3 | |||
33f64f7913 |
34
.github/workflows/ant.yml
vendored
Normal file
34
.github/workflows/ant.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
# Mostly copied from https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-ant
|
||||
# zlatinb
|
||||
|
||||
name: Java CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: GetText
|
||||
run: sudo apt install gettext
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '8'
|
||||
distribution: 'temurin'
|
||||
- name : Generate override.properties
|
||||
run: |
|
||||
rm -f override.properties
|
||||
echo "build.built-by=GitHub Actions" >> override.properties
|
||||
echo "noExe=true" >> override.properties
|
||||
- name: build with Ant
|
||||
run: ant distclean pkg
|
||||
- name: Upload installer.jar
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: I2P-install.jar-${{ github.sha }}
|
||||
path: install.jar
|
||||
|
||||
|
33
.tx/config
33
.tx/config
@ -9,6 +9,7 @@ trans.cs = apps/i2ptunnel/locale/messages_cs.po
|
||||
trans.da = apps/i2ptunnel/locale/messages_da.po
|
||||
trans.de = apps/i2ptunnel/locale/messages_de.po
|
||||
trans.es = apps/i2ptunnel/locale/messages_es.po
|
||||
trans.es_AR = apps/i2ptunnel/locale/messages_es_AR.po
|
||||
trans.fa = apps/i2ptunnel/locale/messages_fa.po
|
||||
trans.fi = apps/i2ptunnel/locale/messages_fi.po
|
||||
trans.fr = apps/i2ptunnel/locale/messages_fr.po
|
||||
@ -43,6 +44,7 @@ trans.cs = apps/i2ptunnel/locale-proxy/messages_cs.po
|
||||
trans.de = apps/i2ptunnel/locale-proxy/messages_de.po
|
||||
trans.el = apps/i2ptunnel/locale-proxy/messages_el.po
|
||||
trans.es = apps/i2ptunnel/locale-proxy/messages_es.po
|
||||
trans.es_AR = apps/i2ptunnel/locale-proxy/messages_es_AR.po
|
||||
trans.fa = apps/i2ptunnel/locale-proxy/messages_fa.po
|
||||
trans.fi = apps/i2ptunnel/locale-proxy/messages_fi.po
|
||||
trans.fr = apps/i2ptunnel/locale-proxy/messages_fr.po
|
||||
@ -66,6 +68,7 @@ trans.tr_TR = apps/i2ptunnel/locale-proxy/messages_tr.po
|
||||
trans.uk_UA = apps/i2ptunnel/locale-proxy/messages_uk.po
|
||||
trans.vi = apps/i2ptunnel/locale-proxy/messages_vi.po
|
||||
trans.zh_CN = apps/i2ptunnel/locale-proxy/messages_zh.po
|
||||
trans.zh_TW = apps/i2ptunnel/locale-proxy/messages_zh_TW.po
|
||||
|
||||
[I2P.core]
|
||||
type = PO
|
||||
@ -96,6 +99,7 @@ trans.pt = core/locale/messages_pt.po
|
||||
trans.pt_BR = core/locale/messages_pt_BR.po
|
||||
trans.ro = core/locale/messages_ro.po
|
||||
trans.ru_RU = core/locale/messages_ru.po
|
||||
trans.sl = core/locale/messages_sl.po
|
||||
trans.sv_SE = core/locale/messages_sv.po
|
||||
trans.tk = core/locale/messages_tk.po
|
||||
trans.tr_TR = core/locale/messages_tr.po
|
||||
@ -115,6 +119,7 @@ trans.da = router/locale/messages_da.po
|
||||
trans.de = router/locale/messages_de.po
|
||||
trans.el = router/locale/messages_el.po
|
||||
trans.es = router/locale/messages_es.po
|
||||
trans.es_AR = router/locale/messages_es_AR.po
|
||||
trans.et_EE = router/locale/messages_et.po
|
||||
trans.fa = router/locale/messages_fa.po
|
||||
trans.fi = router/locale/messages_fi.po
|
||||
@ -133,6 +138,7 @@ trans.pt = router/locale/messages_pt.po
|
||||
trans.pt_BR = router/locale/messages_pt_BR.po
|
||||
trans.ro = router/locale/messages_ro.po
|
||||
trans.ru_RU = router/locale/messages_ru.po
|
||||
trans.sl = router/locale/messages_sl.po
|
||||
trans.sv_SE = router/locale/messages_sv.po
|
||||
trans.tk = router/locale/messages_tk.po
|
||||
trans.tr_TR = router/locale/messages_tr.po
|
||||
@ -151,6 +157,7 @@ trans.da = apps/routerconsole/locale/messages_da.po
|
||||
trans.de = apps/routerconsole/locale/messages_de.po
|
||||
trans.el = apps/routerconsole/locale/messages_el.po
|
||||
trans.es = apps/routerconsole/locale/messages_es.po
|
||||
trans.es_AR = apps/routerconsole/locale/messages_es_AR.po
|
||||
trans.et_EE = apps/routerconsole/locale/messages_et.po
|
||||
trans.fa = apps/routerconsole/locale/messages_fa.po
|
||||
trans.fi = apps/routerconsole/locale/messages_fi.po
|
||||
@ -184,6 +191,7 @@ trans.cs = apps/routerconsole/locale-news/messages_cs.po
|
||||
trans.de = apps/routerconsole/locale-news/messages_de.po
|
||||
trans.el = apps/routerconsole/locale-news/messages_el.po
|
||||
trans.es = apps/routerconsole/locale-news/messages_es.po
|
||||
trans.es_AR = apps/routerconsole/locale-news/messages_es_AR.po
|
||||
trans.fa = apps/routerconsole/locale-news/messages_fa.po
|
||||
trans.fi = apps/routerconsole/locale-news/messages_fi.po
|
||||
trans.fr = apps/routerconsole/locale-news/messages_fr.po
|
||||
@ -206,6 +214,7 @@ trans.pt_BR = apps/routerconsole/locale-news/messages_pt_BR.po
|
||||
trans.ro = apps/routerconsole/locale-news/messages_ro.po
|
||||
trans.ru_RU = apps/routerconsole/locale-news/messages_ru.po
|
||||
trans.sk = apps/routerconsole/locale-news/messages_sk.po
|
||||
trans.sl = apps/routerconsole/locale-news/messages_sl.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
|
||||
@ -228,6 +237,7 @@ trans.da = apps/routerconsole/locale-countries/messages_da.po
|
||||
trans.de = apps/routerconsole/locale-countries/messages_de.po
|
||||
trans.el = apps/routerconsole/locale-countries/messages_el.po
|
||||
trans.es = apps/routerconsole/locale-countries/messages_es.po
|
||||
trans.es_AR = apps/routerconsole/locale-countries/messages_es_AR.po
|
||||
trans.et_EE = apps/routerconsole/locale-countries/messages_et.po
|
||||
trans.fa = apps/routerconsole/locale-countries/messages_fa.po
|
||||
trans.fi = apps/routerconsole/locale-countries/messages_fi.po
|
||||
@ -253,6 +263,7 @@ trans.pt_BR = apps/routerconsole/locale-countries/messages_pt_BR.po
|
||||
trans.ro = apps/routerconsole/locale-countries/messages_ro.po
|
||||
trans.ru_RU = apps/routerconsole/locale-countries/messages_ru.po
|
||||
trans.sk = apps/routerconsole/locale-countries/messages_sk.po
|
||||
trans.sl = apps/routerconsole/locale-countries/messages_sl.po
|
||||
trans.sq = apps/routerconsole/locale-countries/messages_sq.po
|
||||
trans.sr = apps/routerconsole/locale-countries/messages_sr.po
|
||||
trans.sv_SE = apps/routerconsole/locale-countries/messages_sv.po
|
||||
@ -271,6 +282,7 @@ trans.cs = apps/i2psnark/locale/messages_cs.po
|
||||
trans.de = apps/i2psnark/locale/messages_de.po
|
||||
trans.el = apps/i2psnark/locale/messages_el.po
|
||||
trans.es = apps/i2psnark/locale/messages_es.po
|
||||
trans.es_AR = apps/i2psnark/locale/messages_es_AR.po
|
||||
trans.fa = apps/i2psnark/locale/messages_fa.po
|
||||
trans.fi = apps/i2psnark/locale/messages_fi.po
|
||||
trans.fr = apps/i2psnark/locale/messages_fr.po
|
||||
@ -305,6 +317,7 @@ trans.da = apps/susidns/locale/messages_da.po
|
||||
trans.de = apps/susidns/locale/messages_de.po
|
||||
trans.el = apps/susidns/locale/messages_el.po
|
||||
trans.es = apps/susidns/locale/messages_es.po
|
||||
trans.es_AR = apps/susidns/locale/messages_es_AR.po
|
||||
trans.fa = apps/susidns/locale/messages_fa.po
|
||||
trans.fi = apps/susidns/locale/messages_fi.po
|
||||
trans.fr = apps/susidns/locale/messages_fr.po
|
||||
@ -361,6 +374,7 @@ trans.pt_BR = apps/desktopgui/locale/messages_pt_BR.po
|
||||
trans.ro = apps/desktopgui/locale/messages_ro.po
|
||||
trans.ru_RU = apps/desktopgui/locale/messages_ru.po
|
||||
trans.sk = apps/desktopgui/locale/messages_sk.po
|
||||
trans.sl = apps/desktopgui/locale/messages_sl.po
|
||||
trans.sr = apps/desktopgui/locale/messages_sr.po
|
||||
trans.sv_SE = apps/desktopgui/locale/messages_sv.po
|
||||
trans.sq = apps/desktopgui/locale/messages_sq.po
|
||||
@ -383,6 +397,7 @@ 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.es_AR = apps/susimail/locale/messages_es_AR.po
|
||||
trans.fa = apps/susimail/locale/messages_fa.po
|
||||
trans.fi = apps/susimail/locale/messages_fi.po
|
||||
trans.fr = apps/susimail/locale/messages_fr.po
|
||||
@ -423,6 +438,7 @@ trans.cs = debian/po/cs.po
|
||||
trans.de = debian/po/de.po
|
||||
trans.el = debian/po/el.po
|
||||
trans.es = debian/po/es.po
|
||||
trans.es_AR = debian/po/es_AR.po
|
||||
trans.fi = debian/po/fi.po
|
||||
trans.fr = debian/po/fr.po
|
||||
trans.gl = debian/po/gl.po
|
||||
@ -439,6 +455,7 @@ trans.pt_BR = debian/po/pt_BR.po
|
||||
trans.ro = debian/po/ro.po
|
||||
trans.ru_RU = debian/po/ru.po
|
||||
trans.sk = debian/po/sk.po
|
||||
trans.sl = debian/po/sl.po
|
||||
trans.sq = debian/po/sq.po
|
||||
trans.sv_SE = debian/po/sv.po
|
||||
trans.tk = debian/po/tk.po
|
||||
@ -455,6 +472,7 @@ trans.az = installer/resources/locale/po/messages_az.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
|
||||
trans.es_AR = installer/resources/locale/po/messages_es_AR.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
|
||||
@ -473,6 +491,7 @@ trans.sv_SE = installer/resources/locale/po/messages_sv.po
|
||||
trans.tr_TR = installer/resources/locale/po/messages_tr.po
|
||||
trans.uk_UA = installer/resources/locale/po/messages_uk.po
|
||||
trans.zh_CN = installer/resources/locale/po/messages_zh.po
|
||||
trans.zh_TW = installer/resources/locale/po/messages_zh_TW.po
|
||||
|
||||
[I2P.getopt]
|
||||
;;
|
||||
@ -499,6 +518,7 @@ trans.az = core/java/src/gnu/getopt/MessagesBundle_az.properties
|
||||
trans.cs = core/java/src/gnu/getopt/MessagesBundle_cs.properties
|
||||
trans.de = core/java/src/gnu/getopt/MessagesBundle_de.properties
|
||||
trans.es = core/java/src/gnu/getopt/MessagesBundle_es.properties
|
||||
trans.es_AR = core/java/src/gnu/getopt/MessagesBundle_es_AR.properties
|
||||
trans.fi = core/java/src/gnu/getopt/MessagesBundle_fi.properties
|
||||
trans.fr = core/java/src/gnu/getopt/MessagesBundle_fr.properties
|
||||
trans.gl = core/java/src/gnu/getopt/MessagesBundle_gl.properties
|
||||
@ -517,6 +537,7 @@ trans.pt_BR = core/java/src/gnu/getopt/MessagesBundle_pt_BR.properties
|
||||
trans.ro = core/java/src/gnu/getopt/MessagesBundle_ro.properties
|
||||
trans.ru_RU = core/java/src/gnu/getopt/MessagesBundle_ru.properties
|
||||
trans.sk = core/java/src/gnu/getopt/MessagesBundle_sk.properties
|
||||
trans.sl = core/java/src/gnu/getopt/MessagesBundle_sl.properties
|
||||
trans.sq = core/java/src/gnu/getopt/MessagesBundle_sq.properties
|
||||
trans.sr = core/java/src/gnu/getopt/MessagesBundle_sr.properties
|
||||
trans.sv_SE = core/java/src/gnu/getopt/MessagesBundle_sv.properties
|
||||
@ -534,6 +555,7 @@ trans.ca = apps/ministreaming/locale/messages_ca.po
|
||||
trans.cs = apps/ministreaming/locale/messages_cs.po
|
||||
trans.de = apps/ministreaming/locale/messages_de.po
|
||||
trans.es = apps/ministreaming/locale/messages_es.po
|
||||
trans.es_AR = apps/ministreaming/locale/messages_es_AR.po
|
||||
trans.fa = apps/ministreaming/locale/messages_fa.po
|
||||
trans.fi = apps/ministreaming/locale/messages_fi.po
|
||||
trans.fr = apps/ministreaming/locale/messages_fr.po
|
||||
@ -550,11 +572,13 @@ 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
|
||||
trans.sl = apps/ministreaming/locale/messages_sl.po
|
||||
trans.sv_SE = apps/ministreaming/locale/messages_sv.po
|
||||
trans.tk = apps/ministreaming/locale/messages_tk.po
|
||||
trans.tr_TR = apps/ministreaming/locale/messages_tr.po
|
||||
trans.uk_UA = apps/ministreaming/locale/messages_uk.po
|
||||
trans.zh_CN = apps/ministreaming/locale/messages_zh.po
|
||||
trans.zh_TW = apps/ministreaming/locale/messages_zh_TW.po
|
||||
|
||||
[I2P.manpages]
|
||||
;;
|
||||
@ -568,10 +592,13 @@ trans.ar = installer/resources/locale-man/man_ar.po
|
||||
trans.az = installer/resources/locale-man/man_az.po
|
||||
trans.de = installer/resources/locale-man/man_de.po
|
||||
trans.es = installer/resources/locale-man/man_es.po
|
||||
trans.es_AR = installer/resources/locale-man/man_es_AR.po
|
||||
trans.fi = installer/resources/locale-man/man_fi.po
|
||||
trans.fr = installer/resources/locale-man/man_fr.po
|
||||
trans.hu = installer/resources/locale-man/man_hu.po
|
||||
trans.id = installer/resources/locale-man/man_id.po
|
||||
trans.it = installer/resources/locale-man/man_it.po
|
||||
trans.ja = installer/resources/locale-man/man_ja.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
|
||||
@ -582,6 +609,7 @@ trans.ru_RU = installer/resources/locale-man/man_ru.po
|
||||
trans.sv_SE = installer/resources/locale-man/man_sv.po
|
||||
trans.tr_TR = installer/resources/locale-man/man_tr.po
|
||||
trans.zh_CN = installer/resources/locale-man/man_zh.po
|
||||
trans.zh_TW = installer/resources/locale-man/man_zh_TW.po
|
||||
|
||||
[I2P.eepsite]
|
||||
;;
|
||||
@ -621,6 +649,9 @@ trans.zh_CN = installer/resources/eepsite/docroot/help/index_zh.html
|
||||
;;
|
||||
;; Text on /console
|
||||
;;
|
||||
;; NOTE: No support for country suffixes right now.
|
||||
;; See ResourceHelper.java
|
||||
;;
|
||||
type = HTML
|
||||
source_file = apps/routerconsole/resources/docs/readme.html
|
||||
source_lang = en
|
||||
@ -636,9 +667,11 @@ trans.pl = apps/routerconsole/resources/docs/readme_pl.html
|
||||
trans.pt = apps/routerconsole/resources/docs/readme_pt.html
|
||||
trans.ro = apps/routerconsole/resources/docs/readme_ro.html
|
||||
trans.ru_RU = apps/routerconsole/resources/docs/readme_ru.html
|
||||
trans.sl = apps/routerconsole/resources/docs/readme_sl.html
|
||||
trans.tr_TR = apps/routerconsole/resources/docs/readme_tr.html
|
||||
trans.uk_UA = apps/routerconsole/resources/docs/readme_uk.html
|
||||
trans.zh_CN = apps/routerconsole/resources/docs/readme_zh.html
|
||||
trans.zh_TW = apps/routerconsole/resources/docs/readme_zh_TW.html
|
||||
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
49
Docker.md
49
Docker.md
@ -1,5 +1,25 @@
|
||||
# I2P in Docker
|
||||
|
||||
### Very quick start
|
||||
If you just want to give I2P a quick try or are using it on a home network, follow these steps:
|
||||
|
||||
1. Create two directories `i2pconfig` and `i2ptorrents`
|
||||
2. Copy the following text and save it in a file `docker-compose.yml`
|
||||
```
|
||||
version: "3.5"
|
||||
services:
|
||||
i2p:
|
||||
image: geti2p/i2p
|
||||
network_mode: host
|
||||
volumes:
|
||||
- ./i2pconfig:/i2p/.i2p
|
||||
- ./i2ptorrents:/i2psnark
|
||||
```
|
||||
3. Execute `docker-compose up`
|
||||
4. Start a browser and go to `http://127.0.0.1:7657` to complete the setup wizard.
|
||||
|
||||
Note that this quick-start approach is not recommended for production deployments on remote servers. Please read the rest of this document for more information.
|
||||
|
||||
### Building an image
|
||||
There is an i2P image available over at [DockerHub](https://hub.docker.com). If you do not want to use that one, you can build one yourself:
|
||||
```
|
||||
@ -17,21 +37,26 @@ By the default the image limits the memory available to the Java heap to 512MB.
|
||||
#### Ports
|
||||
There are several ports which are exposed by the image. You can choose which ones to publish depending on your specific needs.
|
||||
|
||||
|Port|Description|TCP/UDP|
|
||||
|---|---|---|
|
||||
|4444|HTTP Proxy|TCP|
|
||||
|4445|HTTPS Proxy|TCP|
|
||||
|6668|IRC Proxy|TCP|
|
||||
|7654|I2CP Protocol|TCP|
|
||||
|7656|SAM Bridge TCP|TCP|
|
||||
|7657|Router console|TCP|
|
||||
|7658|I2P Site|TCP|
|
||||
|7659|SMTP Proxy|TCP|
|
||||
|7660|POP Proxy|TCP|
|
||||
|12345|I2NP Protocol|TCP and UDP|
|
||||
|Port|Interface|Description|TCP/UDP|
|
||||
|---|---|---|---|
|
||||
|4444|127.0.0.1|HTTP Proxy|TCP|
|
||||
|4445|127.0.0.1|HTTPS Proxy|TCP|
|
||||
|6668|127.0.0.1|IRC Proxy|TCP|
|
||||
|7654|127.0.0.1|I2CP Protocol|TCP|
|
||||
|7656|127.0.0.1|SAM Bridge TCP|TCP|
|
||||
|7657|127.0.0.1|Router console|TCP|
|
||||
|7658|127.0.0.1|I2P Site|TCP|
|
||||
|7659|127.0.0.1|SMTP Proxy|TCP|
|
||||
|7660|127.0.0.1|POP Proxy|TCP|
|
||||
|7652|LAN interface|UPnP|TCP|
|
||||
|7653|LAN interface|UPnP|UDP|
|
||||
|12345|0.0.0.0|I2NP Protocol|TCP and UDP|
|
||||
|
||||
You probably want at least the Router Console (7657) and the HTTP Proxy (4444). If you want I2P to be able to receive incoming connections from the internet, and hence not think it's firewalled, publish the I2NP Protocol port (12345) - but make sure you publish to a different random port, otherwise others may be able to guess you're running I2P in a Docker image.
|
||||
|
||||
#### Networking
|
||||
A best-practices guide for cloud deployments is beyond the scope of this document, but in general you should try to minimize the number of published ports, while exposing only the `I2NP` ports to the internet. That means that the services in the list above which are bound to `127.0.0.1` (which include the router console) will need to be accessed via other methods like ssh tunneling or be manually configured to bind to a different interface.
|
||||
|
||||
#### Example
|
||||
Here is an example container that mounts `i2phome` as home directory, `i2ptorrents` for torrents, and opens HTTP Proxy, IRC, Router Console and I2NP Protocols. It also limits the memory available to the JVM to 256MB.
|
||||
```
|
||||
|
10
Dockerfile
10
Dockerfile
@ -1,19 +1,19 @@
|
||||
FROM jlesage/baseimage:alpine-3.10-glibc as builder
|
||||
FROM jlesage/baseimage:alpine-3.15-glibc as builder
|
||||
|
||||
ENV APP_HOME="/i2p"
|
||||
|
||||
WORKDIR /tmp/build
|
||||
COPY . .
|
||||
|
||||
RUN add-pkg --virtual build-base gettext tar bzip2 apache-ant openjdk8 \
|
||||
RUN add-pkg --virtual build-base gettext tar bzip2 apache-ant openjdk17 \
|
||||
&& ant preppkg-linux-only \
|
||||
&& rm -rf pkg-temp/osid pkg-temp/lib/wrapper pkg-temp/lib/wrapper.* \
|
||||
&& del-pkg build-base gettext tar bzip2 apache-ant openjdk8
|
||||
&& del-pkg build-base gettext tar bzip2 apache-ant openjdk17
|
||||
|
||||
FROM jlesage/baseimage:alpine-3.10-glibc
|
||||
FROM jlesage/baseimage:alpine-3.15-glibc
|
||||
ENV APP_HOME="/i2p"
|
||||
|
||||
RUN add-pkg openjdk8-jre
|
||||
RUN add-pkg openjdk17-jre
|
||||
WORKDIR ${APP_HOME}
|
||||
COPY --from=builder /tmp/build/pkg-temp .
|
||||
|
||||
|
10
LICENSE.txt
10
LICENSE.txt
@ -212,10 +212,6 @@ Applications:
|
||||
Copyright (c) 2006, Matthew Estes
|
||||
See licenses/LICENSE-BlockFile.txt
|
||||
|
||||
BOB (BOB.jar):
|
||||
Copyright (C) sponge
|
||||
See licenses/COPYING-BOB.txt
|
||||
|
||||
Desktopgui (desktopgui.jar):
|
||||
Copyright (c) Mathias De Maré
|
||||
See licenses/LICENSE-DesktopGUI.txt
|
||||
@ -264,7 +260,7 @@ Applications:
|
||||
Zxing 3.4.1:
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
Jetty 9.3.29.v20201019 (jetty-*.jar, org.mortbay.*.jar):
|
||||
Jetty 9.3.30.v20211001 (jetty-*.jar, org.mortbay.*.jar):
|
||||
(not included in most distribution packages, except for jetty-i2p.jar)
|
||||
See licenses/ABOUT-Jetty.html
|
||||
See licenses/NOTICE-Jetty.html
|
||||
@ -339,9 +335,9 @@ Applications:
|
||||
Systray (systray.jar):
|
||||
Public domain.
|
||||
|
||||
Tomcat 9.0.45 (jasper-runtime.jar):
|
||||
Tomcat 9.0.62 (jasper-runtime.jar):
|
||||
(not included in most distribution packages)
|
||||
Copyright 1999-2020 The Apache Software Foundation
|
||||
Copyright 1999-2022 The Apache Software Foundation
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
See licenses/NOTICE-Tomcat.txt
|
||||
|
||||
|
@ -64,13 +64,16 @@ your `~/.gradle/gradle.properties`:
|
||||
systemProp.socksProxyHost=localhost
|
||||
systemProp.socksProxyPort=9150
|
||||
|
||||
### Development builds
|
||||
Automatic CI builds are available at the [continuous integration](https://github.com/i2p/i2p.i2p/actions/workflows/ant.yml) page.
|
||||
|
||||
### Docker
|
||||
For more information how to run I2P in Docker, see [Docker.md](Docker.md)
|
||||
## Contact info
|
||||
|
||||
Need help? See the IRC channel #i2p on irc.freenode.net
|
||||
|
||||
Bug reports: [https://trac.i2p2.de/report/1](https://trac.i2p2.de/report/1)
|
||||
Bug reports: [https://i2pgit.org/i2p-hackers/i2p.i2p/-/issues](https://i2pgit.org/i2p-hackers/i2p.i2p/-/issues) [http://git.idk.i2p/i2p-hackers/i2p.i2p/-/issues](http://git.idk.i2p/i2p-hackers/i2p.i2p/-/issues)
|
||||
|
||||
Contact information, security issues, press inquiries: [https://geti2p.net/en/contact](https://geti2p.net/en/contact)
|
||||
|
||||
|
@ -47,7 +47,8 @@ Need help?
|
||||
IRC irc.freenode.net #i2p
|
||||
|
||||
Bug reports:
|
||||
https://trac.i2p2.de/report/1
|
||||
https://i2pgit.org/i2p-hackers/i2p.i2p/-/issues
|
||||
http://git.idk.i2p/i2p-hackers/i2p.i2p/-/issues
|
||||
|
||||
Contact information, security issues, press inquiries:
|
||||
https://geti2p.net/en/contact
|
||||
|
@ -1,14 +0,0 @@
|
||||
#bob.config
|
||||
#Tue Dec 30 00:00:00 UTC 2008
|
||||
# Please leave this file here for testing.
|
||||
# Thank you,
|
||||
# Sponge
|
||||
i2cp.tcp.port=7654
|
||||
BOB.host=localhost
|
||||
inbound.lengthVariance=0
|
||||
i2cp.messageReliability=BestEffort
|
||||
BOB.port=45678
|
||||
outbound.length=1
|
||||
inbound.length=1
|
||||
outbound.lengthVariance=0
|
||||
i2cp.tcp.host=localhost
|
@ -1,12 +0,0 @@
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir 'src'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':core')
|
||||
compile project(':apps:ministreaming')
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- You may freely edit this file. See commented blocks below for -->
|
||||
<!-- some examples of how to customize the build. -->
|
||||
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||
<!-- By default, only the Clean and Build commands use this build script. -->
|
||||
<!-- Commands such as Run, Debug, and Test only use this build script if -->
|
||||
<!-- the Compile on Save feature is turned off for the project. -->
|
||||
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
|
||||
<!-- in the project's Project Properties dialog box.-->
|
||||
<project name="BOB" default="default" basedir=".">
|
||||
<description>Builds, tests, and runs the project BOB.</description>
|
||||
<import file="nbproject/build-impl.xml"/>
|
||||
<!--
|
||||
|
||||
There exist several targets which are by default empty and which can be
|
||||
used for execution of your tasks. These targets are usually executed
|
||||
before and after some main targets. They are:
|
||||
|
||||
-pre-init: called before initialization of project properties
|
||||
-post-init: called after initialization of project properties
|
||||
-pre-compile: called before javac compilation
|
||||
-post-compile: called after javac compilation
|
||||
-pre-compile-single: called before javac compilation of single file
|
||||
-post-compile-single: called after javac compilation of single file
|
||||
-pre-compile-test: called before javac compilation of JUnit tests
|
||||
-post-compile-test: called after javac compilation of JUnit tests
|
||||
-pre-compile-test-single: called before javac compilation of single JUnit test
|
||||
-post-compile-test-single: called after javac compilation of single JUunit test
|
||||
-pre-jar: called before JAR building
|
||||
-post-jar: called after JAR building
|
||||
-post-clean: called after cleaning build products
|
||||
|
||||
(Targets beginning with '-' are not intended to be called on their own.)
|
||||
|
||||
Example of inserting an obfuscator after compilation could look like this:
|
||||
|
||||
<target name="-post-compile">
|
||||
<obfuscate>
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
</obfuscate>
|
||||
</target>
|
||||
|
||||
For list of available properties check the imported
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
|
||||
Another way to customize the build is by overriding existing main targets.
|
||||
The targets of interest are:
|
||||
|
||||
-init-macrodef-javac: defines macro for javac compilation
|
||||
-init-macrodef-junit: defines macro for junit execution
|
||||
-init-macrodef-debug: defines macro for class debugging
|
||||
-init-macrodef-java: defines macro for class execution
|
||||
-do-jar-with-manifest: JAR building (if you are using a manifest)
|
||||
-do-jar-without-manifest: JAR building (if you are not using a manifest)
|
||||
run: execution of project
|
||||
-javadoc-build: Javadoc generation
|
||||
test-report: JUnit report generation
|
||||
|
||||
An example of overriding the target for project execution could look like this:
|
||||
|
||||
<target name="run" depends="BOB-impl.jar">
|
||||
<exec dir="bin" executable="launcher.exe">
|
||||
<arg file="${dist.jar}"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
Notice that the overridden target depends on the jar target and not only on
|
||||
the compile target as the regular run target does. Again, for a list of available
|
||||
properties which you can use, check the target you are overriding in the
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
-->
|
||||
|
||||
<target depends="jar" description="Build BOB into a SINGLE JAR." name="onejar">
|
||||
<!-- Make needed working dirs -->
|
||||
<mkdir dir="${dist.dir}/lib" />
|
||||
<mkdir dir="${dist.dir}/classes" />
|
||||
|
||||
<!-- Copy jars -->
|
||||
<copy todir="${dist.dir}/lib" flatten="true" >
|
||||
<path>
|
||||
<pathelement path="${javac.classpath}" />
|
||||
</path>
|
||||
</copy>
|
||||
<copy todir="${dist.dir}/lib" file="../../build/jbigi.jar" />
|
||||
|
||||
<!-- Extract the classes inside the jar files -->
|
||||
<unjar dest="${dist.dir}/classes" >
|
||||
<fileset dir="${dist.dir}/lib" >
|
||||
<include name="**/*.jar" />
|
||||
</fileset>
|
||||
</unjar>
|
||||
|
||||
<!-- Recombine the classes into a new jar file -->
|
||||
<jar jarfile="${dist.dir}/lib/all-in-one.jar" >
|
||||
<fileset dir="${dist.dir}/classes" />
|
||||
</jar>
|
||||
|
||||
<!-- Clean up work area -->
|
||||
<delete dir="${dist.dir}/classes" followsymlinks="false" includeemptydirs="true"/>
|
||||
|
||||
<!-- Make the single jar file -->
|
||||
<jar jarfile="dist/BOB-one.jar" >
|
||||
<zipfileset src="${dist.jar}" excludes="META-INF/*" />
|
||||
<zipfileset src="${dist.dir}/lib/all-in-one.jar" excludes="**/META-INF/*" />
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="net.i2p.BOB.Main" />
|
||||
</manifest>
|
||||
</jar>
|
||||
|
||||
<!-- Clean up the fake jar file -->
|
||||
<delete file="${dist.dir}/lib/all-in-one.jar" />
|
||||
</target>
|
||||
</project>
|
@ -1,3 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
X-COMMENT: Main-Class will be added automatically by build
|
||||
|
@ -1,643 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
*** GENERATED FROM project.xml - DO NOT EDIT ***
|
||||
*** EDIT ../build.xml INSTEAD ***
|
||||
|
||||
For the purpose of easier reading the script
|
||||
is divided into following sections:
|
||||
|
||||
- initialization
|
||||
- compilation
|
||||
- jar
|
||||
- execution
|
||||
- debugging
|
||||
- javadoc
|
||||
- junit compilation
|
||||
- junit execution
|
||||
- junit debugging
|
||||
- applet
|
||||
- cleanup
|
||||
|
||||
-->
|
||||
<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="BOB-impl">
|
||||
<target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
|
||||
<!--
|
||||
======================
|
||||
INITIALIZATION SECTION
|
||||
======================
|
||||
-->
|
||||
<target name="-pre-init">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="-pre-init" name="-init-private">
|
||||
<property file="nbproject/private/config.properties"/>
|
||||
<property file="nbproject/private/configs/${config}.properties"/>
|
||||
<property file="nbproject/private/private.properties"/>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private" name="-init-user">
|
||||
<property file="${user.properties.file}"/>
|
||||
<!-- The two properties below are usually overridden -->
|
||||
<!-- by the active platform. Just a fallback. -->
|
||||
<property name="default.javac.source" value="1.8"/>
|
||||
<property name="default.javac.target" value="1.8"/>
|
||||
<property name="javac.release" value="8"/>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user" name="-init-project">
|
||||
<property file="nbproject/configs/${config}.properties"/>
|
||||
<property file="nbproject/project.properties"/>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
|
||||
<available file="${manifest.file}" property="manifest.available"/>
|
||||
<condition property="manifest.available+main.class">
|
||||
<and>
|
||||
<isset property="manifest.available"/>
|
||||
<isset property="main.class"/>
|
||||
<not>
|
||||
<equals arg1="${main.class}" arg2="" trim="true"/>
|
||||
</not>
|
||||
</and>
|
||||
</condition>
|
||||
<condition property="manifest.available+main.class+mkdist.available">
|
||||
<and>
|
||||
<istrue value="${manifest.available+main.class}"/>
|
||||
<isset property="libs.CopyLibs.classpath"/>
|
||||
</and>
|
||||
</condition>
|
||||
<condition property="have.tests">
|
||||
<or>
|
||||
<available file="${test.src.dir}"/>
|
||||
</or>
|
||||
</condition>
|
||||
<condition property="have.sources">
|
||||
<or>
|
||||
<available file="${src.dir}"/>
|
||||
</or>
|
||||
</condition>
|
||||
<condition property="netbeans.home+have.tests">
|
||||
<and>
|
||||
<isset property="netbeans.home"/>
|
||||
<isset property="have.tests"/>
|
||||
</and>
|
||||
</condition>
|
||||
<condition property="no.javadoc.preview">
|
||||
<and>
|
||||
<isset property="javadoc.preview"/>
|
||||
<isfalse value="${javadoc.preview}"/>
|
||||
</and>
|
||||
</condition>
|
||||
<property name="run.jvmargs" value=""/>
|
||||
<property name="javac.compilerargs" value=""/>
|
||||
<property name="work.dir" value="${basedir}"/>
|
||||
<condition property="no.deps">
|
||||
<and>
|
||||
<istrue value="${no.dependencies}"/>
|
||||
</and>
|
||||
</condition>
|
||||
<property name="javac.debug" value="true"/>
|
||||
<property name="javadoc.preview" value="true"/>
|
||||
<property name="application.args" value=""/>
|
||||
<property name="source.encoding" value="${file.encoding}"/>
|
||||
<condition property="javadoc.encoding.used" value="${javadoc.encoding}">
|
||||
<and>
|
||||
<isset property="javadoc.encoding"/>
|
||||
<not>
|
||||
<equals arg1="${javadoc.encoding}" arg2=""/>
|
||||
</not>
|
||||
</and>
|
||||
</condition>
|
||||
<property name="javadoc.encoding.used" value="${source.encoding}"/>
|
||||
<property name="includes" value="**"/>
|
||||
<property name="excludes" value=""/>
|
||||
<property name="do.depend" value="false"/>
|
||||
<condition property="do.depend.true">
|
||||
<istrue value="${do.depend}"/>
|
||||
</condition>
|
||||
<condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
|
||||
<and>
|
||||
<isset property="jaxws.endorsed.dir"/>
|
||||
<available file="nbproject/jaxws-build.xml"/>
|
||||
</and>
|
||||
</condition>
|
||||
</target>
|
||||
<target name="-post-init">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
|
||||
<fail unless="src.dir">Must set src.dir</fail>
|
||||
<fail unless="test.src.dir">Must set test.src.dir</fail>
|
||||
<fail unless="build.dir">Must set build.dir</fail>
|
||||
<fail unless="dist.dir">Must set dist.dir</fail>
|
||||
<fail unless="build.classes.dir">Must set build.classes.dir</fail>
|
||||
<fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
|
||||
<fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
|
||||
<fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
|
||||
<fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
|
||||
<fail unless="dist.jar">Must set dist.jar</fail>
|
||||
</target>
|
||||
<target name="-init-macrodef-property">
|
||||
<macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute name="name"/>
|
||||
<attribute name="value"/>
|
||||
<sequential>
|
||||
<property name="@{name}" value="${@{value}}"/>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-javac">
|
||||
<macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${src.dir}" name="srcdir"/>
|
||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
||||
<attribute default="${javac.classpath}" name="classpath"/>
|
||||
<attribute default="${includes}" name="includes"/>
|
||||
<attribute default="${excludes}" name="excludes"/>
|
||||
<attribute default="${javac.debug}" name="debug"/>
|
||||
<attribute default="/does/not/exist" name="sourcepath"/>
|
||||
<element name="customize" optional="true"/>
|
||||
<sequential>
|
||||
<javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" release="${javac.release}">
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
<compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
|
||||
<customize/>
|
||||
</javac>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${src.dir}" name="srcdir"/>
|
||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
||||
<attribute default="${javac.classpath}" name="classpath"/>
|
||||
<sequential>
|
||||
<depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
</depend>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
||||
<sequential>
|
||||
<fail unless="javac.includes">Must set javac.includes</fail>
|
||||
<pathconvert pathsep="," property="javac.includes.binary">
|
||||
<path>
|
||||
<filelist dir="@{destdir}" files="${javac.includes}"/>
|
||||
</path>
|
||||
<globmapper from="*.java" to="*.class"/>
|
||||
</pathconvert>
|
||||
<delete>
|
||||
<files includes="${javac.includes.binary}"/>
|
||||
</delete>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-junit">
|
||||
<macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${includes}" name="includes"/>
|
||||
<attribute default="${excludes}" name="excludes"/>
|
||||
<attribute default="**" name="testincludes"/>
|
||||
<sequential>
|
||||
<junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
|
||||
<batchtest todir="${build.test.results.dir}">
|
||||
<fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
|
||||
<filename name="@{testincludes}"/>
|
||||
</fileset>
|
||||
</batchtest>
|
||||
<classpath>
|
||||
<path path="${run.test.classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="test-sys-prop."/>
|
||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<formatter type="brief" usefile="false"/>
|
||||
<formatter type="xml"/>
|
||||
<jvmarg line="${run.jvmargs}"/>
|
||||
</junit>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target depends="-init-debug-args" name="-init-macrodef-nbjpda">
|
||||
<macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute default="${main.class}" name="name"/>
|
||||
<attribute default="${debug.classpath}" name="classpath"/>
|
||||
<attribute default="" name="stopclassname"/>
|
||||
<sequential>
|
||||
<nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
</nbjpdastart>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute default="${build.classes.dir}" name="dir"/>
|
||||
<sequential>
|
||||
<nbjpdareload>
|
||||
<fileset dir="@{dir}" includes="${fix.classes}">
|
||||
<include name="${fix.includes}*.class"/>
|
||||
</fileset>
|
||||
</nbjpdareload>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-debug-args">
|
||||
<property name="version-output" value="java version "${ant.java.version}"/>
|
||||
<condition property="have-jdk-older-than-1.4">
|
||||
<or>
|
||||
<contains string="${version-output}" substring="java version "1.0"/>
|
||||
<contains string="${version-output}" substring="java version "1.1"/>
|
||||
<contains string="${version-output}" substring="java version "1.2"/>
|
||||
<contains string="${version-output}" substring="java version "1.3"/>
|
||||
</or>
|
||||
</condition>
|
||||
<condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
|
||||
<istrue value="${have-jdk-older-than-1.4}"/>
|
||||
</condition>
|
||||
<condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
|
||||
<os family="windows"/>
|
||||
</condition>
|
||||
<condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
|
||||
<isset property="debug.transport"/>
|
||||
</condition>
|
||||
</target>
|
||||
<target depends="-init-debug-args" name="-init-macrodef-debug">
|
||||
<macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${main.class}" name="classname"/>
|
||||
<attribute default="${debug.classpath}" name="classpath"/>
|
||||
<element name="customize" optional="true"/>
|
||||
<sequential>
|
||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
||||
<jvmarg line="${debug-args-line}"/>
|
||||
<jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
|
||||
<jvmarg line="${run.jvmargs}"/>
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="run-sys-prop."/>
|
||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<customize/>
|
||||
</java>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-java">
|
||||
<macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute default="${main.class}" name="classname"/>
|
||||
<element name="customize" optional="true"/>
|
||||
<sequential>
|
||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
||||
<jvmarg line="${run.jvmargs}"/>
|
||||
<classpath>
|
||||
<path path="${run.classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="run-sys-prop."/>
|
||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<customize/>
|
||||
</java>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-presetdef-jar">
|
||||
<presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<jar compress="${jar.compress}" jarfile="${dist.jar}">
|
||||
<j2seproject1:fileset dir="${build.classes.dir}"/>
|
||||
</jar>
|
||||
</presetdef>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
|
||||
<!--
|
||||
===================
|
||||
COMPILATION SECTION
|
||||
===================
|
||||
-->
|
||||
<target depends="init" name="deps-jar" unless="no.deps"/>
|
||||
<target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
|
||||
<target depends="init" name="-check-automatic-build">
|
||||
<available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
|
||||
</target>
|
||||
<target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
|
||||
<antcall target="clean"/>
|
||||
</target>
|
||||
<target depends="init,deps-jar" name="-pre-pre-compile">
|
||||
<mkdir dir="${build.classes.dir}"/>
|
||||
</target>
|
||||
<target name="-pre-compile">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target if="do.depend.true" name="-compile-depend">
|
||||
<j2seproject3:depend/>
|
||||
</target>
|
||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
|
||||
<j2seproject3:javac/>
|
||||
<copy todir="${build.classes.dir}">
|
||||
<fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
||||
</copy>
|
||||
</target>
|
||||
<target name="-post-compile">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
|
||||
<target name="-pre-compile-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
|
||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
||||
<j2seproject3:force-recompile/>
|
||||
<j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
|
||||
</target>
|
||||
<target name="-post-compile-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
|
||||
<!--
|
||||
====================
|
||||
JAR BUILDING SECTION
|
||||
====================
|
||||
-->
|
||||
<target depends="init" name="-pre-pre-jar">
|
||||
<dirname file="${dist.jar}" property="dist.jar.dir"/>
|
||||
<mkdir dir="${dist.jar.dir}"/>
|
||||
</target>
|
||||
<target name="-pre-jar">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
|
||||
<j2seproject1:jar/>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
|
||||
<j2seproject1:jar manifest="${manifest.file}"/>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
|
||||
<j2seproject1:jar manifest="${manifest.file}">
|
||||
<j2seproject1:manifest>
|
||||
<j2seproject1:attribute name="Main-Class" value="${main.class}"/>
|
||||
</j2seproject1:manifest>
|
||||
</j2seproject1:jar>
|
||||
<echo>To run this application from the command line without Ant, try:</echo>
|
||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
||||
<pathconvert property="run.classpath.with.dist.jar">
|
||||
<path path="${run.classpath}"/>
|
||||
<map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
|
||||
</pathconvert>
|
||||
<echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
|
||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
||||
<pathconvert property="run.classpath.without.build.classes.dir">
|
||||
<path path="${run.classpath}"/>
|
||||
<map from="${build.classes.dir.resolved}" to=""/>
|
||||
</pathconvert>
|
||||
<pathconvert pathsep=" " property="jar.classpath">
|
||||
<path path="${run.classpath.without.build.classes.dir}"/>
|
||||
<chainedmapper>
|
||||
<flattenmapper/>
|
||||
<globmapper from="*" to="lib/*"/>
|
||||
</chainedmapper>
|
||||
</pathconvert>
|
||||
<taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
|
||||
<copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="${main.class}"/>
|
||||
<attribute name="Class-Path" value="${jar.classpath}"/>
|
||||
</manifest>
|
||||
</copylibs>
|
||||
<echo>To run this application from the command line without Ant, try:</echo>
|
||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
||||
<echo>java -jar "${dist.jar.resolved}"</echo>
|
||||
</target>
|
||||
<target name="-post-jar">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
|
||||
<!--
|
||||
=================
|
||||
EXECUTION SECTION
|
||||
=================
|
||||
-->
|
||||
<target depends="init,compile" description="Run a main class." name="run">
|
||||
<j2seproject1:java>
|
||||
<customize>
|
||||
<arg line="${application.args}"/>
|
||||
</customize>
|
||||
</j2seproject1:java>
|
||||
</target>
|
||||
<target name="-do-not-recompile">
|
||||
<property name="javac.includes.binary" value=""/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-single" name="run-single">
|
||||
<fail unless="run.class">Must select one file in the IDE or set run.class</fail>
|
||||
<j2seproject1:java classname="${run.class}"/>
|
||||
</target>
|
||||
<!--
|
||||
=================
|
||||
DEBUGGING SECTION
|
||||
=================
|
||||
-->
|
||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger">
|
||||
<j2seproject1:nbjpdastart name="${debug.class}"/>
|
||||
</target>
|
||||
<target depends="init,compile" name="-debug-start-debuggee">
|
||||
<j2seproject3:debug>
|
||||
<customize>
|
||||
<arg line="${application.args}"/>
|
||||
</customize>
|
||||
</j2seproject3:debug>
|
||||
</target>
|
||||
<target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
|
||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
|
||||
<j2seproject1:nbjpdastart stopclassname="${main.class}"/>
|
||||
</target>
|
||||
<target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
|
||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
|
||||
<fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
|
||||
<j2seproject3:debug classname="${debug.class}"/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
|
||||
<target depends="init" name="-pre-debug-fix">
|
||||
<fail unless="fix.includes">Must set fix.includes</fail>
|
||||
<property name="javac.includes" value="${fix.includes}.java"/>
|
||||
</target>
|
||||
<target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
|
||||
<j2seproject1:nbjpdareload/>
|
||||
</target>
|
||||
<target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
|
||||
<!--
|
||||
===============
|
||||
JAVADOC SECTION
|
||||
===============
|
||||
-->
|
||||
<target depends="init" name="-javadoc-build">
|
||||
<mkdir dir="${dist.javadoc.dir}"/>
|
||||
<javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
|
||||
<classpath>
|
||||
<path path="${javac.classpath}"/>
|
||||
</classpath>
|
||||
<fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
|
||||
<filename name="**/*.java"/>
|
||||
</fileset>
|
||||
</javadoc>
|
||||
</target>
|
||||
<target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
|
||||
<nbbrowse file="${dist.javadoc.dir}/index.html"/>
|
||||
</target>
|
||||
<target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
|
||||
<!--
|
||||
=========================
|
||||
JUNIT COMPILATION SECTION
|
||||
=========================
|
||||
-->
|
||||
<target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
|
||||
<mkdir dir="${build.test.classes.dir}"/>
|
||||
</target>
|
||||
<target name="-pre-compile-test">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target if="do.depend.true" name="-compile-test-depend">
|
||||
<j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
|
||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
||||
<copy todir="${build.test.classes.dir}">
|
||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
||||
</copy>
|
||||
</target>
|
||||
<target name="-post-compile-test">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
|
||||
<target name="-pre-compile-test-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
|
||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
||||
<j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
|
||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
|
||||
<copy todir="${build.test.classes.dir}">
|
||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
||||
</copy>
|
||||
</target>
|
||||
<target name="-post-compile-test-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
|
||||
<!--
|
||||
=======================
|
||||
JUNIT EXECUTION SECTION
|
||||
=======================
|
||||
-->
|
||||
<target depends="init" if="have.tests" name="-pre-test-run">
|
||||
<mkdir dir="${build.test.results.dir}"/>
|
||||
</target>
|
||||
<target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
|
||||
<j2seproject3:junit testincludes="**/*Test.java"/>
|
||||
</target>
|
||||
<target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
|
||||
<fail if="tests.failed">Some tests failed; see details above.</fail>
|
||||
</target>
|
||||
<target depends="init" if="have.tests" name="test-report"/>
|
||||
<target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
|
||||
<target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
|
||||
<target depends="init" if="have.tests" name="-pre-test-run-single">
|
||||
<mkdir dir="${build.test.results.dir}"/>
|
||||
</target>
|
||||
<target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
|
||||
<fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
|
||||
<j2seproject3:junit excludes="" includes="${test.includes}"/>
|
||||
</target>
|
||||
<target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
|
||||
<fail if="tests.failed">Some tests failed; see details above.</fail>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
|
||||
<!--
|
||||
=======================
|
||||
JUNIT DEBUGGING SECTION
|
||||
=======================
|
||||
-->
|
||||
<target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
|
||||
<fail unless="test.class">Must select one file in the IDE or set test.class</fail>
|
||||
<property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
|
||||
<delete file="${test.report.file}"/>
|
||||
<mkdir dir="${build.test.results.dir}"/>
|
||||
<j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
|
||||
<customize>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="test-sys-prop."/>
|
||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<arg value="${test.class}"/>
|
||||
<arg value="showoutput=true"/>
|
||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
|
||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
|
||||
</customize>
|
||||
</j2seproject3:debug>
|
||||
</target>
|
||||
<target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
|
||||
<j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
|
||||
<target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
|
||||
<j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
|
||||
</target>
|
||||
<target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
|
||||
<!--
|
||||
=========================
|
||||
APPLET EXECUTION SECTION
|
||||
=========================
|
||||
-->
|
||||
<target depends="init,compile-single" name="run-applet">
|
||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
||||
<j2seproject1:java classname="sun.applet.AppletViewer">
|
||||
<customize>
|
||||
<arg value="${applet.url}"/>
|
||||
</customize>
|
||||
</j2seproject1:java>
|
||||
</target>
|
||||
<!--
|
||||
=========================
|
||||
APPLET DEBUGGING SECTION
|
||||
=========================
|
||||
-->
|
||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
|
||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
||||
<j2seproject3:debug classname="sun.applet.AppletViewer">
|
||||
<customize>
|
||||
<arg value="${applet.url}"/>
|
||||
</customize>
|
||||
</j2seproject3:debug>
|
||||
</target>
|
||||
<target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
|
||||
<!--
|
||||
===============
|
||||
CLEANUP SECTION
|
||||
===============
|
||||
-->
|
||||
<target depends="init" name="deps-clean" unless="no.deps"/>
|
||||
<target depends="init" name="-do-clean">
|
||||
<delete dir="${build.dir}"/>
|
||||
<delete dir="${dist.dir}"/>
|
||||
</target>
|
||||
<target name="-post-clean">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
|
||||
</project>
|
@ -1,8 +0,0 @@
|
||||
build.xml.data.CRC32=209349b6
|
||||
build.xml.script.CRC32=403e69e6
|
||||
build.xml.stylesheet.CRC32=958a1d3e
|
||||
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
|
||||
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
|
||||
nbproject/build-impl.xml.data.CRC32=209349b6
|
||||
nbproject/build-impl.xml.script.CRC32=c51e188e
|
||||
nbproject/build-impl.xml.stylesheet.CRC32=65b8de21
|
@ -1,84 +0,0 @@
|
||||
application.homepage=http://bob.i2p/
|
||||
application.title=BOB
|
||||
application.vendor=Sponge
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs=false
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width=8
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab=8
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=8
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project
|
||||
build.classes.dir=${build.dir}/classes
|
||||
build.classes.excludes=**/*.java,**/*.form
|
||||
# This directory is removed when the project is cleaned:
|
||||
build.dir=build
|
||||
build.generated.dir=${build.dir}/generated
|
||||
build.generated.sources.dir=${build.dir}/generated-sources
|
||||
# Only compile against the classpath explicitly listed here:
|
||||
build.sysclasspath=ignore
|
||||
build.test.classes.dir=${build.dir}/test/classes
|
||||
build.test.results.dir=${build.dir}/test/results
|
||||
debug.classpath=\
|
||||
${run.classpath}
|
||||
debug.test.classpath=\
|
||||
${run.test.classpath}
|
||||
# This directory is removed when the project is cleaned:
|
||||
dist.dir=dist
|
||||
dist.jar=${dist.dir}/BOB.jar
|
||||
dist.javadoc.dir=${dist.dir}/javadoc
|
||||
endorsed.classpath=
|
||||
excludes=**/*.html,**/*.txt
|
||||
file.reference.build-javadoc=../../i2p.i2p/build/javadoc
|
||||
file.reference.i2p.jar=../../core/java/build/i2p.jar
|
||||
file.reference.mstreaming.jar=../ministreaming/java/build/mstreaming.jar
|
||||
includes=**
|
||||
jar.compress=true
|
||||
javac.classpath=\
|
||||
${file.reference.mstreaming.jar}:\
|
||||
${file.reference.i2p.jar}
|
||||
# Space-separated list of extra javac options
|
||||
javac.compilerargs=
|
||||
javac.deprecation=false
|
||||
javac.version=1.8
|
||||
javac.source=${javac.version}
|
||||
javac.target=${javac.version}
|
||||
javac.release=8
|
||||
javac.test.classpath=\
|
||||
${javac.classpath}:\
|
||||
${build.classes.dir}:\
|
||||
${libs.junit.classpath}:\
|
||||
${libs.junit_4.classpath}
|
||||
javadoc.additionalparam=
|
||||
javadoc.author=false
|
||||
javadoc.encoding=${source.encoding}
|
||||
javadoc.noindex=false
|
||||
javadoc.nonavbar=false
|
||||
javadoc.notree=false
|
||||
javadoc.private=false
|
||||
javadoc.splitindex=true
|
||||
javadoc.use=true
|
||||
javadoc.version=false
|
||||
javadoc.windowtitle=
|
||||
jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api"
|
||||
jnlp.codebase.type=local
|
||||
jnlp.codebase.url=file:/usblv/NetBeansProjects/i2p.i2p/apps/BOB/dist
|
||||
jnlp.descriptor=application
|
||||
jnlp.enabled=false
|
||||
jnlp.offline-allowed=false
|
||||
jnlp.signed=false
|
||||
main.class=net.i2p.BOB.Main
|
||||
manifest.file=manifest.mf
|
||||
meta.inf.dir=${src.dir}/META-INF
|
||||
platform.active=default_platform
|
||||
run.classpath=\
|
||||
${javac.classpath}:\
|
||||
${build.classes.dir}
|
||||
# Space-separated list of JVM arguments used when running the project
|
||||
# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
|
||||
# or test-sys-prop.name=value to set system properties for unit tests):
|
||||
run.jvmargs=
|
||||
run.test.classpath=\
|
||||
${javac.test.classpath}:\
|
||||
${build.test.classes.dir}
|
||||
source.encoding=UTF-8
|
||||
src.dir=src
|
||||
test.src.dir=test
|
@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||
<type>org.netbeans.modules.java.j2seproject</type>
|
||||
<configuration>
|
||||
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<name>BOB</name>
|
||||
<minimum-ant-version>1.9.8</minimum-ant-version>
|
||||
<source-roots>
|
||||
<root id="src.dir"/>
|
||||
</source-roots>
|
||||
<test-roots>
|
||||
<root id="test.src.dir"/>
|
||||
</test-roots>
|
||||
</data>
|
||||
</configuration>
|
||||
</project>
|
@ -1,542 +0,0 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import static net.i2p.app.ClientAppState.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.*;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.PortMapper;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
* <span style="font-size:8px;font-family:courier;color:#EEEEEE;background-color:#000000">
|
||||
* ################################################################################<br>
|
||||
* ############################.#..........#..#..........##########################<br>
|
||||
* #######################......................................###################<br>
|
||||
* ####################...........................#.......#........################<br>
|
||||
* #################..................##...................#.........##############<br>
|
||||
* ###############................###...####.....#..###.....#.........#############<br>
|
||||
* #############...........###..#..###...#####...###.##........#.......############<br>
|
||||
* ###########................#......##...#####...##..##.......#..#........########<br>
|
||||
* ##########.........................#....##.##..#...##.....................######<br>
|
||||
* #########...................................#....#.........................#####<br>
|
||||
* ########.........................................#...............#..........####<br>
|
||||
* ########.........................................#..........#######..........###<br>
|
||||
* #######.................................................############..........##<br>
|
||||
* #######..........................................####################.........##<br>
|
||||
* #######............####################......########################.........##<br>
|
||||
* ######.............###############################################.##.........##<br>
|
||||
* ######............################################################..##........##<br>
|
||||
* ######............################################################..##........##<br>
|
||||
* ######.............##############################################..##.........##<br>
|
||||
* ######............##############################################...##..........#<br>
|
||||
* ######............#..###########################################...##..........#<br>
|
||||
* ######.............#############################################....#..........#<br>
|
||||
* #######...........###############################################..##.........##<br>
|
||||
* #######...........#####.#.#.#.########################.....#.####...##........##<br>
|
||||
* ######............#..............##################.................##.........#<br>
|
||||
* ######................####.........###############........#####......##........#<br>
|
||||
* ######..............####..#.........############.......##.#.######...##.......##<br>
|
||||
* ######.................#.####.........########...........##....###...##.......##<br>
|
||||
* #######....#....###...................#######...............#...###..##.......##<br>
|
||||
* #######.........###..###.....###.......######.##.#####.........####..##.......##<br>
|
||||
* #######.....#...##############.........############......###########.###......##<br>
|
||||
* #######....##...##########.......##...##############......#.############.....###<br>
|
||||
* ########....#..########......######...##################################....####<br>
|
||||
* ########....##.####################...##################################....####<br>
|
||||
* ########..#.##..###################..##################################..#..####<br>
|
||||
* ##########..###..#################...##################################...#.####<br>
|
||||
* #########....##...##############....########..#####.################.##..#.#####<br>
|
||||
* ############.##....##########.......#########.###.......###########..#.#########<br>
|
||||
* ###############.....#######...#.......########.....##.....######.....###########<br>
|
||||
* ###############......###....##..........##.......######....#.........#.#########<br>
|
||||
* ##############............##..................##########..............##########<br>
|
||||
* ##############..............................##########..#.............##########<br>
|
||||
* ###############.......##..................#####..............####....###########<br>
|
||||
* ###############.......#####.......#.............####.....#######.....###########<br>
|
||||
* ################...#...####......##################.....########....############<br>
|
||||
* ################...##..#####.........####.##.....#....##########....############<br>
|
||||
* ##################..##..####...........#####.#....############.....#############<br>
|
||||
* ##################......#####.................################....##############<br>
|
||||
* ###################.....####..........##########..###########....###############<br>
|
||||
* ####################..#..#..........................########.....###############<br>
|
||||
* #####################.##.......###.................########....#################<br>
|
||||
* ######################.........#.......#.##.###############....#################<br>
|
||||
* #############.#######...............#####################....###################<br>
|
||||
* ###..#.....##...####..........#.....####################....####################<br>
|
||||
* ####......##........................##################....######################<br>
|
||||
* #.##...###..............###.........###############......#######################<br>
|
||||
* #...###..##............######...........................########################<br>
|
||||
* ##.......###..........##########....#...#...........############################<br>
|
||||
* ##.........##.......############################################################<br>
|
||||
* ###........##.....##############################################################<br>
|
||||
* ####.............###############################################################<br>
|
||||
* ######.........#################################################################<br>
|
||||
* #########....###################################################################<br>
|
||||
* ################################################################################<br>
|
||||
* </span>
|
||||
* BOB, main command socket listener, launches the command parser engine.
|
||||
*
|
||||
* @author sponge
|
||||
* @deprecated Please port applications to SAMv3
|
||||
*/
|
||||
@Deprecated
|
||||
public class BOB implements Runnable, ClientApp {
|
||||
|
||||
public final static String PROP_CONFIG_LOCATION = "BOB.config";
|
||||
public final static String PROP_BOB_PORT = "BOB.port";
|
||||
public final static String PROP_BOB_HOST = "BOB.host";
|
||||
public final static String PROP_CFG_VER = "BOB.CFG.VER";
|
||||
|
||||
/** unused when started via the ClientApp interface */
|
||||
private static BOB _bob;
|
||||
|
||||
private final NamedDB database;
|
||||
private final Properties props = new Properties();
|
||||
private final AtomicBoolean spin = new AtomicBoolean(true);
|
||||
private static final String P_RUNNING = "RUNNING";
|
||||
private static final String P_STARTING = "STARTING";
|
||||
private static final String P_STOPPING = "STOPPING";
|
||||
private final AtomicBoolean lock = new AtomicBoolean(false);
|
||||
// no longer used.
|
||||
// private static int maxConnections = 0;
|
||||
|
||||
private final I2PAppContext _context;
|
||||
private final Logger _log;
|
||||
private final ClientAppManager _mgr;
|
||||
private final String[] _args;
|
||||
private volatile ClientAppState _state = UNINITIALIZED;
|
||||
|
||||
private volatile ServerSocket listener;
|
||||
private volatile Thread _runner;
|
||||
private volatile boolean _warned;
|
||||
|
||||
/**
|
||||
* Stop BOB gracefully
|
||||
* @deprecated unused
|
||||
*/
|
||||
@Deprecated
|
||||
public synchronized static void stop() {
|
||||
if (_bob != null)
|
||||
_bob.shutdown(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* For ClientApp interface.
|
||||
* Does NOT open the listener socket or start threads; caller must call startup()
|
||||
*
|
||||
* @param mgr may be null
|
||||
* @param args non-null
|
||||
* @since 0.9.10
|
||||
*/
|
||||
public BOB(I2PAppContext context, ClientAppManager mgr, String[] args) {
|
||||
_context = context;
|
||||
// If we were run from command line, log to stdout
|
||||
boolean logToStdout = false;
|
||||
URL classResource = BOB.class.getResource("BOB.class");
|
||||
if (classResource != null) {
|
||||
String classPath = classResource.toString();
|
||||
if (classPath.startsWith("jar")) {
|
||||
String manifestPath = classPath.substring(0, classPath.lastIndexOf('!') + 1) +
|
||||
"/META-INF/MANIFEST.MF";
|
||||
try {
|
||||
Manifest manifest = new Manifest(new URL(manifestPath).openStream());
|
||||
Attributes attrs = manifest.getMainAttributes();
|
||||
String mainClass = attrs.getValue("Main-Class");
|
||||
if ("net.i2p.BOB.Main".equals(mainClass))
|
||||
logToStdout = true;
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
_log = new Logger(context.logManager().getLog(BOB.class), logToStdout);
|
||||
|
||||
_mgr = mgr;
|
||||
_args = args;
|
||||
_state = INITIALIZED;
|
||||
database = new NamedDB();
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for incoming connections and handle them
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public synchronized static void main(String[] args) {
|
||||
try {
|
||||
_bob = new BOB(I2PAppContext.getGlobalContext(), null, args);
|
||||
_bob.startup();
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void loadConfig() {
|
||||
int i = 0;
|
||||
boolean save = false;
|
||||
// Set up all defaults to be passed forward to other threads.
|
||||
// Re-reading the config file in each thread is pretty damn stupid.
|
||||
String configLocation = System.getProperty(PROP_CONFIG_LOCATION, "bob.config");
|
||||
// This is here just to ensure there is no interference with our threadgroups.
|
||||
SimpleTimer2 Y2 = SimpleTimer2.getInstance();
|
||||
i = Y2.hashCode();
|
||||
{
|
||||
File cfg = new File(configLocation);
|
||||
if (!cfg.isAbsolute()) {
|
||||
cfg = new File(_context.getConfigDir(), configLocation);
|
||||
}
|
||||
FileInputStream fi = null;
|
||||
try {
|
||||
fi = new FileInputStream(cfg);
|
||||
props.load(fi);
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
_log.warn("Unable to load up the BOB config file " + cfg.getAbsolutePath() + ", Using defaults.", fnfe);
|
||||
save = true;
|
||||
} catch (IOException ioe) {
|
||||
_log.warn("IOException on BOB config file " + cfg.getAbsolutePath() + ", using defaults.", ioe);
|
||||
} finally {
|
||||
if (fi != null) try { fi.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
// Global router and client API configurations that are missing are set to defaults here.
|
||||
if (!props.containsKey(I2PClient.PROP_TCP_HOST)) {
|
||||
props.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(I2PClient.PROP_TCP_PORT)) {
|
||||
props.setProperty(I2PClient.PROP_TCP_PORT, Integer.toString(I2PClient.DEFAULT_LISTEN_PORT));
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(PROP_BOB_PORT)) {
|
||||
props.setProperty(PROP_BOB_PORT, "2827"); // 0xB0B
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("inbound.length")) {
|
||||
props.setProperty("inbound.length", "3");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("outbound.length")) {
|
||||
props.setProperty("outbound.length", "3");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("inbound.lengthVariance")) {
|
||||
props.setProperty("inbound.lengthVariance", "0");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("outbound.lengthVariance")) {
|
||||
props.setProperty("outbound.lengthVariance", "0");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(PROP_BOB_HOST)) {
|
||||
props.setProperty(PROP_BOB_HOST, "localhost");
|
||||
save = true;
|
||||
}
|
||||
// PROP_RELIABILITY_NONE, PROP_RELIABILITY_BEST_EFFORT, PROP_RELIABILITY_GUARANTEED
|
||||
if (!props.containsKey(PROP_CFG_VER)) {
|
||||
props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_NONE);
|
||||
props.setProperty(PROP_CFG_VER,"1");
|
||||
save = true;
|
||||
}
|
||||
if (save) {
|
||||
File cfg = new File(configLocation);
|
||||
if (!cfg.isAbsolute()) {
|
||||
cfg = new File(_context.getConfigDir(), configLocation);
|
||||
}
|
||||
FileOutputStream fo = null;
|
||||
try {
|
||||
_log.warn("Writing new defaults file " + cfg.getAbsolutePath());
|
||||
fo = new FileOutputStream(cfg);
|
||||
props.store(fo, cfg.getAbsolutePath());
|
||||
} catch (IOException ioe) {
|
||||
_log.error("IOException on BOB config file " + cfg.getAbsolutePath(), ioe);
|
||||
} finally {
|
||||
if (fo != null) try { fo.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void startListener() throws IOException {
|
||||
listener = new ServerSocket(Integer.parseInt(props.getProperty(PROP_BOB_PORT)), 10, InetAddress.getByName(props.getProperty(PROP_BOB_HOST)));
|
||||
listener.setSoTimeout(500); // .5 sec
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void startThread() {
|
||||
I2PAppThread t = new I2PAppThread(this, "BOBListener");
|
||||
t.start();
|
||||
_runner = t;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
public void run() {
|
||||
if (listener == null) return;
|
||||
changeState(RUNNING);
|
||||
_log.info("BOB is now running.");
|
||||
if (_mgr != null)
|
||||
_mgr.register(this);
|
||||
_context.portMapper().register(PortMapper.SVC_BOB, props.getProperty(PROP_BOB_HOST),
|
||||
Integer.parseInt(props.getProperty(PROP_BOB_PORT)));
|
||||
|
||||
int i = 0;
|
||||
boolean g = false;
|
||||
spin.set(true);
|
||||
try {
|
||||
Socket server = null;
|
||||
|
||||
while (spin.get()) {
|
||||
//DoCMDS connection;
|
||||
|
||||
try {
|
||||
server = listener.accept();
|
||||
server.setKeepAlive(true);
|
||||
g = true;
|
||||
} catch (ConnectException ce) {
|
||||
g = false;
|
||||
} catch (SocketTimeoutException ste) {
|
||||
g = false;
|
||||
}
|
||||
|
||||
if (g) {
|
||||
if (!_warned) {
|
||||
_warned = true;
|
||||
String s = "BOB is deprecated. Please port applications to SAMv3.";
|
||||
_context.logManager().getLog(BOB.class).logAlways(Log.WARN, s);
|
||||
_log.warn(s);
|
||||
}
|
||||
DoCMDS conn_c = new DoCMDS(spin, lock, server, props, database, _log);
|
||||
Thread t = new I2PAppThread(conn_c);
|
||||
t.setName("BOB.DoCMDS " + i);
|
||||
t.start();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
changeState(STOPPING);
|
||||
} catch (Exception e) {
|
||||
if (spin.get())
|
||||
_log.error("Unexpected error while listening for connections", e);
|
||||
else
|
||||
e = null;
|
||||
changeState(STOPPING, e);
|
||||
} finally {
|
||||
_log.info("BOB is now shutting down...");
|
||||
_context.portMapper().unregister(PortMapper.SVC_BOB);
|
||||
// Clean up everything.
|
||||
try {
|
||||
listener.close();
|
||||
} catch (Exception ex) {
|
||||
// nop
|
||||
}
|
||||
// Find all our "BOB.DoCMDS" threads, wait for them to be finished.
|
||||
// We could order them to stop, but that could cause nasty issues in the locks.
|
||||
visitAllThreads();
|
||||
database.getReadLock();
|
||||
NamedDB nickinfo;
|
||||
try {
|
||||
for (Object ndb : database.values()) {
|
||||
nickinfo = (NamedDB) ndb;
|
||||
nickinfo.getReadLock();
|
||||
boolean released = false;
|
||||
try {
|
||||
if (nickinfo.get(P_RUNNING).equals(Boolean.TRUE) && nickinfo.get(P_STOPPING).equals(Boolean.FALSE) && nickinfo.get(P_STARTING).equals(Boolean.FALSE)) {
|
||||
nickinfo.releaseReadLock();
|
||||
released = true;
|
||||
nickinfo.getWriteLock();
|
||||
try {
|
||||
nickinfo.add(P_STOPPING, Boolean.TRUE);
|
||||
} finally {
|
||||
nickinfo.releaseWriteLock();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (!released)
|
||||
nickinfo.releaseReadLock();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
changeState(STOPPED);
|
||||
_log.info("BOB is now stopped.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the root thread group,
|
||||
* then find all theads with certain names and wait for them all to be dead.
|
||||
*
|
||||
*/
|
||||
private static void visitAllThreads() {
|
||||
ThreadGroup root = Thread.currentThread().getThreadGroup().getParent();
|
||||
while (root.getParent() != null) {
|
||||
root = root.getParent();
|
||||
}
|
||||
|
||||
// Visit each thread group
|
||||
waitjoin(root, 0, root.getName());
|
||||
}
|
||||
|
||||
private static void waitjoin(ThreadGroup group, int level, String tn) {
|
||||
// Get threads in `group'
|
||||
int numThreads = group.activeCount();
|
||||
Thread[] threads = new Thread[numThreads * 2];
|
||||
numThreads = group.enumerate(threads, false);
|
||||
// Enumerate each thread in `group' and wait for it to stop if it is one of ours.
|
||||
for (int i = 0; i < numThreads; i++) {
|
||||
// Get thread
|
||||
Thread thread = threads[i];
|
||||
if (thread.getName().startsWith("BOB.DoCMDS ")) {
|
||||
try {
|
||||
if (thread.isAlive()) {
|
||||
try {
|
||||
thread.join();
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
} catch (SecurityException se) {
|
||||
//nop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get thread subgroups of `group'
|
||||
int numGroups = group.activeGroupCount();
|
||||
ThreadGroup[] groups = new ThreadGroup[numGroups * 2];
|
||||
numGroups = group.enumerate(groups, false);
|
||||
|
||||
// Recursively visit each subgroup
|
||||
for (int i = 0; i < numGroups; i++) {
|
||||
waitjoin(groups[i], level + 1, groups[i].getName());
|
||||
}
|
||||
}
|
||||
|
||||
////// begin ClientApp interface
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public void startup() throws IOException {
|
||||
if (_state != INITIALIZED)
|
||||
return;
|
||||
changeState(STARTING);
|
||||
try {
|
||||
startListener();
|
||||
} catch (IOException e) {
|
||||
_log.error("Error starting BOB on"
|
||||
+ props.getProperty(PROP_BOB_HOST)
|
||||
+ ":" + props.getProperty(PROP_BOB_PORT), e);
|
||||
changeState(START_FAILED, e);
|
||||
throw e;
|
||||
}
|
||||
startThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public void shutdown(String[] args) {
|
||||
if (_state != RUNNING)
|
||||
return;
|
||||
changeState(STOPPING);
|
||||
spin.set(false);
|
||||
if (_runner != null)
|
||||
_runner.interrupt();
|
||||
else
|
||||
changeState(STOPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public ClientAppState getState() {
|
||||
return _state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return "BOB";
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "BOB " + Arrays.toString(_args);
|
||||
}
|
||||
|
||||
////// end ClientApp interface
|
||||
////// begin ClientApp helpers
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void changeState(ClientAppState state) {
|
||||
changeState(state, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private synchronized void changeState(ClientAppState state, Exception e) {
|
||||
_state = state;
|
||||
if (_mgr != null)
|
||||
_mgr.notify(this, state, null, e);
|
||||
}
|
||||
|
||||
////// end ClientApp helpers
|
||||
}
|
@ -1,996 +0,0 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.net.Socket;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
// needed only for debugging.
|
||||
// import java.util.logging.Level;
|
||||
// import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Simplistic command parser for BOB
|
||||
*
|
||||
* @author sponge
|
||||
*
|
||||
*/
|
||||
public class DoCMDS implements Runnable {
|
||||
|
||||
// FIX ME
|
||||
// I need a better way to do versioning, but this will do for now.
|
||||
public static final String BMAJ = "00", BMIN = "00", BREV = "10", BEXT = "";
|
||||
public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT;
|
||||
private final Socket server;
|
||||
private final Properties props;
|
||||
private final NamedDB database;
|
||||
private String line;
|
||||
private Destination d;
|
||||
private ByteArrayOutputStream prikey;
|
||||
private boolean dk, ns, ip, op;
|
||||
private NamedDB nickinfo;
|
||||
private final Logger _log;
|
||||
private final AtomicBoolean LIVE;
|
||||
private final AtomicBoolean lock;
|
||||
/* database strings */
|
||||
private static final String P_DEST = "DESTINATION";
|
||||
private static final String P_INHOST = "INHOST";
|
||||
private static final String P_INPORT = "INPORT";
|
||||
private static final String P_KEYS = "KEYS";
|
||||
private static final String P_NICKNAME = "NICKNAME";
|
||||
private static final String P_OUTHOST = "OUTHOST";
|
||||
private static final String P_OUTPORT = "OUTPORT";
|
||||
private static final String P_PROPERTIES = "PROPERTIES";
|
||||
private static final String P_QUIET = "QUIET";
|
||||
private static final String P_RUNNING = "RUNNING";
|
||||
private static final String P_STARTING = "STARTING";
|
||||
private static final String P_STOPPING = "STOPPING";
|
||||
|
||||
/* command strings */
|
||||
private static final String C_help = "help";
|
||||
private static final String C_clear = "clear";
|
||||
private static final String C_getdest = "getdest";
|
||||
private static final String C_getkeys = "getkeys";
|
||||
private static final String C_getnick = "getnick";
|
||||
private static final String C_inhost = "inhost";
|
||||
private static final String C_inport = "inport";
|
||||
private static final String C_list = "list";
|
||||
private static final String C_lookup = "lookup";
|
||||
private static final String C_newkeys = "newkeys";
|
||||
private static final String C_option = "option";
|
||||
private static final String C_outhost = "outhost";
|
||||
private static final String C_outport = "outport";
|
||||
private static final String C_quiet = "quiet";
|
||||
private static final String C_quit = "quit";
|
||||
private static final String C_setkeys = "setkeys";
|
||||
private static final String C_setnick = "setnick";
|
||||
private static final String C_show = "show";
|
||||
private static final String C_show_props = "showprops";
|
||||
private static final String C_start = "start";
|
||||
private static final String C_status = "status";
|
||||
private static final String C_stop = "stop";
|
||||
private static final String C_verify = "verify";
|
||||
private static final String C_visit = "visit";
|
||||
private static final String C_zap = "zap";
|
||||
|
||||
/* all the commands available, plus description */
|
||||
private static final String C_ALL[][] = {
|
||||
{C_help, C_help + " <command> * Get help on a command."},
|
||||
{C_clear, C_clear + " * Clear the current nickname out of the list."},
|
||||
{C_getdest, C_getdest + " * Return the destination for the current nickname."},
|
||||
{C_getkeys, C_getkeys + " * Return the keypair for the current nickname."},
|
||||
{C_getnick, C_getnick + " tunnelname * Set the nickname from the database."},
|
||||
{C_inhost, C_inhost + " hostname | IP * Set the inbound hostname or IP."},
|
||||
{C_inport, C_inport + " port_number * Set the inbound port number nickname listens on."},
|
||||
{C_list, C_list + " * List all tunnels."},
|
||||
{C_lookup, C_lookup + " * Lookup an i2p address."},
|
||||
{C_newkeys, C_newkeys + " * Generate a new keypair for the current nickname."},
|
||||
{C_option, C_option + " I2CPoption=something * Set an I2CP option. NOTE: Don't use any spaces."},
|
||||
{C_outhost, C_outhost + " hostname | IP * Set the outbound hostname or IP."},
|
||||
{C_outport, C_outport + " port_number * Set the outbound port that nickname contacts."},
|
||||
{C_quiet, C_quiet + " True | False * Don't send to the application the incoming destination."},
|
||||
{C_quit, C_quit + " * Quits this session with BOB."},
|
||||
{C_setkeys, C_setkeys + " BASE64_keypair * Sets the keypair for the current nickname."},
|
||||
{C_setnick, C_setnick + " nickname * Create a new nickname."},
|
||||
{C_show, C_show + " * Display the status of the current nickname."},
|
||||
{C_show_props, C_show_props + " * Display the properties of the current nickname."},
|
||||
{C_start, C_start + " * Start the current nickname tunnel."},
|
||||
{C_status, C_status + " nickname * Display status of a nicknamed tunnel."},
|
||||
{C_stop, C_stop + " * Stops the current nicknamed tunnel."},
|
||||
{C_verify, C_verify + " BASE64_key * Verifies BASE64 destination."},
|
||||
{C_visit, C_visit + " * Thread dump to wrapper.log."},
|
||||
{C_zap, C_zap + " * Shuts down BOB."},
|
||||
{"", "COMMANDS: " + // this is ugly, but...
|
||||
C_help + " " +
|
||||
C_clear + " " +
|
||||
C_getdest + " " +
|
||||
C_getkeys + " " +
|
||||
C_getnick + " " +
|
||||
C_inhost + " " +
|
||||
C_inport + " " +
|
||||
C_list + " " +
|
||||
C_lookup + " " +
|
||||
C_newkeys + " " +
|
||||
C_option + " " +
|
||||
C_outhost + " " +
|
||||
C_outport + " " +
|
||||
C_quiet + " " +
|
||||
C_quit + " " +
|
||||
C_setkeys + " " +
|
||||
C_setnick + " " +
|
||||
C_show + " " +
|
||||
C_show_props + " " +
|
||||
C_start + " " +
|
||||
C_status + " " +
|
||||
C_stop + " " +
|
||||
C_verify + " " +
|
||||
C_visit + " " +
|
||||
C_zap
|
||||
},
|
||||
{" ", " "} // end of list
|
||||
};
|
||||
|
||||
/**
|
||||
* @param LIVE
|
||||
* @param server
|
||||
* @param props
|
||||
* @param database
|
||||
* @param _log
|
||||
*/
|
||||
DoCMDS(AtomicBoolean LIVE, AtomicBoolean lock, Socket server, Properties props, NamedDB database, Logger _log) {
|
||||
this.lock = lock;
|
||||
this.LIVE = LIVE;
|
||||
this.server = server;
|
||||
this.props = new Properties();
|
||||
this.database = database;
|
||||
this._log = _log;
|
||||
Lifted.copyProperties(props, this.props);
|
||||
}
|
||||
|
||||
private void rlock() {
|
||||
rlock(nickinfo);
|
||||
}
|
||||
|
||||
private void rlock(NamedDB Arg) {
|
||||
database.getReadLock();
|
||||
Arg.getReadLock();
|
||||
}
|
||||
|
||||
private void runlock() {
|
||||
runlock(nickinfo);
|
||||
}
|
||||
|
||||
private void runlock(NamedDB Arg) {
|
||||
Arg.releaseReadLock();
|
||||
database.releaseReadLock();
|
||||
}
|
||||
|
||||
private void wlock() {
|
||||
wlock(nickinfo);
|
||||
}
|
||||
|
||||
private void wlock(NamedDB Arg) {
|
||||
database.getWriteLock();
|
||||
Arg.getWriteLock();
|
||||
}
|
||||
|
||||
private void wunlock() {
|
||||
wunlock(nickinfo);
|
||||
}
|
||||
|
||||
private void wunlock(NamedDB Arg) {
|
||||
Arg.releaseWriteLock();
|
||||
database.releaseWriteLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to print info from the database
|
||||
*
|
||||
* @param out
|
||||
* @param info
|
||||
* @param key
|
||||
*/
|
||||
private void trypnt(PrintStream out, NamedDB info, String key) {
|
||||
rlock(info);
|
||||
try {
|
||||
out.print(" " + key + ": ");
|
||||
if (info.exists(key)) {
|
||||
out.print(info.get(key));
|
||||
} else {
|
||||
out.print("not_set");
|
||||
}
|
||||
} finally {
|
||||
runlock(info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print true or false if an object exists
|
||||
*
|
||||
* @param out
|
||||
* @param info
|
||||
* @param key
|
||||
*/
|
||||
private void tfpnt(PrintStream out, NamedDB info, String key) {
|
||||
rlock(info);
|
||||
try {
|
||||
out.print(" " + key + ": ");
|
||||
out.print(info.exists(key));
|
||||
} finally {
|
||||
runlock(info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an error message
|
||||
*
|
||||
* @param out
|
||||
*/
|
||||
private static void nns(PrintStream out) {
|
||||
out.println("ERROR no nickname has been set");
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump various information from the database
|
||||
*
|
||||
* @param out
|
||||
* @param info
|
||||
*/
|
||||
private void nickprint(PrintStream out, NamedDB info) {
|
||||
trypnt(out, info, P_NICKNAME);
|
||||
trypnt(out, info, P_STARTING);
|
||||
trypnt(out, info, P_RUNNING);
|
||||
trypnt(out, info, P_STOPPING);
|
||||
tfpnt(out, info, P_KEYS);
|
||||
trypnt(out, info, P_QUIET);
|
||||
trypnt(out, info, P_INPORT);
|
||||
trypnt(out, info, P_INHOST);
|
||||
trypnt(out, info, P_OUTPORT);
|
||||
trypnt(out, info, P_OUTHOST);
|
||||
out.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump properties information from the database
|
||||
*
|
||||
* @param out
|
||||
* @param info
|
||||
*/
|
||||
private void propprint(PrintStream out, NamedDB info) {
|
||||
trypnt(out, info, P_PROPERTIES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print information on a specific record, indicated by NamedDB
|
||||
* @param out
|
||||
* @param Arg
|
||||
*/
|
||||
private void ttlpnt(PrintStream out, String Arg) {
|
||||
database.getReadLock();
|
||||
try {
|
||||
if (database.exists(Arg)) {
|
||||
out.print("DATA");
|
||||
nickprint(out, (NamedDB) database.get(Arg));
|
||||
}
|
||||
} finally {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this NamedDB's tunnel active?
|
||||
*
|
||||
* @param Arg
|
||||
* @return true if the tunnel is active
|
||||
*/
|
||||
private boolean tunnelactive(NamedDB Arg) {
|
||||
boolean retval;
|
||||
rlock(Arg);
|
||||
try {
|
||||
retval = (Arg.get(P_STARTING).equals(Boolean.TRUE) ||
|
||||
Arg.get(P_STOPPING).equals(Boolean.TRUE) ||
|
||||
Arg.get(P_RUNNING).equals(Boolean.TRUE));
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the base64 information look OK
|
||||
*
|
||||
* @param data
|
||||
* @return OK
|
||||
*/
|
||||
private static boolean is64ok(String data) {
|
||||
try {
|
||||
new Destination(data);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual parser.
|
||||
* It probabbly needs a rewrite into functions, but I kind-of like inline code.
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
dk = ns = ip = op = false;
|
||||
try {
|
||||
try {
|
||||
// Get input from the client
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
|
||||
PrintStream out = new PrintStream(server.getOutputStream());
|
||||
quit:
|
||||
{
|
||||
die:
|
||||
{
|
||||
prikey = new ByteArrayOutputStream();
|
||||
out.println("BOB " + BOBversion);
|
||||
out.println("OK");
|
||||
while ((line = in.readLine()) != null) {
|
||||
StringTokenizer token = new StringTokenizer(line, " "); // use a space as a delimiter
|
||||
String Command = "";
|
||||
String Arg = "";
|
||||
NamedDB info;
|
||||
|
||||
if (token.countTokens() != 0) {
|
||||
Command = token.nextToken();
|
||||
Command =
|
||||
Command.toLowerCase(Locale.US);
|
||||
if (token.countTokens() != 0) {
|
||||
Arg = token.nextToken();
|
||||
} else {
|
||||
Arg = "";
|
||||
}
|
||||
// The rest of the tokens are considered junk,
|
||||
// and discarded without any warnings.
|
||||
if (Command.equals(C_help)) {
|
||||
for (int i = 0; !C_ALL[i][0].equals(" "); i++) {
|
||||
if (C_ALL[i][0].equalsIgnoreCase(Arg)) {
|
||||
out.println("OK " + C_ALL[i][1]);
|
||||
}
|
||||
}
|
||||
} else if (Command.equals(C_visit)) {
|
||||
visitAllThreads();
|
||||
out.println("OK ");
|
||||
} else if (Command.equals(C_lookup)) {
|
||||
Destination dest = null;
|
||||
String reply = null;
|
||||
if (Arg.endsWith(".i2p")) {
|
||||
try {
|
||||
//try {
|
||||
//dest = I2PTunnel.destFromName(Arg);
|
||||
//} catch (DataFormatException ex) {
|
||||
//}
|
||||
dest = I2PAppContext.getGlobalContext().namingService().lookup(Arg);
|
||||
if(dest != null) {
|
||||
reply = dest.toBase64();
|
||||
}
|
||||
} catch (NullPointerException npe) {
|
||||
// Could not find the destination!?
|
||||
}
|
||||
}
|
||||
if (reply == null) {
|
||||
out.println("ERROR Address Not found.");
|
||||
} else {
|
||||
out.println("OK " + reply);
|
||||
}
|
||||
} else if (Command.equals(C_getdest)) {
|
||||
if (ns) {
|
||||
if (dk) {
|
||||
rlock();
|
||||
try {
|
||||
out.println("OK " + nickinfo.get(P_DEST));
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
} else {
|
||||
out.println("ERROR keys not set.");
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_list)) {
|
||||
// Produce a formatted list of all nicknames
|
||||
database.getReadLock();
|
||||
try {
|
||||
for (Object ndb : database.values()) {
|
||||
try {
|
||||
info = (NamedDB) ndb;
|
||||
out.print("DATA");
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
}
|
||||
nickprint(out, info);
|
||||
}
|
||||
} finally {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
out.println("OK Listing done");
|
||||
} else if (Command.equals(C_quit)) {
|
||||
// End the command session
|
||||
break quit;
|
||||
} else if (Command.equals(C_zap)) {
|
||||
// Kill BOB!! (let's hope this works!)
|
||||
LIVE.set(false);
|
||||
// End the command session
|
||||
break quit;
|
||||
} else if (Command.equals(C_newkeys)) {
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
try {
|
||||
// Make a new PublicKey and PrivateKey
|
||||
prikey = new ByteArrayOutputStream();
|
||||
d = I2PClientFactory.createClient().createDestination(prikey);
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.add(P_KEYS, prikey.toByteArray());
|
||||
nickinfo.add(P_DEST, d.toBase64());
|
||||
out.println("OK " + nickinfo.get(P_DEST));
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
dk = true;
|
||||
} catch (I2PException ipe) {
|
||||
_log.error("Error generating keys", ipe);
|
||||
out.println("ERROR generating keys");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_getkeys)) {
|
||||
// Return public key
|
||||
if (dk) {
|
||||
prikey = new ByteArrayOutputStream();
|
||||
rlock();
|
||||
try {
|
||||
prikey.write(((byte[]) nickinfo.get(P_KEYS)));
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
out.println("OK " + net.i2p.data.Base64.encode(prikey.toByteArray()));
|
||||
} else {
|
||||
out.println("ERROR no public key has been set");
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_quiet)) {
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.add(P_QUIET, Boolean.valueOf(Arg));
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
out.println("OK Quiet set");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_verify)) {
|
||||
if (is64ok(Arg)) {
|
||||
out.println("OK");
|
||||
} else {
|
||||
out.println("ERROR not in BASE64 format");
|
||||
}
|
||||
} else if (Command.equals(C_setkeys)) {
|
||||
// Set the NamedDB to a privatekey in BASE64 format
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
try {
|
||||
prikey = new ByteArrayOutputStream();
|
||||
prikey.write(net.i2p.data.Base64.decode(Arg));
|
||||
d = new Destination();
|
||||
d.fromBase64(Arg);
|
||||
} catch (Exception ex) {
|
||||
Arg = "";
|
||||
}
|
||||
|
||||
if ((Arg.length() == 884) && is64ok(Arg)) {
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.add(P_KEYS, prikey.toByteArray());
|
||||
nickinfo.add(P_DEST, d.toBase64());
|
||||
out.println("OK " + nickinfo.get(P_DEST));
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
dk = true;
|
||||
} else {
|
||||
out.println("ERROR not in BASE64 format");
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_setnick)) {
|
||||
ns = dk = ip = op = false;
|
||||
database.getReadLock();
|
||||
try {
|
||||
nickinfo = (NamedDB) database.get(Arg);
|
||||
if (!tunnelactive(nickinfo)) {
|
||||
nickinfo = null;
|
||||
ns = true;
|
||||
}
|
||||
|
||||
} catch (Exception b) {
|
||||
nickinfo = null;
|
||||
ns = true;
|
||||
} finally {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
// Clears and Sets the initial NamedDB structure to work with
|
||||
if (ns) {
|
||||
nickinfo = new NamedDB();
|
||||
wlock();
|
||||
try {
|
||||
database.add(Arg, nickinfo);
|
||||
nickinfo.add(P_NICKNAME, Arg);
|
||||
nickinfo.add(P_STARTING, Boolean.FALSE);
|
||||
nickinfo.add(P_RUNNING, Boolean.FALSE);
|
||||
nickinfo.add(P_STOPPING, Boolean.FALSE);
|
||||
nickinfo.add(P_QUIET, Boolean.FALSE);
|
||||
nickinfo.add(P_INHOST, "localhost");
|
||||
nickinfo.add(P_OUTHOST, "localhost");
|
||||
Properties Q = new Properties();
|
||||
Lifted.copyProperties(this.props, Q);
|
||||
Q.setProperty("inbound.nickname", Arg);
|
||||
Q.setProperty("outbound.nickname", Arg);
|
||||
nickinfo.add(P_PROPERTIES, Q);
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
out.println("OK Nickname set to " + Arg);
|
||||
} else {
|
||||
out.println("ERROR tunnel is active");
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_option)) {
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
StringTokenizer otoken = new StringTokenizer(Arg, "="); // use an equal sign as a delimiter
|
||||
if (otoken.countTokens() != 2) {
|
||||
out.println("ERROR too many or no options.");
|
||||
} else {
|
||||
String pname = otoken.nextToken();
|
||||
String pval = otoken.nextToken();
|
||||
wlock();
|
||||
try {
|
||||
Properties Q = (Properties) nickinfo.get(P_PROPERTIES);
|
||||
Q.setProperty(pname, pval);
|
||||
nickinfo.add(P_PROPERTIES, Q);
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
out.println("OK " + pname + " set to " + pval);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_getnick)) {
|
||||
// Get the NamedDB to work with...
|
||||
boolean nsfail = false;
|
||||
database.getReadLock();
|
||||
try {
|
||||
nickinfo = (NamedDB) database.get(Arg);
|
||||
ns = true;
|
||||
} catch (RuntimeException b) {
|
||||
nsfail = true;
|
||||
nns(out);
|
||||
} finally {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
if (ns && !nsfail) {
|
||||
rlock();
|
||||
try {
|
||||
dk = nickinfo.exists(P_KEYS);
|
||||
ip = nickinfo.exists(P_INPORT);
|
||||
op = nickinfo.exists(P_OUTPORT);
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
// Finally say OK.
|
||||
out.println("OK Nickname set to " + Arg);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_inport)) {
|
||||
// Set the NamedDB inbound TO the router port
|
||||
// app --> BOB
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
int prt;
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.kill(P_INPORT);
|
||||
prt = Integer.parseInt(Arg);
|
||||
if (prt > 1 && prt < 65536) {
|
||||
try {
|
||||
nickinfo.add(P_INPORT, Integer.valueOf(prt));
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
}
|
||||
ip = nickinfo.exists(P_INPORT);
|
||||
} catch (NumberFormatException nfe) {
|
||||
out.println("ERROR not a number");
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
if (ip) {
|
||||
out.println("OK inbound port set");
|
||||
} else {
|
||||
out.println("ERROR port out of range");
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_outport)) {
|
||||
// Set the NamedDB outbound FROM the router port
|
||||
// BOB --> app
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
int prt;
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.kill(P_OUTPORT);
|
||||
prt = Integer.parseInt(Arg);
|
||||
if (prt > 1 && prt < 65536) {
|
||||
nickinfo.add(P_OUTPORT, Integer.valueOf(prt));
|
||||
}
|
||||
ip = nickinfo.exists(P_OUTPORT);
|
||||
} catch (NumberFormatException nfe) {
|
||||
out.println("ERROR not a number");
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
if (ip) {
|
||||
out.println("OK outbound port set");
|
||||
} else {
|
||||
out.println("ERROR port out of range");
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_inhost)) {
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.add(P_INHOST, Arg);
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
out.println("OK inhost set");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_outhost)) {
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.add(P_OUTHOST, Arg);
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
out.println("OK outhost set");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_show)) {
|
||||
// Get the current NamedDB properties
|
||||
if (ns) {
|
||||
out.print("OK");
|
||||
nickprint(out, nickinfo);
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_show_props)) {
|
||||
// Get the current options properties
|
||||
if (ns) {
|
||||
out.print("OK");
|
||||
propprint(out, nickinfo);
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_start)) {
|
||||
// Start the tunnel, if we have all the information
|
||||
if (ns && dk && (ip || op)) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
MUXlisten tunnel;
|
||||
try {
|
||||
while (!lock.compareAndSet(false, true)) {
|
||||
// wait
|
||||
}
|
||||
tunnel = new MUXlisten(lock, database, nickinfo, _log);
|
||||
Thread t = new I2PAppThread(tunnel);
|
||||
t.start();
|
||||
// try {
|
||||
// Thread.sleep(1000 * 10); // Slow down the startup.
|
||||
// } catch(InterruptedException ie) {
|
||||
// // ignore it
|
||||
// }
|
||||
out.println("OK tunnel starting");
|
||||
} catch (I2PException e) {
|
||||
lock.set(false);
|
||||
out.println("ERROR starting tunnel: " + e);
|
||||
} catch (IOException e) {
|
||||
lock.set(false);
|
||||
out.println("ERROR starting tunnel: " + e);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
|
||||
} else {
|
||||
out.println("ERROR tunnel settings incomplete");
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_stop)) {
|
||||
// Stop the tunnel, if it is running
|
||||
if (ns) {
|
||||
rlock();
|
||||
boolean released = false;
|
||||
try {
|
||||
if (nickinfo.get(P_RUNNING).equals(Boolean.TRUE) && nickinfo.get(P_STOPPING).equals(Boolean.FALSE) && nickinfo.get(P_STARTING).equals(Boolean.FALSE)) {
|
||||
runlock();
|
||||
released = true;
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.add(P_STOPPING, Boolean.TRUE);
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
out.println("OK tunnel stopping");
|
||||
} else {
|
||||
out.println("ERROR tunnel is inactive");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
if (!released)
|
||||
runlock();
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_clear)) {
|
||||
// Clear use of the NamedDB if stopped
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
database.getWriteLock();
|
||||
try {
|
||||
database.kill((String) nickinfo.get(P_NICKNAME));
|
||||
} catch (Exception e) {
|
||||
} finally {
|
||||
database.releaseWriteLock();
|
||||
}
|
||||
dk = ns = ip = op = false;
|
||||
out.println("OK cleared");
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_status)) {
|
||||
database.getReadLock();
|
||||
try {
|
||||
if (database.exists(Arg)) {
|
||||
// Show status of a NamedDB
|
||||
out.print("OK ");
|
||||
try {
|
||||
ttlpnt(out, Arg);
|
||||
} catch (Exception e) {
|
||||
out.println(); // this will cause an IOE if IOE
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
} else {
|
||||
out.println("ERROR UNKNOWN COMMAND! Try help");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} // die
|
||||
out.print("ERROR A really bad error just happened, ");
|
||||
} // quit
|
||||
// Say goodbye.
|
||||
|
||||
out.println("OK Bye!");
|
||||
|
||||
} catch (IOException ioe) {
|
||||
// not really needed, except to debug.
|
||||
// BOB.warn("IOException on socket listen: " + ioe);
|
||||
// ioe.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
server.close();
|
||||
} catch (IOException ex) {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
}
|
||||
// Debugging... None of this is normally used.
|
||||
|
||||
/**
|
||||
* Find the root thread group and print them all.
|
||||
*
|
||||
*/
|
||||
private void visitAllThreads() {
|
||||
ThreadGroup root = Thread.currentThread().getThreadGroup().getParent();
|
||||
while (root.getParent() != null) {
|
||||
root = root.getParent();
|
||||
}
|
||||
|
||||
// Visit each thread group
|
||||
visit(root, 0, root.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively visits all thread groups under `group' and dumps them.
|
||||
* @param group ThreadGroup to visit
|
||||
* @param level Current level
|
||||
*/
|
||||
private static void visit(ThreadGroup group, int level, String tn) {
|
||||
// Get threads in `group'
|
||||
int numThreads = group.activeCount();
|
||||
Thread[] threads = new Thread[numThreads * 2];
|
||||
numThreads = group.enumerate(threads, false);
|
||||
String indent = "------------------------------------".substring(0, level) + "-> ";
|
||||
// Enumerate each thread in `group' and print it.
|
||||
for (int i = 0; i < numThreads; i++) {
|
||||
// Get thread
|
||||
Thread thread = threads[i];
|
||||
System.out.println("BOB: " + indent + tn + ": " + thread.toString());
|
||||
}
|
||||
|
||||
// Get thread subgroups of `group'
|
||||
int numGroups = group.activeGroupCount();
|
||||
ThreadGroup[] groups = new ThreadGroup[numGroups * 2];
|
||||
numGroups = group.enumerate(groups, false);
|
||||
|
||||
// Recursively visit each subgroup
|
||||
for (int i = 0; i < numGroups; i++) {
|
||||
visit(groups[i], level + 1, groups[i].getName());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
* Listen on I2P and connect to TCP
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class I2Plistener implements Runnable {
|
||||
|
||||
private final NamedDB info, database;
|
||||
private final Logger _log;
|
||||
private final I2PServerSocket serverSocket;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param SS
|
||||
* @param S unused
|
||||
* @param info
|
||||
* @param database
|
||||
* @param _log
|
||||
*/
|
||||
I2Plistener(I2PServerSocket SS, I2PSocketManager S, NamedDB info, NamedDB database, Logger _log, AtomicBoolean lives) {
|
||||
this.database = database;
|
||||
this.info = info;
|
||||
this._log = _log;
|
||||
this.serverSocket = SS;
|
||||
this.lives = lives;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply listen on I2P port, and thread connections
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
boolean g = false;
|
||||
I2PSocket sessSocket = null;
|
||||
int conn = 0;
|
||||
try {
|
||||
try {
|
||||
serverSocket.setSoTimeout(50);
|
||||
|
||||
while (lives.get()) {
|
||||
try {
|
||||
sessSocket = serverSocket.accept();
|
||||
g = true;
|
||||
} catch (ConnectException ce) {
|
||||
g = false;
|
||||
} catch (SocketTimeoutException ste) {
|
||||
g = false;
|
||||
}
|
||||
if (g) {
|
||||
g = false;
|
||||
conn++;
|
||||
// toss the connection to a new thread.
|
||||
I2PtoTCP conn_c = new I2PtoTCP(sessSocket, info, database, lives);
|
||||
Thread t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " I2PtoTCP " + conn);
|
||||
t.start();
|
||||
}
|
||||
|
||||
}
|
||||
} catch (I2PException e) {
|
||||
// bad stuff
|
||||
System.out.println("Exception " + e);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
serverSocket.close();
|
||||
} catch (I2PException ex) {
|
||||
}
|
||||
// System.out.println("I2Plistener: Close");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
* Process I2P->TCP
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class I2PtoTCP implements Runnable {
|
||||
|
||||
private I2PSocket I2P;
|
||||
private final NamedDB info, database;
|
||||
private Socket sock;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param I2Psock
|
||||
* @param info
|
||||
* @param database
|
||||
*/
|
||||
I2PtoTCP(I2PSocket I2Psock, NamedDB info, NamedDB database, AtomicBoolean lives) {
|
||||
this.I2P = I2Psock;
|
||||
this.info = info;
|
||||
this.database = database;
|
||||
this.lives = lives;
|
||||
}
|
||||
|
||||
private void rlock() {
|
||||
database.getReadLock();
|
||||
info.getReadLock();
|
||||
}
|
||||
|
||||
private void runlock() {
|
||||
info.releaseReadLock();
|
||||
database.releaseReadLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* I2P stream to TCP stream thread starter
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
String host;
|
||||
int port;
|
||||
boolean tell;
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
InputStream Iin = null;
|
||||
OutputStream Iout = null;
|
||||
Thread t = null;
|
||||
Thread q = null;
|
||||
try {
|
||||
die:
|
||||
{
|
||||
try {
|
||||
rlock();
|
||||
try {
|
||||
host = info.get("OUTHOST").toString();
|
||||
port = Integer.parseInt(info.get("OUTPORT").toString());
|
||||
tell = info.get("QUIET").equals(Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
sock = new Socket(host, port);
|
||||
sock.setKeepAlive(true);
|
||||
// make readers/writers
|
||||
in = sock.getInputStream();
|
||||
out = sock.getOutputStream();
|
||||
Iin = I2P.getInputStream();
|
||||
Iout = I2P.getOutputStream();
|
||||
I2P.setReadTimeout(0); // temp bugfix, this *SHOULD* be the default
|
||||
|
||||
if (tell) {
|
||||
// tell who is connecting
|
||||
out.write(DataHelper.getASCII(I2P.getPeerDestination().toBase64()));
|
||||
out.write(10); // nl
|
||||
out.flush(); // not really needed, but...
|
||||
}
|
||||
// setup to cross the streams
|
||||
TCPio conn_c = new TCPio(in, Iout, lives); // app -> I2P
|
||||
TCPio conn_a = new TCPio(Iin, out, lives); // I2P -> app
|
||||
t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " TCPioA");
|
||||
q = new I2PAppThread(conn_a, Thread.currentThread().getName() + " TCPioB");
|
||||
// Fire!
|
||||
t.start();
|
||||
q.start();
|
||||
while (t.isAlive() && q.isAlive() && lives.get()) { // AND is used here to kill off the other thread
|
||||
try {
|
||||
Thread.sleep(10); //sleep for 10 ms
|
||||
} catch (InterruptedException e) {
|
||||
break die;
|
||||
}
|
||||
}
|
||||
// System.out.println("I2PtoTCP: Going away...");
|
||||
} catch (Exception e) {
|
||||
// System.out.println("I2PtoTCP: Owch! damn!");
|
||||
break die;
|
||||
}
|
||||
} // die
|
||||
} finally {
|
||||
try {
|
||||
in.close();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
try {
|
||||
out.close();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
try {
|
||||
Iin.close();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
try {
|
||||
Iout.close();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
try {
|
||||
t.interrupt();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
q.interrupt();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
// System.out.println("I2PtoTCP: Close I2P");
|
||||
I2P.close();
|
||||
} catch (Exception e) {
|
||||
tell = false;
|
||||
}
|
||||
//System.out.println("I2PtoTCP: Closed I2P");
|
||||
try {
|
||||
// System.out.println("I2PtoTCP: Close sock");
|
||||
sock.close();
|
||||
} catch (Exception e) {
|
||||
tell = false;
|
||||
}
|
||||
// System.out.println("I2PtoTCP: Done");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Sets of "friendly" utilities to make life easier.
|
||||
* Any "Lifted" code will apear here, and credits given.
|
||||
* It's better to "Lift" a small chunk of "free" code than add in piles of
|
||||
* code we don't need, and don't want.
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class Lifted {
|
||||
|
||||
/**
|
||||
* Copy a set of properties from one Property to another.
|
||||
* Lifted from Apache Derby code svn repository.
|
||||
* Liscenced as follows:
|
||||
* http://svn.apache.org/repos/asf/db/derby/code/trunk/LICENSE
|
||||
*
|
||||
* @param src_prop Source set of properties to copy from.
|
||||
* @param dest_prop Dest Properties to copy into.
|
||||
*
|
||||
**/
|
||||
public static void copyProperties(Properties src_prop, Properties dest_prop) {
|
||||
for (Map.Entry<Object, Object> e : src_prop.entrySet()) {
|
||||
dest_prop.put((String)e.getKey(), (String)e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package net.i2p.BOB;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class Logger {
|
||||
public Log log;
|
||||
private boolean logToStdout;
|
||||
|
||||
public Logger(Log log, boolean logToStdout) {
|
||||
this.log = log;
|
||||
this.logToStdout = logToStdout;
|
||||
}
|
||||
|
||||
public void info(String msg) {
|
||||
if (logToStdout)
|
||||
System.out.println("INFO: " + msg);
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info(msg);
|
||||
}
|
||||
|
||||
public void warn(String msg) {
|
||||
warn(msg, null);
|
||||
}
|
||||
|
||||
public void warn(String msg, Throwable e) {
|
||||
if (logToStdout) {
|
||||
System.out.println("WARNING: " + msg);
|
||||
if (e != null)
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn(msg, e);
|
||||
}
|
||||
|
||||
public void error(String msg, Throwable e) {
|
||||
if (logToStdout) {
|
||||
System.out.println("ERROR: " + msg);
|
||||
if (e != null)
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (log.shouldLog(Log.ERROR))
|
||||
log.error(msg, e);
|
||||
}
|
||||
}
|
@ -1,416 +0,0 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.client.streaming.I2PSocketManagerFactory;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
*
|
||||
* Multiplex listeners for TCP and I2P
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class MUXlisten implements Runnable {
|
||||
|
||||
private final NamedDB database, info;
|
||||
private final Logger _log;
|
||||
private final I2PSocketManager socketManager;
|
||||
private final ByteArrayInputStream prikey;
|
||||
private ThreadGroup tg;
|
||||
private final String N;
|
||||
private ServerSocket listener;
|
||||
private static final int backlog = 50; // should this be more? less?
|
||||
private final boolean go_out;
|
||||
private final boolean come_in;
|
||||
private final AtomicBoolean lock;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor Will fail if INPORT is occupied.
|
||||
*
|
||||
* @param info DB entry for this tunnel
|
||||
* @param database master database of tunnels
|
||||
* @param _log
|
||||
* @throws net.i2p.I2PException
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
MUXlisten(AtomicBoolean lock, NamedDB database, NamedDB info, Logger _log) throws I2PException, IOException, RuntimeException {
|
||||
int port = 0;
|
||||
InetAddress host = null;
|
||||
this.lock = lock;
|
||||
this.tg = null;
|
||||
this.database = database;
|
||||
this.info = info;
|
||||
this._log = _log;
|
||||
lives = new AtomicBoolean(false);
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
this.info.add("STARTING", Boolean.TRUE);
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
Properties Q = new Properties();
|
||||
rlock();
|
||||
try {
|
||||
N = this.info.get("NICKNAME").toString();
|
||||
prikey = new ByteArrayInputStream((byte[]) info.get("KEYS"));
|
||||
// Make a new copy so that anything else won't muck with our database.
|
||||
Properties R = (Properties) info.get("PROPERTIES");
|
||||
Lifted.copyProperties(R, Q);
|
||||
|
||||
this.go_out = info.exists("OUTPORT");
|
||||
this.come_in = info.exists("INPORT");
|
||||
if (this.come_in) {
|
||||
port = Integer.parseInt(info.get("INPORT").toString());
|
||||
host = InetAddress.getByName(info.get("INHOST").toString());
|
||||
}
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
|
||||
String i2cpHost = Q.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
|
||||
int i2cpPort = I2PClient.DEFAULT_LISTEN_PORT;
|
||||
String i2cpPortStr = Q.getProperty(I2PClient.PROP_TCP_PORT);
|
||||
if (i2cpPortStr != null) {
|
||||
try {
|
||||
i2cpPort = Integer.parseInt(i2cpPortStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new IllegalArgumentException("Invalid I2CP port specified [" + i2cpPortStr + "]");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.come_in) {
|
||||
this.listener = new ServerSocket(port, backlog, host);
|
||||
}
|
||||
socketManager = I2PSocketManagerFactory.createManager(
|
||||
prikey, i2cpHost, i2cpPort, Q);
|
||||
} catch (IOException e) {
|
||||
// Something went bad.
|
||||
wlock();
|
||||
try {
|
||||
this.info.add("STARTING", Boolean.FALSE);
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
// Something went bad.
|
||||
wlock();
|
||||
try {
|
||||
this.info.add("STARTING", Boolean.FALSE);
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
// Something else went bad.
|
||||
wlock();
|
||||
try {
|
||||
this.info.add("STARTING", Boolean.FALSE);
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void rlock() {
|
||||
database.getReadLock();
|
||||
info.getReadLock();
|
||||
}
|
||||
|
||||
private void runlock() {
|
||||
info.releaseReadLock();
|
||||
database.releaseReadLock();
|
||||
}
|
||||
|
||||
private void wlock() {
|
||||
database.getWriteLock();
|
||||
info.getWriteLock();
|
||||
}
|
||||
|
||||
private void wunlock() {
|
||||
info.releaseWriteLock();
|
||||
database.releaseWriteLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* MUX sockets, fire off a thread to connect, get destination info, and do I/O
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
I2PServerSocket SS = null;
|
||||
Thread t = null;
|
||||
Thread q = null;
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
try {
|
||||
info.add("RUNNING", Boolean.TRUE);
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
return;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
lives.set(true);
|
||||
lock.set(false);
|
||||
quit:
|
||||
{
|
||||
try {
|
||||
tg = new ThreadGroup(N);
|
||||
{
|
||||
// toss the connections to a new threads.
|
||||
// will wrap with TCP and UDP when UDP works
|
||||
|
||||
if (go_out) {
|
||||
// I2P -> TCP
|
||||
SS = socketManager.getServerSocket();
|
||||
I2Plistener conn = new I2Plistener(SS, socketManager, info, database, _log, lives);
|
||||
t = new I2PAppThread(tg, conn, "BOBI2Plistener " + N);
|
||||
t.start();
|
||||
}
|
||||
|
||||
if (come_in) {
|
||||
// TCP -> I2P
|
||||
TCPlistener conn = new TCPlistener(listener, socketManager, info, database, _log, lives);
|
||||
q = new I2PAppThread(tg, conn, "BOBTCPlistener " + N);
|
||||
q.start();
|
||||
}
|
||||
|
||||
wlock();
|
||||
try {
|
||||
try {
|
||||
info.add("STARTING", Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
break quit;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
break quit;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
boolean spin = true;
|
||||
while (spin && lives.get()) {
|
||||
try {
|
||||
Thread.sleep(1000); //sleep for 1 second
|
||||
} catch (InterruptedException e) {
|
||||
break quit;
|
||||
}
|
||||
rlock();
|
||||
try {
|
||||
try {
|
||||
spin = info.get("STOPPING").equals(Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
break quit;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
break quit;
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
}
|
||||
} // die
|
||||
|
||||
} catch (Exception e) {
|
||||
// System.out.println("MUXlisten: Caught an exception" + e);
|
||||
break quit;
|
||||
}
|
||||
} // quit
|
||||
} finally {
|
||||
lives.set(false);
|
||||
// Some grace time.
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
info.add("STARTING", Boolean.FALSE);
|
||||
info.add("STOPPING", Boolean.TRUE);
|
||||
info.add("RUNNING", Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
// Start cleanup.
|
||||
while (!lock.compareAndSet(false, true)) {
|
||||
// wait
|
||||
}
|
||||
if (SS != null) {
|
||||
try {
|
||||
SS.close();
|
||||
} catch (I2PException ex) {
|
||||
}
|
||||
}
|
||||
if (listener != null) {
|
||||
try {
|
||||
listener.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
// Some grace time.
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
|
||||
// Hopefully nuke stuff here...
|
||||
{
|
||||
String groupName = tg.getName();
|
||||
try {
|
||||
_log.warn("destroySocketManager " + groupName);
|
||||
socketManager.destroySocketManager();
|
||||
_log.warn("destroySocketManager Successful" + groupName);
|
||||
} catch (Exception e) {
|
||||
// nop
|
||||
_log.warn("destroySocketManager Failed" + groupName);
|
||||
_log.warn(e.toString());
|
||||
}
|
||||
}
|
||||
// zero out everything.
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
info.add("STARTING", Boolean.FALSE);
|
||||
info.add("STOPPING", Boolean.FALSE);
|
||||
info.add("RUNNING", Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
return;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
lock.set(false); // Should we force waiting for all threads??
|
||||
|
||||
// Wait around till all threads are collected.
|
||||
if (tg != null) {
|
||||
String groupName = tg.getName();
|
||||
// System.out.println("BOB: MUXlisten: Starting thread collection for: " + groupName);
|
||||
_log.warn("BOB: MUXlisten: Starting thread collection for: " + groupName);
|
||||
if (tg.activeCount() + tg.activeGroupCount() != 0) {
|
||||
// visit(tg, 0, groupName);
|
||||
int foo = tg.activeCount() + tg.activeGroupCount();
|
||||
// hopefully no longer needed!
|
||||
// int bar = lives;
|
||||
// System.out.println("BOB: MUXlisten: Waiting on threads for " + groupName);
|
||||
// System.out.println("\nBOB: MUXlisten: ThreadGroup dump BEGIN " + groupName);
|
||||
// visit(tg, 0, groupName);
|
||||
// System.out.println("BOB: MUXlisten: ThreadGroup dump END " + groupName + "\n");
|
||||
// Happily spin forever :-(
|
||||
while (foo != 0) {
|
||||
foo = tg.activeCount() + tg.activeGroupCount();
|
||||
// if (lives != bar && lives != 0) {
|
||||
// System.out.println("\nBOB: MUXlisten: ThreadGroup dump BEGIN " + groupName);
|
||||
// visit(tg, 0, groupName);
|
||||
// System.out.println("BOB: MUXlisten: ThreadGroup dump END " + groupName + "\n");
|
||||
// }
|
||||
// bar = lives;
|
||||
try {
|
||||
Thread.sleep(100); //sleep for 100 ms (One tenth second)
|
||||
} catch (InterruptedException ex) {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
}
|
||||
// System.out.println("BOB: MUXlisten: Threads went away. Success: " + groupName);
|
||||
_log.warn("BOB: MUXlisten: Threads went away. Success: " + groupName);
|
||||
tg.destroy();
|
||||
// Zap reference to the ThreadGroup so the JVM can GC it.
|
||||
tg = null;
|
||||
}
|
||||
try {
|
||||
socketManager.destroySocketManager();
|
||||
} catch (Exception e) {
|
||||
// nop
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Debugging... None of this is normally used.
|
||||
/**
|
||||
* Find the root thread group and print them all.
|
||||
*
|
||||
*/
|
||||
private void visitAllThreads() {
|
||||
ThreadGroup root = Thread.currentThread().getThreadGroup().getParent();
|
||||
while (root.getParent() != null) {
|
||||
root = root.getParent();
|
||||
}
|
||||
|
||||
// Visit each thread group
|
||||
visit(root, 0, root.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively visits all thread groups under `group' and dumps them.
|
||||
* @param group ThreadGroup to visit
|
||||
* @param level Current level
|
||||
*/
|
||||
private static void visit(ThreadGroup group, int level, String tn) {
|
||||
// Get threads in `group'
|
||||
int numThreads = group.activeCount();
|
||||
Thread[] threads = new Thread[numThreads * 2];
|
||||
numThreads = group.enumerate(threads, false);
|
||||
String indent = "------------------------------------".substring(0, level) + "-> ";
|
||||
// Enumerate each thread in `group' and print it.
|
||||
for (int i = 0; i < numThreads; i++) {
|
||||
// Get thread
|
||||
Thread thread = threads[i];
|
||||
System.out.println("BOB: MUXlisten: " + tn + ": " + indent + thread.toString());
|
||||
}
|
||||
|
||||
// Get thread subgroups of `group'
|
||||
int numGroups = group.activeGroupCount();
|
||||
ThreadGroup[] groups = new ThreadGroup[numGroups * 2];
|
||||
numGroups = group.enumerate(groups, false);
|
||||
|
||||
// Recursively visit each subgroup
|
||||
for (int i = 0; i < numGroups; i++) {
|
||||
visit(groups[i], level + 1, groups[i].getName());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
* Start from command line
|
||||
*
|
||||
* @author sponge
|
||||
*
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
/**
|
||||
* @param args the command line arguments, these are not used yet
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// THINK THINK THINK THINK THINK THINK
|
||||
SimpleTimer2 Y2 = SimpleTimer2.getInstance();
|
||||
|
||||
BOB.main(args);
|
||||
|
||||
Y2.stop();
|
||||
}
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
|
||||
/**
|
||||
* Internal database to relate nicknames to options to values
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class NamedDB {
|
||||
|
||||
private final Map<String, Object> data;
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public NamedDB() {
|
||||
this.data = new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
public void getReadLock() {
|
||||
lock.readLock().lock();
|
||||
}
|
||||
|
||||
public void releaseReadLock() {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
|
||||
public void getWriteLock() {
|
||||
lock.writeLock().lock();
|
||||
}
|
||||
|
||||
public void releaseWriteLock() {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an object if it exists
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
public void kill(String key) {
|
||||
data.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add object, deletes the old one if it exists
|
||||
*
|
||||
* @param key
|
||||
* @param val
|
||||
*/
|
||||
public void add(String key, Object val) {
|
||||
data.put(key, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object, and return it, throws RuntimeException if not found
|
||||
*
|
||||
* @param key non-null
|
||||
* @return Object non-null
|
||||
* @throws java.lang.RuntimeException if not found
|
||||
*/
|
||||
public Object get(String key) throws RuntimeException {
|
||||
Object rv = data.get(key);
|
||||
if (rv != null)
|
||||
return rv;
|
||||
throw new RuntimeException("Key not found");
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if an object exists, else returns false
|
||||
*
|
||||
* @param key
|
||||
* @return true if an object exists, else returns false
|
||||
*/
|
||||
public boolean exists(String key) {
|
||||
return data.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.29 replaces getcount() and getnext(int)
|
||||
*/
|
||||
public Collection<Object> values() {
|
||||
return data.values();
|
||||
}
|
||||
}
|
@ -1,121 +0,0 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Shove data from one stream to the other.
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class TCPio implements Runnable {
|
||||
|
||||
private final InputStream Ain;
|
||||
private final OutputStream Aout;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Ain InputStream
|
||||
* @param Aout OutputStream
|
||||
*
|
||||
* param database
|
||||
*/
|
||||
TCPio(InputStream Ain, OutputStream Aout, AtomicBoolean lives) {
|
||||
this.Ain = Ain;
|
||||
this.Aout = Aout;
|
||||
this.lives = lives;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy from source to destination...
|
||||
* and yes, we are totally OK to block here on writes,
|
||||
* The OS has buffers, and I intend to use them.
|
||||
* We send an interrupt signal to the threadgroup to
|
||||
* unwedge any pending writes.
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
/*
|
||||
* NOTE:
|
||||
* The write method of OutputStream calls the write method of
|
||||
* one argument on each of the bytes to be written out.
|
||||
* Subclasses are encouraged to override this method and provide
|
||||
* a more efficient implementation.
|
||||
*
|
||||
* So, is this really a performance problem?
|
||||
* Should we expand to several bytes?
|
||||
* I don't believe there would be any gain, since read method
|
||||
* has the same reccomendations. If anyone has a better way to
|
||||
* do this, I'm interested in performance improvements.
|
||||
*
|
||||
* --Sponge
|
||||
*
|
||||
* Tested with 128 bytes, and there was no performance gain.
|
||||
* 8192 bytes did lower load average across many connections.
|
||||
* Should I raise it higer? The correct thing to do would be to
|
||||
* override... perhaps use NTCP, but I2P's streaming lib lacks
|
||||
* anything NTCP compatable.
|
||||
*
|
||||
* --Sponge
|
||||
*/
|
||||
|
||||
int b;
|
||||
byte a[] = new byte[8192];
|
||||
try {
|
||||
try {
|
||||
while (lives.get()) {
|
||||
b = Ain.read(a, 0, 8192);
|
||||
if (b > 0) {
|
||||
Aout.write(a, 0, b);
|
||||
} else if (b == 0) {
|
||||
while(Ain.available() == 0) {
|
||||
Thread.sleep(20);
|
||||
}
|
||||
} else {
|
||||
/* according to the specs:
|
||||
*
|
||||
* The total number of bytes read into the buffer,
|
||||
* or -1 if there is no more data because the end of
|
||||
* the stream has been reached.
|
||||
*
|
||||
*/
|
||||
// System.out.println("TCPio: End Of Stream");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
// System.out.println("TCPio: Leaving.");
|
||||
} finally {
|
||||
// Eject!!! Eject!!!
|
||||
//System.out.println("TCPio: Caught an exception " + e);
|
||||
try {
|
||||
Ain.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
try {
|
||||
Aout.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
* Listen on TCP port and connect to I2P
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class TCPlistener implements Runnable {
|
||||
|
||||
private final NamedDB info, database;
|
||||
private final Logger _log;
|
||||
private final I2PSocketManager socketManager;
|
||||
private final ServerSocket listener;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param S
|
||||
* @param info
|
||||
* @param database
|
||||
* @param _log
|
||||
*/
|
||||
TCPlistener(ServerSocket listener, I2PSocketManager S, NamedDB info, NamedDB database, Logger _log, AtomicBoolean lives) {
|
||||
this.database = database;
|
||||
this.info = info;
|
||||
this._log = _log;
|
||||
this.socketManager = S;
|
||||
this.listener = listener;
|
||||
this.lives = lives;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply listen on TCP port, and thread connections
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
boolean g = false;
|
||||
int conn = 0;
|
||||
Socket server = null;
|
||||
try {
|
||||
try {
|
||||
listener.setSoTimeout(50); // We don't block, we cycle and check.
|
||||
while (lives.get()) {
|
||||
try {
|
||||
server = listener.accept();
|
||||
server.setKeepAlive(true);
|
||||
g = true;
|
||||
} catch (SocketTimeoutException ste) {
|
||||
g = false;
|
||||
}
|
||||
if (g) {
|
||||
conn++;
|
||||
// toss the connection to a new thread.
|
||||
TCPtoI2P conn_c = new TCPtoI2P(socketManager, server, info, database, lives);
|
||||
Thread t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " TCPtoI2P " + conn);
|
||||
t.start();
|
||||
g = false;
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
listener.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
//System.out.println("TCPlistener: " + Thread.currentThread().getName() + "Done.");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,222 +0,0 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ConnectException;
|
||||
import java.net.NoRouteToHostException;
|
||||
import java.net.Socket;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
*
|
||||
* Process TCP->I2P
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class TCPtoI2P implements Runnable {
|
||||
|
||||
private I2PSocket I2P;
|
||||
private final Socket sock;
|
||||
private final I2PSocketManager socketManager;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param i2p
|
||||
* @param socket
|
||||
* @param info unused
|
||||
* @param database unused
|
||||
*/
|
||||
TCPtoI2P(I2PSocketManager i2p, Socket socket, NamedDB info, NamedDB database, AtomicBoolean lives) {
|
||||
this.sock = socket;
|
||||
this.socketManager = i2p;
|
||||
this.lives = lives;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a more forgiving readline,
|
||||
* it works on unbuffered streams
|
||||
*
|
||||
* @param in
|
||||
* @return line of text as a String
|
||||
* @throws IOException
|
||||
*/
|
||||
private static String lnRead(InputStream in) throws IOException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int b;
|
||||
char c;
|
||||
|
||||
while (true) {
|
||||
b = in.read();
|
||||
if (b == 13) {
|
||||
//skip CR
|
||||
continue;
|
||||
}
|
||||
if (b < 20 || b > 126) {
|
||||
// exit on anything not legal
|
||||
break;
|
||||
}
|
||||
c = (char) (b & 0x7f); // We only care about ASCII
|
||||
builder.append(c);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an error message to out
|
||||
*
|
||||
* @param e
|
||||
* @param out
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
private void Emsg(String e, OutputStream out) throws IOException {
|
||||
// Debugging System.out.println("ERROR TCPtoI2P: " + e);
|
||||
out.write("ERROR ".concat(e).getBytes("UTF-8"));
|
||||
out.write(13);
|
||||
out.write(10);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* TCP stream to I2P stream thread starter
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
String line, input;
|
||||
InputStream Iin = null;
|
||||
OutputStream Iout = null;
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
Thread t = null;
|
||||
Thread q = null;
|
||||
try {
|
||||
try {
|
||||
|
||||
in = sock.getInputStream();
|
||||
out = sock.getOutputStream();
|
||||
line = lnRead(in);
|
||||
input = line.toLowerCase(Locale.US);
|
||||
Destination dest = null;
|
||||
if (input.endsWith(".i2p")) {
|
||||
//dest = I2PTunnel.destFromName(input);
|
||||
dest = I2PAppContext.getGlobalContext().namingService().lookup(input);
|
||||
if (dest != null) {
|
||||
line = dest.toBase64();
|
||||
} else {
|
||||
Emsg("Can't find destination: " + input, out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
dest = new Destination();
|
||||
dest.fromBase64(line);
|
||||
|
||||
try {
|
||||
// get a client socket
|
||||
I2P = socketManager.connect(dest);
|
||||
I2P.setReadTimeout(0); // temp bugfix, this *SHOULD* be the default
|
||||
// make readers/writers
|
||||
Iin = I2P.getInputStream();
|
||||
Iout = I2P.getOutputStream();
|
||||
// setup to cross the streams
|
||||
TCPio conn_c = new TCPio(in, Iout, lives); // app -> I2P
|
||||
TCPio conn_a = new TCPio(Iin, out, lives); // I2P -> app
|
||||
t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " TCPioA");
|
||||
q = new I2PAppThread(conn_a, Thread.currentThread().getName() + " TCPioB");
|
||||
// Fire!
|
||||
t.start();
|
||||
q.start();
|
||||
while (t.isAlive() && q.isAlive() && lives.get()) { // AND is used here to kill off the other thread
|
||||
Thread.sleep(10); //sleep for 10 ms
|
||||
}
|
||||
} catch (I2PException e) {
|
||||
Emsg(e.toString(), out);
|
||||
} catch (ConnectException e) {
|
||||
Emsg(e.toString(), out);
|
||||
} catch (NoRouteToHostException e) {
|
||||
Emsg(e.toString(), out);
|
||||
}
|
||||
|
||||
} catch (InterruptedIOException e) {
|
||||
// We're breaking away.
|
||||
} catch (InterruptedException e) {
|
||||
// ditto
|
||||
} catch (IOException e) {
|
||||
try {
|
||||
Emsg(e.toString(), out);
|
||||
} catch (IOException ex) {
|
||||
// ditto
|
||||
}
|
||||
} catch (DataFormatException e) {
|
||||
try {
|
||||
Emsg(e.toString(), out);
|
||||
} catch (IOException ex) {
|
||||
// ditto
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
try {
|
||||
t.interrupt();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
q.interrupt();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
Iin.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
Iout.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
// System.out.println("TCPtoI2P: Close I2P");
|
||||
I2P.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
try {
|
||||
// System.out.println("TCPtoI2P: Close sock");
|
||||
sock.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
// System.out.println("TCPtoI2P: Done.");
|
||||
}
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.client.I2PSessionListener;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* UDP IO on I2P
|
||||
*
|
||||
* FIX ME: Untested, and incomplete!
|
||||
* I have no personal need to UDP yet,
|
||||
* however alot of p2p apps pretty much demand it.
|
||||
* The skeletal frame is here, just needs to be finished.
|
||||
*
|
||||
* @author sponge
|
||||
* @deprecated incomplete, unused
|
||||
*/
|
||||
@Deprecated
|
||||
public class UDPIOthread implements I2PSessionListener, Runnable {
|
||||
|
||||
private final NamedDB info;
|
||||
private final Log _log;
|
||||
private final Socket socket;
|
||||
private DataInputStream in;
|
||||
private DataOutputStream out;
|
||||
private final I2PSession _session;
|
||||
// FIXME never set
|
||||
private Destination _peerDestination;
|
||||
private boolean up;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param info
|
||||
* @param _log
|
||||
* @param socket
|
||||
* @param _session
|
||||
*/
|
||||
UDPIOthread(NamedDB info, Log _log, Socket socket, I2PSession _session) {
|
||||
this.info = info;
|
||||
this._log = _log;
|
||||
this.socket = socket;
|
||||
this._session = _session;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
byte data[] = new byte[1024];
|
||||
up = true;
|
||||
try {
|
||||
in = new DataInputStream(socket.getInputStream());
|
||||
out = new DataOutputStream(socket.getOutputStream());
|
||||
while (up) {
|
||||
int c = in.read(data);
|
||||
// Note: could do a loopback test here with a wrapper.
|
||||
boolean ok = _session.sendMessage(_peerDestination, data, 0, c);
|
||||
|
||||
if (!ok) {
|
||||
up = false; // Is this the right thing to do??
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error running", ioe);
|
||||
} catch (I2PSessionException ise) {
|
||||
_log.error("Error communicating", ise);
|
||||
// } catch(DataFormatException dfe) {
|
||||
// _log.error("Peer destination file is not valid", dfe);
|
||||
} finally {
|
||||
if (_session != null) {
|
||||
try {
|
||||
_session.destroySession();
|
||||
} catch (I2PSessionException ise) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param session
|
||||
* @param msgId
|
||||
* @param size
|
||||
*/
|
||||
public void messageAvailable(I2PSession session, int msgId, long size) {
|
||||
// _log.debug("Message available: id = " + msgId + " size = " + size);
|
||||
try {
|
||||
byte msg[] = session.receiveMessage(msgId);
|
||||
if (msg != null) {
|
||||
out.write(msg);
|
||||
out.flush();
|
||||
}
|
||||
} catch (I2PSessionException ise) {
|
||||
up = false;
|
||||
} catch (IOException ioe) {
|
||||
up = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Great, can these be used to kill ourselves.
|
||||
/** required by {@link I2PSessionListener I2PSessionListener} to notify of disconnect */
|
||||
public void disconnected(I2PSession session) {
|
||||
_log.debug("Disconnected");
|
||||
// up = false;
|
||||
}
|
||||
|
||||
/** required by {@link I2PSessionListener I2PSessionListener} to notify of error */
|
||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
||||
_log.debug("Error occurred: " + message, error);
|
||||
// up = false;
|
||||
}
|
||||
|
||||
/** required by {@link I2PSessionListener I2PSessionListener} to notify of abuse */
|
||||
public void reportAbuse(I2PSession session, int severity) {
|
||||
_log.debug("Abuse reported of severity " + severity);
|
||||
// up = false;
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) sponge
|
||||
Planet Earth
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
See...
|
||||
|
||||
http://sam.zoy.org/wtfpl/
|
||||
and
|
||||
http://en.wikipedia.org/wiki/WTFPL
|
||||
|
||||
...for any additional details and license questions.
|
@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>BOB, the Basic Open Bridge, allows TCP applications to talk over I2P - DEPRECATED - Please port applications to SAMv3.</p>
|
||||
</body>
|
||||
</html>
|
@ -9,6 +9,7 @@
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="javac.version" value="1.8" />
|
||||
<property name="javac.release" value="8" />
|
||||
<property name="manifest.classpath.name" value="Class-Path" />
|
||||
|
||||
<target name="all" depends="jar, emptyWar"/>
|
||||
|
||||
@ -57,8 +58,14 @@
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
<jar basedir="${build}" destfile="${dist}/${jar}">
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="net.i2p.addressbook.Daemon"/>
|
||||
<attribute name="${manifest.classpath.name}" value="i2p.jar" />
|
||||
<attribute name="Main-Class" value="net.i2p.addressbook.CommandLine"/>
|
||||
<attribute name="Specification-Title" value="I2P Address Book" />
|
||||
<attribute name="Specification-Version" value="${api.version}" />
|
||||
<attribute name="Specification-Vendor" value="The I2P Project https://geti2p.net/" />
|
||||
<attribute name="Implementation-Title" value="I2P Java Address Book" />
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Implementation-Vendor" value="The I2P Project https://geti2p.net/" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
<attribute name="Build-Date" value="${build.timestamp}" />
|
||||
<attribute name="Base-Revision" value="${workspace.version}" />
|
||||
|
@ -0,0 +1,42 @@
|
||||
package net.i2p.addressbook;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.CoreVersion;
|
||||
|
||||
/**
|
||||
* Simple command line access to various utilities.
|
||||
* Not a public API. Subject to change.
|
||||
* Apps and plugins should use specific classes.
|
||||
*
|
||||
* @since 0.9.55
|
||||
*/
|
||||
public class CommandLine extends net.i2p.util.CommandLine {
|
||||
|
||||
protected static final List<String> ACLASSES = Arrays.asList(new String[] {
|
||||
"net.i2p.addressbook.HostTxtParser",
|
||||
"net.i2p.router.naming.BlockfileNamingService",
|
||||
"net.metanotion.io.block.BlockFile",
|
||||
});
|
||||
|
||||
protected CommandLine() {}
|
||||
|
||||
public static void main(String args[]) {
|
||||
List<String> classes = new ArrayList<String>(ACLASSES.size() + CLASSES.size());
|
||||
classes.addAll(ACLASSES);
|
||||
classes.addAll(CLASSES);
|
||||
if (args.length > 0) {
|
||||
exec(args, classes);
|
||||
}
|
||||
usage(classes);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
private static void usage(List<String> classes) {
|
||||
System.err.println("I2P Address book version " + CoreVersion.VERSION + '\n' +
|
||||
"USAGE: java -jar /path/to/addressbook.jar command [args]");
|
||||
printCommands(classes);
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ import java.util.Properties;
|
||||
|
||||
import net.i2p.client.naming.HostTxtEntry;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.SecureFile;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.SystemVersion;
|
||||
@ -25,9 +26,9 @@ import net.i2p.util.SystemVersion;
|
||||
* Utility class providing methods to parse and write files in a hosts.txt file
|
||||
* format, and subscription file format.
|
||||
*
|
||||
* @since 0.9.26 modified from ConfigParser
|
||||
* @since 0.9.26 modified from ConfigParser, public since 0.9.55 for CLI
|
||||
*/
|
||||
class HostTxtParser {
|
||||
public class HostTxtParser {
|
||||
|
||||
private static final boolean isWindows = SystemVersion.isWindows();
|
||||
|
||||
@ -255,8 +256,23 @@ class HostTxtParser {
|
||||
System.exit(2);
|
||||
}
|
||||
if (!e.hasValidSig()) {
|
||||
if (!quiet)
|
||||
System.err.println("Bad signature");
|
||||
if (!quiet) {
|
||||
System.err.println("Bad signature for " + e.getName());
|
||||
String dest = e.getDest();
|
||||
try {
|
||||
Destination d = new Destination(dest);
|
||||
System.err.println(dest);
|
||||
System.err.println(d.toString());
|
||||
} catch (Exception ex) {
|
||||
System.err.println("Invalid destination: " + dest);
|
||||
}
|
||||
Properties p = e.getProps();
|
||||
if (p != null) {
|
||||
for (Map.Entry<?,?> m : p.entrySet()) {
|
||||
System.err.println(m.getKey() + "=" + m.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
System.exit(3);
|
||||
}
|
||||
Properties p = e.getProps();
|
||||
@ -266,14 +282,30 @@ class HostTxtParser {
|
||||
p.containsKey(HostTxtEntry.PROP_OLDNAME) ||
|
||||
p.containsKey(HostTxtEntry.PROP_OLDSIG)) {
|
||||
if (!e.hasValidSig()) {
|
||||
if (!quiet)
|
||||
System.err.println("Bad inner signature");
|
||||
if (!quiet) {
|
||||
System.err.println("Bad inner signature for " + e.getName());
|
||||
for (Map.Entry<?,?> m : p.entrySet()) {
|
||||
System.err.println(m.getKey() + "=" + m.getValue());
|
||||
}
|
||||
}
|
||||
System.exit(4);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!quiet)
|
||||
if (!quiet) {
|
||||
System.err.println("Good signature for " + e.getName());
|
||||
try {
|
||||
String dest = e.getDest();
|
||||
Destination d = new Destination(dest);
|
||||
System.err.println(dest);
|
||||
System.err.println(d.toString());
|
||||
} catch (Exception ex) {}
|
||||
if (p != null) {
|
||||
for (Map.Entry<?,?> m : p.entrySet()) {
|
||||
System.err.println(m.getKey() + "=" + m.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
<property name="javac.version" value="1.8" />
|
||||
<property name="javac.release" value="8" />
|
||||
<property name="require.gettext" value="true" />
|
||||
<property name="manifest.classpath.name" value="Class-Path" />
|
||||
|
||||
<condition property="no.bundle">
|
||||
<isfalse value="${require.gettext}" />
|
||||
@ -86,6 +87,7 @@
|
||||
<jar basedir="${build}" excludes="messages-src/**" destfile="${dist}/${jar}">
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="net.i2p.desktopgui.Main"/>
|
||||
<attribute name="${manifest.classpath.name}" value="i2p.jar router.jar" />
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
<attribute name="Build-Date" value="${build.timestamp}" />
|
||||
|
@ -10,9 +10,9 @@ 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: 2017-09-20 20:31+0000\n"
|
||||
"Last-Translator: Vitaly Zdorovenko <stenliterziev@gmail.com>\n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2022-02-09 19:23+0000\n"
|
||||
"Last-Translator: zzzi2p\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"
|
||||
@ -27,69 +27,81 @@ msgstr "Стартиране на I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P е стартиран!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Стартиране"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Стартиране на I2P Браузер"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Деактивиране"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
msgid "Restart I2P"
|
||||
msgstr "Рестартиране на I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
msgid "Stop I2P"
|
||||
msgstr "Спиране на I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Рестартирайте Незабавно"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Изключване в {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "Мрежа"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr ""
|
||||
|
@ -4,14 +4,15 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# slrslr, 2022
|
||||
# slrslr, 2021
|
||||
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: 2021-07-08 09:41+0000\n"
|
||||
"Last-Translator: slrslr\n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: slrslr, 2022\n"
|
||||
"Language-Team: Czech (http://www.transifex.com/otf/I2P/language/cs/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -26,69 +27,81 @@ msgstr "Spustit I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P startuje!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Startuji"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Spouštím I2P Browser"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Nastavit I2P Systémovou Lištu"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Zakázat"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Zapnout upozornění"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Vypnout upozornění"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "Vypnout ikonu systémové lišty"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
msgid "Restart I2P"
|
||||
msgstr "Restart I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
msgid "Stop I2P"
|
||||
msgstr "Zastavit I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Restartovat I2P Hned"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Zastavit I2P Hned"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Zrušit I2P Zastavení"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Zastavení za {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Vypínání"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
msgid "Network"
|
||||
msgstr "Síť"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Pravé-kliknutí pro menu"
|
||||
|
@ -7,14 +7,15 @@
|
||||
# blabla <blabla@trash-mail.com>, 2011
|
||||
# Ettore Atalan <atalanttore@googlemail.com>, 2016
|
||||
# foo <foo@bar>, 2009
|
||||
# Georg Stadler, 2022
|
||||
# Lars Schimmer <echelon@i2pmail.org>, 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: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Lars Schimmer <echelon@i2pmail.org>\n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: Georg Stadler, 2022\n"
|
||||
"Language-Team: German (http://www.transifex.com/otf/I2P/language/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -29,69 +30,81 @@ msgstr "I2P starten"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P startet gerade!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Startend"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "I2P-Browser öffnen"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "I2P System Tray konfigurieren"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Deaktivieren"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Benachrichtigungen aktivieren"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Benachrichtigungen deaktivieren"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
msgid "Restart I2P"
|
||||
msgstr "I2P neustarten"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
msgid "Stop I2P"
|
||||
msgstr "I2P beenden"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "I2P sofort neustarten"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "I2P sofort beenden"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Herunterfahren von I2P abbrechen"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Herunterfahren in {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Herunterfahren bevorstehend"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
msgid "Network"
|
||||
msgstr "Netzwerk"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Rechtsklick für Menü"
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P desktopgui\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-08-11 15:33+0000\n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2010-06-15 14:09+0100\n"
|
||||
"Last-Translator: duck <duck@mail.i2p>\n"
|
||||
"Language-Team: duck <duck@mail.i2p>\n"
|
||||
@ -25,69 +25,81 @@ msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:54
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:206
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:75
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:227
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Disable"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:92
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:244
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
msgid "Restart I2P"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:109
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:261
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
msgid "Stop I2P"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:125
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:277
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:142
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:294
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:156
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:308
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:362
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:364
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:369
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr ""
|
||||
|
@ -4,15 +4,16 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# タカハシ <indexial@outlook.jp>, 2013
|
||||
# daingewuvzeevisiddfddd, 2022
|
||||
# タカハシ, 2013
|
||||
# Masayuki Hatta <mhatta@mhatta.org>, 2018
|
||||
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: 2018-08-17 22:08+0000\n"
|
||||
"Last-Translator: Masayuki Hatta <mhatta@mhatta.org>\n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2022-02-13 03:11+0000\n"
|
||||
"Last-Translator: daingewuvzeevisiddfddd\n"
|
||||
"Language-Team: Japanese (http://www.transifex.com/otf/I2P/language/ja/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -27,69 +28,81 @@ msgstr "I2P を開始"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P を起動中!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "起動中"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "I2P ブラウザを起動"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "I2P システムトレイを設定"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "無効"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "通知を有効化"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "通知を無効化"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "システムトレイを無効化"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
msgid "Restart I2P"
|
||||
msgstr "I2P を再起動"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
msgid "Stop I2P"
|
||||
msgstr "I2P を停止"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "すぐに I2P を再起動"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "すぐに I2P を停止"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "I2P のシャットダウンを中止"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "{0} でシャットダウン"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "即時シャットダウン"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
msgid "Network"
|
||||
msgstr "ネットワーク"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: 右クリックでメニュー"
|
||||
|
@ -4,14 +4,15 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Zagros <zagros21@cmail.nu>, 2020
|
||||
# Zagros, 2021
|
||||
# Zagros, 2020
|
||||
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: 2020-09-23 21:02+0000\n"
|
||||
"Last-Translator: Zagros <zagros21@cmail.nu>\n"
|
||||
"PO-Revision-Date: 2021-08-30 21:05+0000\n"
|
||||
"Last-Translator: Zagros\n"
|
||||
"Language-Team: Kurdish (http://www.transifex.com/otf/I2P/language/ku/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -42,7 +43,7 @@ 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
|
||||
@ -72,7 +73,7 @@ msgstr "دەستبەجێ I2P بوەستێنە"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "کووژاندنەوە I2P هەڵبوەشێنەوە"
|
||||
msgstr "دەستهەڵگرتن لە کووژاندنەوەی I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
|
106
apps/desktopgui/locale/messages_sl.po
Normal file
106
apps/desktopgui/locale/messages_sl.po
Normal file
@ -0,0 +1,106 @@
|
||||
# I2P
|
||||
# Copyright (C) 2009 The I2P Project
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Žan Šadl-Ferš, 2021
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2022-02-09 19:23+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"Language-Team: Slovenian (http://www.transifex.com/otf/I2P/language/sl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: sl\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
msgid "Start I2P"
|
||||
msgstr "Zaženi I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P se zaganja!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Zaganja"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Zaženi I2P brskalnik"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Konfiguriraj I2P opravilno vrstico"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
msgid "Restart I2P"
|
||||
msgstr "Ponovno zaženi I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
msgid "Stop I2P"
|
||||
msgstr "Ustavi I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Nemudoma ponovno zaženi I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Nemudoma ustavi I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Prekliči zaustavitev od I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Zaustavi v {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Zaustavitev je neizbežna"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
msgid "Network"
|
||||
msgstr "Omrežje"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Pritisnite na desno tipko miške za meni"
|
@ -4,15 +4,16 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Besnik <besnik@programeshqip.org>, 2016,2019
|
||||
# Besnik Bleta <besnik@programeshqip.org>, 2022
|
||||
# Besnik Bleta <besnik@programeshqip.org>, 2016,2019
|
||||
# Shpetim <shpetim@privacysolutions.no>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2019-01-10 14:28+0000\n"
|
||||
"Last-Translator: Besnik <besnik@programeshqip.org>\n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2022-02-10 10:34+0000\n"
|
||||
"Last-Translator: Besnik Bleta <besnik@programeshqip.org>\n"
|
||||
"Language-Team: Albanian (http://www.transifex.com/otf/I2P/language/sq/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -27,69 +28,81 @@ msgstr "Nise I2P-në"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P po niset!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Po niset"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Nis Shfletuesin I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Formësoni Panel Sistemi I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Çaktivizoje"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Aktivizoni njoftimet"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Çaktivizoni njoftimet"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "Çaktivizo panel sistemi"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
msgid "Restart I2P"
|
||||
msgstr "Rinise I2P-në"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
msgid "Stop I2P"
|
||||
msgstr "Ndale I2P-në"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Rinise I2P-në Menjëherë"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Ndale I2P-në Menjëherë"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Anuloje Mbylljen e I2P-së"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Mbylle për {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Mbyllje shumë shpejt"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
msgid "Network"
|
||||
msgstr "Rrjet"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Djathtasklikoni për menu"
|
||||
|
@ -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: 2021-05-28 01:02+0000\n"
|
||||
"PO-Revision-Date: 2021-10-02 16:29+0000\n"
|
||||
"Last-Translator: Jonatan Nyberg <jonatan@autistici.org>\n"
|
||||
"Language-Team: Swedish (Sweden) (http://www.transifex.com/otf/I2P/language/sv_SE/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -51,7 +51,7 @@ msgstr "Konfigurera I2P-meddelandefältet"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Avaktivera"
|
||||
msgstr "Inaktivera"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
|
@ -4,20 +4,20 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Kaya Zeren <kayazeren@gmail.com>, 2013,2016
|
||||
# Kaya Zeren <kayazeren@gmail.com>, 2013,2016,2022
|
||||
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: 2017-06-30 21:32+0000\n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2022-02-10 04:40+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"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: tr_TR\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
@ -26,69 +26,81 @@ msgstr "I2P başlasın"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P başlatılıyor!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Başlatılıyor"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "I2P Tarayıcısını Açın"
|
||||
msgstr "I2P tarayıcısını aç"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "I2P Sistem Tepsisi Ayarları"
|
||||
msgstr "I2P sistem tepsisi ayarları"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Devre Dışı"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Bildirimleri etkinleştir"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Bildirimleri devre dışı bırak"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "Sistem tepsisini devre dışı bırak"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
msgid "Restart I2P"
|
||||
msgstr "I2P Yeniden Başlasın"
|
||||
msgstr "I2P yeniden başlasın"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
msgid "Stop I2P"
|
||||
msgstr "I2P Durdurulsun"
|
||||
msgstr "I2P durdurulsun"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "I2P Hemen Yeniden Başlatılsın"
|
||||
msgstr "I2P hemen yeniden başlatılsın"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "I2P Hemen Durdurulsun"
|
||||
msgstr "I2P hemen durdurulsun"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "I2P Kapatmayı İptal Et"
|
||||
msgstr "I2P kapatmayı iptal et"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "{0} içinde kapat"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Kapatılmak üzere"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
msgid "Network"
|
||||
msgstr "Ağ"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Menüde sağ tık"
|
||||
|
@ -5,15 +5,15 @@
|
||||
#
|
||||
# Translators:
|
||||
# ducki2p <ducki2p@gmail.com>, 2011
|
||||
# Scott Rhodes <starring169@gmail.com>, 2021
|
||||
# Scott Rhodes <starring169@gmail.com>, 2021-2022
|
||||
# walking <walking@i2pmail.org>, 2011
|
||||
# YFdyh000 <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: 2021-03-07 07:58+0000\n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2022-02-16 16:05+0000\n"
|
||||
"Last-Translator: Scott Rhodes <starring169@gmail.com>\n"
|
||||
"Language-Team: Chinese (China) (http://www.transifex.com/otf/I2P/language/zh_CN/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -29,69 +29,81 @@ msgstr "启动 I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr " I2P 正在启动!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "正在启动"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "启动 I2P 浏览器"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "配置 I2P 系统托盘"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "禁用"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "启用通知"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "禁用通知"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "禁用系统托盘"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
msgid "Restart I2P"
|
||||
msgstr "重启 I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
msgid "Stop I2P"
|
||||
msgstr "停止 I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "立即重启 I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "立即停止 I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "取消 I2P 关闭"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "{0} 后关闭"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "立即关闭"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
msgid "Network"
|
||||
msgstr "网络"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P:右击获得菜单"
|
||||
|
@ -9,9 +9,9 @@ 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: 2018-10-04 00:43+0000\n"
|
||||
"Last-Translator: erinm\n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: 黃彥儒 <r1235613@gmail.com>, 2017\n"
|
||||
"Language-Team: Chinese (Taiwan) (http://www.transifex.com/otf/I2P/language/zh_TW/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -26,69 +26,81 @@ msgstr "啟動I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P已啟動"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "啟動中"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "開啟I2P瀏覽器"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "設定I2P系統文件夾"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "停用"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "启用通知"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "禁用通知"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "禁用系统托盘"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
msgid "Restart I2P"
|
||||
msgstr "重啟I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
msgid "Stop I2P"
|
||||
msgstr "停止I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "強制重啟I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "強制終止I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "取消停止I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "關閉於 {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "強制關閉"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
msgid "Network"
|
||||
msgstr "網路"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P:右鍵開啟選單"
|
||||
|
227
apps/desktopgui/src/net/i2p/desktopgui/ExternalMain.java
Normal file
227
apps/desktopgui/src/net/i2p/desktopgui/ExternalMain.java
Normal file
@ -0,0 +1,227 @@
|
||||
package net.i2p.desktopgui;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.Toolkit;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.app.ClientApp;
|
||||
import net.i2p.app.ClientAppState;
|
||||
import net.i2p.app.NotificationService;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
* A simplified Main that does not require router.jar, for App Context only.
|
||||
* Invokes ExternalTrayManager only.
|
||||
* No state tracking, ClientAppManager doesn't care.
|
||||
*
|
||||
* @since 0.9.54
|
||||
*/
|
||||
public class ExternalMain implements ClientApp, NotificationService {
|
||||
|
||||
private final I2PAppContext _appContext;
|
||||
private final ClientAppManager _mgr;
|
||||
private final Log log;
|
||||
private TrayManager _trayManager;
|
||||
|
||||
private static final String PROP_SWING = "desktopgui.swing";
|
||||
|
||||
public ExternalMain(I2PAppContext ctx, ClientAppManager mgr, String args[]) {
|
||||
_appContext = ctx;
|
||||
_mgr = mgr;
|
||||
log = _appContext.logManager().getLog(ExternalMain.class);
|
||||
}
|
||||
|
||||
public ExternalMain() {
|
||||
_appContext = I2PAppContext.getGlobalContext();
|
||||
_mgr = _appContext.clientAppManager();
|
||||
log = _appContext.logManager().getLog(ExternalMain.class);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// early check so we can bail out when started via CLI
|
||||
if (!SystemTray.isSupported()) {
|
||||
System.err.println("SystemTray not supported");
|
||||
return;
|
||||
}
|
||||
ExternalMain main = new ExternalMain();
|
||||
main.beginStartup(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the tray icon code (loads tray icon in the tray area).
|
||||
* @throws AWTException on startup error, including systray not supported
|
||||
*/
|
||||
private synchronized void startUp() throws Exception {
|
||||
final TrayManager trayManager;
|
||||
boolean useSwingDefault = !(SystemVersion.isWindows() || SystemVersion.isMac());
|
||||
boolean useSwing = _appContext.getProperty(PROP_SWING, useSwingDefault);
|
||||
_trayManager = new ExternalTrayManager(_appContext, useSwing);
|
||||
_trayManager.startManager();
|
||||
if (_mgr != null)
|
||||
_mgr.register(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method launching the application.
|
||||
*
|
||||
* @param args unused
|
||||
*/
|
||||
private void beginStartup(String[] args) {
|
||||
String headless = System.getProperty("java.awt.headless");
|
||||
boolean isHeadless = Boolean.parseBoolean(headless);
|
||||
if (isHeadless) {
|
||||
log.warn("Headless environment: not starting desktopgui!");
|
||||
return;
|
||||
}
|
||||
if (SystemVersion.isMac())
|
||||
setMacTrayIcon();
|
||||
launchForeverLoop();
|
||||
|
||||
// We'll be doing GUI work, so let's stay in the event dispatcher thread.
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
startUp();
|
||||
} catch(Exception e) {
|
||||
log.error("Failed while running desktopgui!", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unless we do this, when we start DesktopGUI we get a Java coffee cup
|
||||
* in the tray.
|
||||
*
|
||||
* Based on code from https://gist.github.com/bchapuis/1562406 , no apparent license.
|
||||
* See also https://stackoverflow.com/questions/6006173/how-do-you-change-the-dock-icon-of-a-java-program
|
||||
*
|
||||
* TODO, if we wanted to add our own menu, see
|
||||
* https://stackoverflow.com/questions/1319805/java-os-x-dock-menu
|
||||
*
|
||||
* TODO, if we want to make it bounce, see
|
||||
* https://stackoverflow.com/questions/15079783/how-to-make-my-app-icon-bounce-in-the-mac-dock
|
||||
*
|
||||
* TODO, if we want to handle Quit, see
|
||||
* https://nakkaya.com/2009/04/19/java-osx-integration/
|
||||
*
|
||||
* @since 0.9.33
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void setMacTrayIcon() {
|
||||
File f = new File(_appContext.getBaseDir(), "docs/themes/console/images/itoopie_sm.png");
|
||||
if (!f.exists())
|
||||
return;
|
||||
try {
|
||||
Class util = Class.forName("com.apple.eawt.Application");
|
||||
Method getApplication = util.getMethod("getApplication", new Class[0]);
|
||||
Object application = getApplication.invoke(util);
|
||||
Class params[] = new Class[1];
|
||||
params[0] = Image.class;
|
||||
Method setDockIconImage = util.getMethod("setDockIconImage", params);
|
||||
URL url = f.toURI().toURL();
|
||||
Image image = Toolkit.getDefaultToolkit().getImage(url);
|
||||
setDockIconImage.invoke(application, image);
|
||||
} catch (Exception e) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("Can't set OSX Dock icon", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoids the app terminating because no Window is opened anymore.
|
||||
* More info: http://java.sun.com/javase/6/docs/api/java/awt/doc-files/AWTThreadIssues.html#Autoshutdown
|
||||
*/
|
||||
private static void launchForeverLoop() {
|
||||
Runnable r = new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
Object o = new Object();
|
||||
synchronized (o) {
|
||||
o.wait();
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
};
|
||||
Thread t = new Thread(r, "DesktopGUI spinner");
|
||||
t.setDaemon(false);
|
||||
t.start();
|
||||
}
|
||||
|
||||
/////// NotificationService methods
|
||||
|
||||
/**
|
||||
* Send a notification to the user.
|
||||
*
|
||||
* @param source unsupported
|
||||
* @param category unsupported
|
||||
* @param priority unsupported
|
||||
* @param title for the popup, translated
|
||||
* @param message translated
|
||||
* @param path unsupported
|
||||
* @return 0, or -1 on failure
|
||||
*/
|
||||
public int notify(String source, String category, int priority, String title, String message, String path) {
|
||||
TrayManager tm = _trayManager;
|
||||
if (tm == null)
|
||||
return -1;
|
||||
return tm.displayMessage(priority, title, message, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a notification if possible.
|
||||
* Unsupported.
|
||||
*
|
||||
* @return false always
|
||||
*/
|
||||
public boolean cancel(int id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the text of a notification if possible.
|
||||
* Unsupported.
|
||||
*
|
||||
* @return false always
|
||||
*/
|
||||
public boolean update(int id, String title, String message, String path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/////// ClientApp methods
|
||||
|
||||
public synchronized void startup() {
|
||||
beginStartup(null);
|
||||
}
|
||||
|
||||
public synchronized void shutdown(String[] args) {
|
||||
if (_trayManager != null)
|
||||
_trayManager.stopManager();
|
||||
}
|
||||
|
||||
public ClientAppState getState() {
|
||||
return ClientAppState.INITIALIZED;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "desktopgui";
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return "Desktop GUI";
|
||||
}
|
||||
|
||||
/////// end ClientApp methods
|
||||
}
|
@ -22,8 +22,8 @@ import net.i2p.desktopgui.router.RouterManager;
|
||||
*/
|
||||
class ExternalTrayManager extends TrayManager {
|
||||
|
||||
public ExternalTrayManager(I2PAppContext ctx, Main main, boolean useSwing) {
|
||||
super(ctx, main, useSwing);
|
||||
public ExternalTrayManager(I2PAppContext ctx, boolean useSwing) {
|
||||
super(ctx, useSwing);
|
||||
}
|
||||
|
||||
public PopupMenu getMainMenu() {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.i2p.desktopgui;
|
||||
|
||||
import java.awt.AWTException;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Desktop.Action;
|
||||
import java.awt.MenuItem;
|
||||
@ -29,21 +30,33 @@ class InternalTrayManager extends TrayManager {
|
||||
|
||||
private final RouterContext _context;
|
||||
private final Log log;
|
||||
private final Main _main;
|
||||
private MenuItem _statusItem, _browserItem, _configItem, _restartItem, _stopItem,
|
||||
_restartHardItem, _stopHardItem, _cancelItem;
|
||||
_restartHardItem, _stopHardItem, _cancelItem,
|
||||
_notificationItem1, _notificationItem2;
|
||||
private JMenuItem _jstatusItem, _jbrowserItem, _jconfigItem, _jrestartItem, _jstopItem,
|
||||
_jrestartHardItem, _jstopHardItem, _jcancelItem;
|
||||
_jrestartHardItem, _jstopHardItem, _jcancelItem,
|
||||
_jnotificationItem1, _jnotificationItem2;
|
||||
|
||||
private static final boolean CONSOLE_ENABLED = Desktop.isDesktopSupported() &&
|
||||
Desktop.getDesktop().isSupported(Action.BROWSE);
|
||||
private static final String CONSOLE_BUNDLE_NAME = "net.i2p.router.web.messages";
|
||||
|
||||
public InternalTrayManager(RouterContext ctx, Main main, boolean useSwing) {
|
||||
super(ctx, main, useSwing);
|
||||
super(ctx, useSwing);
|
||||
_context = ctx;
|
||||
_main = main;
|
||||
log = ctx.logManager().getLog(InternalTrayManager.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.53
|
||||
*/
|
||||
public void startManager() throws AWTException {
|
||||
super.startManager();
|
||||
displayMessage(Log.INFO, _t("Starting"), _t("I2P is starting!"), null);
|
||||
}
|
||||
|
||||
public synchronized PopupMenu getMainMenu() {
|
||||
PopupMenu popup = new PopupMenu();
|
||||
|
||||
@ -73,7 +86,35 @@ class InternalTrayManager extends TrayManager {
|
||||
}
|
||||
|
||||
PopupMenu desktopguiConfigurationLauncher = new PopupMenu(_t("Configure I2P System Tray"));
|
||||
MenuItem configSubmenu = new MenuItem(_t("Disable"));
|
||||
final MenuItem notificationItem2 = new MenuItem(_t("Enable notifications"));
|
||||
notificationItem2.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
new SwingWorker<Object, Object>() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
configureNotifications(true);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
|
||||
final MenuItem notificationItem1 = new MenuItem(_t("Disable notifications"));
|
||||
notificationItem1.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
new SwingWorker<Object, Object>() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
configureNotifications(false);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
|
||||
MenuItem configSubmenu = new MenuItem(_t("Disable system tray"));
|
||||
configSubmenu.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
@ -173,6 +214,8 @@ class InternalTrayManager extends TrayManager {
|
||||
popup.add(browserLauncher);
|
||||
popup.addSeparator();
|
||||
}
|
||||
desktopguiConfigurationLauncher.add(notificationItem2);
|
||||
desktopguiConfigurationLauncher.add(notificationItem1);
|
||||
desktopguiConfigurationLauncher.add(configSubmenu);
|
||||
popup.add(desktopguiConfigurationLauncher);
|
||||
popup.addSeparator();
|
||||
@ -187,6 +230,8 @@ class InternalTrayManager extends TrayManager {
|
||||
_statusItem = statusItem;
|
||||
_browserItem = browserLauncher;
|
||||
_configItem = desktopguiConfigurationLauncher;
|
||||
_notificationItem1 = notificationItem1;
|
||||
_notificationItem2 = notificationItem2;
|
||||
_restartItem = restartItem;
|
||||
_stopItem = stopItem;
|
||||
_restartHardItem = restartItem2;
|
||||
@ -225,7 +270,35 @@ class InternalTrayManager extends TrayManager {
|
||||
}
|
||||
|
||||
JMenu desktopguiConfigurationLauncher = new JMenu(_t("Configure I2P System Tray"));
|
||||
JMenuItem configSubmenu = new JMenuItem(_t("Disable"));
|
||||
final JMenuItem notificationItem2 = new JMenuItem(_t("Enable notifications"));
|
||||
notificationItem2.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
new SwingWorker<Object, Object>() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
configureNotifications(true);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
|
||||
final JMenuItem notificationItem1 = new JMenuItem(_t("Disable notifications"));
|
||||
notificationItem1.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
new SwingWorker<Object, Object>() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
configureNotifications(false);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
|
||||
JMenuItem configSubmenu = new JMenuItem(_t("Disable system tray"));
|
||||
configSubmenu.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
@ -325,6 +398,8 @@ class InternalTrayManager extends TrayManager {
|
||||
popup.add(browserLauncher);
|
||||
popup.addSeparator();
|
||||
}
|
||||
desktopguiConfigurationLauncher.add(notificationItem2);
|
||||
desktopguiConfigurationLauncher.add(notificationItem1);
|
||||
desktopguiConfigurationLauncher.add(configSubmenu);
|
||||
popup.add(desktopguiConfigurationLauncher);
|
||||
popup.addSeparator();
|
||||
@ -339,6 +414,8 @@ class InternalTrayManager extends TrayManager {
|
||||
_jstatusItem = statusItem;
|
||||
_jbrowserItem = browserLauncher;
|
||||
_jconfigItem = desktopguiConfigurationLauncher;
|
||||
_jnotificationItem1 = notificationItem1;
|
||||
_jnotificationItem2 = notificationItem2;
|
||||
_jrestartItem = restartItem;
|
||||
_jstopItem = stopItem;
|
||||
_jrestartHardItem = restartItem2;
|
||||
@ -390,6 +467,10 @@ class InternalTrayManager extends TrayManager {
|
||||
_stopHardItem.setEnabled(!imminent);
|
||||
if (_cancelItem != null)
|
||||
_cancelItem.setEnabled(x && !imminent);
|
||||
if (_notificationItem1 != null)
|
||||
_notificationItem1.setEnabled(_showNotifications);
|
||||
if (_notificationItem2 != null)
|
||||
_notificationItem2.setEnabled(!_showNotifications);
|
||||
|
||||
if (_jstatusItem != null)
|
||||
_jstatusItem.setText(status);
|
||||
@ -407,6 +488,10 @@ class InternalTrayManager extends TrayManager {
|
||||
_jstopHardItem.setVisible(!imminent);
|
||||
if (_jcancelItem != null)
|
||||
_jcancelItem.setVisible(x && !imminent);
|
||||
if (_jnotificationItem1 != null)
|
||||
_jnotificationItem1.setVisible(_showNotifications);
|
||||
if (_jnotificationItem2 != null)
|
||||
_jnotificationItem2.setVisible(!_showNotifications);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -415,18 +500,24 @@ class InternalTrayManager extends TrayManager {
|
||||
private void configureDesktopgui(boolean enable) {
|
||||
String property = Main.PROP_ENABLE;
|
||||
String value = Boolean.toString(enable);
|
||||
try {
|
||||
|
||||
_context.router().saveConfig(property, value);
|
||||
if (!enable) {
|
||||
// TODO popup that explains how to re-enable in console
|
||||
_main.shutdown(null);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error("Error saving config", ex);
|
||||
if (!_context.router().saveConfig(property, value))
|
||||
log.error("Error saving config");
|
||||
if (!enable) {
|
||||
// TODO popup that explains how to re-enable in console
|
||||
_main.shutdown(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.53
|
||||
*/
|
||||
private void configureNotifications(boolean enable) {
|
||||
_showNotifications = enable;
|
||||
String value = Boolean.toString(enable);
|
||||
if (!_context.router().saveConfig(PROP_NOTIFICATIONS, value))
|
||||
log.error("Error saving config");
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the console URL with info from the port mapper,
|
||||
* and launch the browser at it.
|
||||
|
@ -5,6 +5,7 @@ package net.i2p.desktopgui;
|
||||
*/
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.Toolkit;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
@ -16,6 +17,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.app.ClientAppState;
|
||||
import static net.i2p.app.ClientAppState.*;
|
||||
import net.i2p.app.NotificationService;
|
||||
import net.i2p.desktopgui.router.RouterManager;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.app.RouterApp;
|
||||
@ -27,7 +29,7 @@ import net.i2p.util.I2PProperties.I2PPropertyCallback;
|
||||
/**
|
||||
* The main class of the application.
|
||||
*/
|
||||
public class Main implements RouterApp {
|
||||
public class Main implements RouterApp, NotificationService {
|
||||
|
||||
// non-null
|
||||
private final I2PAppContext _appContext;
|
||||
@ -55,7 +57,7 @@ public class Main implements RouterApp {
|
||||
*/
|
||||
public Main() {
|
||||
_appContext = I2PAppContext.getGlobalContext();
|
||||
if (_appContext instanceof RouterContext)
|
||||
if (_appContext.isRouterContext())
|
||||
_context = (RouterContext) _appContext;
|
||||
else
|
||||
_context = null;
|
||||
@ -75,7 +77,7 @@ public class Main implements RouterApp {
|
||||
if (_context != null)
|
||||
trayManager = new InternalTrayManager(_context, this, useSwing);
|
||||
else
|
||||
trayManager = new ExternalTrayManager(_appContext, this, useSwing);
|
||||
trayManager = new ExternalTrayManager(_appContext, useSwing);
|
||||
trayManager.startManager();
|
||||
_trayManager = trayManager;
|
||||
changeState(RUNNING);
|
||||
@ -95,6 +97,11 @@ public class Main implements RouterApp {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// early check so we can bail out when started via CLI
|
||||
if (!SystemTray.isSupported()) {
|
||||
System.err.println("SystemTray not supported");
|
||||
return;
|
||||
}
|
||||
Main main = new Main();
|
||||
main.beginStartup(args);
|
||||
}
|
||||
@ -198,6 +205,46 @@ public class Main implements RouterApp {
|
||||
t.start();
|
||||
}
|
||||
|
||||
/////// NotificationService methods
|
||||
|
||||
/**
|
||||
* Send a notification to the user.
|
||||
*
|
||||
* @param source unsupported
|
||||
* @param category unsupported
|
||||
* @param priority unsupported
|
||||
* @param title for the popup, translated
|
||||
* @param message translated
|
||||
* @param path unsupported
|
||||
* @return 0, or -1 on failure
|
||||
*/
|
||||
public int notify(String source, String category, int priority, String title, String message, String path) {
|
||||
TrayManager tm = _trayManager;
|
||||
if (tm == null)
|
||||
return -1;
|
||||
return tm.displayMessage(priority, title, message, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a notification if possible.
|
||||
* Unsupported.
|
||||
*
|
||||
* @return false always
|
||||
*/
|
||||
public boolean cancel(int id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the text of a notification if possible.
|
||||
* Unsupported.
|
||||
*
|
||||
* @return false always
|
||||
*/
|
||||
public boolean update(int id, String title, String message, String path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/////// ClientApp methods
|
||||
|
||||
/** @since 0.9.26 */
|
||||
|
@ -8,21 +8,27 @@ import java.awt.PopupMenu;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.TrayIcon;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.event.MenuKeyEvent;
|
||||
import javax.swing.event.MenuKeyListener;
|
||||
import javax.swing.event.PopupMenuEvent;
|
||||
import javax.swing.event.PopupMenuListener;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.apps.systray.UrlLauncher;
|
||||
import net.i2p.desktopgui.i18n.DesktopguiTranslator;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
@ -31,24 +37,25 @@ import net.i2p.util.SystemVersion;
|
||||
abstract class TrayManager {
|
||||
|
||||
protected final I2PAppContext _appContext;
|
||||
protected final Main _main;
|
||||
protected final boolean _useSwing;
|
||||
///The tray area, or null if unsupported
|
||||
protected SystemTray tray;
|
||||
///Our tray icon, or null if unsupported
|
||||
protected TrayIcon trayIcon;
|
||||
protected volatile boolean _showNotifications;
|
||||
|
||||
private static final String PNG_DIR = "/desktopgui/resources/images/";
|
||||
private static final String MAC_ICON = "itoopie_black_24.png";
|
||||
private static final String WIN_ICON = "itoopie_white_24.png";
|
||||
private static final String WIN_ICON_LIGHT = "itoopie_white_24.png";
|
||||
private static final String WIN_ICON_DARK = "itoopie_black_24.png";
|
||||
private static final String LIN_ICON = "logo.png";
|
||||
protected static final String PROP_NOTIFICATIONS = "desktopgui.showNotifications";
|
||||
|
||||
/**
|
||||
* Instantiate tray manager.
|
||||
*/
|
||||
protected TrayManager(I2PAppContext ctx, Main main, boolean useSwing) {
|
||||
protected TrayManager(I2PAppContext ctx, boolean useSwing) {
|
||||
_appContext = ctx;
|
||||
_main = main;
|
||||
_useSwing = useSwing;
|
||||
}
|
||||
|
||||
@ -58,6 +65,7 @@ abstract class TrayManager {
|
||||
public synchronized void startManager() throws AWTException {
|
||||
if (!SystemTray.isSupported())
|
||||
throw new AWTException("SystemTray not supported");
|
||||
_showNotifications = _appContext.getBooleanPropertyDefaultTrue(PROP_NOTIFICATIONS);
|
||||
tray = SystemTray.getSystemTray();
|
||||
// Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
String tooltip = SystemVersion.isWindows() ? _t("I2P: Right-click for menu") : null;
|
||||
@ -191,12 +199,20 @@ abstract class TrayManager {
|
||||
*/
|
||||
private Image getTrayImage() throws AWTException {
|
||||
String img;
|
||||
if (SystemVersion.isWindows())
|
||||
img = WIN_ICON;
|
||||
else if (SystemVersion.isMac())
|
||||
if (SystemVersion.isWindows()) {
|
||||
// too hard to get the theme out of the registry,
|
||||
// use our console theme as a best guess
|
||||
// so we have a contrasting icon
|
||||
String theme = _appContext.getProperty("routerconsole.theme", "light");
|
||||
if (theme.equals("dark"))
|
||||
img = WIN_ICON_LIGHT;
|
||||
else
|
||||
img = WIN_ICON_DARK;
|
||||
} else if (SystemVersion.isMac()) {
|
||||
img = MAC_ICON;
|
||||
else
|
||||
} else {
|
||||
img = LIN_ICON;
|
||||
}
|
||||
URL url = getClass().getResource(PNG_DIR + img);
|
||||
if (url == null)
|
||||
throw new AWTException("cannot load tray image " + img);
|
||||
@ -204,6 +220,74 @@ abstract class TrayManager {
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a notification to the user.
|
||||
*
|
||||
* @param title for the popup, translated
|
||||
* @param message translated
|
||||
* @param path unsupported
|
||||
* @return 0, or -1 on failure
|
||||
*/
|
||||
public int displayMessage(int priority, String title, String message, String path) {
|
||||
if (!_showNotifications)
|
||||
return -1;
|
||||
final TrayIcon ti = trayIcon;
|
||||
if (ti == null)
|
||||
return -1;
|
||||
TrayIcon.MessageType type;
|
||||
if (priority <= Log.DEBUG)
|
||||
type = TrayIcon.MessageType.NONE;
|
||||
else if (priority <= Log.INFO)
|
||||
type = TrayIcon.MessageType.INFO;
|
||||
else if (priority <= Log.WARN)
|
||||
type = TrayIcon.MessageType.WARNING;
|
||||
else
|
||||
type = TrayIcon.MessageType.ERROR;
|
||||
ti.displayMessage(title, message, type);
|
||||
/*
|
||||
* There's apparently no way to bind a particular message to an action
|
||||
that comes back. We can't keep a queue because we don't get
|
||||
an action back when the message is removed via timeout or user x-out.
|
||||
On OSX, new messages dismiss previous ones.
|
||||
On LXDE (and Gnome?), new messages go under previous ones. Timeout is only 10 seconds.
|
||||
Message timeout is platform-dependent.
|
||||
So the order of events is unknowable.
|
||||
This only works if there is only one message ever.
|
||||
|
||||
if (path != null && path.length() > 0) {
|
||||
if (path.charAt(0) == '/');
|
||||
path = path.substring(1);
|
||||
final String url = _appContext.portMapper().getConsoleURL() + path;
|
||||
ti.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
ti.removeActionListener(this);
|
||||
new SwingWorker<Object, Object>() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
System.out.println("DIB " + arg0);
|
||||
UrlLauncher launcher = new UrlLauncher(_appContext, null, null);
|
||||
try {
|
||||
launcher.openUrl(url);
|
||||
System.out.println("DIB success " + url);
|
||||
} catch (IOException e1) {
|
||||
System.out.println("DIB fail " + url);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
System.out.println("done " + arg0);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected String _t(String s) {
|
||||
return DesktopguiTranslator._t(_appContext, s);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
import net.i2p.apache.http.conn.util.InetAddressUtils;
|
||||
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
|
@ -156,11 +156,11 @@ public class JSONRPC2Servlet extends HttpServlet {
|
||||
} else {
|
||||
out.println("<p>Current API password:<input name=\"password\" type=\"password\">");
|
||||
}
|
||||
out.println("<p>New API password (twice):<input name=\"password2\" type=\"password\">" +
|
||||
"<input name=\"password3\" type=\"password\">" +
|
||||
out.println("<p>New API password (twice): <input name=\"password2\" type=\"password\"> " +
|
||||
"<input name=\"password3\" type=\"password\"> " +
|
||||
"<input name=\"save\" type=\"submit\" value=\"Change API Password\">" +
|
||||
"<p>If you forget the API password, stop i2pcontrol, delete the file <tt>" + _conf.getConfFile() +
|
||||
"</tt>, and restart i2pcontrol.");
|
||||
"<p>If you forget the API password, <a href=\"/configwebapps\">stop jsonrpc</a>, delete the file <tt>" + _conf.getConfFile() +
|
||||
"</tt>, and <a href=\"/configwebapps\">restart jsonrpc</a>.");
|
||||
out.println("</form>");
|
||||
} else {
|
||||
out.println("<p><a href=\"password\">Change API Password</a>");
|
||||
|
@ -96,7 +96,7 @@ public class RouterInfoHandler implements RequestHandler {
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.status")) {
|
||||
outParams.put("i2p.router.status", _context.throttle().getTunnelStatus());
|
||||
outParams.put("i2p.router.status", _context.throttle().getLocalizedTunnelStatus());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.status")) {
|
||||
@ -179,25 +179,44 @@ public class RouterInfoHandler implements RequestHandler {
|
||||
|
||||
int status = _context.commSystem().getStatus().getCode();
|
||||
switch (status) {
|
||||
case CommSystemFacade.STATUS_OK:
|
||||
RouterAddress ra = _context.router().getRouterInfo().getTargetAddress("NTCP");
|
||||
|
||||
case CommSystemFacade.STATUS_OK:
|
||||
case CommSystemFacade.STATUS_IPV4_OK_IPV6_UNKNOWN:
|
||||
case CommSystemFacade.STATUS_IPV4_OK_IPV6_FIREWALLED:
|
||||
case CommSystemFacade.STATUS_IPV4_FIREWALLED_IPV6_OK:
|
||||
case CommSystemFacade.STATUS_IPV4_DISABLED_IPV6_OK:
|
||||
RouterAddress ra = _context.router().getRouterInfo().getTargetAddress("NTCP2");
|
||||
if (ra == null || TransportUtil.isPubliclyRoutable(ra.getIP(), true))
|
||||
return NETWORK_STATUS.OK;
|
||||
return NETWORK_STATUS.ERROR_PRIVATE_TCP_ADDRESS;
|
||||
case CommSystemFacade.STATUS_DIFFERENT:
|
||||
|
||||
case CommSystemFacade.STATUS_DIFFERENT:
|
||||
case CommSystemFacade.STATUS_IPV4_SNAT_IPV6_OK:
|
||||
case CommSystemFacade.STATUS_IPV4_SNAT_IPV6_UNKNOWN:
|
||||
return NETWORK_STATUS.ERROR_SYMMETRIC_NAT;
|
||||
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
|
||||
if (_context.router().getRouterInfo().getTargetAddress("NTCP") != null)
|
||||
|
||||
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
|
||||
case CommSystemFacade.STATUS_IPV4_FIREWALLED_IPV6_UNKNOWN:
|
||||
case CommSystemFacade.STATUS_IPV4_DISABLED_IPV6_FIREWALLED:
|
||||
if (_context.router().getRouterInfo().getTargetAddress("NTCP2") != null)
|
||||
return NETWORK_STATUS.WARN_FIREWALLED_WITH_INBOUND_TCP;
|
||||
if (((FloodfillNetworkDatabaseFacade) _context.netDb()).floodfillEnabled())
|
||||
return NETWORK_STATUS.WARN_FIREWALLED_AND_FLOODFILL;
|
||||
if (_context.router().getRouterInfo().getCapabilities().indexOf('O') >= 0)
|
||||
return NETWORK_STATUS.WARN_FIREWALLED_AND_FAST;
|
||||
return NETWORK_STATUS.FIREWALLED;
|
||||
case CommSystemFacade.STATUS_HOSED:
|
||||
|
||||
case CommSystemFacade.STATUS_HOSED:
|
||||
return NETWORK_STATUS.ERROR_UDP_PORT_IN_USE;
|
||||
case CommSystemFacade.STATUS_UNKNOWN: // fallthrough
|
||||
default:
|
||||
|
||||
case CommSystemFacade.STATUS_DISCONNECTED:
|
||||
return NETWORK_STATUS.ERROR_NO_ACTIVE_PEERS_CHECK_CONNECTION_AND_FIREWALL;
|
||||
|
||||
case CommSystemFacade.STATUS_UNKNOWN: // fallthrough
|
||||
case CommSystemFacade.STATUS_IPV4_UNKNOWN_IPV6_OK:
|
||||
case CommSystemFacade.STATUS_IPV4_UNKNOWN_IPV6_FIREWALLED:
|
||||
case CommSystemFacade.STATUS_IPV4_DISABLED_IPV6_UNKNOWN:
|
||||
default:
|
||||
ra = _context.router().getRouterInfo().getTargetAddress("SSU");
|
||||
if (ra == null && _context.router().getUptime() > 5 * 60 * 1000) {
|
||||
if (_context.commSystem().countActivePeers() <= 0)
|
||||
|
@ -2,3 +2,11 @@
|
||||
# This is for app context configuration of standalone i2psnark.
|
||||
# Almost all configuration settings are in i2psnark.config.d/i2psnark.config
|
||||
#
|
||||
# disable browser launch on startup
|
||||
#routerconsole.browser=/bin/false
|
||||
# disable browser launch on startup (Windows)
|
||||
#routerconsole.browser=NUL
|
||||
# change browser
|
||||
#routerconsole.browser=firefox
|
||||
# disable system tray
|
||||
#desktopgui.enabled=false
|
||||
|
@ -21,6 +21,8 @@
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<!-- jsp-api.jar only present for debian builds -->
|
||||
<pathelement location="../../jetty/jettylib/jsp-api.jar" />
|
||||
<!-- following jars only for standalone builds -->
|
||||
<pathelement location="../../desktopgui/dist/desktopgui.jar" />
|
||||
</classpath>
|
||||
</depend>
|
||||
</target>
|
||||
@ -60,6 +62,7 @@
|
||||
<pathelement location="../../systray/java/build/systray.jar" />
|
||||
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-util.jar" />
|
||||
<pathelement location="../../desktopgui/dist/desktopgui.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
@ -197,9 +200,16 @@
|
||||
</target>
|
||||
|
||||
<target name="standalone" depends="standalone_prep">
|
||||
<!-- doesn't support file permissions
|
||||
<zip destfile="i2psnark-standalone.zip">
|
||||
<zipfileset dir="./dist/" prefix="i2psnark/" />
|
||||
<zipfileset dir="./i2psnark/" />
|
||||
</zip>
|
||||
-->
|
||||
<exec executable="zip" failifexecutionfails="true" failonerror="true" >
|
||||
<arg value="-r" />
|
||||
<arg value="i2psnark-standalone.zip" />
|
||||
<arg value="i2psnark" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- make a fat jar for standalone -->
|
||||
@ -228,6 +238,8 @@
|
||||
<zipfileset src="../../ministreaming/java/build/mstreaming.jar" />
|
||||
<zipfileset src="../../streaming/java/build/streaming.jar" />
|
||||
<zipfileset src="../../systray/java/build/systray.jar" />
|
||||
<zipfileset src="../../../build/jbigi.jar" />
|
||||
<zipfileset src="../../desktopgui/dist/desktopgui.jar" />
|
||||
<!-- Countries translations. The i2psnark translations are in the war but it's easier to put these here -->
|
||||
<!-- 300KB just to translate "Brazil", but why not... -->
|
||||
<!--
|
||||
@ -306,33 +318,34 @@
|
||||
</target>
|
||||
|
||||
<target name="standalone_prep" depends="standalone_jar, standalone_war">
|
||||
<delete dir="./dist" />
|
||||
<mkdir dir="./dist" />
|
||||
<copy file="../launch-i2psnark" todir="./dist/" />
|
||||
<copy file="../launch-i2psnark.bat" todir="./dist/" />
|
||||
<mkdir dir="./dist/contexts" />
|
||||
<copy file="../standalone-context.xml" tofile="./dist/contexts/context.xml" />
|
||||
<mkdir dir="./dist/docroot" />
|
||||
<copy file="../standalone-index.html" tofile="./dist/docroot/index.html" />
|
||||
<mkdir dir="./dist/webapps" />
|
||||
<copy file="../i2psnark.war" tofile="./dist/webapps/i2psnark.war" />
|
||||
<copy file="../jetty-i2psnark.xml" tofile="./dist/jetty-i2psnark.xml" />
|
||||
<copy file="../i2psnark-appctx.config" tofile="./dist/i2psnark-appctx.config" />
|
||||
<copy file="./build/i2psnark-standalone.jar" tofile="./dist/i2psnark.jar" />
|
||||
<copy file="../readme-standalone.txt" tofile="./dist/readme.txt" />
|
||||
<delete dir="./i2psnark" />
|
||||
<mkdir dir="./i2psnark" />
|
||||
<copy file="../launch-i2psnark" todir="./i2psnark/" />
|
||||
<chmod type="file" file="./i2psnark/launch-i2psnark" perm="+x" />
|
||||
<copy file="../launch-i2psnark.bat" todir="./i2psnark/" />
|
||||
<mkdir dir="./i2psnark/contexts" />
|
||||
<copy file="../standalone-context.xml" tofile="./i2psnark/contexts/context.xml" />
|
||||
<mkdir dir="./i2psnark/docroot" />
|
||||
<copy file="../standalone-index.html" tofile="./i2psnark/docroot/index.html" />
|
||||
<mkdir dir="./i2psnark/webapps" />
|
||||
<copy file="../i2psnark.war" tofile="./i2psnark/webapps/i2psnark.war" />
|
||||
<copy file="../jetty-i2psnark.xml" tofile="./i2psnark/jetty-i2psnark.xml" />
|
||||
<copy file="../i2psnark-appctx.config" tofile="./i2psnark/i2psnark-appctx.config" />
|
||||
<copy file="./build/i2psnark-standalone.jar" tofile="./i2psnark/i2psnark.jar" />
|
||||
<copy file="../readme-standalone.txt" tofile="./i2psnark/readme.txt" />
|
||||
<!-- temp so announces work -->
|
||||
<copy file="../../../installer/resources/hosts.txt" tofile="./dist/hosts.txt" />
|
||||
<copy todir="./dist/licenses" >
|
||||
<copy file="../../../installer/resources/hosts.txt" tofile="./i2psnark/hosts.txt" />
|
||||
<copy todir="./i2psnark/licenses" >
|
||||
<fileset dir="../../../licenses" includes="LICENSE-GPLv2.txt, ABOUT-Jetty.html" />
|
||||
</copy>
|
||||
<mkdir dir="./dist/logs" />
|
||||
<mkdir dir="./i2psnark/logs" />
|
||||
</target>
|
||||
|
||||
<target name="clean">
|
||||
<delete dir="./build" />
|
||||
<delete file="../i2psnark.war" />
|
||||
<delete file="./i2psnark-standalone.zip" />
|
||||
<delete dir="./dist" />
|
||||
<delete dir="./i2psnark" />
|
||||
</target>
|
||||
<target name="cleandep" depends="clean">
|
||||
</target>
|
||||
|
@ -30,6 +30,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.RouterRestartException;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
@ -131,12 +132,13 @@ class ConnectionAcceptor implements Runnable
|
||||
/**
|
||||
* Effectively unused, would only be called if we changed
|
||||
* I2CP host/port, which is hidden in the gui if in router context
|
||||
* FIXME this only works if already running
|
||||
*/
|
||||
public synchronized void restart() {
|
||||
Thread t = thread;
|
||||
if (t != null)
|
||||
t.interrupt();
|
||||
else
|
||||
startAccepting();
|
||||
}
|
||||
|
||||
public int getPort()
|
||||
@ -201,6 +203,24 @@ class ConnectionAcceptor implements Runnable
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
catch (RouterRestartException rre) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Waiting for router restart", rre);
|
||||
try {
|
||||
Thread.sleep(2*60*1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
while (true) {
|
||||
if (_util.connected())
|
||||
break;
|
||||
if (_util.connect())
|
||||
break;
|
||||
try {
|
||||
Thread.sleep(60*1000);
|
||||
} catch (InterruptedException ie) { break; }
|
||||
}
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Router restarted");
|
||||
}
|
||||
catch (I2PException ioe)
|
||||
{
|
||||
int level = stop ? Log.WARN : Log.ERROR;
|
||||
|
@ -23,6 +23,7 @@ import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketEepGet;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.client.streaming.I2PSocketManager.DisconnectListener;
|
||||
import net.i2p.client.streaming.I2PSocketManagerFactory;
|
||||
import net.i2p.client.streaming.I2PSocketOptions;
|
||||
import net.i2p.data.Base32;
|
||||
@ -47,7 +48,7 @@ import org.klomp.snark.dht.KRPC;
|
||||
* so we can run multiple instances of single Snarks
|
||||
* (but not multiple SnarkManagers, it is still static)
|
||||
*/
|
||||
public class I2PSnarkUtil {
|
||||
public class I2PSnarkUtil implements DisconnectListener {
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
private final String _baseName;
|
||||
@ -75,6 +76,7 @@ public class I2PSnarkUtil {
|
||||
private List<String> _openTrackers;
|
||||
private DHT _dht;
|
||||
private long _startedTime;
|
||||
private final DisconnectListener _discon;
|
||||
|
||||
private static final int EEPGET_CONNECT_TIMEOUT = 45*1000;
|
||||
private static final int EEPGET_CONNECT_TIMEOUT_SHORT = 5*1000;
|
||||
@ -91,17 +93,18 @@ public class I2PSnarkUtil {
|
||||
|
||||
|
||||
public I2PSnarkUtil(I2PAppContext ctx) {
|
||||
this(ctx, "i2psnark");
|
||||
this(ctx, "i2psnark", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param baseName generally "i2psnark"
|
||||
* @since Jetty 7
|
||||
*/
|
||||
public I2PSnarkUtil(I2PAppContext ctx, String baseName) {
|
||||
public I2PSnarkUtil(I2PAppContext ctx, String baseName, DisconnectListener discon) {
|
||||
_context = ctx;
|
||||
_log = _context.logManager().getLog(Snark.class);
|
||||
_log = _context.logManager().getLog(I2PSnarkUtil.class);
|
||||
_baseName = baseName;
|
||||
_discon = discon;
|
||||
_opts = new HashMap<String, String>();
|
||||
//setProxy("127.0.0.1", 4444);
|
||||
setI2CPConfig("127.0.0.1", I2PClient.DEFAULT_LISTEN_PORT, null);
|
||||
@ -324,8 +327,11 @@ public class I2PSnarkUtil {
|
||||
if (opts.getProperty(I2PClient.PROP_GZIP) == null)
|
||||
opts.setProperty(I2PClient.PROP_GZIP, "false");
|
||||
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
|
||||
if (_manager != null)
|
||||
if (_manager != null) {
|
||||
_startedTime = _context.clock().now();
|
||||
if (_discon != null)
|
||||
_manager.addDisconnectListener(this);
|
||||
}
|
||||
_connecting = false;
|
||||
}
|
||||
if (_shouldUseDHT && _manager != null && _dht == null)
|
||||
@ -333,6 +339,23 @@ public class I2PSnarkUtil {
|
||||
return (_manager != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* DisconnectListener interface
|
||||
* @since 0.9.53
|
||||
*/
|
||||
public void sessionDisconnected() {
|
||||
synchronized(this) {
|
||||
_manager = null;
|
||||
_connecting = false;
|
||||
if (_dht != null) {
|
||||
_dht.stop();
|
||||
_dht = null;
|
||||
}
|
||||
}
|
||||
if (_discon != null)
|
||||
_discon.sessionDisconnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null if disabled or not started
|
||||
* @since 0.8.4
|
||||
@ -537,6 +560,9 @@ public class I2PSnarkUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full Base64 of Destination
|
||||
*/
|
||||
public String getOurIPString() {
|
||||
Destination dest = getMyDestination();
|
||||
if (dest != null)
|
||||
|
@ -82,8 +82,9 @@ public class MetaInfo
|
||||
* @param created_by may be null
|
||||
* @param url_list may be null
|
||||
* @param comment may be null
|
||||
* @since public since 0.9.53, was package private
|
||||
*/
|
||||
MetaInfo(String announce, String name, String name_utf8, List<List<String>> files, List<Long> lengths,
|
||||
public MetaInfo(String announce, String name, String name_utf8, List<List<String>> files, List<Long> lengths,
|
||||
int piece_length, byte[] piece_hashes, long length, boolean privateTorrent,
|
||||
List<List<String>> announce_list, String created_by, List<String> url_list, String comment)
|
||||
{
|
||||
@ -442,9 +443,12 @@ public class MetaInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the piece hashes. Only used by storage so package local.
|
||||
* Returns the piece hashes.
|
||||
*
|
||||
* @return not a copy, do not modify
|
||||
* @since public since 0.9.53, was package private
|
||||
*/
|
||||
byte[] getPieceHashes()
|
||||
public byte[] getPieceHashes()
|
||||
{
|
||||
return piece_hashes;
|
||||
}
|
||||
@ -716,8 +720,9 @@ public class MetaInfo
|
||||
if (infoMap != null)
|
||||
return Collections.unmodifiableMap(infoMap);
|
||||
// we should only get here if serving a magnet on a torrent we created
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Creating new infomap", new Exception());
|
||||
// or on edit torrent save
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Creating new infomap", new Exception());
|
||||
// otherwise we must create it
|
||||
Map<String, BEValue> info = new HashMap<String, BEValue>();
|
||||
info.put("name", new BEValue(DataHelper.getUTF8(name)));
|
||||
|
@ -279,7 +279,6 @@ class PeerCoordinator implements PeerListener
|
||||
|
||||
/**
|
||||
* Bytes not yet in storage. Does NOT account for skipped files.
|
||||
* Not exact (does not adjust for last piece size).
|
||||
* Returns how many bytes are still needed to get the complete torrent.
|
||||
* @return -1 if in magnet mode
|
||||
*/
|
||||
@ -287,8 +286,13 @@ class PeerCoordinator implements PeerListener
|
||||
{
|
||||
if (metainfo == null | storage == null)
|
||||
return -1;
|
||||
// XXX - Only an approximation.
|
||||
return ((long) storage.needed()) * metainfo.getPieceLength(0);
|
||||
int psz = metainfo.getPieceLength(0);
|
||||
long rv = ((long) storage.needed()) * psz;
|
||||
int last = metainfo.getPieces() - 1;
|
||||
BitField bf = storage.getBitField();
|
||||
if (bf != null && !bf.get(last))
|
||||
rv -= psz - metainfo.getPieceLength(last);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -590,8 +590,12 @@ public class Snark
|
||||
|
||||
private void x_startTorrent() {
|
||||
boolean ok = _util.connect();
|
||||
if (!ok)
|
||||
fatalRouter("Unable to connect to I2P", null);
|
||||
if (!ok) {
|
||||
if (_util.getContext().isRouterContext())
|
||||
fatalRouter(_util.getString("Unable to connect to I2P"), null);
|
||||
else
|
||||
fatalRouter(_util.getString("Error connecting to I2P - check your I2CP settings!") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort(), null);
|
||||
}
|
||||
if (coordinator == null) {
|
||||
I2PServerSocket serversocket = _util.getServerSocket();
|
||||
if (serversocket == null)
|
||||
@ -1255,7 +1259,9 @@ public class Snark
|
||||
*/
|
||||
private void fatalRouter(String s, Throwable t) throws RouterException {
|
||||
_log.error(s, t);
|
||||
stopTorrent();
|
||||
if (!_util.getContext().isRouterContext())
|
||||
System.out.println(s);
|
||||
stopTorrent(true);
|
||||
if (completeListener != null)
|
||||
completeListener.fatal(this, s);
|
||||
throw new RouterException(s, t);
|
||||
@ -1321,6 +1327,15 @@ public class Snark
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call after editing torrent.
|
||||
* Caller must ensure infohash, files, etc. did not change.
|
||||
*
|
||||
* @since 0.9.53
|
||||
*/
|
||||
public void replaceMetaInfo(MetaInfo metainfo) {
|
||||
meta = metainfo;
|
||||
}
|
||||
|
||||
///////////// Begin StorageListener methods
|
||||
|
||||
|
@ -29,7 +29,9 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientApp;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.app.ClientAppState;
|
||||
import net.i2p.app.NotificationService;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.streaming.I2PSocketManager.DisconnectListener;
|
||||
import net.i2p.crypto.SHA1Hash;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.Base64;
|
||||
@ -57,7 +59,7 @@ import org.klomp.snark.dht.KRPC;
|
||||
/**
|
||||
* Manage multiple snarks
|
||||
*/
|
||||
public class SnarkManager implements CompleteListener, ClientApp {
|
||||
public class SnarkManager implements CompleteListener, ClientApp, DisconnectListener {
|
||||
|
||||
/**
|
||||
* Map of (canonical) filename of the .torrent file to Snark instance.
|
||||
@ -83,8 +85,8 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
private final Log _log;
|
||||
private final UIMessages _messages;
|
||||
private final I2PSnarkUtil _util;
|
||||
private PeerCoordinatorSet _peerCoordinatorSet;
|
||||
private ConnectionAcceptor _connectionAcceptor;
|
||||
private final PeerCoordinatorSet _peerCoordinatorSet;
|
||||
private final ConnectionAcceptor _connectionAcceptor;
|
||||
private Thread _monitor;
|
||||
private volatile boolean _running;
|
||||
private volatile boolean _stopping;
|
||||
@ -130,7 +132,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
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
|
||||
public static final String DEFAULT_AUTO_START = "false";
|
||||
private final boolean DEFAULT_AUTO_START;
|
||||
//public static final String PROP_LINK_PREFIX = "i2psnark.linkPrefix";
|
||||
//public static final String DEFAULT_LINK_PREFIX = "file:///";
|
||||
public static final String PROP_STARTUP_DELAY = "i2psnark.startupDelay";
|
||||
@ -140,7 +142,8 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
public static final String RC_PROP_UNIVERSAL_THEMING = "routerconsole.universal.theme";
|
||||
public static final String PROP_THEME = "i2psnark.theme";
|
||||
public static final String DEFAULT_THEME = "ubergine";
|
||||
private static final String[] THEMES = new String[] { "dark", "light", "ubergine", "vanilla" };
|
||||
// Translators: Translate "ubergine" as "aubergine" or "eggplant" or "purple"
|
||||
private static final String[] THEMES = new String[] { _x("ubergine"), _x("dark"), _x("light"), _x("vanilla") };
|
||||
/** From CSSHelper */
|
||||
private static final String PROP_DISABLE_OLD = "routerconsole.disableOldThemes";
|
||||
private static final boolean DEFAULT_DISABLE_OLD = true;
|
||||
@ -267,7 +270,10 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
_contextName = ctxName;
|
||||
_log = _context.logManager().getLog(SnarkManager.class);
|
||||
_messages = new UIMessages(MAX_MESSAGES);
|
||||
_util = new I2PSnarkUtil(_context, ctxName);
|
||||
_util = new I2PSnarkUtil(_context, ctxName, this);
|
||||
_peerCoordinatorSet = new PeerCoordinatorSet();
|
||||
_connectionAcceptor = new ConnectionAcceptor(_util, _peerCoordinatorSet);
|
||||
DEFAULT_AUTO_START = !ctx.isRouterContext();
|
||||
String cfile = ctxName + CONFIG_FILE_SUFFIX;
|
||||
File configFile = new File(cfile);
|
||||
if (!configFile.isAbsolute())
|
||||
@ -292,8 +298,6 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
if (cmgr != null)
|
||||
cmgr.register(this);
|
||||
}
|
||||
_peerCoordinatorSet = new PeerCoordinatorSet();
|
||||
_connectionAcceptor = new ConnectionAcceptor(_util, _peerCoordinatorSet);
|
||||
_monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor", true);
|
||||
_monitor.start();
|
||||
// only if default instance
|
||||
@ -342,6 +346,18 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DisconnectListener interface
|
||||
* @since 0.9.53
|
||||
*/
|
||||
public void sessionDisconnected() {
|
||||
if (!_context.isRouterContext()) {
|
||||
addMessage(_t("Unable to connect to I2P"));
|
||||
stopAllTorrents(true);
|
||||
_stopping = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the webapp at Jetty shutdown.
|
||||
* Stops all torrents. Does not close the tunnel, so the announces have a chance.
|
||||
@ -463,7 +479,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
}
|
||||
|
||||
public boolean shouldAutoStart() {
|
||||
return Boolean.parseBoolean(_config.getProperty(PROP_AUTO_START, DEFAULT_AUTO_START));
|
||||
return Boolean.parseBoolean(_config.getProperty(PROP_AUTO_START, Boolean.toString(DEFAULT_AUTO_START)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -814,7 +830,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
if (!_config.containsKey(PROP_DIR))
|
||||
_config.setProperty(PROP_DIR, _contextName);
|
||||
if (!_config.containsKey(PROP_AUTO_START))
|
||||
_config.setProperty(PROP_AUTO_START, DEFAULT_AUTO_START);
|
||||
_config.setProperty(PROP_AUTO_START, Boolean.toString(DEFAULT_AUTO_START));
|
||||
if (!_config.containsKey(PROP_REFRESH_DELAY))
|
||||
_config.setProperty(PROP_REFRESH_DELAY, Integer.toString(DEFAULT_REFRESH_DELAY_SECS));
|
||||
if (!_config.containsKey(PROP_STARTUP_DELAY))
|
||||
@ -847,7 +863,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
|
||||
/**
|
||||
* Get current theme.
|
||||
* @return String -- the current theme
|
||||
* @return String -- the current theme, untranslated
|
||||
*/
|
||||
public String getTheme() {
|
||||
String theme;
|
||||
@ -912,20 +928,29 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
|
||||
/**
|
||||
* Get all themes
|
||||
* @return String[] -- Array of all the themes found, non-null, unsorted
|
||||
* @return Array of all the themes found, non-null, unsorted, untranslated. Not a copy, do not modify.
|
||||
*/
|
||||
public static String[] getThemes() {
|
||||
return THEMES;
|
||||
}
|
||||
|
||||
|
||||
/** call from DirMonitor since loadConfig() is called before router I2CP is up */
|
||||
private void getBWLimit() {
|
||||
if (!_config.containsKey(PROP_UPBW_MAX)) {
|
||||
/**
|
||||
* Call from DirMonitor since loadConfig() is called before router I2CP is up.
|
||||
* We also use this as a test that the router is there for standalone.
|
||||
*
|
||||
* @return true if we got a response from the router
|
||||
*/
|
||||
private boolean getBWLimit() {
|
||||
boolean shouldSet = !_config.containsKey(PROP_UPBW_MAX);
|
||||
if (shouldSet || !_context.isRouterContext()) {
|
||||
int[] limits = BWLimits.getBWLimits(_util.getI2CPHost(), _util.getI2CPPort());
|
||||
if (limits != null && limits[1] > 0)
|
||||
if (limits == null)
|
||||
return false;
|
||||
if (shouldSet && limits[1] > 0)
|
||||
_util.setMaxUpBW(limits[1]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateConfig() {
|
||||
@ -1576,7 +1601,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
}
|
||||
if (dataDir == null)
|
||||
dataDir = getDataDir();
|
||||
Snark torrent = null;
|
||||
Snark torrent;
|
||||
synchronized (_snarks) {
|
||||
torrent = _snarks.get(filename);
|
||||
}
|
||||
@ -1693,18 +1718,31 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
String link = linkify(torrent);
|
||||
if (!dontAutoStart && shouldAutoStart() && running) {
|
||||
if (!_util.connected()) {
|
||||
addMessage(_t("Connecting to I2P"));
|
||||
String msg = _t("Connecting to I2P");
|
||||
addMessage(msg);
|
||||
if (!_context.isRouterContext())
|
||||
System.out.println(msg);
|
||||
boolean ok = _util.connect();
|
||||
if (!ok) {
|
||||
addMessage(_t("Error connecting to I2P - check your I2CP settings!"));
|
||||
if (_context.isRouterContext()) {
|
||||
addMessage(_t("Unable to connect to I2P"));
|
||||
} else {
|
||||
msg = _t("Error connecting to I2P - check your I2CP settings!") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort();
|
||||
addMessage(msg);
|
||||
System.out.println(msg);
|
||||
}
|
||||
// this would rename the torrent to .BAD
|
||||
//return false;
|
||||
}
|
||||
}
|
||||
torrent.startTorrent();
|
||||
addMessageNoEscape(_t("Torrent added and started: {0}", link));
|
||||
if (!_context.isRouterContext())
|
||||
System.out.println(_t("Torrent added and started: {0}", torrent.getBaseName()));
|
||||
} else {
|
||||
addMessageNoEscape(_t("Torrent added: {0}", link));
|
||||
if (!_context.isRouterContext())
|
||||
System.out.println(_t("Torrent added: {0}", torrent.getBaseName()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -2492,6 +2530,13 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
addMessage(_t("Torrent removed: \"{0}\"", torrent.getBaseName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This calls monitorTorrents() once a minute.
|
||||
* It also gets the bandwidth limits and loads magnets on first run.
|
||||
* For standalone, it also handles checking that the external router is there,
|
||||
* and restarting torrents once the router appears.
|
||||
*
|
||||
*/
|
||||
private class DirMonitor implements Runnable {
|
||||
public void run() {
|
||||
// don't bother delaying if auto start is false
|
||||
@ -2506,17 +2551,80 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
|
||||
// here because we need to delay until I2CP is up
|
||||
// although the user will see the default until then
|
||||
getBWLimit();
|
||||
boolean routerOK = false;
|
||||
boolean doMagnets = true;
|
||||
while (_running) {
|
||||
File dir = getDataDir();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Directory Monitor loop over " + dir.getAbsolutePath());
|
||||
if (routerOK &&
|
||||
(_context.isRouterContext() || _util.connected() || _util.isConnecting())) {
|
||||
autostart = shouldAutoStart();
|
||||
} else {
|
||||
// Test if the router is there
|
||||
// For standalone, this will probe the router every 60 seconds if not connected
|
||||
boolean oldOK = routerOK;
|
||||
// standalone, first time only
|
||||
if (doMagnets && !_context.isRouterContext())
|
||||
dtgNotify(Log.INFO, _t("Connecting to I2P") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort());
|
||||
routerOK = getBWLimit();
|
||||
if (routerOK) {
|
||||
autostart = shouldAutoStart();
|
||||
if (autostart && !oldOK && !doMagnets && !_snarks.isEmpty()) {
|
||||
// Start previously added torrents
|
||||
for (Snark snark : _snarks.values()) {
|
||||
Properties config = getConfig(snark);
|
||||
String prop = config.getProperty(PROP_META_RUNNING);
|
||||
if (prop == null || Boolean.parseBoolean(prop)) {
|
||||
if (!_util.connected()) {
|
||||
String msg = _t("Connecting to I2P");
|
||||
addMessage(msg);
|
||||
if (!_context.isRouterContext())
|
||||
dtgNotify(Log.INFO, msg + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort());
|
||||
// getBWLimit() was successful so this should work
|
||||
boolean ok = _util.connect();
|
||||
if (!ok) {
|
||||
if (_context.isRouterContext()) {
|
||||
addMessage(_t("Unable to connect to I2P"));
|
||||
} else {
|
||||
msg = _t("Error connecting to I2P - check your I2CP settings!") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort();
|
||||
addMessage(msg);
|
||||
dtgNotify(Log.ERROR, msg);
|
||||
}
|
||||
routerOK = false;
|
||||
autostart = false;
|
||||
break;
|
||||
} else {
|
||||
if (!_context.isRouterContext()) {
|
||||
msg = "Connected to I2P at " + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort();
|
||||
addMessage(msg);
|
||||
dtgNotify(Log.INFO, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
addMessageNoEscape(_t("Starting up torrent {0}", linkify(snark)));
|
||||
try {
|
||||
snark.startTorrent();
|
||||
} catch (Snark.RouterException re) {
|
||||
// Snark.fatal() will log and call fatal() here for user message before throwing
|
||||
break;
|
||||
} catch (RuntimeException re) {
|
||||
// Snark.fatal() will log and call fatal() here for user message before throwing
|
||||
}
|
||||
}
|
||||
}
|
||||
if (routerOK)
|
||||
addMessage(_t("Up bandwidth limit is {0} KBps", _util.getMaxUpBW()));
|
||||
}
|
||||
} else {
|
||||
autostart = false;
|
||||
}
|
||||
}
|
||||
boolean ok;
|
||||
try {
|
||||
// Don't let this interfere with .torrent files being added or deleted
|
||||
synchronized (_snarks) {
|
||||
ok = monitorTorrents(dir);
|
||||
ok = monitorTorrents(dir, autostart);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
_log.error("Error in the DirectoryMonitor", e);
|
||||
@ -2530,7 +2638,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
} catch (RuntimeException e) {
|
||||
_log.error("Error in the DirectoryMonitor", e);
|
||||
}
|
||||
if (!_snarks.isEmpty())
|
||||
if (routerOK && !_snarks.isEmpty())
|
||||
addMessage(_t("Up bandwidth limit is {0} KBps", _util.getMaxUpBW()));
|
||||
// To fix bug where files were left behind,
|
||||
// but also good for when user removes snarks when i2p is not running
|
||||
@ -2539,6 +2647,15 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
// time i2psnark starts. See ticket #1658.
|
||||
if (ok)
|
||||
cleanupTorrentStatus();
|
||||
if (!routerOK) {
|
||||
if (_context.isRouterContext()) {
|
||||
addMessage(_t("Unable to connect to I2P"));
|
||||
} else {
|
||||
String msg = _t("Error connecting to I2P - check your I2CP settings!") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort();
|
||||
addMessage(msg);
|
||||
dtgNotify(Log.ERROR, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
|
||||
}
|
||||
@ -2555,8 +2672,12 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
Storage storage = snark.getStorage();
|
||||
if (meta == null || storage == null)
|
||||
return;
|
||||
if (snark.getDownloaded() > 0)
|
||||
if (snark.getDownloaded() > 0) {
|
||||
addMessageNoEscape(_t("Download finished: {0}", linkify(snark)));
|
||||
dtgNotify(Log.INFO,
|
||||
_t("Download finished: {0}", snark.getName()),
|
||||
"/i2psnark/" + linkify(snark));
|
||||
}
|
||||
updateStatus(snark);
|
||||
}
|
||||
|
||||
@ -2646,6 +2767,38 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
|
||||
// End Snark.CompleteListeners
|
||||
|
||||
/**
|
||||
* Send a notification to the user via desktopgui and,
|
||||
* if standalone, on the console.
|
||||
*
|
||||
* @param priority log level
|
||||
* @param message translated
|
||||
* @since 0.9.54
|
||||
*/
|
||||
private void dtgNotify(int priority, String message) {
|
||||
dtgNotify(priority, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a notification to the user via desktopgui and,
|
||||
* if standalone, on the console.
|
||||
*
|
||||
* @param priority log level
|
||||
* @param message translated
|
||||
* @param path in console for more information, starting with /, must be URL-escaped, or null
|
||||
* @since 0.9.54
|
||||
*/
|
||||
private void dtgNotify(int priority, String message, String path) {
|
||||
ClientAppManager cmgr = _context.clientAppManager();
|
||||
if (cmgr != null) {
|
||||
NotificationService ns = (NotificationService) cmgr.getRegisteredApp("desktopgui");
|
||||
if (ns != null)
|
||||
ns.notify("I2PSnark", null, priority, _t("I2PSnark"), message, path);
|
||||
}
|
||||
if (!_context.isRouterContext())
|
||||
System.out.println(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* An HTML link to the file if complete and a single file,
|
||||
* to the directory if not complete or not a single file,
|
||||
@ -2704,9 +2857,10 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
/**
|
||||
* caller must synchronize on _snarks
|
||||
*
|
||||
* @param shouldStart should we autostart the torrents
|
||||
* @return success, false if an error adding any torrent.
|
||||
*/
|
||||
private boolean monitorTorrents(File dir) {
|
||||
private boolean monitorTorrents(File dir, boolean shouldStart) {
|
||||
boolean rv = true;
|
||||
File files[] = dir.listFiles(new FileSuffixFilter(".torrent"));
|
||||
List<String> foundNames = new ArrayList<String>(0);
|
||||
@ -2726,7 +2880,6 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("DirMon found: " + DataHelper.toString(foundNames) + " existing: " + DataHelper.toString(existingNames));
|
||||
// lets find new ones first...
|
||||
boolean shouldStart = shouldAutoStart();
|
||||
for (String name : foundNames) {
|
||||
if (existingNames.contains(name)) {
|
||||
// already known. noop
|
||||
@ -2791,6 +2944,14 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
return _util.getString(s, o, o2);
|
||||
}
|
||||
|
||||
/**
|
||||
* mark for translation, does not translate
|
||||
* @since 0.9.53
|
||||
*/
|
||||
private static String _x(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsorted map of name to Tracker object
|
||||
* Modifiable, not a copy
|
||||
|
753
apps/i2psnark/java/src/org/klomp/snark/UDPTrackerClient.java
Normal file
753
apps/i2psnark/java/src/org/klomp/snark/UDPTrackerClient.java
Normal file
@ -0,0 +1,753 @@
|
||||
package org.klomp.snark;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.client.I2PSessionMuxedListener;
|
||||
import net.i2p.client.SendMessageOptions;
|
||||
import net.i2p.client.datagram.I2PDatagramDissector;
|
||||
import net.i2p.client.datagram.I2PDatagramMaker;
|
||||
import net.i2p.client.datagram.I2PInvalidDatagramException;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
* One of these for all trackers and info hashes.
|
||||
* Ref: BEP 15, proposal 160
|
||||
*
|
||||
* The main difference from BEP 15 is that the announce response
|
||||
* contains a 32-byte hash instead of a 4-byte IP and a 2-byte port.
|
||||
*
|
||||
* This implements only "fast mode".
|
||||
* We send only repliable datagrams, and
|
||||
* receive only raw datagrams, as follows:
|
||||
*
|
||||
*<pre>
|
||||
* client tracker type
|
||||
* ------ ------- ----
|
||||
* announce --> repliable
|
||||
* <-- ann resp raw
|
||||
*</pre>
|
||||
*
|
||||
* @since 0.9.53, enabled in 0.9.54
|
||||
*/
|
||||
class UDPTrackerClient implements I2PSessionMuxedListener {
|
||||
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
/** hook to inject and receive datagrams */
|
||||
private final I2PSession _session;
|
||||
private final I2PSnarkUtil _util;
|
||||
private final Hash _myHash;
|
||||
/** unsigned dgrams */
|
||||
private final int _rPort;
|
||||
/** dest and port to tracker data */
|
||||
private final ConcurrentHashMap<HostPort, Tracker> _trackers;
|
||||
/** our TID to tracker */
|
||||
private final Map<Integer, ReplyWaiter> _sentQueries;
|
||||
private boolean _isRunning;
|
||||
|
||||
public static final int EVENT_NONE = 0;
|
||||
public static final int EVENT_COMPLETED = 1;
|
||||
public static final int EVENT_STARTED = 2;
|
||||
public static final int EVENT_STOPPED = 3;
|
||||
|
||||
private static final int ACTION_CONNECT = 0;
|
||||
private static final int ACTION_ANNOUNCE = 1;
|
||||
private static final int ACTION_SCRAPE = 2;
|
||||
private static final int ACTION_ERROR = 3;
|
||||
|
||||
private static final int SEND_CRYPTO_TAGS = 8;
|
||||
private static final int LOW_CRYPTO_TAGS = 4;
|
||||
|
||||
private static final long DEFAULT_TIMEOUT = 15*1000;
|
||||
private static final long DEFAULT_QUERY_TIMEOUT = 60*1000;
|
||||
private static final long CLEAN_TIME = 163*1000;
|
||||
|
||||
/** in seconds */
|
||||
private static final int DEFAULT_INTERVAL = 60*60;
|
||||
private static final int MIN_INTERVAL = 15*60;
|
||||
private static final int MAX_INTERVAL = 8*60*60;
|
||||
|
||||
private enum WaitState { INIT, SUCCESS, TIMEOUT, FAIL }
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public UDPTrackerClient(I2PAppContext ctx, I2PSession session, I2PSnarkUtil util) {
|
||||
_context = ctx;
|
||||
_session = session;
|
||||
_util = util;
|
||||
_log = ctx.logManager().getLog(UDPTrackerClient.class);
|
||||
_rPort = TrackerClient.PORT - 1;
|
||||
_myHash = session.getMyDestination().calculateHash();
|
||||
_trackers = new ConcurrentHashMap<HostPort, Tracker>(8);
|
||||
_sentQueries = new ConcurrentHashMap<Integer, ReplyWaiter>(32);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Can't be restarted after stopping?
|
||||
*/
|
||||
public synchronized void start() {
|
||||
if (_isRunning)
|
||||
return;
|
||||
_session.addMuxedSessionListener(this, I2PSession.PROTO_DATAGRAM_RAW, _rPort);
|
||||
_isRunning = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop everything.
|
||||
*/
|
||||
public synchronized void stop() {
|
||||
if (!_isRunning)
|
||||
return;
|
||||
_isRunning = false;
|
||||
_session.removeListener(I2PSession.PROTO_DATAGRAM_RAW, _rPort);
|
||||
_trackers.clear();
|
||||
for (ReplyWaiter w : _sentQueries.values()) {
|
||||
w.cancel();
|
||||
}
|
||||
_sentQueries.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Announce and get peers for a torrent.
|
||||
* Blocking!
|
||||
* Caller should run in a thread.
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param max maximum number of peers to return
|
||||
* @param maxWait the maximum time to wait (ms) must be > 0
|
||||
* @param fast if true, don't wait for dest, no retx, ...
|
||||
* @return null on fail or if fast is true
|
||||
*/
|
||||
public TrackerResponse announce(byte[] ih, byte[] peerID, int max, long maxWait,
|
||||
String toHost, int toPort,
|
||||
long downloaded, long left, long uploaded,
|
||||
int event, boolean fast) {
|
||||
long now = _context.clock().now();
|
||||
long end = now + maxWait;
|
||||
if (toPort < 0)
|
||||
throw new IllegalArgumentException();
|
||||
Tracker tr = getTracker(toHost, toPort);
|
||||
if (tr.getDest(fast) == null) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("cannot resolve " + tr);
|
||||
return null;
|
||||
}
|
||||
long toWait = end - now;
|
||||
if (!fast)
|
||||
toWait = toWait * 3 / 4;
|
||||
if (toWait < 1000) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("out of time after resolving: " + tr);
|
||||
return null;
|
||||
}
|
||||
if (fast) {
|
||||
toWait = 0;
|
||||
} else {
|
||||
toWait = end - now;
|
||||
if (toWait < 1000) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("out of time after getting conn: " + tr);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
ReplyWaiter w = sendAnnounce(tr, 0, ih, peerID,
|
||||
downloaded, left, uploaded, event, max, toWait);
|
||||
if (fast)
|
||||
return null;
|
||||
if (w == null) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("initial announce failed: " + tr);
|
||||
return null;
|
||||
}
|
||||
boolean success = waitAndRetransmit(w, end);
|
||||
_sentQueries.remove(w.getID());
|
||||
if (success)
|
||||
return w.getReplyObject();
|
||||
if (_log.shouldInfo())
|
||||
_log.info("announce failed after retx: " + tr);
|
||||
return null;
|
||||
}
|
||||
|
||||
//////// private below here
|
||||
|
||||
/**
|
||||
* @return non-null
|
||||
*/
|
||||
private Tracker getTracker(String host, int port) {
|
||||
Tracker ndp = new Tracker(host, port);
|
||||
Tracker odp = _trackers.putIfAbsent(ndp, ndp);
|
||||
if (odp != null)
|
||||
ndp = odp;
|
||||
return ndp;
|
||||
}
|
||||
|
||||
///// Sending.....
|
||||
|
||||
/**
|
||||
* Send one time with a new tid
|
||||
* @param toWait if <= 0 does not register
|
||||
* @return null on failure or if toWait <= 0
|
||||
*/
|
||||
private ReplyWaiter sendAnnounce(Tracker tr, long connID,
|
||||
byte[] ih, byte[] id,
|
||||
long downloaded, long left, long uploaded,
|
||||
int event, int numWant, long toWait) {
|
||||
int tid = _context.random().nextInt();
|
||||
byte[] payload = sendAnnounce(tr, tid, connID, ih, id, downloaded, left, uploaded, event, numWant);
|
||||
if (payload != null) {
|
||||
if (toWait > 0) {
|
||||
ReplyWaiter rv = new ReplyWaiter(tid, tr, payload, toWait);
|
||||
_sentQueries.put(Integer.valueOf(tid), rv);
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Sent: " + rv + " timeout: " + toWait);
|
||||
return rv;
|
||||
}
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Sent annc " + event + " to " + tr + " no wait");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send one time with given tid
|
||||
* @return the payload or null on failure
|
||||
*/
|
||||
private byte[] sendAnnounce(Tracker tr, int tid, long connID,
|
||||
byte[] ih, byte[] id,
|
||||
long downloaded, long left, long uploaded,
|
||||
int event, int numWant) {
|
||||
byte[] payload = new byte[98];
|
||||
DataHelper.toLong8(payload, 0, connID);
|
||||
DataHelper.toLong(payload, 8, 4, ACTION_ANNOUNCE);
|
||||
DataHelper.toLong(payload, 12, 4, tid);
|
||||
System.arraycopy(ih, 0, payload, 16, 20);
|
||||
System.arraycopy(id, 0, payload, 36, 20);
|
||||
DataHelper.toLong(payload, 56, 8, downloaded);
|
||||
DataHelper.toLong(payload, 64, 8, left);
|
||||
DataHelper.toLong(payload, 72, 8, uploaded);
|
||||
DataHelper.toLong(payload, 80, 4, event);
|
||||
DataHelper.toLong(payload, 92, 4, numWant);
|
||||
DataHelper.toLong(payload, 96, 2, TrackerClient.PORT);
|
||||
boolean rv = sendMessage(tr.getDest(true), tr.getPort(), payload, true);
|
||||
return rv ? payload : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* wait after initial send, resend if necessary
|
||||
*/
|
||||
private boolean waitAndRetransmit(ReplyWaiter w, long untilTime) {
|
||||
synchronized(w) {
|
||||
while(true) {
|
||||
try {
|
||||
long toWait = untilTime - _context.clock().now();
|
||||
if (toWait <= 0)
|
||||
return false;
|
||||
w.wait(toWait);
|
||||
} catch (InterruptedException ie) {
|
||||
return false;
|
||||
}
|
||||
switch (w.getState()) {
|
||||
case INIT:
|
||||
continue;
|
||||
|
||||
case SUCCESS:
|
||||
return true;
|
||||
|
||||
case FAIL:
|
||||
return false;
|
||||
|
||||
case TIMEOUT:
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Timeout: " + w);
|
||||
long toWait = untilTime - _context.clock().now();
|
||||
if (toWait <= 1000)
|
||||
return false;
|
||||
boolean ok = resend(w, Math.min(toWait, w.getSentTo().getTimeout()));
|
||||
if (!ok)
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resend the stored payload
|
||||
* @return success
|
||||
*/
|
||||
private boolean resend(ReplyWaiter w, long toWait) {
|
||||
Tracker tr = w.getSentTo();
|
||||
int port = tr.getPort();
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Resending: " + w + " timeout: " + toWait);
|
||||
boolean rv = sendMessage(tr.getDest(true), port, w.getPayload(), true);
|
||||
if (rv) {
|
||||
_sentQueries.put(Integer.valueOf(w.getID()), w);
|
||||
w.schedule(toWait);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lowest-level send message call.
|
||||
* @param dest may be null, returns false
|
||||
* @param repliable true for conn request, false for announce
|
||||
* @return success
|
||||
*/
|
||||
private boolean sendMessage(Destination dest, int toPort, byte[] payload, boolean repliable) {
|
||||
if (!_isRunning) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("send failed, not running");
|
||||
return false;
|
||||
}
|
||||
if (dest == null) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("send failed, no dest");
|
||||
return false;
|
||||
}
|
||||
if (dest.calculateHash().equals(_myHash))
|
||||
throw new IllegalArgumentException("don't send to ourselves");
|
||||
|
||||
if (repliable) {
|
||||
I2PDatagramMaker dgMaker = new I2PDatagramMaker(_session);
|
||||
payload = dgMaker.makeI2PDatagram(payload);
|
||||
if (payload == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("DGM fail");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SendMessageOptions opts = new SendMessageOptions();
|
||||
opts.setDate(_context.clock().now() + 60*1000);
|
||||
opts.setTagsToSend(SEND_CRYPTO_TAGS);
|
||||
opts.setTagThreshold(LOW_CRYPTO_TAGS);
|
||||
if (!repliable)
|
||||
opts.setSendLeaseSet(false);
|
||||
try {
|
||||
boolean success = _session.sendMessage(dest, payload, 0, payload.length,
|
||||
repliable ? I2PSession.PROTO_DATAGRAM : I2PSession.PROTO_DATAGRAM_RAW,
|
||||
_rPort, toPort, opts);
|
||||
if (success) {
|
||||
// ...
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("sendMessage fail");
|
||||
}
|
||||
return success;
|
||||
} catch (I2PSessionException ise) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("sendMessage fail", ise);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
///// Reception.....
|
||||
|
||||
/**
|
||||
* @param from dest or null if it didn't come in on signed port
|
||||
*/
|
||||
private void receiveMessage(Destination from, int fromPort, byte[] payload) {
|
||||
if (payload.length < 8) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Got short message: " + payload.length + " bytes");
|
||||
return;
|
||||
}
|
||||
|
||||
int action = (int) DataHelper.fromLong(payload, 0, 4);
|
||||
int tid = (int) DataHelper.fromLong(payload, 4, 4);
|
||||
ReplyWaiter waiter = _sentQueries.remove(Integer.valueOf(tid));
|
||||
if (waiter == null) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Rcvd msg with no one waiting: " + tid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == ACTION_ANNOUNCE) {
|
||||
receiveAnnounce(waiter, payload);
|
||||
} else if (action == ACTION_ERROR) {
|
||||
receiveError(waiter, payload);
|
||||
} else {
|
||||
// includes ACTION_CONNECT
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Rcvd msg with unknown action: " + action + " for: " + waiter);
|
||||
waiter.gotReply(false);
|
||||
Tracker tr = waiter.getSentTo();
|
||||
tr.gotError();
|
||||
}
|
||||
}
|
||||
|
||||
private void receiveAnnounce(ReplyWaiter waiter, byte[] payload) {
|
||||
Tracker tr = waiter.getSentTo();
|
||||
if (payload.length >= 22) {
|
||||
int interval = Math.min(MAX_INTERVAL, Math.max(MIN_INTERVAL,
|
||||
(int) DataHelper.fromLong(payload, 8, 4)));
|
||||
int leeches = (int) DataHelper.fromLong(payload, 12, 4);
|
||||
int seeds = (int) DataHelper.fromLong(payload, 16, 4);
|
||||
int peers = (int) DataHelper.fromLong(payload, 20, 2);
|
||||
if (22 + (peers * Hash.HASH_LENGTH) > payload.length) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Short reply");
|
||||
waiter.gotReply(false);
|
||||
tr.gotError();
|
||||
return;
|
||||
}
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Rcvd " + peers + " peers from " + tr);
|
||||
Set<Hash> hashes;
|
||||
if (peers > 0) {
|
||||
hashes = new HashSet<Hash>(peers);
|
||||
for (int off = 20; off < payload.length; off += Hash.HASH_LENGTH) {
|
||||
hashes.add(Hash.create(payload, off));
|
||||
}
|
||||
} else {
|
||||
hashes = Collections.emptySet();
|
||||
}
|
||||
TrackerResponse resp = new TrackerResponse(interval, seeds, leeches, hashes);
|
||||
waiter.gotResponse(resp);
|
||||
tr.setInterval(interval);
|
||||
} else {
|
||||
waiter.gotReply(false);
|
||||
tr.gotError();
|
||||
}
|
||||
}
|
||||
|
||||
private void receiveError(ReplyWaiter waiter, byte[] payload) {
|
||||
String msg;
|
||||
if (payload.length > 8) {
|
||||
msg = DataHelper.getUTF8(payload, 8, payload.length - 8);
|
||||
} else {
|
||||
msg = "";
|
||||
}
|
||||
TrackerResponse resp = new TrackerResponse(msg);
|
||||
waiter.gotResponse(resp);
|
||||
Tracker tr = waiter.getSentTo();
|
||||
tr.gotError();
|
||||
}
|
||||
|
||||
// I2PSessionMuxedListener interface ----------------
|
||||
|
||||
/**
|
||||
* Instruct the client that the given session has received a message
|
||||
*
|
||||
* Will be called only if you register via addMuxedSessionListener().
|
||||
* Will be called only for the proto(s) and toPort(s) you register for.
|
||||
*
|
||||
* @param session session to notify
|
||||
* @param msgId message number available
|
||||
* @param size size of the message - why it's a long and not an int is a mystery
|
||||
* @param proto 1-254 or 0 for unspecified
|
||||
* @param fromPort 1-65535 or 0 for unspecified
|
||||
* @param toPort 1-65535 or 0 for unspecified
|
||||
*/
|
||||
public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromPort, int toPort) {
|
||||
// TODO throttle
|
||||
try {
|
||||
byte[] payload = session.receiveMessage(msgId);
|
||||
if (payload == null)
|
||||
return;
|
||||
if (toPort == _rPort) {
|
||||
// raw
|
||||
receiveMessage(null, fromPort, payload);
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("msg on bad port");
|
||||
}
|
||||
} catch (I2PSessionException e) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("bad msg");
|
||||
}
|
||||
}
|
||||
|
||||
/** for non-muxed */
|
||||
public void messageAvailable(I2PSession session, int msgId, long size) {}
|
||||
|
||||
public void reportAbuse(I2PSession session, int severity) {}
|
||||
|
||||
public void disconnected(I2PSession session) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("UDPTC disconnected");
|
||||
}
|
||||
|
||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("UDPTC got error msg: ", error);
|
||||
}
|
||||
|
||||
public static class TrackerResponse {
|
||||
|
||||
private final int interval, complete, incomplete;
|
||||
private final String error;
|
||||
private final Set<Hash> peers;
|
||||
|
||||
/** success */
|
||||
public TrackerResponse(int interval, int seeds, int leeches, Set<Hash> peers) {
|
||||
this.interval = interval;
|
||||
complete = seeds;
|
||||
incomplete = leeches;
|
||||
this.peers = peers;
|
||||
error = null;
|
||||
}
|
||||
|
||||
/** failure */
|
||||
public TrackerResponse(String errorMsg) {
|
||||
interval = DEFAULT_INTERVAL;
|
||||
complete = 0;
|
||||
incomplete = 0;
|
||||
peers = null;
|
||||
error = errorMsg;
|
||||
}
|
||||
|
||||
public Set<Hash> getPeers() {
|
||||
return peers;
|
||||
}
|
||||
|
||||
public int getPeerCount() {
|
||||
int pc = peers == null ? 0 : peers.size();
|
||||
return Math.max(pc, complete + incomplete - 1);
|
||||
}
|
||||
|
||||
public int getSeedCount() {
|
||||
return complete;
|
||||
}
|
||||
|
||||
public int getLeechCount() {
|
||||
return incomplete;
|
||||
}
|
||||
|
||||
public String getFailureReason() {
|
||||
return error;
|
||||
}
|
||||
|
||||
/** in seconds */
|
||||
public int getInterval() {
|
||||
return interval;
|
||||
}
|
||||
}
|
||||
|
||||
private static class HostPort {
|
||||
|
||||
protected final String host;
|
||||
protected final int port;
|
||||
|
||||
/**
|
||||
* @param port the announce port
|
||||
*/
|
||||
public HostPort(String host, int port) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the announce port
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return host.hashCode() ^ port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !(o instanceof HostPort))
|
||||
return false;
|
||||
HostPort dp = (HostPort) o;
|
||||
return port == dp.port && host.equals(dp.host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UDP Tracker " + host + ':' + port;
|
||||
}
|
||||
}
|
||||
|
||||
private class Tracker extends HostPort {
|
||||
|
||||
private final Object destLock = new Object();
|
||||
private Destination dest;
|
||||
private long expires;
|
||||
private long lastHeardFrom;
|
||||
private long lastFailed;
|
||||
private int consecFails;
|
||||
private int interval = DEFAULT_INTERVAL;
|
||||
|
||||
private static final long DELAY = 15*1000;
|
||||
|
||||
public Tracker(String host, int port) {
|
||||
super(host, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fast if true, do not lookup
|
||||
* @return dest or null
|
||||
*/
|
||||
public Destination getDest(boolean fast) {
|
||||
synchronized(destLock) {
|
||||
if (dest == null && !fast)
|
||||
dest = _util.getDestination(host);
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
/** does not change state */
|
||||
public synchronized void replyTimeout() {
|
||||
consecFails++;
|
||||
lastFailed = _context.clock().now();
|
||||
}
|
||||
|
||||
public synchronized int getInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
/** sets heardFrom; calls notify */
|
||||
public synchronized void setInterval(int interval) {
|
||||
long now = _context.clock().now();
|
||||
lastHeardFrom = now;
|
||||
consecFails = 0;
|
||||
this.interval = interval;
|
||||
this.notifyAll();
|
||||
}
|
||||
|
||||
/** sets heardFrom; calls notify */
|
||||
public synchronized void gotError() {
|
||||
long now = _context.clock().now();
|
||||
lastHeardFrom = now;
|
||||
consecFails = 0;
|
||||
this.notifyAll();
|
||||
}
|
||||
|
||||
/** doubled for each consecutive failure */
|
||||
public synchronized long getTimeout() {
|
||||
return DEFAULT_TIMEOUT << Math.min(consecFails, 3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UDP Tracker " + host + ':' + port + " hasDest? " + (dest != null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for replies
|
||||
*/
|
||||
private class ReplyWaiter extends SimpleTimer2.TimedEvent {
|
||||
private final int tid;
|
||||
private final Tracker sentTo;
|
||||
private final byte[] data;
|
||||
private TrackerResponse replyObject;
|
||||
private WaitState state = WaitState.INIT;
|
||||
|
||||
/**
|
||||
* Either wait on this object with a timeout, or use non-null Runnables.
|
||||
* Any sent data to be remembered may be stored by setSentObject().
|
||||
* Reply object may be in getReplyObject().
|
||||
*/
|
||||
public ReplyWaiter(int tid, Tracker tracker, byte[] payload, long toWait) {
|
||||
super(SimpleTimer2.getInstance(), toWait);
|
||||
this.tid = tid;
|
||||
sentTo = tracker;
|
||||
data = payload;
|
||||
}
|
||||
|
||||
public int getID() {
|
||||
return tid;
|
||||
}
|
||||
|
||||
public Tracker getSentTo() {
|
||||
return sentTo;
|
||||
}
|
||||
|
||||
public byte[] getPayload() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return may be null depending on what happened. Cast to expected type.
|
||||
*/
|
||||
public synchronized TrackerResponse getReplyObject() {
|
||||
return replyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, we got a reply, and getReplyObject() may contain something.
|
||||
*/
|
||||
public synchronized WaitState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will notify this.
|
||||
* Also removes from _sentQueries and calls heardFrom().
|
||||
* Sets state to SUCCESS or FAIL.
|
||||
*/
|
||||
public synchronized void gotReply(boolean success) {
|
||||
cancel();
|
||||
_sentQueries.remove(Integer.valueOf(tid));
|
||||
setState(success ? WaitState.SUCCESS : WaitState.FAIL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will notify this and run onReply.
|
||||
* Also removes from _sentQueries and calls heardFrom().
|
||||
*/
|
||||
private synchronized void setState(WaitState state) {
|
||||
this.state = state;
|
||||
this.notifyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Will notify this.
|
||||
* Also removes from _sentQueries and calls heardFrom().
|
||||
* Sets state to SUCCESS.
|
||||
*/
|
||||
public synchronized void gotResponse(TrackerResponse resp) {
|
||||
replyObject = resp;
|
||||
gotReply(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets state to INIT.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void schedule(long toWait) {
|
||||
state = WaitState.INIT;
|
||||
super.schedule(toWait);
|
||||
}
|
||||
|
||||
/** timer callback on timeout */
|
||||
public synchronized void timeReached() {
|
||||
// don't trump success or failure
|
||||
if (state != WaitState.INIT)
|
||||
return;
|
||||
//if (action == ACTION_CONNECT)
|
||||
// sentTo.connFailed();
|
||||
//else
|
||||
sentTo.replyTimeout();
|
||||
setState(WaitState.TIMEOUT);
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("timeout waiting for reply from " + sentTo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Waiting for ID: " + tid + " to: " + sentTo + " state: " + state;
|
||||
}
|
||||
}
|
||||
}
|
@ -100,6 +100,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private final ConcurrentHashMap<NID, Token> _incomingTokens;
|
||||
/** recently unreachable, with lastSeen() as the added-to-blacklist time */
|
||||
private final Set<NID> _blacklist;
|
||||
private SimpleTimer2.TimedEvent _cleaner, _explorer;
|
||||
|
||||
/** hook to inject and receive datagrams */
|
||||
private final I2PSession _session;
|
||||
@ -623,6 +624,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
public synchronized void start() {
|
||||
if (_isRunning)
|
||||
return;
|
||||
if (_log.shouldInfo())
|
||||
_log.info("KRPC start", new Exception());
|
||||
_session.addMuxedSessionListener(this, I2PSession.PROTO_DATAGRAM_RAW, _rPort);
|
||||
_session.addMuxedSessionListener(this, I2PSession.PROTO_DATAGRAM, _qPort);
|
||||
_knownNodes.start();
|
||||
@ -630,9 +633,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
PersistDHT.loadDHT(this, _dhtFile, _backupDhtFile);
|
||||
// start the explore thread
|
||||
_isRunning = true;
|
||||
// no need to keep ref, it will eventually stop
|
||||
new Cleaner();
|
||||
new Explorer(5*1000);
|
||||
_cleaner = new Cleaner();
|
||||
_explorer = new Explorer(5*1000);
|
||||
_txPkts.set(0);
|
||||
_rxPkts.set(0);
|
||||
_txBytes.set(0);
|
||||
@ -648,7 +650,10 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
if (!_isRunning)
|
||||
return;
|
||||
_isRunning = false;
|
||||
// FIXME stop the explore thread
|
||||
if (_log.shouldInfo())
|
||||
_log.info("KRPC stop", new Exception());
|
||||
_cleaner.cancel();
|
||||
_explorer.cancel();
|
||||
// unregister port listeners
|
||||
_session.removeListener(I2PSession.PROTO_DATAGRAM, _qPort);
|
||||
_session.removeListener(I2PSession.PROTO_DATAGRAM_RAW, _rPort);
|
||||
@ -1640,6 +1645,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
public void disconnected(I2PSession session) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("KRPC disconnected");
|
||||
stop();
|
||||
}
|
||||
|
||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
||||
@ -1760,7 +1766,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Explore of " + keys.size() + " buckets done, new size: " + _knownNodes.size());
|
||||
new Explorer(EXPLORE_TIME);
|
||||
_explorer = new Explorer(EXPLORE_TIME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,14 +32,16 @@ public class ConfigUIHelper {
|
||||
{ "cs", "cz", "Čeština", null },
|
||||
{ "zh", "cn", "Chinese 中文", null },
|
||||
//{ "zh_TW", "tw", "Chinese 中文", "Taiwan" },
|
||||
//{ "da", "dk", "Dansk", null },
|
||||
{ "da", "dk", "Dansk", null },
|
||||
{ "de", "de", "Deutsch", null },
|
||||
//{ "et", "ee", "Eesti", null },
|
||||
{ "en", "us", "English", null },
|
||||
{ "es", "es", "Español", null },
|
||||
{ "es", "ar", "Español" ,"Argentina" },
|
||||
{ "fa", "ir", "Persian فارسی", null },
|
||||
{ "fr", "fr", "Français", null },
|
||||
//{ "gl", "lang_gl", "Galego", null },
|
||||
//{ "el", "gr", "Greek Ελληνικά", null },
|
||||
{ "el", "gr", "Greek Ελληνικά", null },
|
||||
{ "in", "id", "bahasa Indonesia", null },
|
||||
{ "it", "it", "Italiano", null },
|
||||
{ "ja", "jp", "Japanese 日本語", null },
|
||||
|
@ -1,13 +1,19 @@
|
||||
package org.klomp.snark.standalone;
|
||||
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.apps.systray.UrlLauncher;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.desktopgui.ExternalMain;
|
||||
import net.i2p.jetty.I2PLogger;
|
||||
import net.i2p.jetty.JettyStart;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
* @since moved from ../web and fixed in 0.9.27
|
||||
@ -20,6 +26,7 @@ public class RunStandalone {
|
||||
private String _host = "127.0.0.1";
|
||||
private static RunStandalone _instance;
|
||||
static final File APP_CONFIG_FILE = new File("i2psnark-appctx.config");
|
||||
private static final String PROP_DTG_ENABLED = "desktopgui.enabled";
|
||||
|
||||
private RunStandalone(String args[]) throws Exception {
|
||||
Properties p = new Properties();
|
||||
@ -29,6 +36,13 @@ public class RunStandalone {
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
_context = new I2PAppContext(p);
|
||||
// Do this after we have a context
|
||||
// To take effect, must be set before any Jetty classes are loaded
|
||||
try {
|
||||
Log.setLog(new I2PLogger(_context));
|
||||
} catch (Throwable t) {
|
||||
System.err.println("INFO: I2P Jetty logging class not found, logging to stdout");
|
||||
}
|
||||
File base = _context.getBaseDir();
|
||||
File xml = new File(base, "jetty-i2psnark.xml");
|
||||
_jettyStart = new JettyStart(_context, null, new String[] { xml.getAbsolutePath() } );
|
||||
@ -56,13 +70,18 @@ public class RunStandalone {
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
_jettyStart.startup();
|
||||
String url = "http://" + _host + ':' + _port + "/i2psnark/";
|
||||
System.out.println("Starting i2psnark at " + url);
|
||||
startTrayApp();
|
||||
_jettyStart.startup();
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
UrlLauncher launch = new UrlLauncher(_context, null, new String[] { url } );
|
||||
launch.startup();
|
||||
String p = _context.getProperty("routerconsole.browser");
|
||||
if (!("/bin/false".equals(p) || "NUL".equals(p))) {
|
||||
UrlLauncher launch = new UrlLauncher(_context, null, new String[] { url } );
|
||||
launch.startup();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -82,4 +101,39 @@ public class RunStandalone {
|
||||
} catch (InterruptedException ie) {}
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.54 adapted from RouterConsoleRunner
|
||||
*/
|
||||
private static boolean isSystrayEnabled(I2PAppContext context) {
|
||||
if (GraphicsEnvironment.isHeadless())
|
||||
return false;
|
||||
// default false except on OSX and Windows,
|
||||
// and on Linux KDE and LXDE.
|
||||
// Xubuntu XFCE works but doesn't look very good
|
||||
// Ubuntu Unity was far too buggy to enable
|
||||
// Ubuntu GNOME does not work, SystemTray.isSupported() returns false
|
||||
String xdg = System.getenv("XDG_CURRENT_DESKTOP");
|
||||
boolean dflt = SystemVersion.isWindows() ||
|
||||
SystemVersion.isMac() ||
|
||||
//"XFCE".equals(xdg) ||
|
||||
"KDE".equals(xdg) ||
|
||||
"LXDE".equals(xdg);
|
||||
return context.getProperty(PROP_DTG_ENABLED, dflt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.54 adapted from RouterConsoleRunner
|
||||
*/
|
||||
private void startTrayApp() {
|
||||
try {
|
||||
if (isSystrayEnabled(_context)) {
|
||||
System.setProperty("java.awt.headless", "false");
|
||||
ExternalMain dtg = new ExternalMain(_context, _context.clientAppManager(), null);
|
||||
dtg.startup();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,14 @@ package org.klomp.snark.web;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.text.Collator;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -22,6 +24,7 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
@ -35,8 +38,10 @@ import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.servlet.util.ServletUtil;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFile;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.SystemVersion;
|
||||
import net.i2p.util.Translate;
|
||||
import net.i2p.util.UIMessages;
|
||||
@ -251,11 +256,12 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
} else {
|
||||
String base = addPaths(req.getRequestURI(), "/");
|
||||
boolean showEdit = req.getParameter("showEdit") != null;
|
||||
String listing = getListHTML(resource, base, true, method.equals("POST") ? req.getParameterMap() : null,
|
||||
req.getParameter("sort"));
|
||||
req.getParameter("sort"), showEdit);
|
||||
if (method.equals("POST")) {
|
||||
// P-R-G
|
||||
sendRedirect(req, resp, "");
|
||||
sendRedirect(req, resp, showEdit ? "?showEdit" : "");
|
||||
} else if (listing != null) {
|
||||
setHTMLHeaders(resp, cspNonce, true);
|
||||
resp.getWriter().write(listing);
|
||||
@ -314,7 +320,9 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
|
||||
// we want it to go to the base URI so we don't refresh with some funky action= value
|
||||
int delay = 0;
|
||||
if (!isConfigure) {
|
||||
if (isConfigure) {
|
||||
out.write("<script src=\".resources/js/configui.js?" + CoreVersion.VERSION + "\" type=\"text/javascript\"></script>\n");
|
||||
} else {
|
||||
delay = _manager.getRefreshDelaySeconds();
|
||||
if (delay > 0) {
|
||||
String jsPfx = _context.isRouterContext() ? "" : ".resources";
|
||||
@ -343,25 +351,24 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
out.write("</head>\n" +
|
||||
"<body>" +
|
||||
"<center>");
|
||||
List<Tracker> sortedTrackers = null;
|
||||
if (isConfigure) {
|
||||
out.write("<div class=\"snarknavbar\"><a href=\"" + _contextPath + "/\" title=\"");
|
||||
out.write(_t("Torrents"));
|
||||
out.write("\" class=\"snarkNav nav_main\">");
|
||||
if (_contextName.equals(DEFAULT_NAME))
|
||||
out.write(_t("I2PSnark"));
|
||||
else
|
||||
out.write(_contextName);
|
||||
out.write("</a>");
|
||||
} else {
|
||||
out.write("<div class=\"snarknavbar\"><a href=\"" + _contextPath + '/' + peerString + "\" title=\"");
|
||||
out.write(_t("Refresh page"));
|
||||
out.write("\" class=\"snarkNav nav_main\">");
|
||||
if (_contextName.equals(DEFAULT_NAME))
|
||||
out.write(_t("I2PSnark"));
|
||||
else
|
||||
out.write(_contextName);
|
||||
out.write("</a>\n");
|
||||
}
|
||||
out.write("\" class=\"snarkNav nav_main\">");
|
||||
if (_contextName.equals(DEFAULT_NAME))
|
||||
out.write(_t("I2PSnark"));
|
||||
else
|
||||
out.write(_contextName);
|
||||
if (!_context.isRouterContext()) {
|
||||
out.write(' ' + CoreVersion.VERSION);
|
||||
}
|
||||
out.write("</a>");
|
||||
List<Tracker> sortedTrackers = null;
|
||||
if (!isConfigure) {
|
||||
sortedTrackers = _manager.getSortedTrackers();
|
||||
if (_context.isRouterContext() && _manager.hasModifiedTrackers()) {
|
||||
for (Tracker t : sortedTrackers) {
|
||||
@ -2498,7 +2505,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
out.write(_t("Theme"));
|
||||
out.write(":<td colspan=\"2\">");
|
||||
if (_manager.getUniversalTheming()) {
|
||||
out.write("<select name='theme' disabled=\"disabled\" title=\"");
|
||||
out.write("<select id=\"theme\" name=\"theme\" disabled=\"disabled\" title=\"");
|
||||
out.write(_t("To change themes manually, disable universal theming"));
|
||||
out.write("\"><option>");
|
||||
out.write(_manager.getTheme());
|
||||
@ -2508,15 +2515,21 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
out.write(_t("Configure"));
|
||||
out.write("]</a>");
|
||||
} else {
|
||||
out.write("<select name='theme'>");
|
||||
out.write("<select id=\"theme\" name=\"theme\">");
|
||||
String theme = _manager.getTheme();
|
||||
String[] themes = _manager.getThemes();
|
||||
Arrays.sort(themes);
|
||||
// translated sort
|
||||
Map<String, String> tmap = new TreeMap<String, String>(Collator.getInstance());
|
||||
for (int i = 0; i < themes.length; i++) {
|
||||
if(themes[i].equals(theme))
|
||||
out.write("\n<OPTION value=\"" + themes[i] + "\" SELECTED>" + themes[i]);
|
||||
tmap.put(_t(themes[i]), themes[i]);
|
||||
}
|
||||
for (Map.Entry<String, String> e : tmap.entrySet()) {
|
||||
String tr = e.getKey();
|
||||
String opt = e.getValue();
|
||||
if(opt.equals(theme))
|
||||
out.write("\n<option value=\"" + opt + "\" SELECTED>" + tr + "</option>");
|
||||
else
|
||||
out.write("\n<OPTION value=\"" + themes[i] + "\">" + themes[i]);
|
||||
out.write("\n<option value=\"" + opt + "\">" + tr + "</option>");
|
||||
}
|
||||
out.write("</select>\n");
|
||||
}
|
||||
@ -2943,7 +2956,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
|
||||
private static final String DOCTYPE = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
|
||||
private static final String HEADER_A = "<link href=\"";
|
||||
private static final String HEADER_A = "<link id=\"pagestyle\" href=\"";
|
||||
private static final String HEADER_B = "snark.css?" + CoreVersion.VERSION + "\" rel=\"stylesheet\" type=\"text/css\" >";
|
||||
private static final String HEADER_C = "nocollapse.css?" + CoreVersion.VERSION + "\" rel=\"stylesheet\" type=\"text/css\" >";
|
||||
|
||||
@ -2984,8 +2997,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
* @return String of HTML or null if postParams != null
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private String getListHTML(File xxxr, String base, boolean parent, Map<String, String[]> postParams, String sortParam)
|
||||
throws IOException
|
||||
private String getListHTML(File xxxr, String base, boolean parent, Map<String, String[]> postParams,
|
||||
String sortParam, boolean showEdit) throws IOException
|
||||
{
|
||||
String decodedBase = decodePath(base);
|
||||
String title = decodedBase;
|
||||
@ -3027,6 +3040,10 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
_manager.startTorrent(snark);
|
||||
} else if (postParams.get("recheck") != null) {
|
||||
_manager.recheckTorrent(snark);
|
||||
} else if (postParams.get("editTorrent") != null) {
|
||||
saveTorrentEdit(snark, postParams);
|
||||
} else if (postParams.get("showEdit") != null) {
|
||||
// P-R-G only
|
||||
} else {
|
||||
_manager.addMessage("Unknown command");
|
||||
}
|
||||
@ -3055,7 +3072,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
r = new File("");
|
||||
}
|
||||
|
||||
boolean showStopStart = snark != null;
|
||||
boolean showStopStart = snark != null && !showEdit;
|
||||
Storage storage = snark != null ? snark.getStorage() : null;
|
||||
boolean showPriority = storage != null && !storage.complete() &&
|
||||
r.isDirectory();
|
||||
@ -3093,7 +3110,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
final boolean includeForm = showStopStart || showPriority || er || ec;
|
||||
if (includeForm) {
|
||||
buf.append("<form action=\"").append(base).append("\" method=\"POST\">\n" +
|
||||
"<input type=\"hidden\" name=\"nonce\" value=\"").append(_nonce).append("\" >\n");
|
||||
"<input type=\"hidden\" name=\"nonce\" value=\"").append(_nonce).append("\">\n");
|
||||
if (sortParam != null) {
|
||||
buf.append("<input type=\"hidden\" name=\"sort\" value=\"")
|
||||
.append(DataHelper.stripHTML(sortParam)).append("\" >\n");
|
||||
@ -3103,7 +3120,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
// first table - torrent info
|
||||
buf.append("<table class=\"snarkTorrentInfo\">\n" +
|
||||
"<tr><th></th><th><b>")
|
||||
.append(_t("Torrent"))
|
||||
.append(showEdit ? _t("Edit Torrent") : _t("Torrent"))
|
||||
.append("</b></th><th>")
|
||||
.append(DataHelper.escapeHTML(snark.getBaseName()))
|
||||
.append("</th></tr>\n");
|
||||
@ -3117,7 +3134,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
.append("</b></td><td><a href=\"").append(_contextPath).append('/').append(baseName).append("\">")
|
||||
.append(DataHelper.escapeHTML(fullPath))
|
||||
.append("</a></td></tr>\n");
|
||||
if (storage != null) {
|
||||
if (storage != null && !showEdit) {
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "file");
|
||||
buf.append("</td><td><b>")
|
||||
@ -3127,17 +3144,19 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
.append("</td></tr>\n");
|
||||
}
|
||||
String hex = I2PSnarkUtil.toHex(snark.getInfoHash());
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Info hash"))
|
||||
.append("</b></td><td><span id=\"infohash\">")
|
||||
.append(hex.toUpperCase(Locale.US))
|
||||
.append("</span></td></tr>\n");
|
||||
if (!showEdit) {
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Info hash"))
|
||||
.append("</b></td><td><span id=\"infohash\">")
|
||||
.append(hex.toUpperCase(Locale.US))
|
||||
.append("</span></td></tr>\n");
|
||||
}
|
||||
|
||||
String announce = null;
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
if (meta != null) {
|
||||
if (meta != null && !showEdit) {
|
||||
announce = meta.getAnnounce();
|
||||
if (announce == null)
|
||||
announce = snark.getTrackerURL();
|
||||
@ -3218,7 +3237,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Comment")).append("</b></td><td>")
|
||||
.append(DataHelper.stripHTML(com))
|
||||
.append(DataHelper.escapeHTML(com).replace("\r\n", "<br>").replace("\n", "<br>"))
|
||||
.append("</td></tr>\n");
|
||||
}
|
||||
long dat = meta.getCreationDate();
|
||||
@ -3239,7 +3258,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Created By")).append("</b></td><td>")
|
||||
.append(DataHelper.stripHTML(cby))
|
||||
.append(DataHelper.escapeHTML(cby))
|
||||
.append("</td></tr>\n");
|
||||
}
|
||||
long[] dates = _manager.getSavedAddedAndCompleted(snark);
|
||||
@ -3275,6 +3294,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
}
|
||||
|
||||
if (!showEdit) { // don't bother to reindent
|
||||
if (meta == null || !meta.isPrivate()) {
|
||||
buf.append("<tr><td><a href=\"")
|
||||
.append(MagnetURI.MAGNET_FULL).append(hex);
|
||||
@ -3377,6 +3397,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
.append(":</b> ")
|
||||
.append(formatSize(snark.getPieceLength(0)))
|
||||
.append("</span></td></tr>\n");
|
||||
} // !showEdit
|
||||
|
||||
// buttons
|
||||
if (showStopStart) {
|
||||
@ -3390,7 +3411,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
buf.append("<b>").append(_t("Starting")).append("…</b>");
|
||||
} else if (snark.isAllocating()) {
|
||||
buf.append("<b>").append(_t("Allocating")).append("…</b>");
|
||||
} else {
|
||||
} else if (isTopLevel && !showEdit) {
|
||||
boolean isRunning = !snark.isStopped();
|
||||
buf.append("<input type=\"submit\" value=\"");
|
||||
if (isRunning)
|
||||
@ -3398,14 +3419,23 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
else
|
||||
buf.append(_t("Start")).append("\" name=\"start\" class=\"starttorrent\">\n");
|
||||
buf.append("<input type=\"submit\" name=\"recheck\" value=\"").append(_t("Force Recheck"));
|
||||
if (isRunning)
|
||||
if (isRunning) {
|
||||
buf.append("\" class=\"disabled\" disabled=\"disabled\" title=\"")
|
||||
.append(_t("Stop the torrent in order to check file integrity"))
|
||||
.append("\">\n");
|
||||
else
|
||||
.append(_t("Torrent must be stopped"));
|
||||
} else {
|
||||
buf.append("\" class=\"reload\" title=\"")
|
||||
.append(_t("Check integrity of the downloaded files"))
|
||||
.append("\">\n");
|
||||
.append(_t("Check integrity of the downloaded files"));
|
||||
}
|
||||
buf.append("\">\n" +
|
||||
"<input type=\"submit\" name=\"showEdit\" value=\"").append(_t("Edit Torrent"));
|
||||
if (isRunning) {
|
||||
buf.append("\" class=\"disabled\" disabled=\"disabled\" title=\"")
|
||||
.append(_t("Torrent must be stopped"));
|
||||
} else {
|
||||
buf.append("\" class=\"reload\" title=\"")
|
||||
.append(_t("Add or remove trackers"));
|
||||
}
|
||||
buf.append("\">\n");
|
||||
}
|
||||
boolean showInOrder = storage != null && !storage.complete() &&
|
||||
meta != null;
|
||||
@ -3439,6 +3469,13 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
buf.append("</table>\n");
|
||||
|
||||
if (snark != null && isTopLevel && showEdit) {
|
||||
// Edit torrent. Show edit section only.
|
||||
displayTorrentEdit(snark, base, buf);
|
||||
buf.append("</form></div></div></center></body></html>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
if (snark != null && !r.exists()) {
|
||||
// fixup TODO
|
||||
buf.append("<table class=\"resourceError\" id=\"DoesNotExist\"><tr><th colspan=\"2\">")
|
||||
@ -3481,7 +3518,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
displayComments(snark, er, ec, esc, buf);
|
||||
if (includeForm)
|
||||
buf.append("</form>");
|
||||
buf.append("</div></div></body></html>");
|
||||
buf.append("</div></div></center></body></html>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@ -3778,7 +3815,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
// for stop/start/check
|
||||
if (includeForm)
|
||||
buf.append("</form>");
|
||||
buf.append("</div></div></body></html>");
|
||||
buf.append("</div></div></center></body></html>");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
@ -4150,6 +4187,136 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
buf.append("</div>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param snark non-null
|
||||
* @since 0.9.53
|
||||
*/
|
||||
private void displayTorrentEdit(Snark snark, String base, StringBuilder buf) {
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
if (meta == null)
|
||||
return;
|
||||
buf.append("<div id=\"snarkCommentSection\"><table class=\"snarkTorrentInfo\">\n");
|
||||
//.append("<tr><th colspan=\"5\">")
|
||||
//.append(_t("Edit Torrent"))
|
||||
//.append("</th>")
|
||||
//.append("</tr>");
|
||||
boolean isRunning = !snark.isStopped();
|
||||
if (isRunning) {
|
||||
// shouldn't happen
|
||||
buf.append("<tr><td colspan=\"5\">")
|
||||
.append(_t("Torrent must be stopped"))
|
||||
.append("</td></tr></table></div></form>");
|
||||
return;
|
||||
}
|
||||
String announce = meta.getAnnounce();
|
||||
if (announce == null)
|
||||
announce = snark.getTrackerURL();
|
||||
if (announce != null) {
|
||||
// strip non-i2p trackers
|
||||
if (!isI2PTracker(announce))
|
||||
announce = null;
|
||||
}
|
||||
List<List<String>> alist = meta.getAnnounceList();
|
||||
Set<String> annlist = new TreeSet<String>();
|
||||
if (alist != null && !alist.isEmpty()) {
|
||||
// strip non-i2p trackers
|
||||
for (List<String> alist2 : alist) {
|
||||
for (String s : alist2) {
|
||||
if (isI2PTracker(s))
|
||||
annlist.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (announce != null)
|
||||
annlist.add(announce);
|
||||
if (!annlist.isEmpty()) {
|
||||
buf.append("<tr><td colspan=\"3\"></td><td>").append("Primary").append("</td><td>")
|
||||
.append("Delete").append("</td></tr>");
|
||||
for (String s : annlist) {
|
||||
int hc = s.hashCode();
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Tracker")).append("</b></td><td>");
|
||||
s = DataHelper.stripHTML(s);
|
||||
buf.append("<span class=\"info_tracker\">");
|
||||
buf.append(getShortTrackerLink(s, snark.getInfoHash()));
|
||||
buf.append("</span> ");
|
||||
//buf.append(s);
|
||||
buf.append("</td><td>");
|
||||
buf.append("<input type=\"radio\" class=\"optbox\" name=\"primary\" ");
|
||||
if (s.equals(announce))
|
||||
buf.append("checked=\"checked\" ");
|
||||
buf.append("value=\"").append(hc);
|
||||
buf.append("\"></td><td>");
|
||||
buf.append("<input type=\"checkbox\" class=\"optbox\" name=\"trdelete.")
|
||||
.append(hc).append("\" title=\"").append(_t("Mark for deletion")).append("\">");
|
||||
buf.append("</td></tr>\n");
|
||||
}
|
||||
}
|
||||
|
||||
List<Tracker> newTrackers = _manager.getSortedTrackers();
|
||||
for (Iterator<Tracker> iter = newTrackers.iterator(); iter.hasNext(); ) {
|
||||
Tracker t = iter.next();
|
||||
String announceURL = t.announceURL.replace("=", "=");
|
||||
if (announceURL.equals(announce) || annlist.contains(announceURL))
|
||||
iter.remove();
|
||||
}
|
||||
if (!newTrackers.isEmpty()) {
|
||||
buf.append("<tr><td colspan=\"3\"></td><td>").append("Primary").append("</td><td>")
|
||||
.append("Add").append("</td></tr>");
|
||||
for (Tracker t : newTrackers) {
|
||||
String name = t.name;
|
||||
int hc = t.announceURL.hashCode();
|
||||
String announceURL = t.announceURL.replace("=", "=");
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Add Tracker")).append("</b></td><td>");
|
||||
buf.append(name);
|
||||
buf.append("</td><td><input type=\"radio\" class=\"optbox\" name=\"primary\" value=\"");
|
||||
buf.append(hc);
|
||||
buf.append("\"></td><td>");
|
||||
buf.append("<input type=\"checkbox\" class=\"optbox\" id=\"").append(name).append("\" name=\"tradd.")
|
||||
.append(hc).append("\" title=\"").append(_t("Add tracker")).append("\"> ")
|
||||
.append("</td><td></td></tr>\n");
|
||||
}
|
||||
}
|
||||
|
||||
String com = meta.getComment();
|
||||
if (com == null) {
|
||||
com = "";
|
||||
} else if (com.length() > 0) {
|
||||
com = DataHelper.escapeHTML(com);
|
||||
}
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Comment")).append("</b></td>");
|
||||
buf.append("<td colspan=\"2\" id=\"addCommentText\"><textarea name=\"nofilter_newTorrentComment\" cols=\"88\" rows=\"4\">")
|
||||
.append(com).append("</textarea></td><td></td>");
|
||||
buf.append("</tr>\n");
|
||||
|
||||
String cb = meta.getCreatedBy();
|
||||
if (cb == null) {
|
||||
cb = "";
|
||||
} else if (cb.length() > 0) {
|
||||
cb = DataHelper.escapeHTML(cb);
|
||||
}
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Created By")).append("</b></td>");
|
||||
buf.append("<td id=\"editTorrentCreatedBy\"><input type=\"text\" name=\"nofilter_newTorrentCreatedBy\" cols=\"44\" rows=\"1\" value=\"")
|
||||
.append(cb).append("\"></td></tr>");
|
||||
|
||||
buf.append("<tr id=\"torrentInfoControl\"><td colspan=\"5\">");
|
||||
buf.append("<input type=\"submit\" name=\"editTorrent\" value=\"");
|
||||
buf.append(_t("Save Changes"));
|
||||
buf.append("\" class=\"accept\"></td></tr>\n");
|
||||
buf.append("</table></div>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param so null ok
|
||||
* @return query string or ""
|
||||
@ -4370,6 +4537,147 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
_manager.setSavedCommentsEnabled(snark, yes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.53
|
||||
*/
|
||||
private void saveTorrentEdit(Snark snark, Map<String, String[]> postParams) {
|
||||
if (!snark.isStopped()) {
|
||||
// shouldn't happen
|
||||
_manager.addMessage(_t("Torrent must be stopped"));
|
||||
return;
|
||||
}
|
||||
List<Integer> toAdd = new ArrayList<Integer>();
|
||||
List<Integer> toDel = new ArrayList<Integer>();
|
||||
Integer primary = null;
|
||||
String newComment = "";
|
||||
String newCreatedBy = "";
|
||||
for (Map.Entry<String, String[]> entry : postParams.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String val = entry.getValue()[0]; // jetty arrays
|
||||
if (key.startsWith("tradd.")) {
|
||||
try {
|
||||
toAdd.add(Integer.parseInt(key.substring(6)));
|
||||
} catch (NumberFormatException nfe) {}
|
||||
} else if (key.startsWith("trdelete.")) {
|
||||
try {
|
||||
toDel.add(Integer.parseInt(key.substring(9)));
|
||||
} catch (NumberFormatException nfe) {}
|
||||
} else if (key.equals("primary")) {
|
||||
try {
|
||||
primary = Integer.parseInt(val);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
} else if (key.equals("nofilter_newTorrentComment")) {
|
||||
newComment = val.trim();
|
||||
} else if (key.equals("nofilter_newTorrentCreatedBy")) {
|
||||
newCreatedBy = val.trim();
|
||||
}
|
||||
}
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
if (meta == null) {
|
||||
// shouldn't happen
|
||||
_manager.addMessage("Can't edit magnet");
|
||||
return;
|
||||
}
|
||||
String oldPrimary = meta.getAnnounce();
|
||||
String oldComment = meta.getComment();
|
||||
if (oldComment == null)
|
||||
oldComment = "";
|
||||
String oldCreatedBy = meta.getCreatedBy();
|
||||
if (oldCreatedBy == null)
|
||||
oldCreatedBy = "";
|
||||
if (toAdd.isEmpty() && toDel.isEmpty() &&
|
||||
(primary == null || primary.equals(oldPrimary)) &&
|
||||
oldComment.equals(newComment) &&
|
||||
oldCreatedBy.equals(newCreatedBy)) {
|
||||
_manager.addMessage("No changes to torrent, not saved");
|
||||
return;
|
||||
}
|
||||
List<List<String>> alist = meta.getAnnounceList();
|
||||
Set<String> annlist = new TreeSet<String>();
|
||||
if (alist != null && !alist.isEmpty()) {
|
||||
// strip non-i2p trackers
|
||||
for (List<String> alist2 : alist) {
|
||||
for (String s : alist2) {
|
||||
if (isI2PTracker(s))
|
||||
annlist.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldPrimary != null)
|
||||
annlist.add(oldPrimary);
|
||||
List<Tracker> newTrackers = _manager.getSortedTrackers();
|
||||
for (Integer i : toDel) {
|
||||
int hc = i.intValue();
|
||||
for (Iterator<String> iter = annlist.iterator(); iter.hasNext(); ) {
|
||||
String s = iter.next();
|
||||
if (s.hashCode() == hc)
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
for (Integer i : toAdd) {
|
||||
int hc = i.intValue();
|
||||
for (Tracker t : newTrackers) {
|
||||
if (t.announceURL.hashCode() == hc) {
|
||||
annlist.add(t.announceURL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
String thePrimary = oldPrimary;
|
||||
if (primary != null) {
|
||||
int hc = primary.intValue();
|
||||
for (String s : annlist) {
|
||||
if (s.hashCode() == hc) {
|
||||
thePrimary = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
List<List<String>> newAnnList;
|
||||
if (annlist.isEmpty()) {
|
||||
newAnnList = null;
|
||||
thePrimary = null;
|
||||
} else {
|
||||
List<String> aalist = new ArrayList<String>(annlist);
|
||||
newAnnList = Collections.singletonList(aalist);
|
||||
if (!aalist.contains(thePrimary))
|
||||
thePrimary = aalist.get(0);
|
||||
}
|
||||
if (newComment.equals(""))
|
||||
newComment = null;
|
||||
if (newCreatedBy.equals(""))
|
||||
newCreatedBy = null;
|
||||
MetaInfo newMeta = new MetaInfo(thePrimary, meta.getName(), null, meta.getFiles(), meta.getLengths(),
|
||||
meta.getPieceLength(0), meta.getPieceHashes(), meta.getTotalLength(), meta.isPrivate(),
|
||||
newAnnList, newCreatedBy, meta.getWebSeedURLs(), newComment);
|
||||
if (!DataHelper.eq(meta.getInfoHash(), newMeta.getInfoHash())) {
|
||||
// shouldn't happen
|
||||
_manager.addMessage("Torrent edit failed, infohash mismatch");
|
||||
return;
|
||||
}
|
||||
File f = new File(_manager.util().getTempDir(), "edit-" + _manager.util().getContext().random().nextLong() + ".torrent");
|
||||
OutputStream out = null;
|
||||
try {
|
||||
out = new SecureFileOutputStream(f);
|
||||
out.write(newMeta.getTorrentData());
|
||||
out.close();
|
||||
boolean ok = FileUtil.rename(f, new File(snark.getName()));
|
||||
if (!ok) {
|
||||
_manager.addMessage("Save edit changes failed");
|
||||
return;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
try { if (out != null) out.close(); } catch (IOException ioe2) {}
|
||||
_manager.addMessage("Save edit changes failed: " + ioe);
|
||||
return;
|
||||
} finally {
|
||||
f.delete();
|
||||
}
|
||||
snark.replaceMetaInfo(newMeta);
|
||||
_manager.addMessage("Torrent changes saved");
|
||||
}
|
||||
|
||||
|
||||
/** @since 0.9.32 */
|
||||
private static boolean noCollapsePanels(HttpServletRequest req) {
|
||||
// check for user agents that can't toggle the collapsible panels...
|
||||
|
@ -14,6 +14,10 @@
|
||||
|
||||
package org.klomp.snark.web;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@ -21,6 +25,9 @@ import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
@ -47,6 +54,8 @@ class MimeTypes
|
||||
|
||||
public MimeTypes() {
|
||||
_mimeMap = new ConcurrentHashMap<String, String>();
|
||||
if (!(SystemVersion.isWindows() || SystemVersion.isMac() || SystemVersion.getMaxMemory() < 100*1024*1024L))
|
||||
loadSystemMimeTypes();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@ -86,6 +95,37 @@ class MimeTypes
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load mime types from /etc/mime.types
|
||||
* Format: mimetype suffix1 suffix2 ...
|
||||
*
|
||||
* @since 0.9.54
|
||||
*/
|
||||
private void loadSystemMimeTypes() {
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(new FileInputStream("/etc/mime.types"), "ISO-8859-1"));
|
||||
while (true) {
|
||||
String line = in.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
if (line.startsWith("#"))
|
||||
continue;
|
||||
String[] s = DataHelper.split(line, "[ \t]+", 16);
|
||||
if (s.length < 2)
|
||||
continue;
|
||||
for (int i = 1; i < s.length; i++) {
|
||||
_mimeMap.put(s[i].toLowerCase(Locale.US), s[0]);
|
||||
//System.out.println("Mapping: '" + s[i] + "' -> '" + s[0] + "'");
|
||||
}
|
||||
}
|
||||
//System.out.println("Loaded " + _mimeMap.size() + " mime types from /etc/mime.types");
|
||||
} catch (IOException ioe) {
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the MIME type by filename extension.
|
||||
*
|
||||
|
@ -4,5 +4,36 @@
|
||||
# The file jetty-i2psnark.xml must be present in the current directory.
|
||||
# i2psnark will be accessed at http://127.0.0.1:8002/
|
||||
#
|
||||
|
||||
# Raise the soft open files soft ulimit to this value, if able
|
||||
OPEN_FILES_ULIMIT=2048
|
||||
|
||||
# Increase memory to 512 MB
|
||||
JAVA_OPTS='-Xmx512m'
|
||||
|
||||
raiseopenfilesulimit() {
|
||||
OPEN_FILES_SOFT=`ulimit -S -n` 2> /dev/null || return
|
||||
if [ "$OPEN_FILES_SOFT" != "unlimited" ]
|
||||
then
|
||||
if [ "$OPEN_FILES_ULIMIT" -gt "$OPEN_FILES_SOFT" ]
|
||||
then
|
||||
OPEN_FILES_HARD=`ulimit -H -n` 2> /dev/null || return
|
||||
if [ "$OPEN_FILES_HARD" != "unlimited" ]
|
||||
then
|
||||
if [ "$OPEN_FILES_ULIMIT" -gt "$OPEN_FILES_HARD" ]
|
||||
then
|
||||
OPEN_FILES_ULIMIT="$OPEN_FILES_HARD"
|
||||
fi
|
||||
fi
|
||||
if [ "$OPEN_FILES_ULIMIT" -gt "$OPEN_FILES_SOFT" ]
|
||||
then
|
||||
ulimit -S -n "$OPEN_FILES_ULIMIT" > /dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
raiseopenfilesulimit
|
||||
|
||||
I2P="."
|
||||
java -jar "$I2P/i2psnark.jar"
|
||||
java $JAVA_OPTS -jar "$I2P/i2psnark.jar"
|
||||
|
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
1822
apps/i2psnark/locale/messages_es_AR.po
Normal file
1822
apps/i2psnark/locale/messages_es_AR.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@
|
||||
3gpp = video/3gpp
|
||||
7z = application/x-7z-compressed
|
||||
ape = audio/x-monkeys-audio
|
||||
avif = image/avif
|
||||
bz2 = application/x-bzip2
|
||||
cue = application/x-cue
|
||||
dff = audio/x-dsd
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user