Compare commits
1168 Commits
i2pbote_0_
...
v0.4.1
Author | SHA1 | Date | |
---|---|---|---|
d3cb0ab3f6 | |||
e5fa4706db | |||
ecdd2ee426 | |||
784a716f0a | |||
afdde691b4 | |||
65d9439e03 | |||
8d19c4c78a | |||
8731a72887 | |||
9e1b42134c | |||
17b4d035eb | |||
d967d50c51 | |||
42770cf01b | |||
c910e17f6c | |||
d408242173 | |||
c52429f393 | |||
aac9df446f | |||
da40d97185 | |||
e661e4306c | |||
18005cdfb3 | |||
1a7e8daf8e | |||
771153c4d7 | |||
f1e750cef1 | |||
52e30278b5 | |||
4f1b81510f | |||
0c4d1dc5c3 | |||
c1934b4576 | |||
0edd014c6b | |||
27e02ce527 | |||
9aa14a705e | |||
219de6e499 | |||
d07f790dbb | |||
8abfc8345b | |||
3cbcfaa331 | |||
63b63a9f65 | |||
29f5db767d | |||
90545b7046 | |||
37b64f25c0 | |||
5f7bb2d7d1 | |||
69fc637bd7 | |||
cb0b0822a3 | |||
f83f7cd085 | |||
bab92e082f | |||
d1e7eeb7bc | |||
a5409b0e43 | |||
742c697150 | |||
42c6269cda | |||
03012e5323 | |||
97825a3d60 | |||
dc5d066775 | |||
c2bc331b55 | |||
8f29c7d6fb | |||
6b530a364b | |||
3f60643c2e | |||
3539b5a13b | |||
e37f70506d | |||
4196486799 | |||
6dbecc0f6e | |||
d209c9f94b | |||
6e528dae6a | |||
90f9de4861 | |||
f7b3499e93 | |||
5901565d74 | |||
f98990ad65 | |||
d290665a17 | |||
e2e2233b89 | |||
29190552d9 | |||
371ae89bc3 | |||
a34da62789 | |||
8ff2c84cd4 | |||
96140a49c5 | |||
b3f9ea8a0b | |||
034660d4af | |||
bbda61316e | |||
297a26cc63 | |||
c9686adaef | |||
82d31e4813 | |||
63c17045b6 | |||
0566e42f21 | |||
a21f67d429 | |||
d7ae6d51e5 | |||
271eb004b2 | |||
c65e3f8f7e | |||
dd7f4055a1 | |||
9b0836249d | |||
0585f211fb | |||
4df989d63a | |||
a76789cf30 | |||
ae55336e06 | |||
437c5ddbac | |||
63a574f578 | |||
42c3e0d369 | |||
5d89032b90 | |||
fc738142fa | |||
4e9b0e93cb | |||
8912cfcf7d | |||
73e22a3d34 | |||
dfa1dfbd74 | |||
eff95792ac | |||
bf53778367 | |||
74bde7aac6 | |||
848ec8cb14 | |||
a72bd3fb04 | |||
87ce7646f6 | |||
e2c003503a | |||
4f9d717725 | |||
f638d8e43a | |||
2377acf15c | |||
f11af022b2 | |||
9ef5a15650 | |||
02bb52dc67 | |||
bd9a0bd6b4 | |||
fe7516c24f | |||
97c567a105 | |||
62308d053d | |||
f4ebc330a1 | |||
93cabe4f44 | |||
5222861962 | |||
9c09c739d2 | |||
8b3770f33d | |||
cd5d8b0ac2 | |||
596aa39d5d | |||
212ca673b0 | |||
9833590266 | |||
77242920bd | |||
19a8c09a8d | |||
2634398ea4 | |||
2fac3e9eae | |||
182bafb524 | |||
d7c673e4be | |||
33273675da | |||
7b405764fb | |||
750305fef1 | |||
269f0d07e9 | |||
7aae76ef4b | |||
302414a53d | |||
22732d33c1 | |||
b113dd3e51 | |||
ca0ac05614 | |||
54ac8425b5 | |||
67e36d834e | |||
48c2294517 | |||
e3e694d0c9 | |||
06fc60af43 | |||
9cd8bddf8a | |||
6c4ab4b1b3 | |||
3155308054 | |||
538a5b2e06 | |||
dd126df6c7 | |||
a1edb3173c | |||
edc51acedb | |||
0394b591a7 | |||
9a5a161342 | |||
ec3acae530 | |||
7fea66978b | |||
b7abb03b32 | |||
958a072d9b | |||
e7042dc5a7 | |||
bc6df473be | |||
04ac90d478 | |||
7e8421c63b | |||
3591d25f72 | |||
1f1b328f70 | |||
823491ae4d | |||
6856773636 | |||
2cc3df7c1f | |||
852bad8d61 | |||
4f357db8ec | |||
877b02cc04 | |||
8b5b7507c6 | |||
5ff770bc31 | |||
4107467c64 | |||
05def741fb | |||
82ac0d59d8 | |||
10445e1082 | |||
a968f5c6f4 | |||
d6564680a5 | |||
ec41d7d82e | |||
8a29b9dd0d | |||
6c53344f51 | |||
2768297be9 | |||
740197e439 | |||
6e4c52b746 | |||
cc59ed9adb | |||
3c0a431f09 | |||
392ba71037 | |||
e39abcc145 | |||
e030e9ed5f | |||
1896fccf1f | |||
ca19e9d9e0 | |||
729a4ac99f | |||
729f59e729 | |||
78b66b5509 | |||
5cdcae1416 | |||
348262e7a2 | |||
adb290658a | |||
c04fc24445 | |||
4762f8486e | |||
52a717d7e3 | |||
e9ffb4643b | |||
382f75f4a4 | |||
4a6fc262e7 | |||
4cd24cd579 | |||
32ce92d7cb | |||
96bb3a0719 | |||
5cebc0b2ee | |||
4e7d57d8e3 | |||
0ad03ce6d4 | |||
2082fd0856 | |||
98c56de3a5 | |||
1801e308b2 | |||
f318587209 | |||
38d5833551 | |||
6bbd015d38 | |||
f20ae940a8 | |||
f7f185a68c | |||
a47100d14a | |||
32bd6e45a8 | |||
ca19b0929e | |||
3dede23a24 | |||
e190587330 | |||
0d92ddb6e1 | |||
52f6405c74 | |||
99fb8bf264 | |||
fa22d47252 | |||
2dfa656b06 | |||
56aa3f3c1c | |||
c2e75290fb | |||
3387cdd90c | |||
c3088c5931 | |||
a149a0bdc0 | |||
7867198fe9 | |||
38293ebcf9 | |||
6f4a0615ff | |||
22fa6105ac | |||
946fa8d6ec | |||
5a8d9c6f4f | |||
5c53c2071b | |||
c84770036d | |||
d9756407b0 | |||
fe9c6d9d47 | |||
ee96addef6 | |||
1f9aa61d2f | |||
5e559846db | |||
bbd24eaaff | |||
4db680f067 | |||
79595913f2 | |||
b387b77bfb | |||
dce43d11ae | |||
8fec6b28e8 | |||
17282cda4d | |||
1664253417 | |||
654b136cb0 | |||
2b5de7958e | |||
10b9b2e593 | |||
3571adf2ec | |||
44027e80ca | |||
9d821f66b9 | |||
bef847d270 | |||
967a523130 | |||
c35e9c9139 | |||
c0b62e25c1 | |||
936e1e7d50 | |||
592793eaac | |||
f1c9ac9788 | |||
bdd24d7e9d | |||
de36d91909 | |||
1a6ae22b99 | |||
adde9736be | |||
173d08d798 | |||
acb1ace33a | |||
ffa7450e77 | |||
d9d9c47124 | |||
d960308461 | |||
616df8de9f | |||
427e293bac | |||
163f8d58c0 | |||
43e34eb221 | |||
c2d1334c04 | |||
a05890eaec | |||
2a744fb1cd | |||
ed60728edd | |||
48e69d3cd5 | |||
df09332ead | |||
585413fa2b | |||
68c48cb332 | |||
ae87a801ea | |||
901b051546 | |||
6590e3122a | |||
e601b5f46e | |||
641085ab24 | |||
5d371f983a | |||
8fc111fc86 | |||
2dc809c025 | |||
7b0149dc39 | |||
24509df2b5 | |||
263b68550e | |||
ae3ac8b5be | |||
7c22899f32 | |||
3970d6d015 | |||
4464301119 | |||
f0622d202b | |||
63e260f932 | |||
87ad8db92a | |||
cc7e0209ec | |||
1a4c25a32f | |||
79339a0208 | |||
249389a4d3 | |||
92a5a998d6 | |||
e1a49cf440 | |||
e30cb79c5f | |||
49268b21ea | |||
74384e0517 | |||
9bfde0ac33 | |||
55d5ea6084 | |||
0e36c3dcc3 | |||
0155e5f57f | |||
a2b3833602 | |||
200d9ceb3d | |||
69fd2431c8 | |||
66020b2fc7 | |||
34e76ce627 | |||
79393f0ea8 | |||
c4cdbb98cc | |||
eda69d1e7e | |||
1f2c528b9b | |||
6a99b034b7 | |||
17db81eee1 | |||
d50c843145 | |||
7cbf364407 | |||
bd8674950e | |||
0a0418f83f | |||
a5832cd1e4 | |||
ccbc2c3e01 | |||
d5c6c2d046 | |||
d8a606946e | |||
dbaa00d5b1 | |||
844bfcb842 | |||
882a6d207a | |||
730aff2a50 | |||
de6d2d60a9 | |||
a724a521cd | |||
eaaf3c90e4 | |||
a0febacdfd | |||
2f0cb066ab | |||
fc89896246 | |||
e9c262c8b7 | |||
093e439e04 | |||
bf07f9c664 | |||
bba9ac405a | |||
a0457273b7 | |||
e6a330944d | |||
20940f209b | |||
bbc71ad13c | |||
64917757a4 | |||
b966ebbcfd | |||
ca7f4124c4 | |||
b24fe46661 | |||
1db3aee096 | |||
db5a566e15 | |||
f485c68472 | |||
78013d6725 | |||
261d05a1d6 | |||
4a79a28d28 | |||
844a7a24fe | |||
9937ec73f9 | |||
54629d8d05 | |||
15dcd78936 | |||
087a4a68be | |||
7829c44d0d | |||
476d06a588 | |||
53223be28d | |||
691b8ade1c | |||
7ac255ec94 | |||
cf84a6aeee | |||
15c45ad820 | |||
10100b8177 | |||
6adef679ac | |||
00411877ad | |||
7dc9f6caa0 | |||
dcb2b0f945 | |||
f396729698 | |||
0aff058acd | |||
55f363aa73 | |||
6fec35ce85 | |||
42001079b5 | |||
ab3d23a3af | |||
3b0c9774e5 | |||
8b44166b90 | |||
46ca0add8a | |||
6e967b2365 | |||
bc72ebb865 | |||
e204d01fa2 | |||
4a05868f11 | |||
6400913229 | |||
d7d3f2f01c | |||
f9014d7b7b | |||
a64ece90c2 | |||
e8f16cc83c | |||
d7b7355dd1 | |||
816cf49b8c | |||
bf2e2224c9 | |||
2824c9a773 | |||
1dbbcd8b78 | |||
d7d6257a36 | |||
020ef33b95 | |||
8cfe4c5660 | |||
e04deb28a1 | |||
fc0837ede4 | |||
3ade199175 | |||
7bffe19e9e | |||
3ffcf9d8d3 | |||
111b3d8dbb | |||
e177cd4567 | |||
a0ce9017a9 | |||
bc1848ddcf | |||
1f595fc079 | |||
22c0b789c4 | |||
967286aed0 | |||
ef357a5f2a | |||
9fb05ba42e | |||
f13743d7b9 | |||
549c39681d | |||
a3e8f657dd | |||
9cb3409a1a | |||
5e87d6fa2d | |||
902db81fa7 | |||
3cd0f09a41 | |||
d0204effa2 | |||
ec619dd830 | |||
10e3df428c | |||
e11f41d7f2 | |||
f7aae2de4e | |||
d1e4d1a715 | |||
9435b23ab2 | |||
bdffd2b8c4 | |||
590b9a9e05 | |||
86ec61db22 | |||
45f60e9242 | |||
56be74a167 | |||
14376afddb | |||
d2adcd64a7 | |||
13c19addff | |||
dad6a579c1 | |||
83d585f651 | |||
90ceef4583 | |||
0bf899e291 | |||
bbdfc5745b | |||
c3e6a437c9 | |||
dbe4400409 | |||
bb5d075a54 | |||
5d87d5ec00 | |||
d24eea365c | |||
abd4a55956 | |||
732b14115b | |||
4455fbb172 | |||
673d43cd86 | |||
b45a162826 | |||
b06d07b5d1 | |||
52ac133ffa | |||
4a240fbb5b | |||
252a7f0dad | |||
1cd51c811d | |||
dda1f5b585 | |||
fb5e1fc4c2 | |||
0e6a32a0a0 | |||
d9bc25e1aa | |||
b124a1046d | |||
cd21435b60 | |||
44130323ad | |||
ef339c29d0 | |||
8ea8e86d99 | |||
0edf2cfbe3 | |||
a4b284a71c | |||
e1b9eb5bbc | |||
f45a185502 | |||
be545c0836 | |||
6c10b8ac64 | |||
92ff6b58f0 | |||
1f54ed9ba8 | |||
19452867b4 | |||
1fe6c4fc36 | |||
57ead77d39 | |||
dd3ed14174 | |||
efc2363ade | |||
19a586b170 | |||
3851faff3f | |||
a583a1fcc3 | |||
0464c76884 | |||
2bc05d4d46 | |||
19970b1d53 | |||
035cd346a4 | |||
cb1d5bbf67 | |||
058d09d262 | |||
14c728c588 | |||
7823b38d24 | |||
cb76489c0f | |||
fb0352cd7e | |||
b7fcbb5829 | |||
80a04a70cb | |||
b48534b5f7 | |||
a1085909e1 | |||
dc7777be7f | |||
0ed58252d9 | |||
660b77c112 | |||
1c2fe9a481 | |||
dc2d8397ab | |||
926a06e53c | |||
7de621dc82 | |||
35bf2b930a | |||
307e4e8863 | |||
04569977d5 | |||
b11bd7b53b | |||
4f42050cf2 | |||
4b95f34430 | |||
e10933cc0e | |||
db4c3f23cd | |||
3ff443fcc8 | |||
3c302441ea | |||
250f12a69f | |||
cc15bff91b | |||
9b8828109c | |||
72e033e2ef | |||
08f96661fa | |||
7bfffe413c | |||
1425a641ae | |||
8ed88d1331 | |||
a99c08d0e7 | |||
c1191d803c | |||
844f1d3741 | |||
612753b39e | |||
cecccddc2e | |||
17073ed8cd | |||
efe14faae4 | |||
08aa68d257 | |||
2b9b19030f | |||
87b70ff7bc | |||
6776b87040 | |||
210ad7f767 | |||
3960e41534 | |||
32fc56754a | |||
082f553be4 | |||
bcb31dca0a | |||
a508fa1a0f | |||
dbf769a8cb | |||
f3825f3890 | |||
4125ada488 | |||
6a425d1880 | |||
173c89306f | |||
8c6e3bedfb | |||
db9a13d58d | |||
fa37238e00 | |||
2f10c2bb29 | |||
437686b1e6 | |||
53508009b9 | |||
49ff872b88 | |||
115688de03 | |||
346d0cd822 | |||
e51c29565e | |||
15f209844d | |||
86a50c7986 | |||
5d44198fef | |||
3ab31b8b16 | |||
4bbab19d14 | |||
d532c6117d | |||
63dc3ea208 | |||
82cb40e9bf | |||
ec0b072fca | |||
4718424d11 | |||
4300ce67e8 | |||
c58a8bb562 | |||
55859d669b | |||
e11e80832a | |||
e5f7e1e78f | |||
6893f5b487 | |||
d56ccaf436 | |||
1110bbe386 | |||
65290098e6 | |||
5fd314753b | |||
8dd98f2733 | |||
d33882848f | |||
0e174f870c | |||
6954e5c7a8 | |||
8de6a0ab07 | |||
d7cf897072 | |||
596d19d212 | |||
4e44fc204e | |||
277a535be4 | |||
1dbd6ebc4c | |||
bcf869586f | |||
6b5bc59775 | |||
17feb49b0a | |||
9625566913 | |||
b2a3308c2e | |||
89e5a195f8 | |||
e4c130f51f | |||
899c0f6796 | |||
fc1287a016 | |||
fd3ca21a0c | |||
789f8ddc3f | |||
bda40109ed | |||
8c44d3890d | |||
0e2b89dff1 | |||
7c46127738 | |||
d33b8da111 | |||
5d6c30f0bb | |||
7008c26545 | |||
b8597014dd | |||
0457bd307f | |||
eb446f8703 | |||
9df307365c | |||
fcd4fb63ed | |||
27713fb7b6 | |||
0db3ce5026 | |||
0f04c2c51a | |||
206be17615 | |||
2511c5b851 | |||
4db98b2c2b | |||
8d2d360180 | |||
7d7b509eb9 | |||
124ca1ba62 | |||
e9cd4a9f6c | |||
c627299a35 | |||
e0f183efa8 | |||
89395885ba | |||
a77309ab36 | |||
efd04f322c | |||
a12f90d432 | |||
5919da4930 | |||
2d291f1ce2 | |||
e299d2e83d | |||
76551e3d3f | |||
2f134bfbe4 | |||
c03c481b94 | |||
7d1fed76da | |||
7148172e9c | |||
5c102da3a0 | |||
f3a1c618e5 | |||
5c221f31d9 | |||
7ac42c0e4e | |||
4da3ee6aed | |||
3aecc56933 | |||
5ea124b7d2 | |||
7fb33a0d35 | |||
6d2878db5c | |||
9bee5057f1 | |||
bdce2ecad5 | |||
d3ee66ff60 | |||
647efead3b | |||
09f5d107c9 | |||
d33bc11a31 | |||
2d6066ad58 | |||
0bdd216ef0 | |||
75336e899d | |||
3870ca5335 | |||
5aaae853af | |||
a64cdca34e | |||
99f64adc05 | |||
05c24ab545 | |||
897d43ff51 | |||
46b9e3a039 | |||
018209bed5 | |||
fe14f97dfd | |||
f2b1d3cc1c | |||
0600b6e595 | |||
aab0b139d3 | |||
872c386929 | |||
23f57da28e | |||
7fbd29b75d | |||
b8fd5b5c1d | |||
c81f2cb46c | |||
6c859388c5 | |||
087b8ddc42 | |||
0eba059a8c | |||
7136ab215c | |||
3f867d8041 | |||
a8bb3652fe | |||
1672d95ae6 | |||
c3c832649c | |||
c115278016 | |||
a65653b30b | |||
d92f66720a | |||
0954398245 | |||
50da5ccc80 | |||
2b71438c82 | |||
1b5356e845 | |||
9c951dc2f1 | |||
117c35cc5a | |||
dbd46e2b4f | |||
a2c55a78fc | |||
c423b7331e | |||
a031f4da28 | |||
7573eed929 | |||
1e78638692 | |||
19ea512d79 | |||
b9155254d0 | |||
0ebe7e68c7 | |||
48340cb790 | |||
02b803fda7 | |||
4d306d5f85 | |||
388843a439 | |||
e03521821a | |||
ac77853485 | |||
ce664ebf8b | |||
93041cb1d4 | |||
d68fd5429e | |||
a7b4b9acc6 | |||
20841b7a4a | |||
e7f186b7f3 | |||
2a2adc6645 | |||
9989940693 | |||
f71f0e1470 | |||
3c7bd9acf6 | |||
1439358221 | |||
0895c3c976 | |||
8a6c6c2627 | |||
c3883bb069 | |||
4e5104ff52 | |||
ecf093119a | |||
cbd7a8dffb | |||
08cd5406f4 | |||
af2f63565f | |||
1b481dcf24 | |||
6ee5005944 | |||
b4b94ad38a | |||
aa4551fc14 | |||
9d77f57123 | |||
3115faedb2 | |||
4e7b0c115e | |||
c28c99060b | |||
d3a072c54b | |||
174a94f492 | |||
9f6806a738 | |||
809dc90bc8 | |||
c14faf5fc4 | |||
d9034fee31 | |||
c30180fcbf | |||
110a4bd484 | |||
06106c0b57 | |||
a006e3b688 | |||
d37911e5b1 | |||
b128693296 | |||
85d958a0cc | |||
ca1d5e6d8c | |||
245e50aaa9 | |||
4d0d5d245c | |||
f1d8554ab3 | |||
c76a33fa69 | |||
5a3e49c4dd | |||
2ad8d2ac8e | |||
d0c4fd7ca8 | |||
6b38f0d520 | |||
0105bb360a | |||
35d22a9638 | |||
2cce8c9a7a | |||
a525c3d920 | |||
8f090fd3e3 | |||
c36b24bad6 | |||
9b8fd8e6fc | |||
078f0952ef | |||
834331903e | |||
b112ad89f3 | |||
73f8295c9c | |||
c2da679543 | |||
ac46459a0d | |||
c663415ebc | |||
68f9360f40 | |||
e0436d1e43 | |||
a488294bb3 | |||
175345dfe4 | |||
78bb4f7366 | |||
29f485f4e2 | |||
b794aaf0e0 | |||
465d509bcc | |||
a4b25f0ae8 | |||
7feba2f15e | |||
2e72fb7f36 | |||
a225fa20b1 | |||
f87fe26811 | |||
d2b38a3f33 | |||
ef8a143eb0 | |||
e33d186ed0 | |||
0527c1f246 | |||
7ee570e375 | |||
c7182a27cf | |||
f972c9e2e6 | |||
29df785ab6 | |||
e3448c9b17 | |||
a3a8c77963 | |||
67dab8aaf7 | |||
7c2fc47933 | |||
0a3a2bedf8 | |||
38ab0630af | |||
d6be15ca4d | |||
77ec925d45 | |||
2bd6eac5af | |||
50673b6358 | |||
ee578e7b06 | |||
4025734680 | |||
2bbba38007 | |||
479b28f35f | |||
07a3340ffa | |||
69f289b0d9 | |||
308330c8a0 | |||
aac785dda8 | |||
6bd4cab290 | |||
816eb59c94 | |||
889a28063d | |||
a783a0ddb8 | |||
617122dfe1 | |||
e471ddd57a | |||
1999eb2432 | |||
a22eea0f66 | |||
62a1a85a8f | |||
00fbabded2 | |||
53a052382f | |||
66b970d22b | |||
2b5ce8cf27 | |||
408f63bdf9 | |||
1ce75177e9 | |||
0df770bc68 | |||
120de4af9e | |||
b557973e79 | |||
6b833622cf | |||
99baa56af3 | |||
95ac52b7ea | |||
e4696b2bad | |||
00226dfe4d | |||
c0ad846a3d | |||
9a0226674f | |||
5d5a4bc50f | |||
2976f10d92 | |||
9ac0fd128d | |||
4062cfa97c | |||
ff0d6e2ff2 | |||
9461bc1edb | |||
f06e936e10 | |||
f90f6059c3 | |||
f529cd3db0 | |||
a9a0a5202e | |||
67a871814e | |||
24d2f2fd9a | |||
c3a3a19751 | |||
37643b15b7 | |||
defbb68445 | |||
744c1c1b61 | |||
88e0f5c081 | |||
01eb01dc24 | |||
03dac6db84 | |||
535dd76c8e | |||
e211b1040c | |||
8533790aa3 | |||
09a271ba12 | |||
09337fa259 | |||
97a93db976 | |||
9c9b4b5404 | |||
9212075b08 | |||
0c95890d77 | |||
135190058a | |||
ff87fa0a78 | |||
ed1b2bcdd2 | |||
6b796c1715 | |||
cdfbd98612 | |||
8e471d84a1 | |||
db72661f57 | |||
5524b4b563 | |||
6488aeac56 | |||
91e6daa303 | |||
f56c3ca1bf | |||
8c5b402edf | |||
50eb754d19 | |||
ae41a90478 | |||
892a795c68 | |||
d2634bd130 | |||
6a482911b3 | |||
35f1320ef1 | |||
6a9729f898 | |||
a064857ff5 | |||
3fab712da7 | |||
d4542b8449 | |||
366fa10ce8 | |||
050a6dbbbc | |||
8bb3313f38 | |||
e7575018bc | |||
863b58913a | |||
6d581f37a9 | |||
8c152f27d8 | |||
07fdac2312 | |||
e467e6232a | |||
6e2a14af39 | |||
f23db925ad | |||
93bf69d0aa | |||
cd74c6aac2 | |||
240a8c12eb | |||
4888f0dd54 | |||
7c5aa40d26 | |||
d9fb751b7b | |||
a23983f686 | |||
561b4244d6 | |||
331f72ef67 | |||
bea47a0c5d | |||
232e6f72e7 | |||
1557694beb | |||
1824e0ddcc | |||
d6d0756a6b | |||
c08eff08db | |||
7664dcbd5b | |||
1e666ca7de | |||
56baffba77 | |||
529b39efe9 | |||
a826750254 | |||
cff2752b8f | |||
3daa866b10 | |||
e1e1a7a8e4 | |||
b76288e944 | |||
905f9aa974 | |||
57a2d2b891 | |||
7d060c495a | |||
e2c83d58da | |||
f5a73349fb | |||
f02148e159 | |||
e03a6640fb | |||
a4c37841ec | |||
b44f67011f | |||
a5765a5920 | |||
ae7c0947cf | |||
af9e46c983 | |||
eb39dfdb90 | |||
6b5ad92ea6 | |||
d60f26561a | |||
f36d0f2298 | |||
c796311c55 | |||
4ee42e9a1f | |||
33ce63db4e | |||
340d6cd83b | |||
560f6e4946 | |||
6445dd5d4c | |||
86fe700d9f | |||
d2d4031f2e | |||
e598b81600 | |||
7f9f55fbac | |||
a707f2830b | |||
0f95825828 | |||
c764008170 | |||
dc43492537 | |||
e87a4efe2f | |||
95cfab5e71 | |||
ab1ad92278 | |||
f2afa866f9 | |||
de53a1fbc9 | |||
97f078721b | |||
52f295396c | |||
ade9804ae6 | |||
7fb9cb8290 | |||
3d262db251 | |||
d093f81e46 | |||
1c2af589fd | |||
daa289bed1 | |||
40ad4cd488 | |||
d9931f519d | |||
78a5d59ef3 | |||
c4be1543ce | |||
2652d75346 | |||
72621a20c9 | |||
960f480d3a | |||
d8f7d8bfb2 | |||
4b0fdf6932 | |||
739e517c3f | |||
0af131b043 | |||
b780f79e30 | |||
6fcaf3761c | |||
9bf0f45ad0 | |||
d23ac867a4 | |||
3195fd1cb3 | |||
76fd98c346 | |||
f3bdebead6 | |||
d735db129a | |||
903c58d7ce | |||
96d51bbc4d | |||
35050ae5c9 | |||
2fa58dc2c2 | |||
f307561705 | |||
d313cd89e6 | |||
13174f7164 | |||
668c7c4896 | |||
8724648d53 | |||
707aaf0bd0 | |||
bb055ce55c | |||
5bf930102c | |||
554b0e251d | |||
9a84bb2ae1 | |||
ce6bcecff9 | |||
d6fd4e971e | |||
f399f8d691 | |||
5acebd19fe | |||
80db993ca7 | |||
9d59cd074c | |||
e86ef43058 | |||
b928c41d22 | |||
1cc96558d5 | |||
7371f835de | |||
e9010555a1 | |||
ee2f9f7471 | |||
8c1e166007 | |||
030ec907f4 | |||
e9bd3ed60e | |||
e156962b24 | |||
aa6b5489f7 | |||
1adec9c83b | |||
e21db2b740 | |||
5ff0f41988 | |||
302db49850 | |||
b11459eeba | |||
1ae332a85d | |||
653a162643 | |||
cf3d58920a | |||
1431272618 | |||
3c0fb794d9 | |||
5135d07fa4 | |||
c1aa31bdfb | |||
79b2197dc0 | |||
ec7e057b6d | |||
d6a69e237e | |||
cc48d56682 | |||
831d263449 | |||
7ae9575ed2 | |||
215a9ab160 | |||
5fac243e70 | |||
6b3c11322e | |||
422eacd53d | |||
947ad0014c | |||
b91f08fa01 | |||
2227955eee | |||
fddefc9275 | |||
352df69d51 | |||
82917aaaab | |||
75fbcbaecf | |||
45c0fff636 | |||
cc846c3abe | |||
a131fa6600 | |||
40bd121a77 | |||
e4d317c76d | |||
22a0ef14b6 | |||
f26c8116e8 | |||
0a03d90696 | |||
3d7788632e | |||
6df3e8b845 | |||
39fed9ec83 | |||
e2cc37c3e3 | |||
b17bcbaa07 | |||
8bdb64c39b | |||
ff880b25ef | |||
c341d47b27 | |||
8212edca98 | |||
bdabe0e19a | |||
37cbc5cfd9 | |||
015af37a59 | |||
38ad423353 | |||
b64614ea45 | |||
c7277b944a | |||
27e8723753 | |||
bdce1fc8a3 | |||
cf6a89a731 | |||
e929c8fb18 | |||
4e051a8111 | |||
e5385f4017 | |||
146bb6f876 | |||
3698843c0f | |||
24fb21ea2c | |||
44d09a1b37 | |||
64fe831e0c | |||
0bc9825a46 | |||
4c7f3bd029 | |||
be58442eed | |||
aafcce644e | |||
24c76dd897 | |||
38171179df | |||
808b895951 | |||
540b0990eb | |||
f5e3b128f3 | |||
0d12ee1240 | |||
d26032595e | |||
80c66d7c23 | |||
5785c5836b | |||
1c52df1713 | |||
591c3bff98 | |||
28f09ac7d4 | |||
6a059521f0 | |||
7164b7f428 | |||
471e8f4b25 | |||
84eb1ded73 | |||
db3429d0e0 | |||
ed4439ef4d | |||
63b8bf29e8 | |||
7a09d652d1 | |||
2ebb389900 | |||
2e50916897 | |||
b40e327508 | |||
b9534b2a2a | |||
c022088190 | |||
f5db9e18b5 | |||
87cc21dcf1 | |||
d0af45125e | |||
39b66e85ce | |||
b4072d4813 | |||
525cd894f5 | |||
b8dfe7f96d | |||
289c78108d | |||
fb9fb7844e | |||
10e32012f9 | |||
1218c1545c | |||
a3be21648b | |||
7694ea3e58 | |||
c6a12fc336 | |||
f50b0a9da1 | |||
e9554c7924 | |||
424e0dfb35 | |||
fec9052413 | |||
cc85ef3d0b | |||
35fbaa377c | |||
ee941ea7a1 | |||
2005a2db4d | |||
31cd0b9bd1 | |||
e8fc716b21 | |||
bb33c8dcd9 | |||
ff88ec13ba | |||
62f1850144 | |||
0e4a732c76 | |||
7d4b9d051f | |||
b3d883f7fa | |||
7c51fd6848 | |||
02e28eb59f | |||
737237c836 | |||
3d343e7949 | |||
278f612aa4 | |||
b858cccb59 | |||
6d535e6699 | |||
af193ec09b | |||
e5d84be0db | |||
e330c4bf22 | |||
b5191bc663 | |||
9dc72d2c70 | |||
19758eab8e | |||
fb2726b0b0 | |||
11438e3649 | |||
b03534c339 | |||
f6ac9b910a | |||
ed765834fe | |||
459075ca7f | |||
47da9495fe | |||
b51733ff2f | |||
df463fd6d0 | |||
d50d9a5318 | |||
3b7b748e71 | |||
d77b61e939 | |||
fbba5d461b | |||
0fc434b6d7 | |||
57be687166 | |||
9880a54337 | |||
08629cb759 | |||
4fe2c5c08d | |||
f9ee3ae653 | |||
ab26312aa0 | |||
a1687cb50d | |||
76d6008daa | |||
05e93e53dd | |||
1509a65bee | |||
ab6a498fb7 | |||
bf50d5049d |
69
.mtn-ignore
Normal file
@ -0,0 +1,69 @@
|
||||
# Just to try and prevent some noob disasters.
|
||||
# Use mtn add --no-respect-ignore foo.jar to ignore this ignore list
|
||||
|
||||
# Temporary/build files
|
||||
i2pbote.jar
|
||||
i2pbote.xpi2p
|
||||
i2pbote-update.xpi2p
|
||||
~$
|
||||
|
||||
# Temporary/build dirs
|
||||
^ant_build
|
||||
^plugin/plugin.tmp
|
||||
|
||||
# Build property overrides
|
||||
override.properties
|
||||
|
||||
# Downloaded JavaMail libraries
|
||||
mailapi-.*.jar
|
||||
|
||||
# Downloaded BouncyCastle Provider libraries
|
||||
bcprov-jdk15on-.*.jar
|
||||
|
||||
# Downloaded IMAP libraries
|
||||
apache-james-imap-api-0.4-.*.jar
|
||||
apache-james-imap-message-0.4-.*.jar
|
||||
apache-james-imap-processor-0.4-.*.jar
|
||||
apache-james-mailbox-api-0.6-.*.jar
|
||||
apache-james-mailbox-store-0.6-.*.jar
|
||||
apache-mime4j-core-.*.jar
|
||||
apache-mime4j-dom-.*.jar
|
||||
commons-codec-.*.jar
|
||||
commons-collections-.*.jar
|
||||
commons-configuration-.*.jar
|
||||
commons-io-.*.jar
|
||||
commons-lang-.*.jar
|
||||
james-server-filesystem-api-3.0.0-beta5-SNAPSHOT.jar
|
||||
james-server-lifecycle-api-3.0.0-beta5-SNAPSHOT.jar
|
||||
james-server-protocols-imap4-3.0.0-beta5-SNAPSHOT.jar
|
||||
james-server-protocols-library-3.0.0-beta5-SNAPSHOT.jar
|
||||
james-server-util-3.0.0-beta5-SNAPSHOT.jar
|
||||
jutf7-1.0.0.jar
|
||||
log4j-1.2.17.jar
|
||||
netty-3.3.1.Final.jar
|
||||
protocols-api-1.6.4-.*.jar
|
||||
protocols-imap-1.6.4-.*.jar
|
||||
protocols-netty-1.6.4-.*.jar
|
||||
slf4j-api-.*.jar
|
||||
slf4j-log4j12-.*.jar
|
||||
|
||||
# Downloaded SMTP libraries
|
||||
subethasmtp-3.1.7.jar
|
||||
|
||||
# Downloaded JUnit libraries
|
||||
junit-4.8.1.jar
|
||||
|
||||
# Downloaded Mockito libraries
|
||||
mockito-core-2.0.31-beta.jar
|
||||
|
||||
# Downloaded jMock libraries
|
||||
cglib-nodep-2.2.3.jar
|
||||
hamcrest-core-1.3.jar
|
||||
hamcrest-library-1.3.jar
|
||||
hamcrest-unit-test-1.3.jar
|
||||
jmock-2.6.0.jar
|
||||
jmock-junit3-2.6.0.jar
|
||||
jmock-junit4-2.6.0.jar
|
||||
jmock-legacy-2.6.0.jar
|
||||
jmock-script-2.6.0.jar
|
||||
objenesis-1.0.jar
|
21
.tx/config
Normal file
@ -0,0 +1,21 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
lang_map = ru_RU: ru, sv_SE: sv, tr_TR: tr, uk_UA: uk, zh_CN: zh
|
||||
|
||||
[I2P.plugin_i2pbote]
|
||||
file_filter = locale/messages_<lang>.po
|
||||
source_file = locale/messages_en.po
|
||||
source_lang = en
|
||||
minimum_perc = 40
|
||||
|
||||
[I2P.plugin_i2pbote_userguide]
|
||||
file_filter = src/main/webapp/html/userGuide_<lang>.html
|
||||
source_file = src/main/webapp/html/userGuide.html
|
||||
source_lang = en
|
||||
minimum_perc = 50
|
||||
|
||||
[I2P.plugin_i2pbote_faq]
|
||||
file_filter = src/main/webapp/html/FAQ_<lang>.html
|
||||
source_file = src/main/webapp/html/FAQ.html
|
||||
source_lang = en
|
||||
minimum_perc = 50
|
28
README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# I2P-Bote
|
||||
|
||||
I2P-Bote is a plugin for I2P that allows users to send and receive emails while preserving privacy. It does not need a mail server because emails are stored in a distributed hash table. They are automatically encrypted and digitally signed, which ensures no one but the intended recipient can read the email, and third parties cannot forge them.
|
||||
|
||||
**Features:**
|
||||
|
||||
* Themeable webmail interface
|
||||
* User interface translated in many languages
|
||||
* One-click creation of email accounts (called email identities)
|
||||
* Emails can be sent under a sender identity, or anonymously
|
||||
* ElGamal, Elliptic Curve, and NTRU Encryption
|
||||
* Encryption and signing is transparent, without the need to know about PGP
|
||||
* Delivery confirmation
|
||||
* Basic support for short recipient names
|
||||
* IMAP / SMTP
|
||||
|
||||
**Planned Features:**
|
||||
|
||||
* Custom folders
|
||||
* Sending and receiving via relays, similar to Mixmaster
|
||||
* Lots of small improvements
|
||||
|
||||
**More information**
|
||||
|
||||
The links below only work within I2P, i.e., make sure you are running I2P and your browser is using the proxy at localhost:4444.
|
||||
|
||||
* http://i2pbote.i2p I2P-Bote homepage
|
||||
* http://forum.i2p/viewforum.php?f=35 I2P-Bote forum
|
28
TODO
Normal file
@ -0,0 +1,28 @@
|
||||
User guide
|
||||
- Howto for IMAP/SMTP
|
||||
Email composition
|
||||
- "Include sent time" as an option, pre-filled with settings default
|
||||
Identities
|
||||
- Generate dests in background rather than needing a dedicated tab open
|
||||
IMAP/SMTP
|
||||
- "Force SSL" option
|
||||
- When disabled, users can connect with "insecure" or "STARTTLS"
|
||||
- When enabled, users can connect with "SSL"
|
||||
- Separate password cache (IMAP auth should not unlock webui)
|
||||
|
||||
Not-yet-implemented
|
||||
- Drafts folder
|
||||
- User-defined folders
|
||||
- Automatically disable timestamps when using mail routes
|
||||
- Random offset for fetch interval (#1360)
|
||||
- Per-identity settings (#1359)
|
||||
- Fetch interval
|
||||
- Mail route per-hop settings
|
||||
- Delay (#1361)
|
||||
- Fixed forward time
|
||||
- Traffic correlation countermeasures at relays
|
||||
- Mixing anonymity (minimum threshold on relay packets to send)
|
||||
- Constant traffic (test and dummy messages)
|
||||
- Balanced incoming/outgoing ratio
|
||||
- Packet padding
|
||||
- Retrieving email via relays
|
@ -1,2 +0,0 @@
|
||||
<%@ attribute name="index" required="true" description="0-based index of the email identity" %>
|
||||
test
|
@ -1,17 +0,0 @@
|
||||
<%@ attribute name="text" required="true" description="The text to display" %>
|
||||
<%@ attribute name="width" required="false" description="The maximum number of characters in each line" %>
|
||||
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
|
||||
|
||||
<c:if test="${empty width}">
|
||||
<c:set var="width" value="64"/>
|
||||
</c:if>
|
||||
|
||||
<c:set var="length" value="${fn:length(text)}"/>
|
||||
<c:forEach var="i" begin="0" end="${length}" step="${width}">
|
||||
<c:if test="${i > 0}">
|
||||
<br/>
|
||||
</c:if>
|
||||
${fn:substring(text, i, i+64)}
|
||||
</c:forEach>
|
@ -1,127 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
|
||||
|
||||
<tlib-version>1.0</tlib-version>
|
||||
<short-name>i2pbote</short-name>
|
||||
<uri>I2pBoteTags</uri>
|
||||
|
||||
<tag>
|
||||
<name>sendEmail</name>
|
||||
<tag-class>i2p.bote.web.SendEmailTag</tag-class>
|
||||
<body-content>empty</body-content>
|
||||
<attribute>
|
||||
<name>sender</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>true</required>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>recipient</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>true</required>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>subject</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>true</required>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>message</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>true</required>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>checkForMail</name>
|
||||
<tag-class>i2p.bote.web.CheckMailTag</tag-class>
|
||||
<body-content>empty</body-content>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>numDhtPeers</name>
|
||||
<tag-class>i2p.bote.web.PrintNumDhtPeersTag</tag-class>
|
||||
<body-content>empty</body-content>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>numRelayPeers</name>
|
||||
<tag-class>i2p.bote.web.PrintNumRelayPeersTag</tag-class>
|
||||
<body-content>empty</body-content>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>printVersion</name>
|
||||
<tag-class>i2p.bote.web.PrintAppVersionTag</tag-class>
|
||||
<body-content>empty</body-content>
|
||||
</tag>
|
||||
|
||||
<function>
|
||||
<name>getIdentities</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
i2p.bote.email.Identities getIdentities()
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>getLocalDestination</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
java.lang.String getLocalDestination()
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>saveIdentity</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
boolean saveIdentity(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>deleteIdentity</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
boolean deleteIdentity(java.lang.String)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>isCheckingForMail</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
boolean isCheckingForMail()
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>newMailReceived</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
boolean newMailReceived()
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>getMailFolder</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
i2p.bote.EmailFolder getMailFolder(java.lang.String)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>getEmail</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
i2p.bote.email.Email getEmail(java.lang.String, java.lang.String)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<tag-file>
|
||||
<name>printWithLineBreaks</name>
|
||||
<path>/WEB-INF/tags/printWithLineBreaks.tag</path>
|
||||
</tag-file>
|
||||
|
||||
</taglib>
|
@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
|
||||
<display-name>i2pbote</display-name>
|
||||
<!-- precompiled servlets -->
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.html</welcome-file>
|
||||
<welcome-file>index.htm</welcome-file>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
<welcome-file>default.html</welcome-file>
|
||||
<welcome-file>default.htm</welcome-file>
|
||||
<welcome-file>default.jsp</welcome-file>
|
||||
</welcome-file-list>
|
||||
|
||||
<listener>
|
||||
<listener-class>i2p.bote.web.ServiceInitializer</listener-class>
|
||||
</listener>
|
||||
</web-app>
|
@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
|
||||
<display-name>i2pbote</display-name>
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.html</welcome-file>
|
||||
<welcome-file>index.htm</welcome-file>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
<welcome-file>default.html</welcome-file>
|
||||
<welcome-file>default.htm</welcome-file>
|
||||
<welcome-file>default.jsp</welcome-file>
|
||||
</welcome-file-list>
|
||||
|
||||
<listener>
|
||||
<listener-class>i2p.bote.web.ServiceInitializer</listener-class>
|
||||
</listener>
|
||||
</web-app>
|
@ -1,75 +0,0 @@
|
||||
<%--
|
||||
Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
|
||||
The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
|
||||
This file is part of I2P-Bote.
|
||||
I2P-Bote is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
I2P-Bote is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
--%>
|
||||
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
|
||||
|
||||
<c:if test="${ib:isCheckingForMail()}">
|
||||
<c:set var="checkingForMail" value="true"/>
|
||||
</c:if>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<link rel="stylesheet" href="i2pbote.css" />
|
||||
<c:if test="${checkingForMail}">
|
||||
<meta http-equiv="refresh" content="20" />
|
||||
</c:if>
|
||||
</head>
|
||||
|
||||
<body style="background-color: transparent; margin: 0px;">
|
||||
|
||||
<table><tr>
|
||||
<td>
|
||||
<c:if test="${checkingForMail}">
|
||||
<div class="checkmail">
|
||||
<img src="images/wait.gif"/>Checking for mail...
|
||||
</div>
|
||||
</c:if>
|
||||
<c:if test="${!checkingForMail}">
|
||||
<div class="checkmail">
|
||||
<form action="checkMail.jsp" target="_top" method="GET">
|
||||
<input type="hidden" name="path" value="Inbox"/>
|
||||
<button type="submit" value="Check Mail">Check Mail</button>
|
||||
</form>
|
||||
</div>
|
||||
<c:if test="${ib:newMailReceived()}">
|
||||
<script language="Javascript">
|
||||
// If inbox is being displayed, reload so the new email(s) show
|
||||
if (document.getElementById("inboxFlag"))
|
||||
window.location.reload();
|
||||
</script>
|
||||
</c:if>
|
||||
</c:if>
|
||||
</td>
|
||||
<td>
|
||||
<form action="newEmail.jsp" target="_top" method="GET">
|
||||
<button type="submit" value="New">New</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr></table>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,99 +0,0 @@
|
||||
<%--
|
||||
Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
|
||||
The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
|
||||
This file is part of I2P-Bote.
|
||||
I2P-Bote is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
I2P-Bote is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
--%>
|
||||
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
|
||||
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="${param.new}">
|
||||
<c:set var="title" value="New Email Identity"/>
|
||||
<c:set var="commitAction" value="Create"/>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<c:set var="title" value="Edit Email Identity"/>
|
||||
<c:set var="commitAction" value="Save"/>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
<jsp:include page="header.jsp">
|
||||
<jsp:param name="title" value="${title}"/>
|
||||
</jsp:include>
|
||||
|
||||
<div class="errorMessage">
|
||||
${param.errorMessage}
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<form name="form" action="saveIdentity.jsp">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="font-weight: bold;">Public Name:</div>
|
||||
<div style="font-size: 0.8em;">(required field, shown to recipients)</div>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" size="25" name="publicName" value="${param.publicName}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="font-weight: bold;">Description:</div>
|
||||
<div style="font-size: 0.8em;">(optional, kept private)</div>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" size="25" name="description" value="${param.description}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="font-weight: bold;">Email Address:</div>
|
||||
<div style="font-size: 0.8em;">(optional)</div>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" size="50" name="emailAddress" value="${param.emailAddress}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<c:if test="${!empty param.key}">
|
||||
<tr>
|
||||
<td style="font-weight: bold; vertical-align: top;">
|
||||
Email Destination:
|
||||
</td>
|
||||
<td>
|
||||
<textarea cols="64" rows="8" readonly="yes" wrap="soft" style="background-color: transparent; border: none;">${param.key}</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</c:if>
|
||||
</table>
|
||||
<input type="submit" name="action" value="${commitAction}"/>
|
||||
<input type="submit" name="action" value="Cancel"/>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript" language="JavaScript">
|
||||
document.forms['form'].elements['name'].focus();
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<jsp:include page="footer.jsp"/>
|
@ -1,81 +0,0 @@
|
||||
<%--
|
||||
Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
|
||||
The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
|
||||
This file is part of I2P-Bote.
|
||||
I2P-Bote is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
I2P-Bote is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
--%>
|
||||
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
|
||||
|
||||
<jsp:include page="header.jsp">
|
||||
<jsp:param name="title" value="${param.path}"/>
|
||||
</jsp:include>
|
||||
|
||||
<c:if test="${folderName == 'Inbox'}">
|
||||
<div id="inboxFlag"></div>
|
||||
</c:if>
|
||||
|
||||
[DEBUG MSG: This is the <b>${param.path}</b> folder.]
|
||||
|
||||
<div class="main">
|
||||
<div class="folder">
|
||||
<table>
|
||||
<tr>
|
||||
<th style="width: 100px;">Sender</th>
|
||||
<th style="width: 150px;">Subject</th>
|
||||
<th style="width: 100px;">Date</th>
|
||||
<th style="width: 20px;"></th>
|
||||
</tr>
|
||||
<c:set var="folderName" value="Inbox"/>
|
||||
<c:forEach items="${ib:getMailFolder(folderName).elements}" var="email">
|
||||
<tr>
|
||||
<c:set var="sender" value="${email.sender}"/>
|
||||
<c:if test="${empty sender}">
|
||||
<c:set var="sender" value="Anonymous"/>
|
||||
</c:if>
|
||||
|
||||
<c:set var="date" value="${email.date}"/>
|
||||
<c:if test="${empty date}">
|
||||
<c:set var="date" value="${email.dateString}"/>
|
||||
</c:if>
|
||||
<c:if test="${empty date}">
|
||||
<c:set var="date" value="Unknown"/>
|
||||
</c:if>
|
||||
|
||||
<c:set var="subject" value="${email.subject}"/>
|
||||
<c:if test="${empty subject}">
|
||||
<c:set var="subject" value="(No subject)"/>
|
||||
</c:if>
|
||||
|
||||
<c:set var="mailUrl" value="showEmail.jsp?folder=${folderName}&messageID=${email.messageID}"/>
|
||||
|
||||
<td><div class="ellipsis"><a href="${mailUrl}">${sender}</a></div></td>
|
||||
<td><div class="ellipsis"><a href="${mailUrl}">${subject}</a></div></td>
|
||||
<td><a href="${mailUrl}">${date}</a></td>
|
||||
<td><a href="deleteEmail.jsp?folder=${folderName}&messageID=${email.messageID}"><img src="images/delete.png" alt="Delete" title="Delete this email"/></a></td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<jsp:include page="footer.jsp"/>
|
@ -1,85 +0,0 @@
|
||||
<%--
|
||||
Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
|
||||
The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
|
||||
This file is part of I2P-Bote.
|
||||
I2P-Bote is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
I2P-Bote is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
--%>
|
||||
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<link rel="stylesheet" href="i2pbote.css" />
|
||||
<link rel="icon" type="image/png" href="images/favicon.png" />
|
||||
<title>${param.title} - I2P-Bote</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="titlebar" style="cursor:pointer" onclick="document.location='.'">
|
||||
<div class="title">I2P-Bote</div>
|
||||
<br/>
|
||||
<div class="subtitle">Secure Distributed Email</div>
|
||||
</div>
|
||||
|
||||
<div class="menubox">
|
||||
<iframe src="buttonFrame.jsp" width="100%" height="40px" scrolling="no" frameborder="0" allowtransparency="true"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="menubox">
|
||||
<h2>Folders</h2>
|
||||
<a href="folder.jsp?path=Inbox"><img src="images/folder.png"/>Inbox</a><br/>
|
||||
<a href="folder.jsp?path=Outbox"><img src="images/folder.png"/>Outbox</a><br/>
|
||||
<a href="folder.jsp?path=Sent"><img src="images/folder.png"/>Sent</a><br/>
|
||||
<a href="folder.jsp?path=Drafts"><img src="images/folder.png"/>Drafts</a><br/>
|
||||
<a href="folder.jsp?path=Trash"><img src="images/folder.png"/>Trash</a><br/>
|
||||
<hr/>
|
||||
<a href="folder.jsp?path=User_created_Folder_1"><img src="images/folder.png"/>User_created_Folder_1</a><br/>
|
||||
<a href="folder.jsp?path=User_created_Folder_2"><img src="images/folder.png"/>User_created_Folder_2</a><br/>
|
||||
<a href="folder.jsp?path=User_created_Folder_3"><img src="images/folder.png"/>User_created_Folder_3</a><br/>
|
||||
<hr/>
|
||||
<a href=.>Manage Folders</a>
|
||||
</div>
|
||||
|
||||
<div class="menubox">
|
||||
<h2>Addresses</h2>
|
||||
<a href="identities.jsp">Identities</a><br/>
|
||||
<a href=.>Private Address Book</a><br/>
|
||||
<a href=.>Public Address Directory</a><br/>
|
||||
</div>
|
||||
|
||||
<div class="menubox">
|
||||
<h2>Configuration</h2>
|
||||
<a href=.>Settings</a><br/>
|
||||
</div>
|
||||
|
||||
<div class="menubox">
|
||||
<h2>Network Status</h2>
|
||||
<iframe src="statusFrame.jsp" width="100%" height="40px" scrolling="no" frameborder="0" allowtransparency="true"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="menubox">
|
||||
<h2>Help</h2>
|
||||
<a href=.>User Guide</a><br/>
|
||||
<a href="about.jsp">About</a><br/>
|
||||
</div>
|
@ -1,336 +0,0 @@
|
||||
/* Based on the router console css */
|
||||
|
||||
body {
|
||||
margin: 15px 0 0 10px;
|
||||
padding: 0em;
|
||||
text-align: center;
|
||||
background: #eef;
|
||||
color: #000;
|
||||
font: 9pt/130% "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.titlebar {
|
||||
width: 800px;
|
||||
margin: 0px 20px 20px 240px;
|
||||
background-color: #fff;
|
||||
color: #310;
|
||||
padding: 0 15px 15px 25px;
|
||||
-moz-border-radius: 4px;
|
||||
-khtml-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #001;
|
||||
text-align: center;
|
||||
-moz-box-shadow: inset 0px 0px 1px 0px #002;
|
||||
white-space: normal;
|
||||
background: #ddf url('images/lightbluetile.png');
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
.title {
|
||||
font: normal bold 20pt/120% 'Lucida Sans Unicode', 'Bitstream Vera Sans', Verdana, Tahoma, Helvetica, sans-serif;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.15em;
|
||||
text-shadow: 0px 0px 1px rgba(0, 0, 148, 0.9);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 8pt;
|
||||
color: #cf0606;
|
||||
letter-spacing: 5pt;
|
||||
text-shadow: 0px 0px 1px rgba(148, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
.checkmail {
|
||||
font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.menubox {
|
||||
float: left;
|
||||
width: 175px;
|
||||
margin: 0px 0px 10px 10px;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
border: 1px solid #000033;
|
||||
background: #ddf url('images/lightbluetile.png');
|
||||
color: #000;
|
||||
font-size: 8pt;
|
||||
clear: left;
|
||||
-moz-border-radius: 4px;
|
||||
-khtml-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
-moz-box-shadow: inset 0px 0px 1px 0px #002;
|
||||
}
|
||||
|
||||
.menubox a:link, .menubox a:visited {
|
||||
text-shadow: 0px 0px 1px rgba(0, 0, 32, 0.5);
|
||||
}
|
||||
|
||||
.menubox a:hover {
|
||||
text-shadow: 0px 0px 0.5px rgba(255, 255, 255, 0.7);
|
||||
color: #f60;
|
||||
}
|
||||
|
||||
.menubox h2 {
|
||||
font-size: 12pt;
|
||||
color: #001;
|
||||
letter-spacing: 0.05em;
|
||||
text-shadow: 0px 0px 1px rgba(0, 0, 64, 0.5);
|
||||
wordwrap: none;
|
||||
}
|
||||
|
||||
.statusbox table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.statusbox td {
|
||||
text-align: left;
|
||||
color: #000;
|
||||
font-size: 8pt;
|
||||
vertical-align: top;
|
||||
padding: 0px 30px 0px 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.main {
|
||||
width: 800px;
|
||||
margin: 0px 20px 20px 240px;
|
||||
padding: 0 15px 15px 25px;
|
||||
background: #eef;
|
||||
font: 9pt/130% "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
|
||||
text-align: left;
|
||||
color: #001;
|
||||
border: 1px solid #000033;
|
||||
background: #ddf url('images/lightbluetile.png');
|
||||
-moz-border-radius: 4px;
|
||||
-khtml-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
-moz-box-shadow: inset 0px 0px 1px 0px #002;
|
||||
}
|
||||
|
||||
.infoMessage {
|
||||
margin: 0px 20px 5px 240px;
|
||||
text-align: left;
|
||||
color: green;
|
||||
}
|
||||
|
||||
.errorMessage {
|
||||
margin: 0px 20px 5px 240px;
|
||||
text-align: left;
|
||||
color: red;
|
||||
}
|
||||
|
||||
img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #900;
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: #007;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #606;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #f60;
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #f93;
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button, button:visited {
|
||||
font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
|
||||
border: 1px outset #999;
|
||||
padding: 1px 3px;
|
||||
background: #ddf !important;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
-khtml-border-radius: 4px;
|
||||
margin: 0 1px;
|
||||
text-align: center;
|
||||
min-width: 80px;
|
||||
-moz-box-shadow: inset 0px 2px 8px 0px #fff;
|
||||
color: #006;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border: 1px solid #f60;
|
||||
background: #f60 !important;
|
||||
color: #fff;
|
||||
-moz-box-shadow: inset 0px 0px 0px 1px #fff;
|
||||
}
|
||||
|
||||
button:active {
|
||||
border: 1px solid #f60;
|
||||
background: #001 !important;
|
||||
color: #f60;
|
||||
-moz-box-shadow: inset 0px 0px 0px 1px #f60;
|
||||
}
|
||||
|
||||
.underline {
|
||||
border-bottom: 1px solid #000022;
|
||||
padding: 5px 0px 5px 0px;
|
||||
margin: 0px 0px 10px 0px;
|
||||
}
|
||||
|
||||
input {
|
||||
background: #eef;
|
||||
color: #001;
|
||||
margin: 5px 10px 5px 10px;
|
||||
padding: 4px 2px;
|
||||
font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
|
||||
border: 1px solid #001;
|
||||
text-decoration: none;
|
||||
min-width: 110px;
|
||||
}
|
||||
|
||||
input, input:visited {
|
||||
border: 1px outset #999;
|
||||
background: #ddf;
|
||||
color: #001;
|
||||
margin: 5px;
|
||||
font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
|
||||
padding: 1px 2px;
|
||||
text-decoration: none;
|
||||
min-width: 110px;
|
||||
border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
-khtml-border-radius: 4px;
|
||||
-moz-box-shadow: inset 0px 2px 8px 0px #fff;
|
||||
color: #006;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
input:hover {
|
||||
background: #f60;
|
||||
color: #fff;
|
||||
border: 1px solid #f60;
|
||||
opacity: 1.0;
|
||||
-moz-box-shadow: inset 0px 0px 0px 1px #fff;
|
||||
}
|
||||
|
||||
input:active {
|
||||
background: #002;
|
||||
color: #f60;
|
||||
border: 1px solid #f60;
|
||||
opacity: 1.0;
|
||||
-moz-box-shadow: inset 0px 0px 0px 1px #f60;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
width: 100%;
|
||||
background: #eef;
|
||||
color: #001;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
|
||||
border: 1px solid #001;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
input checkbox {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
background: #eef;
|
||||
color: #003;
|
||||
border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
-khtml-border-radius: 4px;
|
||||
font: 8pt "Lucida Console", "DejaVu Sans Mono", Courier, mono;
|
||||
min-height: 100px;
|
||||
border: 1px solid #001;
|
||||
}
|
||||
|
||||
submit {
|
||||
background: #f00;
|
||||
color: #eef;
|
||||
margin: 10px 2px 10px 0;
|
||||
padding: 2px;
|
||||
font-family: "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
|
||||
font-weight: bold;
|
||||
border: 1px solid #001;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
select {
|
||||
background: #eef;
|
||||
color: #003;
|
||||
padding: 2px;
|
||||
margin: 5px;
|
||||
border: 1px solid #001;
|
||||
font: 9pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
|
||||
border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
-khtml-border-radius: 4px;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.folder table {
|
||||
table-layout: fixed;
|
||||
width: 800px;
|
||||
}
|
||||
|
||||
.identities table {
|
||||
table-layout: fixed;
|
||||
width: 800px;
|
||||
}
|
||||
|
||||
.identities td, .identities th {
|
||||
vertical-align: top;
|
||||
padding: 0px 30px 0px 0px;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.ellipsis
|
||||
{
|
||||
line-height: 1.2em;
|
||||
height: 1.2em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
-o-text-overflow: ellipsis;
|
||||
-moz-binding: url(moz_fix2.xml#ellipsis);
|
||||
}
|
||||
|
||||
.moz-ellipsis > DIV:first-child
|
||||
{
|
||||
float: left;
|
||||
margin-right: -26px;
|
||||
}
|
||||
|
||||
.moz-ellipsis > DIV + DIV
|
||||
{
|
||||
float: right;
|
||||
margin-top: -1.2em;
|
||||
background: url('images/lightbluefade.png');
|
||||
padding-left: 26px;
|
||||
}
|
||||
|
||||
.moz-ellipsis > DIV + DIV::after
|
||||
{
|
||||
background: url('images/lightbluetile.png');
|
||||
content: '...';
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
<%--
|
||||
Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
|
||||
The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
|
||||
This file is part of I2P-Bote.
|
||||
I2P-Bote is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
I2P-Bote is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
--%>
|
||||
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
|
||||
|
||||
<jsp:include page="header.jsp">
|
||||
<jsp:param name="title" value="Identities"/>
|
||||
</jsp:include>
|
||||
|
||||
<div class="infoMessage">
|
||||
${param.infoMessage}
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<h2>
|
||||
Email Identities
|
||||
</h2>
|
||||
|
||||
<c:if test="${empty ib:getIdentities().all}">
|
||||
No email identities are defined.
|
||||
</c:if>
|
||||
|
||||
<div class="identities">
|
||||
<table>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<th>Public Name</th>
|
||||
<th>Email Address</th>
|
||||
<th>Key</th>
|
||||
<th style="width: 20px; padding: 0px"></th>
|
||||
</tr>
|
||||
<c:forEach items="${ib:getIdentities().all}" var="identity">
|
||||
<tr>
|
||||
<td style="width: 100px;">
|
||||
<div class="ellipsis">
|
||||
<a href="editIdentity.jsp?new=false&key=${identity.key}&publicName=${identity.publicName}&description=${identity.description}&emailAddress=${identity.emailAddress}">
|
||||
${identity.publicName}
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 150px;">
|
||||
<div class="ellipsis">
|
||||
${identity.description}
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 150px;">
|
||||
<div class="ellipsis">
|
||||
${identity.emailAddress}
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 100px;">
|
||||
<div class="ellipsis">
|
||||
${identity.key}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="deleteIdentity.jsp?key=${identity.key}"><img src="images/delete.png" alt="Delete" title="Delete this identity"/></a>
|
||||
</td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<p/>
|
||||
<form action="editIdentity.jsp?new=true" method="POST">
|
||||
<button type="submit" value="New">New Identity</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<jsp:include page="footer.jsp"/>
|
Before Width: | Height: | Size: 298 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 852 B |
Before Width: | Height: | Size: 780 B |
Before Width: | Height: | Size: 234 B |
Before Width: | Height: | Size: 564 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 794 B |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 227 B |
Before Width: | Height: | Size: 803 B |
Before Width: | Height: | Size: 1.3 KiB |
@ -1,22 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<bindings xmlns="http://www.mozilla.org/xbl" xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<binding id="ellipsis" applyauthorstyles="false">
|
||||
<implementation>
|
||||
<constructor><![CDATA[
|
||||
(function(block){
|
||||
setTimeout(function(){
|
||||
block.style.mozBinding = '';
|
||||
var t = document.createElement('DIV');
|
||||
block.appendChild(t);
|
||||
block.appendChild(document.createElement('DIV'));
|
||||
while (block.firstChild != t)
|
||||
t.appendChild(block.firstChild);
|
||||
block.className = block.className + ' moz-ellipsis';
|
||||
}, 0);
|
||||
})(this);
|
||||
]]></constructor>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
</bindings>
|
@ -1,84 +0,0 @@
|
||||
<%--
|
||||
Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
|
||||
The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
|
||||
This file is part of I2P-Bote.
|
||||
I2P-Bote is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
I2P-Bote is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
--%>
|
||||
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
|
||||
|
||||
<jsp:include page="header.jsp">
|
||||
<jsp:param name="title" value="New Email"/>
|
||||
</jsp:include>
|
||||
|
||||
<div class="main">
|
||||
<form action="sendEmail.jsp">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
From:
|
||||
</td>
|
||||
<td>
|
||||
<select name="sender">
|
||||
<option value="anonymous">Anonymous</option>
|
||||
<c:forEach items="${ib:getIdentities().all}" var="identity">
|
||||
<option value="${identity.key}">
|
||||
${identity.publicName}
|
||||
<c:if test="${!empty identity.description}"> - ${identity.description}</c:if>
|
||||
</option>
|
||||
</c:forEach>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<select name="recipientType0">
|
||||
<option value="to">To:</option>
|
||||
<option value="cc">CC:</option>
|
||||
<option value="bcc">BCC:</option>
|
||||
<option value="reply_to">Reply To:</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" size="80" name="recipient0"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top"><br/>Subject:</td>
|
||||
<td><input type="text" size="80" name="subject"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top"><br/>Message:</td>
|
||||
<td><textarea rows="30" cols="80" name="message"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2 align="center">
|
||||
<input type="submit" value="Send"/>
|
||||
<input type="submit" value="Cancel"/>
|
||||
<input type="submit" value="Save"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<jsp:include page="footer.jsp"/>
|
@ -1,64 +0,0 @@
|
||||
<%--
|
||||
Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
|
||||
The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
|
||||
This file is part of I2P-Bote.
|
||||
I2P-Bote is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
I2P-Bote is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
--%>
|
||||
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
|
||||
<jsp:include page="header.jsp">
|
||||
<jsp:param name="title" value="No Identity"/>
|
||||
</jsp:include>
|
||||
|
||||
<div class="main">
|
||||
<h2>No Email Identity Defined</h2>
|
||||
<p>
|
||||
In order to receive email from other people, you need to create an email identity
|
||||
first.
|
||||
</p><p>
|
||||
This is similar to traditional email where you first have to set up an email account
|
||||
with an email provider. The difference is that in I2P-Bote, there is no provider that
|
||||
can read all your email because I2P-Bote stores all emails encrypted on the network.
|
||||
</p><p>
|
||||
I2P-Bote automatically decrypts emails sent to you, using the email identity you
|
||||
created.
|
||||
</p><p>
|
||||
An email identity has a secret key, and a public key. Anybody who has the secret key
|
||||
can download and decrypt your emails, so you should never give it to anybody. The
|
||||
public key, on the other hand, allows people to send email to you. Think of it as
|
||||
the equivalent of a traditional email address. Give it to everybody you want to be
|
||||
able to contact you, or make it public.
|
||||
</p><p>
|
||||
[TODO paragraph about spam, HashCash].
|
||||
</p><p>
|
||||
You will find that email identities are long combinations of numbers and letters,
|
||||
and that they are impossible to memorize. I2P-Bote lets you assign an email address
|
||||
to an email identity to make it easier for you and your contacts to remember.
|
||||
</p><p>
|
||||
[TODO name attribute, address attribute].
|
||||
The public addressbook should not be 100% trusted. Only destination keys are secure.
|
||||
</p><p>
|
||||
[TODO private address book, address directory].
|
||||
<form action="editIdentity.jsp?new=true" method="POST">
|
||||
<button type="submit" value="New">Create a New Email Identity</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<jsp:include page="footer.jsp"/>
|
@ -1,60 +0,0 @@
|
||||
<%--
|
||||
Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
|
||||
The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
|
||||
This file is part of I2P-Bote.
|
||||
I2P-Bote is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
I2P-Bote is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
--%>
|
||||
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
|
||||
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
|
||||
|
||||
<c:set var="email" value="${ib:getEmail(param.folder, param.messageID)}"/>
|
||||
|
||||
<jsp:include page="header.jsp">
|
||||
<jsp:param name="title" value="${email.subject}"/>
|
||||
</jsp:include>
|
||||
|
||||
<div class="main">
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top"><strong>From:</strong></td>
|
||||
<td>
|
||||
<c:forEach var="i" begin="0" end="${fn:length(email.sender)}" step="64">
|
||||
<c:if test="${i > 0}">
|
||||
<br/>
|
||||
</c:if>
|
||||
${fn:substring(email.sender, i, i+64)}
|
||||
</c:forEach>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top"><strong>Subject:</strong></td>
|
||||
<td>${email.subject}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top"><strong>Message:</strong></td>
|
||||
<td>${email.bodyText}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<jsp:include page="footer.jsp"/>
|
927
build.xml
@ -1,35 +1,203 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="all" name="I2P-Bote">
|
||||
<property name="i2plib" value="/opt/i2p/lib"/>
|
||||
<property name="jstllib" value="WebContent/WEB-INF/lib"/>
|
||||
<property name="jettylib" value="${i2plib}"/>
|
||||
<property name="antlib" value="/opt/ant/lib"/>
|
||||
<project basedir="." default="help" name="I2P-Bote" xmlns:if="ant:if" xmlns:unless="ant:unless">
|
||||
<property environment="env"/>
|
||||
<condition property="i2pbase" value="${env.I2P}">
|
||||
<isset property="env.I2P"/>
|
||||
</condition>
|
||||
|
||||
<property name="i2plib" value="${i2pbase}/lib"/>
|
||||
<property name="jstllib" value="${i2pbase}/apps/susidns/src/WEB-INF/lib"/>
|
||||
<property name="lib" value="lib"/>
|
||||
<property name="makeplugin" value="../i2p.scripts/plugin/makeplugin.sh"/>
|
||||
|
||||
<property name="junit.filename" value="junit-4.8.1.jar"/>
|
||||
<property name="junit.url" value="http://sourceforge.net/projects/junit/files/junit/4.8.1/junit-4.8.1.jar/download"/>
|
||||
<property name="mockito.filename" value="mockito-core-2.0.31-beta.jar"/>
|
||||
<property name="mockito.url" value="https://bintray.com/artifact/download/szczepiq/maven/org/mockito/mockito-core/2.0.31-beta/mockito-core-2.0.31-beta.jar"/>
|
||||
<!-- Just the main jMock .jar, the .zip contains this and dependencies -->
|
||||
<property name="jmock.filename" value="jmock-2.6.0.jar"/>
|
||||
<property name="jmock.archive" value="jmock-2.6.0-jars.zip"/>
|
||||
<property name="jmockcp" value="${lib}/jmock-2.6.0.jar:${lib}/hamcrest-core-1.3.jar:${lib}/hamcrest-library-1.3.jar:${lib}/jmock-legacy-2.6.0.jar:${lib}/cglib-nodep-2.2.3.jar:${lib}/objenesis-1.0.jar"/>
|
||||
<property name="jmock.url" value="http://jmock.org/downloads/${jmock.archive}"/>
|
||||
|
||||
<!-- Dependencies -->
|
||||
<!-- Local -->
|
||||
<property name="ntruenc.filename" value="ntruenc-1.2.jar"/>
|
||||
<property name="ntruenc.includeinupdate" value="false"/>
|
||||
<property name="scrypt.filename" value="scrypt-1.4.0.jar"/>
|
||||
<property name="scrypt.includeinupdate" value="false"/>
|
||||
<property name="lzma.filename" value="lzma-9.20.jar"/>
|
||||
<property name="lzma.includeinupdate" value="false"/>
|
||||
<property name="gmss.filename" value="flexi-gmss-1.7p1.jar"/>
|
||||
<property name="gmss.includeinupdate" value="false"/>
|
||||
<property name="jcommon.filename" value="jcommon-1.0.23.jar"/>
|
||||
<property name="jcommon.includeinupdate" value="false"/>
|
||||
<property name="jfreechart.filename" value="jfreechart-1.0.19.jar"/>
|
||||
<property name="jfreechart.includeinupdate" value="false"/>
|
||||
<!-- JavaMail -->
|
||||
<property name="mailapi.name" value="JavaMail"/>
|
||||
<property name="mailapi.filename" value="mailapi-1.5.4.jar"/>
|
||||
<property name="mailapi.hash" value="6786437244ec10d05601c52ee418b33e31f7ce5ed2cad8c8893139b28cb18ac3"/>
|
||||
<property name="mailapi.url" value="https://maven.java.net/content/repositories/releases/com/sun/mail/mailapi/1.5.4/mailapi-1.5.4.jar"/>
|
||||
<property name="mailapi.includeinupdate" value="false"/>
|
||||
<!-- BouncyCastle Provider -->
|
||||
<property name="bcprov.name" value="BouncyCastle Provider"/>
|
||||
<property name="bcprov.filename" value="bcprov-jdk15on-152.jar"/>
|
||||
<property name="bcprov.hash" value="0dc4d181e4d347893c2ddbd2e6cd5d7287fc651c03648fa64b2341c7366b1773"/>
|
||||
<property name="bcprov.url" value="http://downloads.bouncycastle.org/java/bcprov-jdk15on-152.jar"/>
|
||||
<property name="bcprov.includeinupdate" value="false"/>
|
||||
<!-- Apache James IMAP server -->
|
||||
<!-- Just one of the James .jars for checking presence of James, the .zip contains this and dependencies -->
|
||||
<property name="james.filename" value="apache-james-mailbox-api-0.6-20150508.040939-710.jar"/>
|
||||
<property name="james.archive" value="james-server-app-3.0.0-beta5-20150627.102412-1076-app.zip"/>
|
||||
<!--
|
||||
James snapshots are only kept for a few days. The URL below points to a copy of the 6/27/2015 snapshot.
|
||||
To be replaced with a repository.apache.org URL once beta5 is out.
|
||||
-->
|
||||
<property name="james.url" value="http://download.i2p2.de/mirror/lib/james-server-app-3.0.0-beta5-20150627.102412-1076-app.zip"/>
|
||||
<property name="james.includeinupdate" value="false"/>
|
||||
<!-- SubEtha SMTP -->
|
||||
<property name="subetha.name" value="SubEtha SMTP"/>
|
||||
<property name="subetha.filename" value="subethasmtp-3.1.7.jar"/>
|
||||
<property name="subetha.hash" value="95cf031f1bc1b1a69b08554fbf8970bad994b32db32388d482c6afb482f1db76"/>
|
||||
<property name="subetha.archive" value="subethasmtp-3.1.7.zip"/>
|
||||
<property name="subetha.url" value="http://subethasmtp.googlecode.com/files/subethasmtp-3.1.7.zip"/>
|
||||
<property name="subetha.includeinupdate" value="false"/>
|
||||
|
||||
<!-- the following are only used by win32installer -->
|
||||
<property name="i2p.mtn" value="../i2p.mtn"/>
|
||||
<property name="win32launcherurl" value="http://dist.codehaus.org/izpack/native-launcher/izpack-launcher-1.3.zip"/>
|
||||
<property name="win32jreurl" value="http://javadl.sun.com/webapps/download/AutoDL?BundleId=47363"/>
|
||||
|
||||
<fileset dir="${lib}" id="imaplibs">
|
||||
<include name="apache-james-mailbox-api-0.6-20150508.040939-710.jar"/>
|
||||
<include name="apache-james-mailbox-store-0.6-20150508.041003-704.jar"/>
|
||||
<include name="apache-mime4j-core-0.8.0-20150617.024907-738.jar"/>
|
||||
<include name="apache-mime4j-dom-0.8.0-20150617.024927-735.jar"/>
|
||||
<include name="commons-codec-1.7.jar"/>
|
||||
<include name="commons-collections-3.2.1.jar"/>
|
||||
<include name="commons-configuration-1.9.jar"/>
|
||||
<include name="commons-io-2.4.jar"/>
|
||||
<include name="commons-lang-2.6.jar"/>
|
||||
<include name="james-server-filesystem-api-3.0.0-beta5-SNAPSHOT.jar"/>
|
||||
<include name="james-server-lifecycle-api-3.0.0-beta5-SNAPSHOT.jar"/>
|
||||
<include name="james-server-protocols-imap4-3.0.0-beta5-SNAPSHOT.jar"/>
|
||||
<include name="james-server-protocols-library-3.0.0-beta5-SNAPSHOT.jar"/>
|
||||
<include name="james-server-util-3.0.0-beta5-SNAPSHOT.jar"/>
|
||||
<include name="jutf7-1.0.0.jar"/>
|
||||
<include name="log4j-1.2.17.jar"/>
|
||||
<include name="netty-3.3.1.Final.jar"/>
|
||||
<include name="protocols-api-1.6.4-20150617.121129-1080.jar"/>
|
||||
<include name="protocols-imap-1.6.4-20150617.121245-927.jar"/>
|
||||
<include name="protocols-netty-1.6.4-20150617.121137-1044.jar"/>
|
||||
<include name="slf4j-api-1.7.2.jar"/>
|
||||
<include name="slf4j-log4j12-1.7.2.jar"/>
|
||||
</fileset>
|
||||
<path id="cp">
|
||||
<pathelement path="${classpath}" />
|
||||
<pathelement path="${java.class.path}" />
|
||||
<pathelement location="${i2plib}/i2p.jar" />
|
||||
<pathelement location="${jstllib}/jstl.jar" />
|
||||
<pathelement location="${jstllib}/standard.jar" />
|
||||
<pathelement location="${jettylib}/org.mortbay.jetty.jar"/>
|
||||
<pathelement location="${jettylib}/jasper-compiler.jar" />
|
||||
<pathelement location="${jettylib}/jasper-runtime.jar" />
|
||||
<pathelement location="${jettylib}/javax.servlet.jar" />
|
||||
<pathelement location="${jettylib}/commons-logging.jar" />
|
||||
<pathelement location="${jettylib}/commons-el.jar" />
|
||||
<pathelement location="${antlib}/ant.jar" />
|
||||
<pathelement location="${i2plib}/mstreaming.jar" />
|
||||
<pathelement location="${i2plib}/streaming.jar" />
|
||||
<pathelement location="${i2plib}/router.jar" />
|
||||
<pathelement location="${i2plib}/org.mortbay.jetty.jar"/>
|
||||
<pathelement location="${i2plib}/jasper-compiler.jar" />
|
||||
<pathelement location="${i2plib}/jasper-runtime.jar" />
|
||||
<pathelement location="${i2plib}/javax.servlet.jar" />
|
||||
<pathelement location="${i2plib}/commons-logging.jar" />
|
||||
<pathelement location="${i2plib}/commons-el.jar" />
|
||||
<pathelement location="${i2plib}/jstl.jar" />
|
||||
<pathelement location="${i2plib}/standard.jar" />
|
||||
<pathelement location="${lib}/${mailapi.filename}" />
|
||||
<pathelement location="${lib}/${bcprov.filename}" />
|
||||
<pathelement location="${lib}/${ntruenc.filename}" />
|
||||
<pathelement location="${lib}/${scrypt.filename}" />
|
||||
<pathelement location="${lib}/${lzma.filename}" />
|
||||
<pathelement location="${lib}/${gmss.filename}" />
|
||||
<pathelement location="${lib}/${subetha.filename}" />
|
||||
<pathelement location="${lib}/${jcommon.filename}" />
|
||||
<pathelement location="${lib}/${jfreechart.filename}" />
|
||||
<pathelement location="src/main/webapp/WEB-INF/classes" />
|
||||
<fileset refid="imaplibs"/>
|
||||
</path>
|
||||
|
||||
<target name="all" depends="clean, war" />
|
||||
<pathconvert property="manifest.classpath" pathsep=" ">
|
||||
<path refid="cp" />
|
||||
<mapper>
|
||||
<flattenmapper />
|
||||
</mapper>
|
||||
</pathconvert>
|
||||
|
||||
<target name="compile">
|
||||
|
||||
<target name="help">
|
||||
<echo message="Useful targets:" />
|
||||
<echo message=" war: Makes a .war file" />
|
||||
<echo message=" plugin: Makes a I2P plugin. Only runs on Linux." />
|
||||
<echo message=" Requires the i2p.scripts branch to be checked out" />
|
||||
<echo message=" alongside this directory (for makeplugin.sh)." />
|
||||
<echo message=" all: src + war + plugin" />
|
||||
<echo message=" win32installer: Makes a Windows-only installer that installs" />
|
||||
<echo message=" I2P, I2P-Bote, and optionally a JRE. Needs 7z and" />
|
||||
<echo message=" mtn on the path, and a mtn DB containing i2p.i2p." />
|
||||
<echo message=" Requires Ant 1.8.0 or higher." />
|
||||
<echo message=" poupdate: Updates the messages_*.po files from the source code," />
|
||||
<echo message=" adding any untranslated strings. Depends on additional" />
|
||||
<echo message=" software, see bundle-messages.sh." />
|
||||
<echo message=" This task only runs on Linux at the moment." />
|
||||
<echo message=" src: Zips up the source code" />
|
||||
<echo message=" junit: Runs all unit tests" />
|
||||
<echo message=" javadoc: Generates code documentation in the javadoc dir" />
|
||||
<echo message=" clean: Removes all generated files and directories" />
|
||||
<echo message=" distclean: Removes all generated files and directories, and all downloaded libraries" />
|
||||
</target>
|
||||
|
||||
<target name="all" depends="clean, src, war, plugin" />
|
||||
|
||||
<target name="checki2pbase">
|
||||
<fail unless="i2pbase" message="The I2P environment variable is not set.${line.separator}It must point to an I2P installation. It is usually a path of the form ${line.separator}/xxx/yyy/i2p."/>
|
||||
</target>
|
||||
|
||||
<macrodef name="checkdep">
|
||||
<attribute name="dep"/>
|
||||
<sequential>
|
||||
<local name="hashValid"/>
|
||||
<checksum file="${lib}/${@{dep}.filename}" algorithm="SHA-256" property="${@{dep}.hash}" verifyProperty="hashValid"/>
|
||||
<fail message="The ${@{dep}.name} library has an invalid hash.">
|
||||
<condition>
|
||||
<isfalse value="${hashValid}"/>
|
||||
</condition>
|
||||
</fail>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<target name="checkdependencies">
|
||||
<available property="mailapiexists" file="${lib}/${mailapi.filename}" type="file"/>
|
||||
<available property="bcprovexists" file="${lib}/${bcprov.filename}" type="file"/>
|
||||
<available property="jamesexists" file="${lib}/${james.filename}" type="file"/>
|
||||
<available property="subethaexists" file="${lib}/${subetha.filename}" type="file"/>
|
||||
|
||||
<ant target="downloadmailapi"/>
|
||||
<ant target="downloadbcprov"/>
|
||||
<ant target="downloadjames"/>
|
||||
<ant target="downloadsubetha"/>
|
||||
|
||||
<checkdep dep="mailapi"/>
|
||||
<checkdep dep="bcprov"/>
|
||||
<checkdep dep="subetha"/>
|
||||
</target>
|
||||
|
||||
<!-- compiles everything under src/main/java, but not src/test/ or src/build/ -->
|
||||
<target name="compile" depends="checki2pbase, checkdependencies">
|
||||
<mkdir dir="./ant_build" />
|
||||
<mkdir dir="./ant_build/classes" />
|
||||
<javac
|
||||
srcdir="./src"
|
||||
debug="true" deprecation="on" source="1.5" target="1.5"
|
||||
srcdir="./src/main/java"
|
||||
debug="true"
|
||||
deprecation="on"
|
||||
source="1.6" target="1.6"
|
||||
includeAntRuntime="false"
|
||||
destdir="./ant_build/classes"
|
||||
classpathref="cp" >
|
||||
<compilerarg line="" />
|
||||
</javac>
|
||||
classpathref="cp"
|
||||
failonerror="true" />
|
||||
</target>
|
||||
|
||||
<target name="precompilejsp">
|
||||
@ -48,51 +216,720 @@
|
||||
<arg value="-webinc" />
|
||||
<arg value="ant_build/web-fragment.xml" />
|
||||
<arg value="-webapp" />
|
||||
<arg value="WebContent" />
|
||||
<arg value="src/main/webapp" />
|
||||
</java>
|
||||
|
||||
<javac debug="true" deprecation="on" source="1.5" target="1.5"
|
||||
destdir="ant_build/classes" srcdir="./ant_build/jspjava"
|
||||
includes="**/*.java" classpathref="jspcp">
|
||||
<compilerarg line="" />
|
||||
</javac>
|
||||
<copy file="WebContent/WEB-INF/web-template.xml" tofile="ant_build/web.xml" />
|
||||
<javac
|
||||
debug="true"
|
||||
deprecation="on"
|
||||
source="1.6" target="1.6"
|
||||
includeAntRuntime="false"
|
||||
destdir="ant_build/classes"
|
||||
srcdir="./ant_build/jspjava"
|
||||
includes="**/*.java"
|
||||
classpathref="jspcp"
|
||||
failonerror="true" />
|
||||
<copy file="src/main/webapp/WEB-INF/web.xml" tofile="ant_build/web.xml" />
|
||||
<loadfile property="jspc.web.fragment" srcfile="ant_build/web-fragment.xml" />
|
||||
<replace file="ant_build/web.xml">
|
||||
<replacefilter token="<!-- precompiled servlets -->" value="${jspc.web.fragment}" />
|
||||
</replace>
|
||||
</target>
|
||||
|
||||
<target name="war" depends="compile,precompilejsp">
|
||||
<target name="war" depends="jar">
|
||||
<mkdir dir="ant_build" />
|
||||
<war destfile="i2pbote.war" webxml="ant_build/web.xml">
|
||||
<classes dir="ant_build/classes" includes="**/*.class" />
|
||||
<classes dir="src" includes="i2p/bote/network/kademlia/built-in-peers.txt" />
|
||||
<fileset dir="WebContent" includes="*.jsp"/>
|
||||
<fileset dir="WebContent" includes="*.css"/>
|
||||
<fileset dir="WebContent/" includes="*.xml"/>
|
||||
<webinf dir="WebContent/WEB-INF/tlds" includes="*.tld"/>
|
||||
<lib file="${jstllib}/jstl.jar"/>
|
||||
<lib file="${jstllib}/standard.jar"/>
|
||||
<zipfileset dir="WebContent/images" prefix="images"/>
|
||||
<classes dir="ant_build/classes" includes="i2p/bote/web/** i2p/bote/jsp/** org/apache/jsp/**" />
|
||||
<fileset dir="src/main/webapp" includes="*.html"/>
|
||||
<fileset dir="src/main/webapp/" includes="*.xml"/>
|
||||
<webinf dir="src/main/webapp/WEB-INF/tlds" includes="*.tld"/>
|
||||
<lib file="${lib}/i2pbote.jar"/>
|
||||
<lib file="${lib}/${mailapi.filename}"/>
|
||||
<lib file="${lib}/${bcprov.filename}"/>
|
||||
<lib file="${lib}/${ntruenc.filename}"/>
|
||||
<lib file="${lib}/${scrypt.filename}"/>
|
||||
<lib file="${lib}/${lzma.filename"/>
|
||||
<lib file="${lib}/${gmss.filename}"/>
|
||||
<lib file="${lib}/${jcommon.filename}" />
|
||||
<lib file="${lib}/${jfreechart.filename}" />
|
||||
<zipfileset dir="src/main/webapp/html" prefix="html"/>
|
||||
<zipfileset dir="src/main/webapp/themes" prefix="themes"/>
|
||||
</war>
|
||||
|
||||
<echo message="SHA256 sum:"/>
|
||||
<exec executable="sha256sum">
|
||||
<arg value="i2pbote.war"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="compile,precompilejsp,bundle">
|
||||
<mkdir dir="ant_build" />
|
||||
<jar destfile="${lib}/i2pbote.jar">
|
||||
<fileset dir="ant_build/classes" excludes="i2p/bote/web/** i2p/bote/jsp/** org/apache/jsp/** codec/** **/*Test.class"/>
|
||||
<fileset dir="src/main/java" includes="i2p/bote/network/built-in-peers.txt"/>
|
||||
<fileset dir="src/main/java" includes="i2p/bote/crypto/wordlist/"/>
|
||||
<manifest>
|
||||
<attribute name="Class-Path" value="${manifest.classpath}" />
|
||||
</manifest>
|
||||
<metainf file="src/main/webapp/META-INF/mime.types"/>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<macrodef name="packdep">
|
||||
<attribute name="dep"/>
|
||||
<element name="exec-args" optional="yes" implicit="yes"/>
|
||||
<sequential>
|
||||
<exec executable="pack200" failonerror="true">
|
||||
<arg value="--no-gzip"/>
|
||||
<arg value="--effort=9"/>
|
||||
<arg value="--modification-time=latest"/>
|
||||
<exec-args/>
|
||||
<arg value="plugin/plugin.tmp/lib/${@{dep}.filename}.pack"/>
|
||||
<arg value="${lib}/${@{dep}.filename}"/>
|
||||
</exec>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="removepack">
|
||||
<attribute name="dep"/>
|
||||
<sequential>
|
||||
<delete file="plugin/plugin.tmp/lib/${@{dep}.filename}"/>
|
||||
<delete file="plugin/plugin.tmp/lib/${@{dep}.filename}.pack"/>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<!--
|
||||
- Make two plugins, one for initial installs and one for updates.
|
||||
- Neither includes jstl.jar or standard.jar, as any i2p version that has
|
||||
- plugin support has these two jars pulled out of susidns.war and put in
|
||||
- $I2P/lib. We set the classpath in webapps.config to find them.
|
||||
-->
|
||||
<target name="plugin" depends="pluginwar,getversion">
|
||||
<mkdir dir="plugin/plugin.tmp/lib" />
|
||||
<mkdir dir="plugin/plugin.tmp/console/webapps" />
|
||||
<copy file="plugin/webapps.config" todir="plugin/plugin.tmp/console/" />
|
||||
|
||||
<!-- run all .jars and i2pbote-plugin.war through pack200 -->
|
||||
<move file="i2pbote-plugin.war" tofile="plugin/plugin.tmp/console/webapps/i2pbote.jar" />
|
||||
<exec executable="pack200" failonerror="true">
|
||||
<arg value="--no-gzip"/>
|
||||
<arg value="--effort=9"/>
|
||||
<arg value="--modification-time=latest"/>
|
||||
<arg value="plugin/plugin.tmp/console/webapps/i2pbote.war.pack"/>
|
||||
<arg value="plugin/plugin.tmp/console/webapps/i2pbote.jar"/>
|
||||
</exec>
|
||||
<delete file="plugin/plugin.tmp/console/webapps/i2pbote.jar" />
|
||||
<exec executable="pack200" failonerror="true">
|
||||
<arg value="--no-gzip"/>
|
||||
<arg value="--effort=9"/>
|
||||
<arg value="--modification-time=latest"/>
|
||||
<arg value="plugin/plugin.tmp/lib/i2pbote.jar.pack"/>
|
||||
<arg value="${lib}/i2pbote.jar"/>
|
||||
</exec>
|
||||
|
||||
<!-- pack local dependencies in updater -->
|
||||
<includedep updater="true" dep="ntruenc"/>
|
||||
<includedep updater="true" dep="scrypt"/>
|
||||
<includedep updater="true" dep="lzma"/>
|
||||
<includedep updater="true" dep="gmss"/>
|
||||
<includedep updater="true" dep="jcommon"/>
|
||||
<includedep updater="true" dep="jfreechart"/>
|
||||
<!-- pack remote dependencies in updater -->
|
||||
<includedep updater="true" dep="mailapi"/>
|
||||
<includedep updater="true" dep="bcprov"/>
|
||||
<includedep updater="true" dep="james"/>
|
||||
<includedep updater="true" dep="subetha"/>
|
||||
|
||||
<copy file="src/main/scripts/encrypt.sh" todir="plugin/plugin.tmp/lib"/>
|
||||
<copy file="src/main/scripts/decrypt.sh" todir="plugin/plugin.tmp/lib"/>
|
||||
<copy file="src/main/scripts/fileinfo.sh" todir="plugin/plugin.tmp/lib"/>
|
||||
<copy file="src/main/scripts/encrypt.bat" todir="plugin/plugin.tmp/lib"/>
|
||||
<copy file="src/main/scripts/decrypt.bat" todir="plugin/plugin.tmp/lib"/>
|
||||
<copy file="src/main/scripts/fileinfo.bat" todir="plugin/plugin.tmp/lib"/>
|
||||
|
||||
<!-- get build number -->
|
||||
<buildnumber file="plugin/build.number" />
|
||||
|
||||
<!-- make the update xpi2p -->
|
||||
<copy file="plugin/plugin.config" todir="plugin/plugin.tmp" overwrite="true" />
|
||||
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.tmp/plugin.config" append="true">
|
||||
<arg value="version=${version}" />
|
||||
</exec>
|
||||
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.tmp/plugin.config" append="true">
|
||||
<arg value="update-only=true" />
|
||||
</exec>
|
||||
<input message="Enter key password for plugin-su3-keystore.ks:" addproperty="release.password.su3" />
|
||||
<fail message="You must enter a password." >
|
||||
<condition>
|
||||
<equals arg1="${release.password.su3}" arg2=""/>
|
||||
</condition>
|
||||
</fail>
|
||||
<exec executable="${makeplugin}" inputstring="${release.password.su3}" failonerror="true" >
|
||||
<arg value="plugin/plugin.tmp" />
|
||||
</exec>
|
||||
<move file="i2pbote.xpi2p" tofile="i2pbote-update.xpi2p" />
|
||||
<move file="i2pbote.su3" tofile="i2pbote-update.su3" />
|
||||
|
||||
<!-- pack local dependencies in installer -->
|
||||
<includedep dep="ntruenc"/>
|
||||
<includedep dep="scrypt"/>
|
||||
<includedep dep="lzma"/>
|
||||
<includedep dep="gmss"/>
|
||||
<includedep dep="jcommon"/>
|
||||
<includedep dep="jfreechart"/>
|
||||
<!-- pack remote dependencies in installer -->
|
||||
<includedep dep="mailapi"/>
|
||||
<includedep dep="bcprov"/>
|
||||
<includedep dep="james"/>
|
||||
<includedep dep="subetha"/>
|
||||
|
||||
<!-- make the install xpi2p -->
|
||||
<copy file="plugin/plugin.config" todir="plugin/plugin.tmp" overwrite="true" />
|
||||
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.tmp/plugin.config" append="true">
|
||||
<arg value="version=${version}" />
|
||||
</exec>
|
||||
<exec executable="${makeplugin}" inputstring="${release.password.su3}" failonerror="true" >
|
||||
<arg value="plugin/plugin.tmp" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- same as war but without the library jars -->
|
||||
<target name="pluginwar" depends="compile,jar,precompilejsp,bundle">
|
||||
<mkdir dir="ant_build" />
|
||||
<war destfile="i2pbote-plugin.war" webxml="ant_build/web.xml">
|
||||
<classes dir="ant_build/classes" includes="i2p/bote/web/** i2p/bote/jsp/** org/apache/jsp/**" />
|
||||
<fileset dir="src/main/webapp" includes="*.html"/>
|
||||
<fileset dir="src/main/webapp/" includes="*.xml"/>
|
||||
<webinf dir="src/main/webapp/WEB-INF/tlds" includes="*.tld"/>
|
||||
<zipfileset dir="src/main/webapp/html" prefix="html"/>
|
||||
<zipfileset dir="src/main/webapp/themes" prefix="themes"/>
|
||||
</war>
|
||||
</target>
|
||||
|
||||
<macrodef name="includedep">
|
||||
<attribute name="updater" default="false"/>
|
||||
<attribute name="dep"/>
|
||||
<sequential>
|
||||
<local name="packDep"/>
|
||||
<local name="removePack"/>
|
||||
<condition property="packDep" else="false">
|
||||
<or>
|
||||
<and>
|
||||
<istrue value="@{updater}"/>
|
||||
<istrue value="${@{dep}.includeinupdate}"/>
|
||||
</and>
|
||||
<and>
|
||||
<isfalse value="@{updater}"/>
|
||||
<isfalse value="${@{dep}.includeinupdate}"/>
|
||||
</and>
|
||||
</or>
|
||||
</condition>
|
||||
<condition property="removePack" else="false">
|
||||
<and>
|
||||
<istrue value="@{updater}"/>
|
||||
<isfalse value="${@{dep}.includeinupdate}"/>
|
||||
</and>
|
||||
</condition>
|
||||
|
||||
<ant target="packdep.@{dep}" if:true="${packDep}"/>
|
||||
<ant target="removepack.@{dep}" if:true="${removePack}"/>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<target name="packdep.ntruenc">
|
||||
<packdep dep="ntruenc"/>
|
||||
</target>
|
||||
<target name="removepack.ntruenc">
|
||||
<removepack dep="ntruenc"/>
|
||||
</target>
|
||||
|
||||
<target name="packdep.scrypt">
|
||||
<packdep dep="scrypt"/>
|
||||
</target>
|
||||
<target name="removepack.scrypt">
|
||||
<removepack dep="scrypt"/>
|
||||
</target>
|
||||
|
||||
<target name="packdep.lzma">
|
||||
<packdep dep="lzma"/>
|
||||
</target>
|
||||
<target name="removepack.lzma">
|
||||
<removepack dep="lzma"/>
|
||||
</target>
|
||||
|
||||
<target name="packdep.gmss">
|
||||
<packdep dep="gmss"/>
|
||||
</target>
|
||||
<target name="removepack.gmss">
|
||||
<removepack dep="gmss"/>
|
||||
</target>
|
||||
|
||||
<target name="packdep.jcommon">
|
||||
<packdep dep="jcommon"/>
|
||||
</target>
|
||||
<target name="removepack.jcommon">
|
||||
<removepack dep="jcommon"/>
|
||||
</target>
|
||||
|
||||
<target name="packdep.jfreechart">
|
||||
<packdep dep="jfreechart"/>
|
||||
</target>
|
||||
<target name="removepack.jfreechart">
|
||||
<removepack dep="jfreechart"/>
|
||||
</target>
|
||||
|
||||
<target name="packdep.mailapi">
|
||||
<packdep dep="mailapi"/>
|
||||
</target>
|
||||
<target name="removepack.mailapi">
|
||||
<removepack dep="mailapi"/>
|
||||
</target>
|
||||
|
||||
<target name="packdep.bcprov">
|
||||
<packdep dep="bcprov">
|
||||
<arg value="--segment-limit=-1"/>
|
||||
</packdep>
|
||||
</target>
|
||||
<target name="removepack.bcprov">
|
||||
<removepack dep="bcprov"/>
|
||||
</target>
|
||||
|
||||
<target name="packdep.james">
|
||||
<apply executable="pack200" failonerror="true" parallel="false" dest="plugin/plugin.tmp/lib/">
|
||||
<arg value="--no-gzip"/>
|
||||
<arg value="--effort=9"/>
|
||||
<arg value="--modification-time=latest"/>
|
||||
<targetfile/>
|
||||
<srcfile/>
|
||||
<fileset refid="imaplibs"/>
|
||||
<mapper type="glob" from="*.jar" to="*.jar.pack"/>
|
||||
</apply>
|
||||
</target>
|
||||
<target name="removepack.james">
|
||||
<apply executable="rm" dest="plugin/plugin.tmp/lib/" addsourcefile="false">
|
||||
<targetfile/>
|
||||
<fileset refid="imaplibs"/>
|
||||
<flattenmapper/>
|
||||
</apply>
|
||||
<apply executable="rm" dest="plugin/plugin.tmp/lib/" addsourcefile="false">
|
||||
<targetfile suffix=".pack"/>
|
||||
<fileset refid="imaplibs"/>
|
||||
<flattenmapper/>
|
||||
</apply>
|
||||
</target>
|
||||
|
||||
<target name="packdep.subetha">
|
||||
<packdep dep="subetha"/>
|
||||
</target>
|
||||
<target name="removepack.subetha">
|
||||
<removepack dep="subetha"/>
|
||||
</target>
|
||||
|
||||
<target name="bundle" depends="compile">
|
||||
<!-- Update the messages_*.po files.
|
||||
We need to supply the bat file for windows, and then change the fail property to true -->
|
||||
<exec executable="sh" osfamily="unix" failifexecutionfails="false" >
|
||||
<arg value="src/build/scripts/bundle-messages.sh" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="mac" failifexecutionfails="false" >
|
||||
<arg value="src/build/scripts/bundle-messages.sh" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="windows" failifexecutionfails="false" >
|
||||
<arg value="src/build/scripts/bundle-messages.sh" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- depends on compileantclasses for SearchAndInsert.class -->
|
||||
<target name="win32installer" depends="pluginwar, compileantclasses">
|
||||
<property name="installerdir" value="ant_build/win32installer"/>
|
||||
<property name="installerresources" value="${installerdir}/resources"/>
|
||||
<property name="i2pinstallerdir" value="${installerdir}/i2p.i2p/installer"/>
|
||||
<property name="i2pinstallerplugindir" value="${installerdir}/i2p.i2p/plugins/i2pbote"/>
|
||||
<mkdir dir="${installerdir}"/>
|
||||
|
||||
<!-- get 7z_extra.7z and extract 7zS.sfx -->
|
||||
<get src="http://sourceforge.net/projects/sevenzip/files/7-Zip/9.19/7z919_extra.7z/download" dest="${installerdir}/7z_extra.7z" skipexisting="true"/>
|
||||
<exec executable="7z" dir="${installerdir}">
|
||||
<arg value="e"/>
|
||||
<arg value="-y"/>
|
||||
<arg value="7z_extra.7z"/>
|
||||
<arg value="7zS.sfx"/>
|
||||
</exec>
|
||||
|
||||
<!-- check out i2p.i2p from mtn, add i2pbote, and build install.jar -->
|
||||
<delete dir="${installerdir}/i2p.i2p" />
|
||||
<exec executable="mtn" dir="${installerdir}" failonerror="true">
|
||||
<arg value="--db" />
|
||||
<arg value="../../${i2p.mtn}" />
|
||||
<arg value="--branch" />
|
||||
<arg value="i2p.i2p" />
|
||||
<arg value="co" />
|
||||
</exec>
|
||||
<!-- add I2P-Bote to install.xml -->
|
||||
<java classname="i2p.bote.ant.SearchAndInsert" classpathref="cp" classpath="./ant_build/classes" failonerror="true">
|
||||
<arg value="-before"/>
|
||||
<arg value="${i2pinstallerdir}/install.xml"/>
|
||||
<arg value="${i2pinstallerdir}/install.xml.new"/>
|
||||
<arg value="</packs>"/>
|
||||
<arg value="win32installer/i2pbote_pack.txt"/>
|
||||
</java>
|
||||
<move file="${i2pinstallerdir}/install.xml.new" tofile="${i2pinstallerdir}/install.xml"/>
|
||||
<!-- make the plugins dir -->
|
||||
<mkdir dir="${i2pinstallerplugindir}/console/webapps"/>
|
||||
<mkdir dir="${i2pinstallerplugindir}/lib"/>
|
||||
<copy file="plugin/plugin.config" todir="${i2pinstallerplugindir}"/>
|
||||
<copy file="i2pbote-plugin.war" tofile="${i2pinstallerplugindir}/console/webapps/i2pbote.war"/>
|
||||
<copy file="plugin/webapps.config" todir="${i2pinstallerplugindir}/console"/>
|
||||
<copy file="${lib}/i2pbote.jar" todir="${i2pinstallerplugindir}/lib"/>
|
||||
<copy file="${lib}/${mailapi.filename}" todir="${i2pinstallerplugindir}/lib"/>
|
||||
<copy file="${lib}/${bcprov.filename}" todir="${i2pinstallerplugindir}/lib"/>
|
||||
<copy file="${lib}/ntruenc-1.2.jar" todir="${i2pinstallerplugindir}/lib"/>
|
||||
<copy file="${lib}/scrypt-1.4.0.jar" todir="${i2pinstallerplugindir}/lib"/>
|
||||
<copy file="${lib}/lzma-9.20.jar" todir="${i2pinstallerplugindir}/lib"/>
|
||||
<copy file="${lib}/flexi-gmss-1.7p1.jar" todir="${i2pinstallerplugindir}/lib"/>
|
||||
<copy file="${lib}/jcommon-1.0.23.jar" todir="${i2pinstallerplugindir}/lib"/>
|
||||
<copy file="${lib}/jfreechart-1.0.19.jar" todir="${i2pinstallerplugindir}/lib"/>
|
||||
<!-- build the I2P + I2P-Bote install.jar -->
|
||||
<ant antfile="${installerdir}/i2p.i2p/build.xml" target="installer" inheritAll="false"/>
|
||||
<copy file="${installerdir}/i2p.i2p/install.jar" todir="${installerresources}"/>
|
||||
|
||||
<!-- download Windows JRE -->
|
||||
<get src="${win32jreurl}" verbose="true" dest="${installerresources}/jre.exe" skipexisting="true"/>
|
||||
<copy file="win32installer/jreinstall.bat" todir="${installerresources}"/>
|
||||
<copy file="win32installer/launcher.ini" todir="${installerresources}"/>
|
||||
<copy file="win32installer/config.txt" todir="${installerdir}"/>
|
||||
|
||||
<!-- download the launcher .zip and unzip dist/* to resources/ -->
|
||||
<get src="${win32launcherurl}" verbose="true" dest="${installerdir}/izpack-launcher.zip" skipexisting="true"/>
|
||||
<unzip src="${installerdir}/izpack-launcher.zip" dest="${installerresources}">
|
||||
<patternset>
|
||||
<include name="*/dist/*"/>
|
||||
</patternset>
|
||||
<flattenmapper/>
|
||||
</unzip>
|
||||
|
||||
<exec executable="7z" dir="${installerresources}" failonerror="true">
|
||||
<arg value="a"/>
|
||||
<arg value="../files.7z"/>
|
||||
<arg value="*"/>
|
||||
</exec>
|
||||
|
||||
<concat destfile="i2pinstall.exe" binary="true">
|
||||
<fileset dir="${installerdir}" includes="7zS.sfx, config.txt, files.7z"/>
|
||||
</concat>
|
||||
</target>
|
||||
|
||||
<!-- depends on compileantclasses for JspStrings.class -->
|
||||
<target name="poupdate" depends="compileantclasses">
|
||||
<!-- Update the messages_*.po files. -->
|
||||
<exec executable="sh" osfamily="unix" failonerror="true" >
|
||||
<arg value="src/build/scripts/bundle-messages.sh" />
|
||||
<arg value="-p" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="mac" failonerror="true" >
|
||||
<arg value="src/build/scripts/bundle-messages.sh" />
|
||||
<arg value="-p" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="windows" failifexecutionfails="true" >
|
||||
<arg value="src/build/scripts/bundle-messages.sh" />
|
||||
<arg value="-p" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- compiles the Java code in the ant directory -->
|
||||
<target name="compileantclasses" depends="compile">
|
||||
<javac
|
||||
srcdir="./src/build/java"
|
||||
debug="true"
|
||||
deprecation="on"
|
||||
source="1.6" target="1.6"
|
||||
includeAntRuntime="false"
|
||||
destdir="./ant_build/classes"
|
||||
classpathref="cp"
|
||||
classpath="./ant_build/classes"
|
||||
failonerror="true" />
|
||||
</target>
|
||||
|
||||
<target name="junit" depends="compile">
|
||||
<junit printsummary="yes">
|
||||
<available property="junitexists" file="${lib}/${junit.filename}" type="file"/>
|
||||
<ant target="downloadjunit"/>
|
||||
<available property="mockitoexists" file="${lib}/${mockito.filename}" type="file"/>
|
||||
<ant target="downloadmockito"/>
|
||||
<available property="jmockexists" file="${lib}/${jmock.filename}" type="file"/>
|
||||
<ant target="downloadjmock"/>
|
||||
|
||||
<path id="junitcp">
|
||||
<pathelement location="${lib}/${junit.filename}"/>
|
||||
<pathelement location="${lib}/${mockito.filename}"/>
|
||||
<pathelement path="${jmockcp}"/>
|
||||
<pathelement location="./ant_build/classes"/>
|
||||
<path refid="cp"/>
|
||||
</path>
|
||||
|
||||
<javac
|
||||
srcdir="./src/test/java"
|
||||
debug="true"
|
||||
deprecation="on"
|
||||
source="1.6" target="1.6"
|
||||
includeAntRuntime="false"
|
||||
destdir="./ant_build/classes"
|
||||
classpathref="junitcp"
|
||||
failonerror="true" />
|
||||
<copy file="src/test/java/i2p/bote/packet/dht/Struwwelpeter.jpg" tofile="ant_build/classes/i2p/bote/packet/dht/Struwwelpeter.jpg"/>
|
||||
<copy file="src/main/java/i2p/bote/crypto/wordlist/words_de.txt" tofile="ant_build/classes/i2p/bote/crypto/wordlist/words_de.txt"/>
|
||||
<copy file="src/main/java/i2p/bote/crypto/wordlist/words_en.txt" tofile="ant_build/classes/i2p/bote/crypto/wordlist/words_en.txt"/>
|
||||
|
||||
<echo message="Running JUnit tests..."/>
|
||||
<junit printsummary="off" failureproperty="failed">
|
||||
<classpath>
|
||||
<path refid="classpath"/>
|
||||
<path refid="application"/>
|
||||
<path refid="junitcp"/>
|
||||
</classpath>
|
||||
|
||||
<batchtest fork="yes">
|
||||
<fileset dir="${src.dir}" includes="*Test.java"/>
|
||||
<batchtest fork="true" todir="./ant_build">
|
||||
<fileset dir="./ant_build/classes" includes="**/*Test.class"/>
|
||||
<formatter type="plain"/>
|
||||
</batchtest>
|
||||
</junit>
|
||||
<condition property="result"
|
||||
value="At least one unit test failed."
|
||||
else="All unit tests were successful.">
|
||||
<isset property="failed"/>
|
||||
</condition>
|
||||
<echo message="${result}"/>
|
||||
<echo message="For details, see ant_build/TEST*"/>
|
||||
</target>
|
||||
|
||||
<target name="downloadjunit" unless="junitexists">
|
||||
<input message="JUnit not found, download now?" validargs="y,n" addproperty="userinput.junit"/>
|
||||
<fail message="OK, aborting build.">
|
||||
<condition>
|
||||
<equals arg1="${userinput.junit}" arg2="n"/>
|
||||
</condition>
|
||||
</fail>
|
||||
<get src="${junit.url}" verbose="true" dest="${lib}/${junit.filename}"/>
|
||||
</target>
|
||||
|
||||
<target name="downloadmockito" unless="mockitoexists">
|
||||
<input message="Mockito not found, download now?" validargs="y,n" addproperty="userinput.mockito"/>
|
||||
<fail message="OK, aborting build." >
|
||||
<condition>
|
||||
<equals arg1="${userinput.mockito}" arg2="n"/>
|
||||
</condition>
|
||||
</fail>
|
||||
<get src="${mockito.url}" verbose="true" dest="${lib}/${mockito.filename}"/>
|
||||
</target>
|
||||
|
||||
<target name="downloadjmock" unless="jmockexists">
|
||||
<input message="jMock not found, download now?" validargs="y,n" addproperty="userinput.jmock"/>
|
||||
<fail message="OK, aborting build." >
|
||||
<condition>
|
||||
<equals arg1="${userinput.jmock}" arg2="n"/>
|
||||
</condition>
|
||||
</fail>
|
||||
<get src="${jmock.url}" verbose="true" dest="${lib}/${jmock.archive}"/>
|
||||
<unzip src="${lib}/${jmock.archive}" dest="${lib}">
|
||||
<patternset includes="**/*.jar"/>
|
||||
<mapper>
|
||||
<flattenmapper/>
|
||||
</mapper>
|
||||
</unzip>
|
||||
<delete file="${lib}/${jmock.archive}"/>
|
||||
</target>
|
||||
|
||||
<target name="downloadmailapi" unless="mailapiexists">
|
||||
<input message="JavaMail not found, download now?" validargs="y,n" addproperty="userinput.mailapi"/>
|
||||
<fail message="OK, aborting build.">
|
||||
<condition>
|
||||
<equals arg1="${userinput.mailapi}" arg2="n"/>
|
||||
</condition>
|
||||
</fail>
|
||||
<get src="${mailapi.url}" verbose="true" dest="${lib}/${mailapi.filename}"/>
|
||||
</target>
|
||||
|
||||
<target name="downloadbcprov" unless="bcprovexists">
|
||||
<input message="BouncyCastle Provider not found, download now?" validargs="y,n" addproperty="userinput.bcprov"/>
|
||||
<fail message="OK, aborting build.">
|
||||
<condition>
|
||||
<equals arg1="${userinput.bcprov}" arg2="n"/>
|
||||
</condition>
|
||||
</fail>
|
||||
<get src="${bcprov.url}" verbose="true" dest="${lib}/${bcprov.filename}"/>
|
||||
</target>
|
||||
|
||||
<target name="downloadjames" unless="jamesexists">
|
||||
<input message="Apache James not found, download now?" validargs="y,n" addproperty="userinput.junit"/>
|
||||
<fail message="OK, aborting build.">
|
||||
<condition>
|
||||
<equals arg1="${userinput.junit}" arg2="n"/>
|
||||
</condition>
|
||||
</fail>
|
||||
<get src="${james.url}" verbose="true" dest="${lib}/${james.archive}"/>
|
||||
<unzip src="${lib}/${james.archive}" dest="${lib}">
|
||||
<patternset>
|
||||
<include name="**/apache-james-mailbox-api-0.6-20150508.040939-710.jar"/>
|
||||
<include name="**/apache-james-mailbox-store-0.6-20150508.041003-704.jar"/>
|
||||
<include name="**/apache-mime4j-core-0.8.0-20150617.024907-738.jar"/>
|
||||
<include name="**/apache-mime4j-dom-0.8.0-20150617.024927-735.jar"/>
|
||||
<include name="**/commons-codec-1.7.jar"/>
|
||||
<include name="**/commons-collections-3.2.1.jar"/>
|
||||
<include name="**/commons-configuration-1.9.jar"/>
|
||||
<include name="**/commons-io-2.4.jar"/>
|
||||
<include name="**/commons-lang-2.6.jar"/>
|
||||
<include name="**/james-server-filesystem-api-3.0.0-beta5-SNAPSHOT.jar"/>
|
||||
<include name="**/james-server-lifecycle-api-3.0.0-beta5-SNAPSHOT.jar"/>
|
||||
<include name="**/james-server-protocols-imap4-3.0.0-beta5-SNAPSHOT.jar"/>
|
||||
<include name="**/james-server-protocols-library-3.0.0-beta5-SNAPSHOT.jar"/>
|
||||
<include name="**/james-server-util-3.0.0-beta5-SNAPSHOT.jar"/>
|
||||
<include name="**/jutf7-1.0.0.jar"/>
|
||||
<include name="**/log4j-1.2.17.jar"/>
|
||||
<include name="**/netty-3.3.1.Final.jar"/>
|
||||
<include name="**/protocols-api-1.6.4-20150617.121129-1080.jar"/>
|
||||
<include name="**/protocols-imap-1.6.4-20150617.121245-927.jar"/>
|
||||
<include name="**/protocols-netty-1.6.4-20150617.121137-1044.jar"/>
|
||||
<include name="**/slf4j-api-1.7.2.jar"/>
|
||||
<include name="**/slf4j-log4j12-1.7.2.jar"/>
|
||||
</patternset>
|
||||
<mapper>
|
||||
<flattenmapper/>
|
||||
</mapper>
|
||||
</unzip>
|
||||
<delete file="${lib}/${james.archive}"/>
|
||||
</target>
|
||||
|
||||
<target name="downloadsubetha" unless="subethaexists">
|
||||
<input message="SubEtha SMTP not found, download now?" validargs="y,n" addproperty="userinput.junit"/>
|
||||
<fail message="OK, aborting build.">
|
||||
<condition>
|
||||
<equals arg1="${userinput.junit}" arg2="n"/>
|
||||
</condition>
|
||||
</fail>
|
||||
<get src="${subetha.url}" verbose="true" dest="${lib}/${subetha.archive}"/>
|
||||
<unzip src="${lib}/${subetha.archive}" dest="${lib}">
|
||||
<patternset includes="**/${subetha.filename}"/>
|
||||
<mapper>
|
||||
<flattenmapper/>
|
||||
</mapper>
|
||||
</unzip>
|
||||
<delete file="${lib}/${subetha.archive}"/>
|
||||
</target>
|
||||
|
||||
<!-- Write the app version into ${version} -->
|
||||
<!-- Depends on compile b/c it gets the app version from I2PBote.class -->
|
||||
<target name="getversion" depends="compile">
|
||||
<javac
|
||||
srcdir="./src/build/java"
|
||||
debug="true"
|
||||
deprecation="on"
|
||||
source="1.6" target="1.6"
|
||||
includeAntRuntime="false"
|
||||
destdir="./ant_build/classes"
|
||||
classpathref="cp"
|
||||
classpath="./ant_build/classes"
|
||||
failonerror="true" />
|
||||
<java
|
||||
classname="i2p.bote.ant.PrintAppVersion"
|
||||
classpathref="cp"
|
||||
classpath="./ant_build/classes"
|
||||
outputproperty="version"
|
||||
failonerror="true"/>
|
||||
<delete dir="./ant_build/classes/i2p/bote/ant" />
|
||||
<echo message="I2P-Bote version is ${version}"/>
|
||||
</target>
|
||||
|
||||
<target name="src" depends="getversion">
|
||||
<property name="subdir" value="i2pbote-${version}-src"/>
|
||||
<zip destfile="src.zip">
|
||||
<zipfileset dir="src" prefix="${subdir}/src" followsymlinks="false"/>
|
||||
<zipfileset dir="doc" prefix="${subdir}/doc"/>
|
||||
<zipfileset dir="plugin" prefix="${subdir}/plugin">
|
||||
<exclude name="plugin.tmp/**"/>
|
||||
</zipfileset>
|
||||
<zipfileset file="build.xml" prefix="${subdir}"/>
|
||||
<zipfileset file="locale/messages_de.po" prefix="${subdir}/locale"/>
|
||||
<zipfileset file="src/build/scripts/bundle-messages.sh" prefix="${subdir}"/>
|
||||
<zipfileset file="history.txt" prefix="${subdir}"/>
|
||||
<zipfileset file="license.txt" prefix="${subdir}"/>
|
||||
</zip>
|
||||
|
||||
<echo message="SHA256 sum:"/>
|
||||
<exec executable="sha256sum">
|
||||
<arg value="src.zip"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="javadoc" depends="getversion">
|
||||
<!--
|
||||
Instead of eepgetting the package list, it would be easier to just let Ant
|
||||
do it through the eeproxy:
|
||||
|
||||
<javadoc ...>
|
||||
<link href="http://download.oracle.com/docs/cd/E17409_01/javase/6/docs/api/"/>
|
||||
|
||||
But Ant falls back to no proxy if a proxied download fails, so eepget is preferable.
|
||||
-->
|
||||
<mkdir dir="ant_build" />
|
||||
<property name="pkglistdir" value="ant_build/package_lists"/>
|
||||
<mkdir dir="${pkglistdir}" />
|
||||
<echo message="Downloading package lists"/>
|
||||
|
||||
<mkdir dir="${pkglistdir}/jdk"/>
|
||||
<java classname="net.i2p.util.EepGet" classpath="${i2plib}/i2p.jar">
|
||||
<arg value="-o"/>
|
||||
<arg value="${pkglistdir}/jdk/package-list"/>
|
||||
<arg value="http://docs.oracle.com/javase/6/docs/api/package-list"/>
|
||||
</java>
|
||||
<mkdir dir="${pkglistdir}/servlet"/>
|
||||
<java classname="net.i2p.util.EepGet" classpath="${i2plib}/i2p.jar">
|
||||
<arg value="-o"/>
|
||||
<arg value="${pkglistdir}/servlet/package-list"/>
|
||||
<arg value="http://docs.oracle.com/javaee/1.4/api/package-list"/>
|
||||
</java>
|
||||
<mkdir dir="${pkglistdir}/javamail"/>
|
||||
<java classname="net.i2p.util.EepGet" classpath="${i2plib}/i2p.jar">
|
||||
<arg value="-o"/>
|
||||
<arg value="${pkglistdir}/javamail/package-list"/>
|
||||
<arg value="http://java.sun.com/products/javamail/javadocs/package-list"/>
|
||||
</java>
|
||||
<mkdir dir="${pkglistdir}/bouncycastle"/>
|
||||
<java classname="net.i2p.util.EepGet" classpath="${i2plib}/i2p.jar">
|
||||
<arg value="-o"/>
|
||||
<arg value="${pkglistdir}/bouncycastle/package-list"/>
|
||||
<arg value="http://bouncycastle.org/docs/docs1.6/package-list"/>
|
||||
</java>
|
||||
|
||||
<javadoc
|
||||
sourcepath="src"
|
||||
destdir="javadoc"
|
||||
classpathref="cp"
|
||||
windowtitle="I2P-Bote"
|
||||
doctitle="I2P-Bote ${version} Javadoc"
|
||||
use="true"
|
||||
linksource="yes">
|
||||
<arg value="-J-Dhttp.proxyHost=localhost"/>
|
||||
<arg value="-J-Dhttp.proxyPort=4444"/>
|
||||
<link href="http://download.oracle.com/docs/cd/E17409_01/javase/6/docs/api/" offline="true" packagelistloc="${pkglistdir}/jdk"/>
|
||||
<link href="http://download.oracle.com/docs/cd/E17477_01/javaee/1.4/api/" offline="true" packagelistloc="${pkglistdir}/servlet"/>
|
||||
<link href="http://java.sun.com/products/javamail/javadocs/" offline="true" packagelistloc="${pkglistdir}/javamail"/>
|
||||
<link href="http://bouncycastle.org/docs/docs1.6/" offline="true" packagelistloc="${pkglistdir}/bouncycastle"/>
|
||||
<link href="http://i2p-javadocs.i2p/"/>
|
||||
</javadoc>
|
||||
</target>
|
||||
|
||||
<target name="clean">
|
||||
<delete dir="ant_build" />
|
||||
<delete file="${lib}/i2pbote.jar" />
|
||||
<delete file="i2pbote.war" />
|
||||
<delete file="i2pbote-plugin.war" />
|
||||
<delete dir="plugin/plugin.tmp" />
|
||||
<delete file="i2pbote.xpi2p" />
|
||||
<delete file="i2pbote-update.xpi2p" />
|
||||
<delete file="src.zip" />
|
||||
<delete file="i2pinstall.exe" />
|
||||
<delete dir="javadoc" />
|
||||
</target>
|
||||
|
||||
<target name="distclean" depends="clean">
|
||||
<delete>
|
||||
<fileset refid="imaplibs"/>
|
||||
</delete>
|
||||
<delete file="${lib}/${subetha.filename}" />
|
||||
</target>
|
||||
</project>
|
||||
|
BIN
doc/manual.odt
Normal file
BIN
doc/manual_fr.odt
Normal file
996
doc/techdoc.txt
@ -1,18 +0,0 @@
|
||||
I2P-Bote
|
||||
--------
|
||||
|
||||
This file explains how to use I2P-Bote. For technical documentation, see techdoc.txt.
|
||||
|
||||
1. To install the app, [TODO]
|
||||
|
||||
2. Set up an email address [TODO]
|
||||
|
||||
3. Send a test email to yourself [TODO]
|
||||
|
||||
4. Checking for email
|
||||
|
||||
When you view your inbox through the web interface or click the "Get Mail" button on your email
|
||||
client, two things happen: New downloaded emails are shown, if there are any, and the I2P-Bote
|
||||
network is contacted to see if more emails are waiting to be downloaded.
|
||||
|
||||
5. The Configuration File
|
177
history.txt
Normal file
@ -0,0 +1,177 @@
|
||||
I2P-Bote Version History
|
||||
------------------------
|
||||
|
||||
0.4.1 (Released on Sep 12, 2015)
|
||||
* Fixed bugs:
|
||||
o ConcurrentModificationException errors in logs
|
||||
o User guide and FAQ not loading translations
|
||||
|
||||
0.4 (Released on Aug 21, 2015)
|
||||
* Fixed bugs:
|
||||
o Email checking would get stuck if no new mail was found
|
||||
o "Last checked" time was inconsistent for manual checks
|
||||
o Anonymous emails would never send
|
||||
* Removed build number from version
|
||||
* Skipped 0.3.* series
|
||||
o 0.3.1 is less than 0.3-b195; would need to go to 0.3.196
|
||||
|
||||
0.3 (Released on Aug 15, 2015)
|
||||
* New default theme based on Material design
|
||||
* New features:
|
||||
o Exclude individual identities from global email checking
|
||||
o Manual per-identity email checking
|
||||
o Import and export identities
|
||||
* Strip prefixes, spaces and newlines from email destinations (to better
|
||||
handle destinations pasted from different sources). Supported prefixes:
|
||||
o mailto:
|
||||
o bote:
|
||||
o i2pbote:
|
||||
* Translations for user guide and FAQ
|
||||
* Use I2P XSSFilter on inputs
|
||||
* Bundle BouncyCastle 1.51 crypto provider
|
||||
* In-app update notification removed
|
||||
* IMAP temporarily disabled due to bug
|
||||
* Bug fixes
|
||||
|
||||
0.2.10 (Released on Jul 23, 2014)
|
||||
* Vanity destinations
|
||||
* Security fix related to ElGamal email destinations
|
||||
* IMAP fixes
|
||||
* "After merging, IndexPacket is too big..." error fixed
|
||||
* Numerous fixes and enhancements by str4d
|
||||
|
||||
0.2.9 (Released on Feb 7, 2014)
|
||||
* Experimental SMTP and IMAP support
|
||||
* UI and IMAP improvements by str4d
|
||||
* Misc. UI improvements
|
||||
|
||||
0.2.8 (Released on Mar 8, 2013)
|
||||
* Fixes and enhancements to the address directory
|
||||
|
||||
0.2.7 (Released on Feb 13, 2013)
|
||||
* Delivery confirmations
|
||||
* Basic distributed address directory
|
||||
* Datagram-only communication, streams no longer used;
|
||||
smaller datagrams for better reliability
|
||||
* More translations
|
||||
* UI Themes
|
||||
* Misc. UI improvements
|
||||
* Bug fixes
|
||||
|
||||
0.2.6 (Released on Apr 26, 2011)
|
||||
* Password encryption of mail folders, identities, and contacts
|
||||
* New translations (in chronological order):
|
||||
o French by albat, redzara and magma
|
||||
o Spanish by mixxy
|
||||
o Dutch by KwukDuck
|
||||
o Portuguese by mixxy
|
||||
o Norwegian by nej
|
||||
o Swedish by hottuna
|
||||
o Chinese by walking
|
||||
o Arabic by hamada
|
||||
* In-app user guide and FAQ in English (by mixxy) and its French translation (by magma)
|
||||
* Availability of updates now displayed in I2P-Bote itself
|
||||
* NTRU encryption / GMSS signatures
|
||||
* Big packets are now sent as streams (see http://trac.i2p2.de/ticket/346)
|
||||
* Fix: Email files not deleted on Windows
|
||||
* Fix: Emails in sent folder and outbox not signed
|
||||
* Fix: Seedless not used if I2P-Bote is ready before Seedless
|
||||
* Minor fixes and enhancements
|
||||
|
||||
0.2.5 (Released on Oct 04, 2010)
|
||||
* Email attachments
|
||||
* Seedless peer exchange
|
||||
* Russian translation by suhr
|
||||
* All generated files are now read/writable by the owner only
|
||||
* I2P-Bote always uses the router console language (if translation available)
|
||||
* The network page now shows peers in b32 for I2P compatibility
|
||||
* Address book and identities are now sorted alphabetically
|
||||
* The "unknown" column has been inverted to a "known" column
|
||||
* Signatures for sent emails are not shown because signatures are not saved
|
||||
* Emails, including attachments, are compressed before sending
|
||||
* Protocol version 4
|
||||
* Improvements and fixes to relaying
|
||||
* Fix for a bug that sometimes made emails to ECC destinations disappear
|
||||
* Many more bugfixes and improvements. Thanks to mixxy and others for testing!
|
||||
|
||||
0.2.4 (Released on Jun 14, 2010)
|
||||
* Elliptic curve encryption
|
||||
* Email can be sent via relays for added anonymity
|
||||
* Experimental gateway (in/outproxy) support
|
||||
* Protocol version 3, fixes two weaknesses
|
||||
* Kademlia replication
|
||||
* Fix: Error when trying to send anonymously
|
||||
|
||||
0.2.3 (Released on May 10, 2010)
|
||||
* Email signatures
|
||||
* Multiple recipients / CC / BCC implemented
|
||||
* A button on the "new email" page for adding a recipient to the address book
|
||||
* Local names for Email Destinations are displayed automatically and
|
||||
take priority over names chosen by senders
|
||||
* Local names for Email Destinations are removed from emails before sending
|
||||
* Sorted mail folders
|
||||
* Outbox functionality; emails are now sent in the background
|
||||
* Sent folder, Trash folder
|
||||
* No more getting the same email over and over again
|
||||
* New options on the Settings page:
|
||||
o Enable/disable sending of date/time
|
||||
o Enable/disable localization of text that recipients see, such as "Re:"
|
||||
o UI language
|
||||
* Misc UI enhancements
|
||||
* Fix for a bug that caused excessive CPU usage
|
||||
* Fix for a bug that hid the first row in a table (identities, inbox, etc.)
|
||||
|
||||
0.2.2 (Released on Mar 22, 2010)
|
||||
* German translation
|
||||
* Automatic mail checking is now user-configurable
|
||||
* Save peers.txt on exit
|
||||
* Several minor changes
|
||||
* Several bug fixes
|
||||
|
||||
0.2.1 (Released on Mar 05, 2010)
|
||||
* I2P-Bote can now be run as an I2P plugin (I2P 0.7.12 or newer required)
|
||||
* Option to set a default Email Identity
|
||||
* Address book
|
||||
* Umlaute and other non-ASCII characters are now supported
|
||||
* Increase k-bucket size to 20
|
||||
* Several bug fixes
|
||||
|
||||
0.2 (Released on Feb 01, 2010)
|
||||
* Sending and retrieving now much faster
|
||||
* Email packets are now deleted from storage nodes after retrieval
|
||||
* Reply button
|
||||
* Drop dead peers
|
||||
* Automatically check for new email
|
||||
* Inbox shows new emails in bold, number of new emails is shown
|
||||
* "To" addresses are shown in the inbox and when displaying an email
|
||||
* Network protocol changed to version 2, peers that use the old protocol are ignored
|
||||
* "Network" page now shows peer info
|
||||
* Several bug fixes and minor enhancements
|
||||
|
||||
0.1.5 (Released on Dec 19, 2009)
|
||||
* Fixes a number of bugs in the Kademlia code
|
||||
* Removed unimplemented HTML links
|
||||
* Network-related UI elements are now disabled until tunnel is ready
|
||||
* Emails can now be deleted from a folder
|
||||
* "No Identity Defined" page didn't come up, fixed
|
||||
|
||||
0.1.4
|
||||
* Do the 3-minute wait in the background
|
||||
* Added a "Connect now" button to skip the wait
|
||||
* Network Status panel now gives more useful information
|
||||
|
||||
0.1.3
|
||||
* After startup, wait 3 minutes before connecting
|
||||
* Change the paths in the build file from /opt/... to ../i2p/...
|
||||
* Fix: "Check Mail" button doesn't show new emails, user has to reload inbox
|
||||
* Fix: high cpu usage when sending or receiving
|
||||
* Set tunnel names to "I2P-Bote"
|
||||
|
||||
0.1.2
|
||||
Mostly a bug fix release. It adds two .jar files that were missing.
|
||||
|
||||
0.1.1
|
||||
Fix: Clicking on an email in the inbox causes an error.
|
||||
|
||||
0.1 (Released on Dec 06, 2009)
|
||||
First version. Lets a user create email identities and send/receive plain-text emails
|
BIN
lib/flexi-gmss-1.7p1.jar
Normal file
BIN
lib/jcommon-1.0.23.jar
Normal file
BIN
lib/jfreechart-1.0.19.jar
Normal file
BIN
lib/lzma-9.20.jar
Normal file
BIN
lib/ntruenc-1.2.jar
Normal file
BIN
lib/scrypt-1.4.0.jar
Normal file
680
license.txt
@ -1,674 +1,10 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
LICENSES
|
||||
--------
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
This software is licensed under the GPL version 3 (see licenses/GPLv3.txt),
|
||||
except for:
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
- bcprov-jdk15on-152.jar which is licensed under the Bouncy Castle License
|
||||
(see licenses/BouncyCastle.txt).
|
||||
- nl.jteam.tls.StrongTls which is licensed under the Apache License, Version
|
||||
2.0 (see licenses/LICENSE-Apache2.0.txt).
|
||||
|
22
licenses/BouncyCastle.txt
Normal file
@ -0,0 +1,22 @@
|
||||
License
|
||||
|
||||
Copyright (c) 2000 - 2009 The Legion Of The Bouncy Castle
|
||||
(http://www.bouncycastle.org)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
674
licenses/GPLv3.txt
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
202
licenses/LICENSE-Apache2.0.txt
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
1326
locale/messages_ar.po
Normal file
1332
locale/messages_cs.po
Normal file
1373
locale/messages_de.po
Normal file
1356
locale/messages_en.po
Normal file
1392
locale/messages_es.po
Normal file
1317
locale/messages_fi.po
Normal file
1378
locale/messages_fr.po
Normal file
1332
locale/messages_hu.po
Normal file
1352
locale/messages_id.po
Normal file
1369
locale/messages_it.po
Normal file
1343
locale/messages_ja.po
Normal file
1347
locale/messages_nb.po
Normal file
1366
locale/messages_nl.po
Normal file
1363
locale/messages_pl.po
Normal file
1366
locale/messages_pt.po
Normal file
1339
locale/messages_pt_BR.po
Normal file
1356
locale/messages_ro.po
Normal file
1367
locale/messages_ru.po
Normal file
1353
locale/messages_sk.po
Normal file
1357
locale/messages_sv.po
Normal file
1326
locale/messages_uk.po
Normal file
1325
locale/messages_vi.po
Normal file
1335
locale/messages_zh.po
Normal file
113
nbproject/project.xml
Normal file
@ -0,0 +1,113 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||
<type>org.netbeans.modules.ant.freeform</type>
|
||||
<configuration>
|
||||
<general-data xmlns="http://www.netbeans.org/ns/freeform-project/1">
|
||||
<name>I2P-Bote</name>
|
||||
</general-data>
|
||||
<general-data xmlns="http://www.netbeans.org/ns/freeform-project/2">
|
||||
<!-- Do not use Project Properties customizer when editing this file manually. -->
|
||||
<name>I2P-Bote</name>
|
||||
<properties/>
|
||||
<folders>
|
||||
<source-folder>
|
||||
<label>I2P-Bote</label>
|
||||
<location>.</location>
|
||||
<encoding>UTF-8</encoding>
|
||||
</source-folder>
|
||||
<source-folder>
|
||||
<label>WebContent</label>
|
||||
<type>doc_root</type>
|
||||
<location>WebContent</location>
|
||||
</source-folder>
|
||||
<source-folder>
|
||||
<label>WEB-INF</label>
|
||||
<type>web_inf</type>
|
||||
<location>WebContent/WEB-INF</location>
|
||||
</source-folder>
|
||||
<source-folder>
|
||||
<label>test</label>
|
||||
<type>java</type>
|
||||
<location>test</location>
|
||||
<encoding>UTF-8</encoding>
|
||||
</source-folder>
|
||||
<source-folder>
|
||||
<label>src</label>
|
||||
<type>java</type>
|
||||
<location>src</location>
|
||||
<encoding>UTF-8</encoding>
|
||||
</source-folder>
|
||||
</folders>
|
||||
<ide-actions>
|
||||
<action name="build">
|
||||
<target>all</target>
|
||||
</action>
|
||||
<action name="clean">
|
||||
<target>clean</target>
|
||||
</action>
|
||||
<action name="test">
|
||||
<target>junit</target>
|
||||
</action>
|
||||
<action name="rebuild">
|
||||
<target>clean</target>
|
||||
<target>all</target>
|
||||
</action>
|
||||
</ide-actions>
|
||||
<export>
|
||||
<type>folder</type>
|
||||
<location>ant_build/classes</location>
|
||||
<build-target>all</build-target>
|
||||
</export>
|
||||
<view>
|
||||
<items>
|
||||
<source-folder style="tree">
|
||||
<label>Web Pages</label>
|
||||
<location>WebContent</location>
|
||||
</source-folder>
|
||||
<source-folder style="tree">
|
||||
<label>WEB-INF Content</label>
|
||||
<location>WebContent/WEB-INF</location>
|
||||
</source-folder>
|
||||
<source-folder style="packages">
|
||||
<label>test</label>
|
||||
<location>test</location>
|
||||
</source-folder>
|
||||
<source-folder style="packages">
|
||||
<label>src</label>
|
||||
<location>src</location>
|
||||
</source-folder>
|
||||
<source-file>
|
||||
<location>build.xml</location>
|
||||
</source-file>
|
||||
</items>
|
||||
<context-menu>
|
||||
<ide-action name="build"/>
|
||||
<ide-action name="rebuild"/>
|
||||
<ide-action name="clean"/>
|
||||
<ide-action name="test"/>
|
||||
</context-menu>
|
||||
</view>
|
||||
<subprojects/>
|
||||
</general-data>
|
||||
<java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/1">
|
||||
<compilation-unit>
|
||||
<package-root>test</package-root>
|
||||
<classpath mode="compile">../i2p/core/java/build/i2p.jar:../i2p/apps/jetty/jettylib/org.mortbay.jetty.jar:../i2p/apps/jetty/jettylib/jasper-compiler.jar:../i2p/apps/jetty/jettylib/jasper-runtime.jar:../i2p/apps/jetty/jettylib/javax.servlet.jar:../i2p/apps/jetty/jettylib/commons-logging.jar:../i2p/apps/jetty/jettylib/commons-el.jar:../i2p/apps/susidns/src/WEB-INF/lib/jstl.jar:../i2p/apps/susidns/src/WEB-INF/lib/standard.jar:WebContent/WEB-INF/lib/mailapi.jar:WebContent/WEB-INF/lib/bcprov-jdk15on-152.jar:ant_build/classes:/usr/local/netbeans-6.5/platform9/modules/ext/junit-4.5.jar:../i2p/router/java/build/router.jar:WebContent/WEB-INF/lib/commons-fileupload-1.2.1.jar:WebContent/WEB-INF/lib/commons-io-1.4.jar</classpath>
|
||||
<source-level>1.5</source-level>
|
||||
</compilation-unit>
|
||||
<compilation-unit>
|
||||
<package-root>src</package-root>
|
||||
<classpath mode="compile">../i2p/core/java/build/i2p.jar:../i2p/apps/jetty/jettylib/org.mortbay.jetty.jar:../i2p/apps/jetty/jettylib/jasper-compiler.jar:../i2p/apps/jetty/jettylib/jasper-runtime.jar:../i2p/apps/jetty/jettylib/javax.servlet.jar:../i2p/apps/jetty/jettylib/commons-logging.jar:../i2p/apps/jetty/jettylib/commons-el.jar:../i2p/apps/susidns/src/WEB-INF/lib/jstl.jar:../i2p/apps/susidns/src/WEB-INF/lib/standard.jar:WebContent/WEB-INF/lib/mailapi.jar:WebContent/WEB-INF/lib/bcprov-jdk15on-152.jar:ant_build/classes:../i2p/apps/streaming/java/build/streaming.jar:../i2p/apps/ministreaming/java/build/mstreaming.jar:../i2p/apps/routerconsole/java/build/routerconsole.jar:../i2p/router/java/build/router.jar:WebContent/WEB-INF/lib/commons-fileupload-1.2.1.jar:WebContent/WEB-INF/lib/commons-io-1.4.jar</classpath>
|
||||
<built-to>ant_build/classes</built-to>
|
||||
<source-level>1.5</source-level>
|
||||
</compilation-unit>
|
||||
</java-data>
|
||||
<web-data xmlns="http://www.netbeans.org/ns/freeform-project-web/1">
|
||||
<web-module>
|
||||
<doc-root>WebContent</doc-root>
|
||||
<classpath/>
|
||||
<j2ee-spec-level>1.5</j2ee-spec-level>
|
||||
</web-module>
|
||||
</web-data>
|
||||
</configuration>
|
||||
</project>
|
44
plugin/plugin.config
Normal file
@ -0,0 +1,44 @@
|
||||
name=i2pbote
|
||||
signer=str4d@mail.i2p
|
||||
consoleLinkName=SecureMail
|
||||
consoleLinkName_de=Sichere.Mail
|
||||
consoleLinkName_nl=Beveiligde Mail
|
||||
consoleLinkName_fr=CourrierSécurisé
|
||||
consoleLinkName_es=CorreoSeguro
|
||||
consoleLinkName_pt=CorreioSeguro
|
||||
consoleLinkName_ru=Безопасная почта
|
||||
consoleLinkName_no=SecureMail #SikkerPost
|
||||
consoleLinkName_sv=SäkerPost
|
||||
consoleLinkName_zh=安全电邮
|
||||
consoleLinkName_ar=بريد الكتروني الآمن
|
||||
consoleLinkTooltip=Decentralized secure email
|
||||
consoleLinkTooltip_de=Dezentrale, sichere Mailanwendung
|
||||
consoleLinkTooltip_nl=Gedecentraliseerde beveiligde email
|
||||
consoleLinkTooltip_fr=Courrier sécurisé et décentralisé
|
||||
consoleLinkTooltip_es=Correo electrónico seguro y descentralizado
|
||||
consoleLinkTooltip_pt=Correio eletrônico seguro e descentralizado
|
||||
consoleLinkTooltip_ru=Безопасная распределённая почта
|
||||
consoleLinkTooltip_no=Desentralisert sikker epost
|
||||
consoleLinkTooltip_sv=Decentraliserad säker e-post
|
||||
consoleLinkTooltip_zh=安全分布式电子邮件
|
||||
consoleLinkTooltip_ar=بريد الكتروني الآمن و الغير متمركز
|
||||
consoleLinkURL=/i2pbote/index.jsp
|
||||
console-icon=/themes/material/icon.png
|
||||
websiteURL=http://bote.i2p/
|
||||
updateURL=http://bote.i2p/i2pbote-update.xpi2p
|
||||
updateURL.su3=http://bote.i2p/i2pbote-update.su3
|
||||
description=Decentralized secure email
|
||||
description_de=Dezentrale, sichere Mailanwendung
|
||||
description_nl=Gedecentraliseerde beveiligde email
|
||||
description_fr=Courrier sécurisé et décentralisé
|
||||
description_es=Correo electrónico seguro y descentralizado
|
||||
description_pt=Correio eletrônico seguro e descentralizado
|
||||
description_ru=Безопасная распределённая почта
|
||||
description_no=Desentralisert sikker epost
|
||||
description_sv=Decentraliserad säker e-post
|
||||
description_zh=安全分布式电子邮件
|
||||
description_ar=بريد الكتروني الآمن و الغير متمركز
|
||||
license=GPL V3
|
||||
min-java-version=1.6
|
||||
min-i2p-version=0.9.18
|
||||
author=str4d@mail.i2p
|
1
plugin/webapps.config
Normal file
@ -0,0 +1 @@
|
||||
webapps.i2pbote.classpath=$I2P/lib/jstl.jar,$I2P/lib/standard.jar,$PLUGIN/lib/i2pbote.jar,$PLUGIN/lib/mailapi-1.5.4.jar,$PLUGIN/lib/bcprov-jdk15on-152.jar,$PLUGIN/lib/flexi-gmss-1.7p1.jar,$PLUGIN/lib/ntruenc-1.2.jar,$PLUGIN/lib/scrypt-1.4.0.jar,$PLUGIN/lib/lzma-9.20.jar,$PLUGIN/lib/apache-james-mailbox-api-0.6-20150508.040939-710.jar,$PLUGIN/lib/apache-james-mailbox-store-0.6-20150508.041003-704.jar,$PLUGIN/lib/apache-mime4j-core-0.8.0-20150617.024907-738.jar,$PLUGIN/lib/commons-codec-1.7.jar,$PLUGIN/lib/commons-collections-3.2.1.jar,$PLUGIN/lib/commons-configuration-1.9.jar,$PLUGIN/lib/commons-io-2.4.jar,$PLUGIN/lib/commons-lang-2.6.jar,$PLUGIN/lib/james-server-filesystem-api-3.0.0-beta5-SNAPSHOT.jar,$PLUGIN/lib/james-server-lifecycle-api-3.0.0-beta5-SNAPSHOT.jar,$PLUGIN/lib/james-server-protocols-imap4-3.0.0-beta5-SNAPSHOT.jar,$PLUGIN/lib/james-server-protocols-library-3.0.0-beta5-SNAPSHOT.jar,$PLUGIN/lib/james-server-util-3.0.0-beta5-SNAPSHOT.jar,$PLUGIN/lib/jcommon-1.0.23.jar,$PLUGIN/lib/jfreechart-1.0.19.jar,$PLUGIN/lib/jutf7-1.0.0.jar,$PLUGIN/lib/log4j-1.2.17.jar,$PLUGIN/lib/netty-3.3.1.Final.jar,$PLUGIN/lib/protocols-api-1.6.4-20150617.121129-1080.jar,$PLUGIN/lib/protocols-imap-1.6.4-20150617.121245-927.jar,$PLUGIN/lib/protocols-netty-1.6.4-20150617.121137-1044.jar,$PLUGIN/lib/slf4j-api-1.7.2.jar,$PLUGIN/lib/slf4j-log4j12-1.7.2.jar,$PLUGIN/lib/subethasmtp-3.1.7.jar
|
207
src/build/java/i2p/bote/ant/JspStrings.java
Normal file
@ -0,0 +1,207 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.ant;
|
||||
|
||||
import i2p.bote.Util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A command line program that extracts strings from <ib:message> tags and prints them
|
||||
* to stdout in .po format (the .po header is not printed).
|
||||
*/
|
||||
public class JspStrings {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
if (args.length < 1) {
|
||||
System.out.println("Syntax: JspStrings <Directory>");
|
||||
System.out.println();
|
||||
System.out.println("Prints .po entries for the <ib:message> keys in all .jsp");
|
||||
System.out.println("and .tag files in <Directory> and its subdirectories.");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
List<PoEntry> poEntries = processDirectory(new File(args[0]));
|
||||
removeDuplicates(poEntries);
|
||||
|
||||
for (PoEntry poEntry: poEntries)
|
||||
print(poEntry);
|
||||
}
|
||||
|
||||
static void removeDuplicates(List<PoEntry> poEntries) {
|
||||
for (int i=poEntries.size()-1; i>=0; i--)
|
||||
for (int j=poEntries.size()-1; j>i; j--) {
|
||||
PoEntry entry1 = poEntries.get(i);
|
||||
PoEntry entry2 = poEntries.get(j);
|
||||
if (entry1!=entry2 && entry1.msgKey.equals(entry2.msgKey))
|
||||
poEntries.remove(entry2);
|
||||
}
|
||||
}
|
||||
|
||||
static void print(PoEntry poEntry) {
|
||||
System.out.println("#: " + poEntry.comment);
|
||||
System.out.println("msgid \"" + poEntry.msgKey + "\"");
|
||||
System.out.println("msgstr \"\"");
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
static List<PoEntry> processDirectory(File dir) throws IOException {
|
||||
List<PoEntry> results = new ArrayList<PoEntry>();
|
||||
|
||||
// extract strings from all files in the directory
|
||||
File[] files = dir.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.toLowerCase().endsWith(".jsp") || name.toLowerCase().endsWith(".tag");
|
||||
}
|
||||
});
|
||||
for (File file: files)
|
||||
results.addAll(processFile(file));
|
||||
|
||||
// recurse into subdirectories
|
||||
File[] subdirs = dir.listFiles(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return pathname.isDirectory();
|
||||
}
|
||||
});
|
||||
for (File subdir: subdirs)
|
||||
results.addAll(processDirectory(subdir));
|
||||
return results;
|
||||
}
|
||||
|
||||
static List<PoEntry> processFile(File file) throws IOException {
|
||||
FileInputStream inputStream = new FileInputStream(file);
|
||||
BufferedReader inputReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
|
||||
// Read the file into a string
|
||||
StringBuilder strBuilder = new StringBuilder();
|
||||
while (true) {
|
||||
String line = inputReader.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
if (strBuilder.length() > 0)
|
||||
strBuilder = strBuilder.append(System.getProperty("line.separator"));
|
||||
strBuilder = strBuilder.append(line);
|
||||
}
|
||||
inputStream.close();
|
||||
String fileContents = strBuilder.toString();
|
||||
|
||||
String[] tags = fileContents.split("<ib:message");
|
||||
|
||||
List<String> msgKeys = new ArrayList<String>();
|
||||
for (int i=1; i<tags.length; i++) {
|
||||
String tag = tags[i];
|
||||
if (!shouldSkip(tag)) {
|
||||
String key = extract(tag);
|
||||
if (key != null)
|
||||
msgKeys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
List<PoEntry> entries = new ArrayList<PoEntry>();
|
||||
entries.addAll(PoEntry.create(msgKeys, file));
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a message key from a ib:message tag
|
||||
* @param tag a ib:message tag minus the <ib:message part at the beginning
|
||||
* @return
|
||||
*/
|
||||
static String extract(String tag) {
|
||||
if (tag.contains(" key=")) // format is <ib:message key="foobar" .../> ...
|
||||
return extractFromKeyAttribute(tag);
|
||||
else // format is <ib:message ...>foobar</ib:message> ...
|
||||
return extractFromTagBody(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the text between <ib:message> and </ib:message>
|
||||
* @param tag a ib:message tag minus the <ib:message part at the beginning
|
||||
*/
|
||||
static String extractFromTagBody(String tag) {
|
||||
int gtIndex = tag.indexOf(">");
|
||||
tag = tag.substring(gtIndex + 1);
|
||||
int endIdx = tag.indexOf("</ib:message");
|
||||
if (endIdx < 0) {
|
||||
System.err.println("No \"</ib:message\" end tag in string: <" + tag + ">");
|
||||
return null;
|
||||
}
|
||||
String str = tag.substring(0, endIdx);
|
||||
// convert multiple whitespaces in a row to a single space, remove leading and trailing whitespace
|
||||
str = Util.removeExtraWhitespace(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
/** Extracts the values of the "key" attribute */
|
||||
static String extractFromKeyAttribute(String string) {
|
||||
String keyAttrSQ = "key='";
|
||||
String keyAttrDQ = "key=\"";
|
||||
int keyAttrLen = keyAttrDQ.length();
|
||||
|
||||
int startIdxSQ = string.indexOf(keyAttrSQ);
|
||||
int endIdxSQ = string.indexOf("'", startIdxSQ+keyAttrLen);
|
||||
int startIdxDQ = string.indexOf(keyAttrDQ);
|
||||
int endIdxDQ = string.indexOf("\"", startIdxDQ+keyAttrLen);
|
||||
|
||||
if (startIdxSQ>=0 && (startIdxSQ<startIdxDQ || startIdxDQ<0) && endIdxSQ>=0)
|
||||
return string.substring(startIdxSQ+keyAttrLen, endIdxSQ);
|
||||
else if (startIdxDQ>=0 && (startIdxDQ<startIdxSQ || startIdxSQ<0) && endIdxDQ>=0)
|
||||
return string.substring(startIdxDQ+keyAttrLen, endIdxDQ);
|
||||
else {
|
||||
System.err.println("Expected a string containing key=\"...\" or key='...', got " + string);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns <code>true</code> if a ib:message tag has the noextract attribute set to <code>true</code>. */
|
||||
static boolean shouldSkip(String tag) {
|
||||
tag = tag.replaceAll("\\s+", "");
|
||||
return tag.contains("noextract=\"true\"") || tag.contains("noextract='true'");
|
||||
}
|
||||
|
||||
static class PoEntry {
|
||||
String msgKey;
|
||||
String comment;
|
||||
|
||||
static List<PoEntry> create(List<String> msgKeys, File file) {
|
||||
List<PoEntry> poEntries = new ArrayList<PoEntry>();
|
||||
for (String msgKey: msgKeys) {
|
||||
PoEntry entry = new PoEntry();
|
||||
entry.msgKey = msgKey;
|
||||
entry.comment = file.toString();
|
||||
poEntries.add(entry);
|
||||
}
|
||||
return poEntries;
|
||||
}
|
||||
}
|
||||
}
|
31
src/build/java/i2p/bote/ant/PrintAppVersion.java
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.ant;
|
||||
|
||||
import i2p.bote.I2PBote;
|
||||
|
||||
public class PrintAppVersion {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(I2PBote.getAppVersion());
|
||||
}
|
||||
}
|
107
src/build/java/i2p/bote/ant/SearchAndInsert.java
Normal file
@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.ant;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Searches a text file for a string and adds text before
|
||||
* or after the first occurrence of the search string.
|
||||
*/
|
||||
public class SearchAndInsert {
|
||||
enum Position {BEFORE, AFTER};
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
if (args.length < 4) {
|
||||
System.out.println("Syntax: SearchAndInsert [-after|-before] <inputFile> <outputFile> <key> <newTextFile>");
|
||||
System.out.println();
|
||||
System.out.println("Searches a text file for a string and adds text before or after");
|
||||
System.out.println("the first occurrence of the search string. The new text is read");
|
||||
System.out.println("from a file.");
|
||||
System.out.println("The default is to insert after the search string.");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
int argIndex = 0;
|
||||
Position position = Position.AFTER;
|
||||
if ("-before".equals(args[0]) || "-after".equals(args[0])) {
|
||||
argIndex++;
|
||||
if ("-before".equals(args[0]))
|
||||
position = Position.BEFORE;
|
||||
}
|
||||
|
||||
BufferedReader reader = null;
|
||||
BufferedWriter writer = null;
|
||||
try {
|
||||
reader = new BufferedReader(new FileReader(args[argIndex++]));
|
||||
writer = new BufferedWriter(new FileWriter(args[argIndex++]));
|
||||
|
||||
String searchString = args[argIndex++];
|
||||
String newTextFile = args[argIndex];
|
||||
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
boolean match = line.contains(searchString);
|
||||
if (match && position==Position.BEFORE) {
|
||||
write(writer, newTextFile);
|
||||
writer.newLine();
|
||||
}
|
||||
writer.write(line);
|
||||
writer.newLine();
|
||||
if (match && position==Position.AFTER) {
|
||||
write(writer, newTextFile);
|
||||
writer.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (reader != null)
|
||||
reader.close();
|
||||
if (writer != null)
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void write(BufferedWriter writer, String filename) throws IOException {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new FileReader(filename));
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
writer.write(line);
|
||||
writer.newLine();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (reader != null)
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
}
|
120
src/build/scripts/bundle-messages.sh
Executable file
@ -0,0 +1,120 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Update messages_xx.po and messages_xx.class files,
|
||||
# from both java and jsp sources.
|
||||
# Requires installed programs xgettext, msgfmt, msgmerge, msguniq, find,
|
||||
# and the Java class i2p.bote.ant.JspStrings.
|
||||
# On Linux, the programs xgettext and msg* can be found in the gettext package.
|
||||
#
|
||||
# usage:
|
||||
# bundle-messages.sh (generates the resource bundle from the .po file)
|
||||
# bundle-messages.sh -p (updates the .po file from the source tags, then generates the resource bundle)
|
||||
#
|
||||
# zzz - public domain
|
||||
#
|
||||
JAVA=java
|
||||
CLASS=i2p.bote.locale.Messages
|
||||
TMPFILE=ant_build/javafiles.txt
|
||||
export TZ=UTC
|
||||
|
||||
if [ "$1" = "-p" ]
|
||||
then
|
||||
POUPDATE=1
|
||||
fi
|
||||
|
||||
# on windows, one must specify the path of commnad find
|
||||
# since windows has its own retarded version of find.
|
||||
if which find|grep -q -i windows ; then
|
||||
export PATH=.:/bin:/usr/local/bin:$PATH
|
||||
fi
|
||||
# Fast mode - update ondemond
|
||||
echo setting the environment variable LG2={LangCode}
|
||||
echo will limit .po file update to the language specified by {LangCode}.
|
||||
|
||||
# add ../java/ so the refs will work in the po file
|
||||
JPATHS="src/main/java"
|
||||
JSPPATHS="src/main/webapp"
|
||||
for i in locale/messages_*.po
|
||||
do
|
||||
# get language
|
||||
LG=${i#locale/messages_}
|
||||
LG=${LG%.po}
|
||||
|
||||
# skip, if specified
|
||||
if [ $LG2 ]; then
|
||||
[ $LG != $LG2 ] && continue || echo INFO: Language update is set to [$LG2] only.
|
||||
fi
|
||||
|
||||
if [ "$POUPDATE" = "1" ]
|
||||
then
|
||||
# make list of java files newer than the .po file
|
||||
find $JPATHS -name *.java -newer $i > $TMPFILE
|
||||
find $JSPPATHS -name *.java -newer $i >> $TMPFILE
|
||||
fi
|
||||
|
||||
if [ -s ant_build/classes/i2p/bote/locale/messages_$LG.class -a \
|
||||
ant_build/classes/i2p/bote/locale/messages_$LG.class -nt $i -a \
|
||||
! -s $TMPFILE ]
|
||||
then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$POUPDATE" = "1" ]
|
||||
then
|
||||
echo "Updating the $i file from the tags..."
|
||||
# extract strings from java and jsp files, and update messages.po files
|
||||
# translate calls must be one of the forms:
|
||||
# _("foo")
|
||||
# _x("foo")
|
||||
# To start a new translation, copy the header from an old translation to the new .po file,
|
||||
# then ant distclean poupdate.
|
||||
find $JPATHS -name *.java > $TMPFILE
|
||||
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 \
|
||||
--keyword=_ --keyword=_x \
|
||||
-o ${i}t
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo 'Warning - xgettext failed, not updating translations'
|
||||
rm -f ${i}t
|
||||
break
|
||||
fi
|
||||
|
||||
# extract strings from jsp files
|
||||
$JAVA -cp ant_build/classes:$I2P/lib/i2p.jar i2p.bote.ant.JspStrings $JSPPATHS >> ${i}t
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo 'Warning - JspStrings failed, not updating translations'
|
||||
rm -f ${i}t
|
||||
break
|
||||
fi
|
||||
|
||||
msguniq -o ${i}t ${i}t
|
||||
msgmerge -U --backup=none $i ${i}t
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo 'Warning - msgmerge failed, not updating translations'
|
||||
rm -f ${i}t
|
||||
break
|
||||
fi
|
||||
rm -f ${i}t
|
||||
# so we don't do this again
|
||||
touch $i
|
||||
fi
|
||||
|
||||
if [ "$LG" != "en" ]
|
||||
then
|
||||
# only generate for non-source language
|
||||
echo "Generating ${CLASS}_$LG ResourceBundle..."
|
||||
|
||||
# convert to class files in ant_build/classes
|
||||
msgfmt --java --statistics -r $CLASS -l $LG -d ant_build/classes $i
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo 'Warning - msgfmt failed, not updating translations'
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
rm -f $TMPFILE
|
||||
# todo: return failure
|
||||
exit 0
|
@ -1,223 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class Configuration extends Properties {
|
||||
private static final long serialVersionUID = -6318245413106186095L;
|
||||
private static final String I2P_BOTE_SUBDIR = "i2pbote"; // relative to the I2P app dir
|
||||
private static final String CONFIG_FILE_NAME = "i2pbote.config";
|
||||
private static final String DEST_KEY_FILE_NAME = "local_dest.key";
|
||||
private static final String PEER_FILE_NAME = "peers.txt";
|
||||
private static final String IDENTITIES_FILE_NAME = "identities.txt";
|
||||
private static final String OUTBOX_DIR = "outbox"; // relative to I2P_BOTE_SUBDIR
|
||||
private static final String OUTBOX_SUBDIR_LOCAL = "local"; // relative to OUTBOX_DIR
|
||||
private static final String OUTBOX_SUBDIR_RELAY = "relay"; // relative to OUTBOX_DIR
|
||||
private static final String INCOMPLETE_SUBDIR = "incomplete"; // relative to I2P_BOTE_SUBDIR
|
||||
private static final String EMAIL_DHT_SUBDIR = "dht_email_pkt"; // relative to I2P_BOTE_SUBDIR
|
||||
private static final String INDEX_PACKET_DHT_SUBDIR = "dht_index_pkt"; // relative to I2P_BOTE_SUBDIR
|
||||
private static final String INBOX_SUBDIR = "inbox"; // relative to I2P_BOTE_SUBDIR
|
||||
|
||||
// Parameter names in the config file
|
||||
private static final String PARAMETER_REDUNDANCY = "redundancy";
|
||||
private static final String PARAMETER_STORAGE_SPACE_INBOX = "storageSpaceInbox";
|
||||
private static final String PARAMETER_STORAGE_SPACE_RELAY = "storageSpaceRelay";
|
||||
private static final String PARAMETER_STORAGE_TIME = "storageTime";
|
||||
private static final String PARAMETER_MAX_FRAGMENT_SIZE = "maxFragmentSize";
|
||||
private static final String PARAMETER_HASHCASH_STRENGTH = "hashCashStrength";
|
||||
private static final String PARAMETER_SMTP_PORT = "smtpPort";
|
||||
private static final String PARAMETER_POP3_PORT = "pop3Port";
|
||||
private static final String PARAMETER_MAX_CONCURRENT_IDENTITIES_CHECK_MAIL = "maxConcurIdCheckMail";
|
||||
|
||||
// Defaults for each parameter
|
||||
private static final int DEFAULT_REDUNDANCY = 2;
|
||||
private static final int DEFAULT_STORAGE_SPACE_INBOX = 1024 * 1024 * 1024;
|
||||
private static final int DEFAULT_STORAGE_SPACE_RELAY = 100 * 1024 * 1024;
|
||||
private static final int DEFAULT_STORAGE_TIME = 31; // in days
|
||||
private static final int DEFAULT_MAX_FRAGMENT_SIZE = 10 * 1024 * 1024; // the maximum size one email fragment can be, in bytes
|
||||
private static final int DEFAULT_HASHCASH_STRENGTH = 10;
|
||||
private static final int DEFAULT_SMTP_PORT = 7661;
|
||||
private static final int DEFAULT_POP3_PORT = 7662;
|
||||
private static final int DEFAULT_MAX_CONCURRENT_IDENTITIES_CHECK_MAIL = 10;
|
||||
|
||||
private Log log = new Log(Configuration.class);
|
||||
private File i2pBoteDir;
|
||||
|
||||
/**
|
||||
* Reads configuration settings from the <code>I2P_BOTE_SUBDIR</code> subdirectory under
|
||||
* the I2P application directory. The I2P application directory can be changed via the
|
||||
* <code>i2p.dir.app</code> system property.
|
||||
*
|
||||
* Logging is done through the I2P logger. I2P reads the log configuration from the
|
||||
* <code>logger.config</code> file whose location is determined by the
|
||||
* <code>i2p.dir.config</code> system property.
|
||||
* @return
|
||||
*/
|
||||
public Configuration() {
|
||||
// get the I2PBote directory and make sure it exists
|
||||
i2pBoteDir = getI2PBoteDirectory();
|
||||
if (!i2pBoteDir.exists())
|
||||
i2pBoteDir.mkdirs();
|
||||
|
||||
// read the configuration file
|
||||
File configFile = new File(i2pBoteDir, CONFIG_FILE_NAME);
|
||||
boolean configurationLoaded = false;
|
||||
if (configFile.exists()) {
|
||||
log.debug("Loading config file <" + configFile.getAbsolutePath() + ">");
|
||||
|
||||
try {
|
||||
DataHelper.loadProps(this, configFile);
|
||||
configurationLoaded = true;
|
||||
} catch (IOException e) {
|
||||
log.error("Error loading configuration file <" + configFile.getAbsolutePath() + ">", e);
|
||||
}
|
||||
}
|
||||
if (!configurationLoaded)
|
||||
log.info("Can't read configuration file <" + configFile.getAbsolutePath() + ">, using default settings.");
|
||||
}
|
||||
|
||||
public File getDestinationKeyFile() {
|
||||
return new File(i2pBoteDir, DEST_KEY_FILE_NAME);
|
||||
}
|
||||
|
||||
public File getPeerFile() {
|
||||
return new File(i2pBoteDir, PEER_FILE_NAME);
|
||||
}
|
||||
|
||||
public File getIdentitiesFile() {
|
||||
return new File(i2pBoteDir, IDENTITIES_FILE_NAME);
|
||||
}
|
||||
|
||||
public File getLocalOutboxDir() {
|
||||
return new File(getOutboxBaseDir(), OUTBOX_SUBDIR_LOCAL);
|
||||
}
|
||||
|
||||
public File getRelayOutboxDir() {
|
||||
return new File(getOutboxBaseDir(), OUTBOX_SUBDIR_RELAY);
|
||||
}
|
||||
|
||||
private File getOutboxBaseDir() {
|
||||
return new File(i2pBoteDir, OUTBOX_DIR);
|
||||
}
|
||||
|
||||
public File getInboxDir() {
|
||||
return new File(i2pBoteDir, INBOX_SUBDIR);
|
||||
}
|
||||
|
||||
public File getIncompleteDir() {
|
||||
return new File(i2pBoteDir, INCOMPLETE_SUBDIR);
|
||||
}
|
||||
|
||||
public File getEmailDhtStorageDir() {
|
||||
return new File(i2pBoteDir, EMAIL_DHT_SUBDIR);
|
||||
}
|
||||
|
||||
public File getIndexPacketDhtStorageDir() {
|
||||
return new File(i2pBoteDir, INDEX_PACKET_DHT_SUBDIR);
|
||||
}
|
||||
|
||||
private static File getI2PBoteDirectory() {
|
||||
// the parent directory of the I2PBote directory ($HOME or the value of the i2p.dir.app property)
|
||||
File i2pAppDir = I2PAppContext.getGlobalContext().getAppDir();
|
||||
|
||||
return new File(i2pAppDir, I2P_BOTE_SUBDIR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the configuration
|
||||
* @param configFile
|
||||
* @throws IOException
|
||||
*/
|
||||
public void saveToFile(File configFile) throws IOException {
|
||||
log.debug("Saving config file <" + configFile.getAbsolutePath() + ">");
|
||||
DataHelper.storeProps(this, configFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of relays to use for sending and receiving email. Zero is a legal value.
|
||||
* @return
|
||||
*/
|
||||
public int getRedundancy() {
|
||||
return getIntParameter(PARAMETER_REDUNDANCY, DEFAULT_REDUNDANCY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum size (in bytes) the inbox can take up.
|
||||
* @return
|
||||
*/
|
||||
public int getStorageSpaceInbox() {
|
||||
return getIntParameter(PARAMETER_STORAGE_SPACE_INBOX, DEFAULT_STORAGE_SPACE_INBOX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum size (in bytes) all messages stored for relaying can take up.
|
||||
* @return
|
||||
*/
|
||||
public int getStorageSpaceRelay() {
|
||||
return getIntParameter(PARAMETER_STORAGE_SPACE_RELAY, DEFAULT_STORAGE_SPACE_RELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time (in milliseconds) after which an email is deleted from the outbox if it cannot be sent / relayed.
|
||||
* @return
|
||||
*/
|
||||
public long getStorageTime() {
|
||||
return 24L * 3600 * 1000 * getIntParameter(PARAMETER_STORAGE_TIME, DEFAULT_STORAGE_TIME);
|
||||
}
|
||||
|
||||
public int getMaxFragmentSize() {
|
||||
return getIntParameter(PARAMETER_MAX_FRAGMENT_SIZE, DEFAULT_MAX_FRAGMENT_SIZE);
|
||||
}
|
||||
|
||||
public int getHashCashStrength() {
|
||||
return getIntParameter(PARAMETER_HASHCASH_STRENGTH, DEFAULT_HASHCASH_STRENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of email identities to retrieve new emails for at a time.
|
||||
* @return
|
||||
*/
|
||||
public int getMaxConcurIdCheckMail() {
|
||||
return getIntParameter(PARAMETER_MAX_CONCURRENT_IDENTITIES_CHECK_MAIL, DEFAULT_MAX_CONCURRENT_IDENTITIES_CHECK_MAIL);
|
||||
}
|
||||
|
||||
private int getIntParameter(String parameterName, int defaultValue) {
|
||||
String stringValue = getProperty(parameterName);
|
||||
if (stringValue == null)
|
||||
return defaultValue;
|
||||
else
|
||||
try {
|
||||
return new Integer(getProperty(parameterName));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
log.warn("Can't convert value <" + stringValue + "> for parameter <" + parameterName + "> to int, using default.");
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,358 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote;
|
||||
|
||||
import i2p.bote.email.Email;
|
||||
import i2p.bote.email.EmailDestination;
|
||||
import i2p.bote.email.EmailIdentity;
|
||||
import i2p.bote.email.Identities;
|
||||
import i2p.bote.folder.DhtPacketFolder;
|
||||
import i2p.bote.folder.EmailFolder;
|
||||
import i2p.bote.folder.IncompleteEmailFolder;
|
||||
import i2p.bote.folder.IndexPacketFolder;
|
||||
import i2p.bote.folder.Outbox;
|
||||
import i2p.bote.folder.PacketFolder;
|
||||
import i2p.bote.network.CheckEmailTask;
|
||||
import i2p.bote.network.DHT;
|
||||
import i2p.bote.network.I2PPacketDispatcher;
|
||||
import i2p.bote.network.I2PSendQueue;
|
||||
import i2p.bote.network.PeerManager;
|
||||
import i2p.bote.network.kademlia.KademliaDHT;
|
||||
import i2p.bote.packet.DataPacket;
|
||||
import i2p.bote.packet.EncryptedEmailPacket;
|
||||
import i2p.bote.packet.IndexPacket;
|
||||
import i2p.bote.packet.RelayPacket;
|
||||
import i2p.bote.packet.UnencryptedEmailPacket;
|
||||
import i2p.bote.service.OutboxProcessor;
|
||||
import i2p.bote.service.POP3Service;
|
||||
import i2p.bote.service.RelayPacketSender;
|
||||
import i2p.bote.service.SMTPService;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* This is the core class of the application. Is is implemented as a singleton.
|
||||
*/
|
||||
public class I2PBote {
|
||||
private static final String VERSION = "0.1.2";
|
||||
private static I2PBote instance;
|
||||
|
||||
private Log log = new Log(I2PBote.class);
|
||||
private I2PAppContext appContext;
|
||||
private I2PClient i2pClient;
|
||||
private I2PSession i2pSession;
|
||||
private Configuration configuration;
|
||||
private Identities identities;
|
||||
private I2PSendQueue sendQueue;
|
||||
private Outbox outbox; // stores outgoing emails for all local users
|
||||
private EmailFolder inbox; // stores incoming emails for all local users
|
||||
private PacketFolder<RelayPacket> relayPacketFolder; // stores email packets we're forwarding for other machines
|
||||
private IncompleteEmailFolder incompleteEmailFolder; // stores email packets addressed to a local user
|
||||
private DhtPacketFolder<? extends DataPacket> emailDhtStorageFolder; // stores email packets and index packets for other peers
|
||||
private IndexPacketFolder indexPacketDhtStorageFolder; // stores index packets
|
||||
//TODO private PacketFolder<> addressDhtStorageFolder; // stores email address-destination mappings
|
||||
private SMTPService smtpService;
|
||||
private POP3Service pop3Service;
|
||||
private OutboxProcessor outboxProcessor; // reads emails stored in the outbox and sends them
|
||||
private RelayPacketSender relayPacketSender; // reads packets stored in the relayPacketFolder and sends them
|
||||
private DHT dht;
|
||||
private PeerManager peerManager;
|
||||
private ThreadFactory mailCheckThreadFactory;
|
||||
private ExecutorService mailCheckExecutor;
|
||||
private Collection<Future<Boolean>> mailCheckResults;
|
||||
|
||||
private I2PBote() {
|
||||
Thread.currentThread().setName("I2PBoteMain");
|
||||
|
||||
initializeLogging();
|
||||
|
||||
appContext = new I2PAppContext();
|
||||
i2pClient = I2PClientFactory.createClient();
|
||||
configuration = new Configuration();
|
||||
|
||||
mailCheckThreadFactory = Util.createThreadFactory("ChkMailTask", CheckEmailTask.THREAD_STACK_SIZE);
|
||||
mailCheckExecutor = Executors.newFixedThreadPool(configuration.getMaxConcurIdCheckMail(), mailCheckThreadFactory);
|
||||
|
||||
identities = new Identities(configuration.getIdentitiesFile());
|
||||
initializeSession();
|
||||
initializeFolderAccess();
|
||||
initializeServices();
|
||||
startAllServices();
|
||||
}
|
||||
|
||||
private void initializeLogging() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes objects for accessing emails and packet files on the filesystem.
|
||||
*/
|
||||
private void initializeFolderAccess() {
|
||||
inbox = new EmailFolder(configuration.getInboxDir());
|
||||
outbox = new Outbox(configuration.getLocalOutboxDir());
|
||||
relayPacketFolder = new PacketFolder<RelayPacket>(configuration.getRelayOutboxDir());
|
||||
incompleteEmailFolder = new IncompleteEmailFolder(configuration.getIncompleteDir(), inbox);
|
||||
emailDhtStorageFolder = new DhtPacketFolder<EncryptedEmailPacket>(configuration.getEmailDhtStorageDir());
|
||||
indexPacketDhtStorageFolder = new IndexPacketFolder(configuration.getIndexPacketDhtStorageDir());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a {@link I2PSession}, using the I2P destination stored on disk or creating a new I2P
|
||||
* destination if no key file exists.
|
||||
*/
|
||||
private void initializeSession() {
|
||||
Properties sessionProperties = new Properties();
|
||||
sessionProperties.setProperty("i2cp.gzip", String.valueOf(false)); // most of the data we send is encrypted and therefore uncompressible
|
||||
|
||||
// read the local destination key from the key file if it exists
|
||||
File destinationKeyFile = configuration.getDestinationKeyFile();
|
||||
try {
|
||||
FileReader fileReader = new FileReader(destinationKeyFile);
|
||||
char[] destKeyBuffer = new char[(int)destinationKeyFile.length()];
|
||||
fileReader.read(destKeyBuffer);
|
||||
byte[] localDestinationKey = Base64.decode(new String(destKeyBuffer));
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(localDestinationKey);
|
||||
i2pSession = i2pClient.createSession(inputStream, sessionProperties);
|
||||
i2pSession.connect();
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.debug("Destination key file doesn't exist or isn't readable: " + e);
|
||||
} catch (I2PSessionException e) {
|
||||
log.warn("Error creating I2PSession", e);
|
||||
}
|
||||
|
||||
// if the local destination key can't be read or is invalid, create a new one
|
||||
if (i2pSession == null) {
|
||||
log.debug("Creating new local destination key");
|
||||
try {
|
||||
ByteArrayOutputStream arrayStream = new ByteArrayOutputStream();
|
||||
i2pClient.createDestination(arrayStream);
|
||||
byte[] localDestinationArray = arrayStream.toByteArray();
|
||||
|
||||
i2pSession = i2pClient.createSession(new ByteArrayInputStream(localDestinationArray), sessionProperties);
|
||||
i2pSession.connect();
|
||||
|
||||
saveLocalDestinationKeys(destinationKeyFile, localDestinationArray);
|
||||
} catch (I2PException e) {
|
||||
log.error("Error creating local destination key or I2PSession.", e);
|
||||
} catch (IOException e) {
|
||||
log.error("Error writing local destination key to file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
Destination localDestination = i2pSession.getMyDestination();
|
||||
log.debug("Local destination key = " + localDestination.toBase64());
|
||||
log.debug("Local destination hash = " + localDestination.calculateHash().toBase64());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes daemon threads, doesn't start them yet.
|
||||
*/
|
||||
private void initializeServices() {
|
||||
I2PPacketDispatcher dispatcher = new I2PPacketDispatcher();
|
||||
i2pSession.addSessionListener(dispatcher, I2PSession.PROTO_ANY, I2PSession.PORT_ANY);
|
||||
|
||||
smtpService = new SMTPService();
|
||||
pop3Service = new POP3Service();
|
||||
relayPacketSender = new RelayPacketSender(sendQueue, relayPacketFolder, appContext);
|
||||
sendQueue = new I2PSendQueue(i2pSession, dispatcher);
|
||||
|
||||
dht = new KademliaDHT(i2pSession.getMyDestination(), sendQueue, dispatcher, configuration.getPeerFile());
|
||||
|
||||
dht.setStorageHandler(EncryptedEmailPacket.class, emailDhtStorageFolder);
|
||||
dht.setStorageHandler(IndexPacket.class, indexPacketDhtStorageFolder);
|
||||
//TODO dht.setStorageHandler(AddressPacket.class, );
|
||||
|
||||
peerManager = new PeerManager();
|
||||
|
||||
outboxProcessor = new OutboxProcessor(dht, outbox, configuration, peerManager, appContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes private + public keys for the local destination out to a file.
|
||||
* @param keyFile
|
||||
* @param localDestinationArray
|
||||
* @throws DataFormatException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void saveLocalDestinationKeys(File keyFile, byte[] localDestinationArray) throws DataFormatException, IOException {
|
||||
if (keyFile.exists()) {
|
||||
File oldKeyFile = new File(keyFile.getPath() + "_backup");
|
||||
keyFile.renameTo(oldKeyFile);
|
||||
}
|
||||
else
|
||||
keyFile.createNewFile();
|
||||
|
||||
FileWriter fileWriter = new FileWriter(keyFile);
|
||||
fileWriter.write(Base64.encode(localDestinationArray));
|
||||
fileWriter.close();
|
||||
}
|
||||
|
||||
public static void startUp() {
|
||||
getInstance();
|
||||
}
|
||||
|
||||
public static void shutDown() {
|
||||
if (instance != null)
|
||||
instance.stopAllServices();
|
||||
}
|
||||
|
||||
public static I2PBote getInstance() {
|
||||
if (instance == null)
|
||||
instance = new I2PBote();
|
||||
return instance;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
public Identities getIdentities() {
|
||||
return identities;
|
||||
}
|
||||
|
||||
public String getLocalDestination() {
|
||||
return i2pSession.getMyDestination().toBase64();
|
||||
}
|
||||
|
||||
public void sendEmail(Email email) throws Exception {
|
||||
/*XXX*/
|
||||
email.updateHeaders();
|
||||
String recipient = email.getAllRecipients().iterator().next();
|
||||
if (recipient.indexOf('@')>=0)
|
||||
recipient = recipient.substring(0, recipient.indexOf('@'));
|
||||
EmailDestination emailDestination = new EmailDestination(recipient);
|
||||
Collection<UnencryptedEmailPacket> emailPackets = email.createEmailPackets(recipient);
|
||||
Collection<EncryptedEmailPacket> encryptedPackets = EncryptedEmailPacket.encrypt(emailPackets, emailDestination, appContext);
|
||||
for (EncryptedEmailPacket packet: encryptedPackets)
|
||||
dht.store(packet);
|
||||
dht.store(new IndexPacket(encryptedPackets, emailDestination));
|
||||
// TODO uncomment for next milestone, move code above into OutboxProcessor
|
||||
/* outbox.add(email);
|
||||
outboxProcessor.checkForEmail();*/
|
||||
}
|
||||
|
||||
public synchronized void checkForMail() {
|
||||
if (!isCheckingForMail())
|
||||
mailCheckResults = Collections.synchronizedCollection(new ArrayList<Future<Boolean>>());
|
||||
for (EmailIdentity identity: getIdentities()) {
|
||||
Callable<Boolean> checkMailTask = new CheckEmailTask(identity, dht, peerManager, incompleteEmailFolder, appContext);
|
||||
Future<Boolean> result = mailCheckExecutor.submit(checkMailTask);
|
||||
mailCheckResults.add(result);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean isCheckingForMail() {
|
||||
if (mailCheckResults == null)
|
||||
return false;
|
||||
|
||||
for (Future<Boolean> result: mailCheckResults)
|
||||
if (!result.isDone())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the last call to {@link checkForMail} has completed
|
||||
* and added new mail to the inbox.
|
||||
* If this method returns <code>true</code>, subsequent calls will always return
|
||||
* <code>false</code> until {@link checkForMail} is executed again.
|
||||
* @return
|
||||
*/
|
||||
public synchronized boolean newMailReceived() {
|
||||
if (mailCheckResults == null)
|
||||
return false;
|
||||
if (isCheckingForMail())
|
||||
return false;
|
||||
|
||||
try {
|
||||
for (Future<Boolean> result: mailCheckResults)
|
||||
if (result.get(1, TimeUnit.MILLISECONDS)) {
|
||||
mailCheckResults = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Error while checking whether new mail has arrived.", e);
|
||||
}
|
||||
|
||||
mailCheckResults = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public EmailFolder getInbox() {
|
||||
return inbox;
|
||||
}
|
||||
|
||||
public int getNumDhtPeers() {
|
||||
return dht.getNumPeers();
|
||||
}
|
||||
|
||||
public int getNumRelayPeers() {
|
||||
return peerManager.getNumPeers();
|
||||
}
|
||||
|
||||
private void startAllServices() {
|
||||
dht.start();
|
||||
outboxProcessor.start();
|
||||
relayPacketSender.start();
|
||||
smtpService.start();
|
||||
pop3Service.start();
|
||||
sendQueue.start();
|
||||
}
|
||||
|
||||
private void stopAllServices() {
|
||||
dht.shutDown();
|
||||
outboxProcessor.shutDown();
|
||||
relayPacketSender.requestShutdown();
|
||||
smtpService.shutDown();
|
||||
pop3Service.shutDown();
|
||||
sendQueue.requestShutdown();
|
||||
mailCheckExecutor.shutdownNow();
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.data.DataFormatException;
|
||||
|
||||
public class Util {
|
||||
private static final int BUFFER_SIZE = 32 * 1024;
|
||||
|
||||
private Util() { }
|
||||
|
||||
public static void copyBytes(InputStream inputStream, OutputStream outputStream) throws IOException {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
int bytesRead = inputStream.read(buffer);
|
||||
outputStream.write(buffer);
|
||||
if (bytesRead < 0)
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeKeyStream(I2PSession i2pSession, OutputStream outputStream) throws DataFormatException, IOException {
|
||||
i2pSession.getMyDestination().writeBytes(outputStream);
|
||||
i2pSession.getDecryptionKey().writeBytes(outputStream);
|
||||
i2pSession.getPrivateKey().writeBytes(outputStream);
|
||||
}
|
||||
|
||||
public static InputStream createKeyStream(I2PSession i2pSession) throws DataFormatException, IOException {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
writeKeyStream(i2pSession, outputStream);
|
||||
return new ByteArrayInputStream(outputStream.toByteArray());
|
||||
}
|
||||
|
||||
public static byte[] readInputStream(InputStream inputStream) throws IOException {
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[32*1024];
|
||||
while (true) {
|
||||
int bytesToRead = Math.min(inputStream.available(), buffer.length);
|
||||
if (bytesToRead <= 0)
|
||||
break;
|
||||
else {
|
||||
int bytesRead = inputStream.read(buffer, 0, bytesToRead);
|
||||
byteStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
return byteStream.toByteArray();
|
||||
}
|
||||
|
||||
public static ThreadFactory createThreadFactory(final String threadName, final int stackSize) {
|
||||
return new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable runnable) {
|
||||
return new Thread(null, runnable, threadName, stackSize);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -1,408 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.email;
|
||||
|
||||
import i2p.bote.UniqueId;
|
||||
import i2p.bote.Util;
|
||||
import i2p.bote.folder.FolderElement;
|
||||
import i2p.bote.packet.UnencryptedEmailPacket;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import com.nettgryppa.security.HashCash;
|
||||
|
||||
// TODO move one package up
|
||||
public class Email implements FolderElement {
|
||||
private static final int MAX_BYTES_PER_PACKET = 30 * 1024;
|
||||
private static final char[] NEW_LINE = new char[] {13, 10}; // separates header section from mail body; same for all platforms per RFC 5322
|
||||
private static final Set<String> HEADER_WHITELIST = createHeaderWhitelist();
|
||||
|
||||
private static Log log = new Log(Email.class);
|
||||
private File file;
|
||||
private List<Header> headers;
|
||||
private byte[] content; // save memory by using bytes rather than chars
|
||||
private MessageId messageId;
|
||||
|
||||
public Email() {
|
||||
headers = Collections.synchronizedList(new ArrayList<Header>());
|
||||
content = new byte[0];
|
||||
messageId = new MessageId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Email object from an InputStream containing a MIME email.
|
||||
*
|
||||
* @param inputStream
|
||||
* @throws IOException
|
||||
*/
|
||||
public Email(InputStream inputStream) throws IOException {
|
||||
this(Util.readInputStream(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Email object from a byte array containing a MIME email.
|
||||
*
|
||||
* @param bytes
|
||||
*/
|
||||
public Email(byte[] bytes) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes)));
|
||||
|
||||
headers = Collections.synchronizedList(new ArrayList<Header>());
|
||||
ByteArrayOutputStream contentStream = new ByteArrayOutputStream();
|
||||
boolean allHeadersRead = false;
|
||||
try {
|
||||
// read mail headers
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
if (line == null) // EOF
|
||||
break;
|
||||
if ("".equals(line)) { // empty line separates header from mail body
|
||||
allHeadersRead = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!allHeadersRead) {
|
||||
String[] splitString = line.split(":\\s*", 2);
|
||||
if (splitString.length > 1) {
|
||||
String name = splitString[0];
|
||||
String value = splitString[1];
|
||||
if (HEADER_WHITELIST.contains(name))
|
||||
headers.add(new Header(name, value));
|
||||
}
|
||||
else
|
||||
allHeadersRead = true;
|
||||
}
|
||||
|
||||
if (allHeadersRead)
|
||||
contentStream.write(line.getBytes());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Can't read from ByteArrayInputStream.", e);
|
||||
}
|
||||
content = contentStream.toByteArray();
|
||||
|
||||
String messageIdString = getHeader("Message-Id");
|
||||
if (messageIdString != null)
|
||||
messageId = new MessageId(messageIdString);
|
||||
}
|
||||
|
||||
private static Set<String> createHeaderWhitelist() {
|
||||
String[] headerArray = new String[] {
|
||||
"From", "Sender", "To", "CC", "BCC", "Reply-To", "Subject", "Date", "MIME-Version", "Content-Type",
|
||||
"Content-Transfer-Encoding", "Message-Id", "In-Reply-To", "X-HashCash"
|
||||
};
|
||||
|
||||
ConcurrentHashSet<String> headerSet = new ConcurrentHashSet<String>();
|
||||
headerSet.addAll(Arrays.asList(headerArray));
|
||||
return headerSet;
|
||||
}
|
||||
|
||||
public void setHashCash(HashCash hashCash) {
|
||||
setHeader("X-HashCash", hashCash.toString());
|
||||
}
|
||||
|
||||
public void setHeader(String name, String value) {
|
||||
for (Header header: headers)
|
||||
if (name.equals(header.name))
|
||||
headers.remove(header);
|
||||
addHeader(name, value);
|
||||
}
|
||||
|
||||
public void addHeader(String name, String value) {
|
||||
if (HEADER_WHITELIST.contains(name))
|
||||
headers.add(new Header(name, value));
|
||||
else
|
||||
log.debug("Ignoring non-whitelisted header: " + name);
|
||||
}
|
||||
|
||||
public void setSender(String sender) {
|
||||
setHeader("Sender", sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the RFC 5322 "From" header field. If the "From" header
|
||||
* field is absent, the value of the "Sender" field is returned. If both
|
||||
* fields are absent, <code>null</code> is returned.
|
||||
* @return
|
||||
*/
|
||||
public String getSender() {
|
||||
String sender = getHeader("From");
|
||||
if (sender != null)
|
||||
return sender;
|
||||
sender = getHeader("Sender");
|
||||
return sender;
|
||||
}
|
||||
|
||||
public void setSubject(String subject) {
|
||||
setHeader("Subject", subject);
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
return getHeader("Subject");
|
||||
}
|
||||
|
||||
public Collection<String> getAllRecipients() {
|
||||
List<String> recipients = new ArrayList<String>();
|
||||
for (Header header: headers)
|
||||
if (isRecipient(header.name))
|
||||
recipients.add(header.value);
|
||||
return recipients;
|
||||
}
|
||||
|
||||
private boolean isRecipient(String headerName) {
|
||||
return RecipientType.TO.equalsString(headerName) || RecipientType.CC.equalsString(headerName) || RecipientType.BCC.equalsString(headerName);
|
||||
}
|
||||
|
||||
public void addRecipient(RecipientType type, String address) {
|
||||
addHeader(type.toString(), address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes some basic header fields.
|
||||
*/
|
||||
public void updateHeaders() {
|
||||
setHeader("Content-Type", "text/plain");
|
||||
setHeader("Content-Transfer-Encoding", "7bit");
|
||||
setHeader("Message-Id", messageId.getEmailMessageId());
|
||||
|
||||
// Set the "Date" field, using english for the locale.
|
||||
Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT+0"));
|
||||
DateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy kk:mm:ss +0000", Locale.ENGLISH); // always use UTC for outgoing mail
|
||||
setHeader("Date", formatter.format(calendar.getTime()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the "Date" header field.
|
||||
* @return
|
||||
*/
|
||||
public String getDateString() {
|
||||
return getHeader("Date");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the value of the "Date" header field into a {@link Date}.
|
||||
* If the field cannot be parsed, <code>null</code> is returned.
|
||||
* @return
|
||||
*/
|
||||
public Date getDate() {
|
||||
try {
|
||||
return parseDate(getDateString());
|
||||
}
|
||||
catch (ParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Example for a valid date string: Sat, 19 Aug 2006 20:05:41 +0100
|
||||
* @param dateString
|
||||
* @return
|
||||
* @throws ParseException
|
||||
*/
|
||||
private Date parseDate(String dateString) throws ParseException {
|
||||
// remove day of week if present
|
||||
String[] tokens = dateString.split(",\\s+", 2);
|
||||
if (tokens.length > 1)
|
||||
dateString = tokens[1];
|
||||
|
||||
DateFormat parser = new SimpleDateFormat("dd MMM yyyy kk:mm:ss Z", Locale.ENGLISH);
|
||||
return parser.parse(dateString);
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content.getBytes();
|
||||
}
|
||||
|
||||
public void setContent(byte[] content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public byte[] getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public String getBodyText() {
|
||||
return new String(content);
|
||||
}
|
||||
|
||||
public MessageId getMessageID() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the email into one or more email packets.
|
||||
*
|
||||
* @param bccToKeep All BCC fields in the header section of the email are removed, except this field. If this parameter is <code>null</code>, all BCC fields are written.
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public Collection<UnencryptedEmailPacket> createEmailPackets(String bccToKeep) {
|
||||
ArrayList<UnencryptedEmailPacket> packets = new ArrayList<UnencryptedEmailPacket>();
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
writeTo(outputStream, bccToKeep);
|
||||
byte[] emailArray = outputStream.toByteArray();
|
||||
|
||||
// calculate fragment count
|
||||
int numFragments = (emailArray.length+MAX_BYTES_PER_PACKET-1) / MAX_BYTES_PER_PACKET;
|
||||
|
||||
int fragmentIndex = 0;
|
||||
int blockStart = 0; // the array index where the next block of data starts
|
||||
while (true) {
|
||||
int blockSize = Math.min(emailArray.length-blockStart, MAX_BYTES_PER_PACKET);
|
||||
if (blockSize <= 0)
|
||||
break;
|
||||
else {
|
||||
// make a new array with the right length
|
||||
byte[] block = new byte[blockSize];
|
||||
System.arraycopy(emailArray, blockStart, block, 0, blockSize);
|
||||
UniqueId deletionKeyPlain = new UniqueId();
|
||||
UniqueId deletionKeyEncrypted = deletionKeyPlain.clone(); // encryption happens in the constructor call below
|
||||
UnencryptedEmailPacket packet = new UnencryptedEmailPacket(deletionKeyPlain, deletionKeyEncrypted, messageId, fragmentIndex, numFragments, block);
|
||||
packets.add(packet);
|
||||
fragmentIndex++;
|
||||
blockStart += blockSize;
|
||||
}
|
||||
}
|
||||
|
||||
return packets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all "BCC" headers except the one for <code>recipient<code>.
|
||||
* The mail body is not copied (which means the new email shares its body with the original).
|
||||
* @String recipient
|
||||
* @return
|
||||
*/
|
||||
public Email removeBCCs(String recipient) {
|
||||
List<Header> newHeaders = Collections.synchronizedList(new ArrayList<Header>());
|
||||
for (Header header: headers)
|
||||
if (!"BCC".equals(header.name) || !recipient.equals(header.value))
|
||||
newHeaders.add(header);
|
||||
|
||||
Email newEmail = new Email();
|
||||
newEmail.headers = newHeaders;
|
||||
newEmail.content = content;
|
||||
newEmail.messageId = messageId;
|
||||
|
||||
return newEmail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the first header field for a header field name,
|
||||
* or <code>null</code> if no field by that name exists.
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
private String getHeader(String name) {
|
||||
for (Header header: headers)
|
||||
if (name.equals(header.name))
|
||||
return header.value;
|
||||
return null;
|
||||
}
|
||||
|
||||
// FolderElement implementation
|
||||
@Override
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
// FolderElement implementation
|
||||
@Override
|
||||
public void setFile(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the email as an RFC 5322 stream.
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5322">http://tools.ietf.org/html/rfc5322</a>
|
||||
*/
|
||||
@Override
|
||||
public void writeTo(OutputStream outputStream) throws IOException {
|
||||
writeTo(outputStream, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the email as an RFC 5322 stream.
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5322">http://tools.ietf.org/html/rfc5322</a>
|
||||
* @param outputStream
|
||||
* @param bccToKeep All BCC fields in the header section of the email are removed, except this field. If this parameter is <code>null</code>, all BCC fields are written.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void writeTo(OutputStream outputStream, String bccToKeep) {
|
||||
PrintWriter writer = new PrintWriter(outputStream);
|
||||
|
||||
writeHeaders(writer, bccToKeep);
|
||||
writer.print(NEW_LINE);
|
||||
writer.print(new String(content));
|
||||
writer.print(NEW_LINE);
|
||||
|
||||
writer.close();
|
||||
}
|
||||
|
||||
private void writeHeaders(PrintWriter writer, String bccToKeep) {
|
||||
for (Header header: headers)
|
||||
if (bccToKeep==null || !"BCC".equals(header.name) || bccToKeep.equals(header.value))
|
||||
writer.println(header.toString());
|
||||
}
|
||||
|
||||
private class Header {
|
||||
String name;
|
||||
String value;
|
||||
|
||||
Header(String name, String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + ": " + value;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,173 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.email;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Uniquely identifies an email recipient. This implementation uses I2P keypairs.
|
||||
*/
|
||||
public class EmailDestination {
|
||||
private Log log = new Log(EmailDestination.class);
|
||||
private PublicKey publicEncryptionKey;
|
||||
private SigningPublicKey publicSigningKey;
|
||||
|
||||
/**
|
||||
* Creates a fresh <code>EmailDestination</code>.
|
||||
*/
|
||||
public EmailDestination() {
|
||||
try {
|
||||
I2PClient i2pClient = I2PClientFactory.createClient();
|
||||
ByteArrayOutputStream arrayStream = new ByteArrayOutputStream();
|
||||
i2pClient.createDestination(arrayStream);
|
||||
byte[] destinationArray = arrayStream.toByteArray();
|
||||
I2PSession i2pSession = i2pClient.createSession(new ByteArrayInputStream(destinationArray), null);
|
||||
|
||||
initKeys(i2pSession);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Can't generate EmailDestination.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <code>EmailDestination</code> using data read from a {@link ByteBuffer}.
|
||||
* @param buffer
|
||||
*/
|
||||
public EmailDestination(ByteBuffer buffer) {
|
||||
byte[] encryptionKeyArray = new byte[PublicKey.KEYSIZE_BYTES];
|
||||
buffer.get(encryptionKeyArray);
|
||||
publicEncryptionKey = new PublicKey(encryptionKeyArray);
|
||||
|
||||
byte[] signingKeyArray = new byte[SigningPublicKey.KEYSIZE_BYTES];
|
||||
buffer.get(signingKeyArray);
|
||||
publicSigningKey = new SigningPublicKey(signingKeyArray);
|
||||
}
|
||||
|
||||
public EmailDestination(String base64Data) {
|
||||
try {
|
||||
base64Data += "AAAA"; // add a null certificate
|
||||
Destination i2pDestination = new Destination(base64Data);
|
||||
publicEncryptionKey = i2pDestination.getPublicKey();
|
||||
publicSigningKey = i2pDestination.getSigningPublicKey();
|
||||
} catch (DataFormatException e) {
|
||||
log.error("Can't generate EmailDestination.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/* public EmailDestination(byte[] data) {
|
||||
try {
|
||||
byte[] destinationPlusCert = addNullCertificate(data);
|
||||
ByteArrayInputStream byteStream = new ByteArrayInputStream(destinationPlusCert);
|
||||
I2PSession i2pSession = I2PClientFactory.createClient().createSession(byteStream, null);
|
||||
initKeys(i2pSession);
|
||||
}
|
||||
catch (I2PSessionException e) {
|
||||
log.error("Can't generate EmailDestination.", e);
|
||||
}
|
||||
}*/
|
||||
|
||||
/* private byte[] addNullCertificate(byte[] data) {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try {
|
||||
outputStream.write(data);
|
||||
outputStream.write(new Certificate().toByteArray());
|
||||
// Add an extra zero byte so I2PSessionImpl.readDestination doesn't fall on its face. I believe this is a bug.
|
||||
outputStream.write(0);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Can't write to ByteArrayOutputStream.", e);
|
||||
}
|
||||
return outputStream.toByteArray();
|
||||
}*/
|
||||
|
||||
protected void initKeys(I2PSession i2pSession) {
|
||||
publicEncryptionKey = i2pSession.getMyDestination().getPublicKey();
|
||||
publicSigningKey = i2pSession.getMyDestination().getSigningPublicKey();
|
||||
}
|
||||
|
||||
public PublicKey getPublicEncryptionKey() {
|
||||
return publicEncryptionKey;
|
||||
}
|
||||
|
||||
public SigningPublicKey getPublicSigningKey() {
|
||||
return publicSigningKey;
|
||||
}
|
||||
|
||||
private byte[] getKeysAsArray() {
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
try {
|
||||
writeTo(byteStream);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Can't write to ByteArrayOutputStream.", e);
|
||||
}
|
||||
return byteStream.toByteArray();
|
||||
}
|
||||
|
||||
private void writeTo(OutputStream outputStream) throws IOException {
|
||||
try {
|
||||
publicEncryptionKey.writeBytes(outputStream);
|
||||
publicSigningKey.writeBytes(outputStream);
|
||||
}
|
||||
catch (DataFormatException e) {
|
||||
log.error("Invalid encryption key or signing key.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Hash getHash() {
|
||||
// TODO cache the hash value?
|
||||
return SHA256Generator.getInstance().calculateHash(getKeysAsArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the two public keys in Base64 representation.
|
||||
*/
|
||||
public String getKey() {
|
||||
return Base64.encode(getKeysAsArray());
|
||||
}
|
||||
|
||||
public String toBase64() {
|
||||
return getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getKey();
|
||||
}
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.email;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class EmailIdentity extends EmailDestination {
|
||||
private Log log = new Log(EmailIdentity.class);
|
||||
private PrivateKey privateEncryptionKey;
|
||||
private SigningPrivateKey privateSigningKey;
|
||||
private String publicName;
|
||||
private String description; // optional
|
||||
private String emailAddress; // optional
|
||||
|
||||
/**
|
||||
* Creates a random <code>EmailIdentity</code>.
|
||||
*/
|
||||
public EmailIdentity() {
|
||||
// key initialization happens in the super constructor, which calls initKeys
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <code>EmailIdentity</code> from a Base64-encoded string. The format is the same as
|
||||
* for Base64-encoded local I2P destinations, except there is no null certificate.
|
||||
* @param key
|
||||
*/
|
||||
public EmailIdentity(String key) {
|
||||
try {
|
||||
key = key.substring(0, 512) + "AAAA" + key.substring(512); // insert a null certificate for I2PClient.createSession()
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.decode(key));
|
||||
|
||||
I2PClient i2pClient = I2PClientFactory.createClient();
|
||||
I2PSession i2pSession = i2pClient.createSession(inputStream, null);
|
||||
initKeys(i2pSession);
|
||||
}
|
||||
catch (I2PSessionException e) {
|
||||
log.error("Can't generate EmailIdentity.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public PrivateKey getPrivateEncryptionKey() {
|
||||
return privateEncryptionKey;
|
||||
}
|
||||
|
||||
public SigningPrivateKey getPrivateSigningKey() {
|
||||
return privateSigningKey;
|
||||
}
|
||||
|
||||
public void setPublicName(String publicName) {
|
||||
this.publicName = publicName;
|
||||
}
|
||||
|
||||
public String getPublicName() {
|
||||
return publicName;
|
||||
}
|
||||
|
||||
public void setDescription(String name) {
|
||||
this.description = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setEmailAddress(String emailAddress) {
|
||||
this.emailAddress = emailAddress;
|
||||
}
|
||||
|
||||
public String getEmailAddress() {
|
||||
return emailAddress;
|
||||
}
|
||||
|
||||
protected void initKeys(I2PSession i2pSession) {
|
||||
super.initKeys(i2pSession);
|
||||
privateEncryptionKey = i2pSession.getDecryptionKey();
|
||||
privateSigningKey = i2pSession.getPrivateKey();
|
||||
}
|
||||
|
||||
private byte[] getKeysAsArray() {
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
try {
|
||||
byteStream.write(getPublicEncryptionKey().getData());
|
||||
byteStream.write(getPublicSigningKey().getData());
|
||||
byteStream.write(getPrivateEncryptionKey().getData());
|
||||
byteStream.write(getPrivateSigningKey().getData());
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Can't write to ByteArrayOutputStream.", e);
|
||||
}
|
||||
return byteStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the two key pairs (public + private) as one Base64-encoded string.
|
||||
* @return
|
||||
*/
|
||||
public String getFullKey() {
|
||||
return Base64.encode(getKeysAsArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getKey() + " address=<" + getEmailAddress() + "> identity name=<" + getDescription() + "> visible name=<" + getPublicName() + ">";
|
||||
}
|
||||
|
||||
/* @Override
|
||||
public boolean equals(Object anotherObject) {
|
||||
if (anotherObject instanceof EmailIdentity) {
|
||||
EmailIdentity otherIdentity = (EmailIdentity)anotherObject;
|
||||
return Arrays.equals(getKeysAsArray(), otherIdentity.getKeysAsArray());
|
||||
}
|
||||
return false;
|
||||
}*/
|
||||
}
|
@ -1,193 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.email;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Represents a set of <code>EmailIdentities</code>.
|
||||
*/
|
||||
public class Identities implements Iterable<EmailIdentity> {
|
||||
private Log log = new Log(Identities.class);
|
||||
private File identitiesFile;
|
||||
private List<EmailIdentity> identities;
|
||||
|
||||
public Identities(File identitiesFile) {
|
||||
this.identitiesFile = identitiesFile;
|
||||
identities = Collections.synchronizedList(new ArrayList<EmailIdentity>());
|
||||
|
||||
if (!identitiesFile.exists()) {
|
||||
log.debug("Identities file does not exist: <" + identitiesFile.getAbsolutePath() + ">");
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Reading identities file: <" + identitiesFile.getAbsolutePath() + ">");
|
||||
BufferedReader input = null;
|
||||
try {
|
||||
input = new BufferedReader(new FileReader(identitiesFile));
|
||||
|
||||
while (true) {
|
||||
String line = input.readLine();
|
||||
if (line == null) // EOF
|
||||
break;
|
||||
|
||||
EmailIdentity identity = parse(line);
|
||||
if (identity != null)
|
||||
identities.add(identity);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Can't read identities file.", e);
|
||||
}
|
||||
finally {
|
||||
if (input != null)
|
||||
try {
|
||||
input.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Error closing input stream.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EmailIdentity parse(String emailIdentityString) {
|
||||
try {
|
||||
String[] fields = emailIdentityString.split("\\t", 4);
|
||||
if (fields.length < 2) {
|
||||
log.debug("Unparseable email identity: <" + emailIdentityString + ">");
|
||||
return null;
|
||||
}
|
||||
EmailIdentity identity = new EmailIdentity(fields[0]);
|
||||
if (fields.length > 1)
|
||||
identity.setPublicName(fields[1]);
|
||||
if (fields.length > 2)
|
||||
identity.setDescription(fields[2]);
|
||||
if (fields.length > 3)
|
||||
identity.setEmailAddress(fields[3]);
|
||||
return identity;
|
||||
}
|
||||
catch (PatternSyntaxException e) {
|
||||
log.debug("Unparseable email identity: <" + emailIdentityString + ">");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the counterpart of the <code>parse</code> method. It encodes a {@link EmailIdentity} into
|
||||
* an entry for the identities file.
|
||||
* @param identity
|
||||
* @return
|
||||
*/
|
||||
private String toFileFormat(EmailIdentity identity) {
|
||||
StringBuilder string = new StringBuilder();
|
||||
string = string.append(identity.getFullKey());
|
||||
string = string.append("\t");
|
||||
string = string.append(identity.getPublicName());
|
||||
string = string.append("\t");
|
||||
if (identity.getDescription() != null)
|
||||
string = string.append(identity.getDescription());
|
||||
string = string.append("\t");
|
||||
if (identity.getEmailAddress() != null)
|
||||
string = string.append(identity.getEmailAddress());
|
||||
return string.toString();
|
||||
}
|
||||
|
||||
public void save() throws IOException {
|
||||
String newLine = System.getProperty("line.separator");
|
||||
try {
|
||||
Writer writer = new BufferedWriter(new FileWriter(identitiesFile));
|
||||
for (EmailIdentity identity: identities)
|
||||
writer.write(toFileFormat(identity) + newLine);
|
||||
writer.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Can't save email identities to file <" + identitiesFile.getAbsolutePath() + ">.", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void add(EmailIdentity identity) {
|
||||
identities.add(identity);
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
EmailIdentity identity = get(key);
|
||||
if (identity != null)
|
||||
identities.remove(identity);
|
||||
}
|
||||
|
||||
public EmailIdentity get(int i) {
|
||||
return identities.get(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up an {@link EmailIdentity} by its Base64 key. If none is found,
|
||||
* <code>null</code> is returned.
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public EmailIdentity get(String key) {
|
||||
if (key==null || key.isEmpty())
|
||||
return null;
|
||||
|
||||
for (EmailIdentity identity: identities)
|
||||
if (key.equals(identity.getKey()))
|
||||
return identity;
|
||||
return null;
|
||||
}
|
||||
|
||||
public Collection<EmailIdentity> getAll() {
|
||||
return identities;
|
||||
}
|
||||
|
||||
public EmailIdentity[] getArray() {
|
||||
return identities.toArray(new EmailIdentity[0]);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return identities.size();
|
||||
}
|
||||
|
||||
public boolean contains(Hash emailDestination) {
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<EmailIdentity> iterator() {
|
||||
return identities.iterator();
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package i2p.bote.email;
|
||||
|
||||
public enum RecipientType {
|
||||
TO("To"), CC("CC"), BCC("BCC");
|
||||
|
||||
private String value;
|
||||
|
||||
private RecipientType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean equalsString(String string) {
|
||||
return value.equalsIgnoreCase(string);
|
||||
}
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.folder;
|
||||
|
||||
import i2p.bote.email.Email;
|
||||
import i2p.bote.email.MessageId;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Stores emails in a directory on the file system. Each email is stored in one file.
|
||||
* The filename is the message Id plus an extension.
|
||||
*/
|
||||
public class EmailFolder extends Folder<Email> {
|
||||
protected static final String EMAIL_FILE_EXTENSION = ".mail";
|
||||
|
||||
private Log log = new Log(EmailFolder.class);
|
||||
|
||||
public EmailFolder(File storageDir) {
|
||||
super(storageDir, EMAIL_FILE_EXTENSION);
|
||||
}
|
||||
|
||||
// store an email file
|
||||
public void add(Email email) throws IOException {
|
||||
// write out the email file
|
||||
File emailFile = getEmailFile(email);
|
||||
log.info("Storing email in outbox: '" + emailFile.getAbsolutePath() + "'");
|
||||
OutputStream emailOutputStream = new FileOutputStream(emailFile);
|
||||
email.writeTo(emailOutputStream);
|
||||
emailOutputStream.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an <code>Email</code> by message id. If the <code>Email</code> is
|
||||
* not found, <code>null</code> is returned.
|
||||
* The <code>messageId</code> parameter must be a 44-character base64-encoded
|
||||
* message id. If the message id contains an ampersand, the ampersand and
|
||||
* everything after it is ignored.
|
||||
* @param messageId
|
||||
* @return
|
||||
*/
|
||||
public Email getEmail(String messageIdString) {
|
||||
MessageId messageId = new MessageId(messageIdString);
|
||||
|
||||
File file = getEmailFile(messageId);
|
||||
try {
|
||||
return createFolderElement(file);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Can't read email from file: <" + file.getAbsolutePath() + ">", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private File getEmailFile(Email email) {
|
||||
return getEmailFile(email.getMessageID());
|
||||
}
|
||||
|
||||
private File getEmailFile(MessageId messageId) {
|
||||
return new File(storageDir, messageId.toBase64() + EMAIL_FILE_EXTENSION);
|
||||
}
|
||||
|
||||
public void delete(Email email) {
|
||||
if (!getEmailFile(email).delete())
|
||||
log.error("Cannot delete file: '" + getEmailFile(email) + "'");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Email createFolderElement(File file) throws Exception {
|
||||
FileInputStream inputStream = new FileInputStream(file);
|
||||
return new Email(inputStream);
|
||||
}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.folder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <T> The type of objects the folder can store.
|
||||
*/
|
||||
public abstract class Folder<T extends FolderElement> implements Iterable<T> {
|
||||
private Log log = new Log(Folder.class);
|
||||
protected File storageDir;
|
||||
protected String fileExtension;
|
||||
|
||||
protected Folder(File storageDir, String fileExtension) {
|
||||
this.storageDir = storageDir;
|
||||
this.fileExtension = fileExtension;
|
||||
|
||||
if (!storageDir.exists() && !storageDir.mkdirs())
|
||||
log.error("Can't create directory: '" + storageDir + "'");
|
||||
}
|
||||
|
||||
public File getStorageDirectory() {
|
||||
return storageDir;
|
||||
}
|
||||
|
||||
public Collection<T> getElements() {
|
||||
Collection<T> elements = new ArrayList<T>();
|
||||
Iterator<T> iterator = iterator();
|
||||
while (iterator.hasNext())
|
||||
elements.add(iterator.next());
|
||||
return elements;
|
||||
}
|
||||
|
||||
// An {@link Iterator} implementation that loads one file into memory at a time.
|
||||
@Override
|
||||
public final Iterator<T> iterator() {
|
||||
final File[] files = storageDir.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.toUpperCase().endsWith(fileExtension.toUpperCase());
|
||||
}
|
||||
});
|
||||
if (files == null)
|
||||
log.error("Cannot list files in directory <" + storageDir + ">");
|
||||
else
|
||||
log.debug(files.length + " files with the extension '" + fileExtension + "' found in '" + storageDir + "'.");
|
||||
|
||||
// sort files by date, newest first
|
||||
Arrays.sort(files, new Comparator<File>() {
|
||||
@Override
|
||||
public int compare(File f1, File f2) {
|
||||
return (int)Math.signum(f1.lastModified() - f2.lastModified());
|
||||
}
|
||||
});
|
||||
|
||||
return new Iterator<T>() {
|
||||
int nextIndex = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return nextIndex < files.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
File file = files[nextIndex];
|
||||
String filePath = file.getAbsolutePath();
|
||||
log.info("Reading file: '" + filePath + "'");
|
||||
try {
|
||||
T nextElement = createFolderElement(file);
|
||||
nextElement.setFile(file);
|
||||
nextIndex++;
|
||||
return nextElement;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Can't create a FolderElement from file: " + filePath, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Use the Folder instance to delete a folder element.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract T createFolderElement(File file) throws Exception;
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.folder;
|
||||
|
||||
import i2p.bote.packet.I2PBotePacket;
|
||||
import i2p.bote.packet.IndexPacket;
|
||||
import i2p.bote.packet.dht.DhtStorablePacket;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* This class differs from {@link DhtPacketFolder} in that it doesn't overwrite an existing
|
||||
* packet when a new packet is stored under the same key, but merges the packets.
|
||||
*/
|
||||
public class IndexPacketFolder extends DhtPacketFolder<IndexPacket> {
|
||||
private final Log log = new Log(I2PBotePacket.class);
|
||||
|
||||
public IndexPacketFolder(File storageDir) {
|
||||
super(storageDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(DhtStorablePacket packetToStore) {
|
||||
if (!(packetToStore instanceof IndexPacket))
|
||||
throw new IllegalArgumentException("This class only stores packets of type " + IndexPacket.class.getSimpleName() + ".");
|
||||
|
||||
IndexPacket indexPacketToStore = (IndexPacket)packetToStore;
|
||||
DhtStorablePacket existingPacket = retrieve(packetToStore.getDhtKey());
|
||||
|
||||
// If an index packet with the same key exists in the folder, merge the two packets.
|
||||
if (existingPacket instanceof IndexPacket) {
|
||||
packetToStore = new IndexPacket(indexPacketToStore, (IndexPacket)existingPacket);
|
||||
if (packetToStore.isTooBig())
|
||||
// TODO make two new index packets, put half the email packet keys in each one, store the two index packets on the DHT, and put the two index packet keys into the local index file (only keep those two).
|
||||
log.error("After merging, IndexPacket is too big for a datagram: size=" + packetToStore.getSize());
|
||||
}
|
||||
|
||||
super.store(packetToStore);
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.folder;
|
||||
|
||||
import i2p.bote.email.Email;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Stores emails in a directory on the filesystem. For each email, two files are created; the actual
|
||||
* email and a status file.
|
||||
* Status files and email files have the same name, except for the extension.
|
||||
* Even emails that need to be fragmented are stored as a whole.
|
||||
* Message IDs are used for filenames.
|
||||
*
|
||||
* Status files contain a status for each recipient address.
|
||||
*/
|
||||
public class Outbox extends EmailFolder implements Iterable<Email> {
|
||||
private static final String STATUS_FILE_EXTENSION = ".status";
|
||||
// private static final String PARAM_QUEUE_DATE = "X-QueueDate";
|
||||
private static final Log log = new Log(Outbox.class);
|
||||
|
||||
public Outbox(File storageDir) {
|
||||
super(storageDir);
|
||||
}
|
||||
|
||||
// store one email file + one status file.
|
||||
@Override
|
||||
public void add(Email email) throws IOException {
|
||||
// write out the email file
|
||||
super.add(email);
|
||||
|
||||
// collect info for status file
|
||||
String queueDate = String.valueOf(System.currentTimeMillis());
|
||||
|
||||
// write out the status file
|
||||
File statusFile = getStatusFile(email);
|
||||
FileWriter statusFileWriter = new FileWriter(statusFile);
|
||||
statusFileWriter.write(queueDate);
|
||||
statusFileWriter.close();
|
||||
}
|
||||
|
||||
private File getStatusFile(Email email) {
|
||||
return new File(storageDir, email.getMessageID() + STATUS_FILE_EXTENSION);
|
||||
}
|
||||
|
||||
// delete an email file + the status file
|
||||
@Override
|
||||
public void delete(Email email) {
|
||||
super.delete(email);
|
||||
|
||||
if (!getStatusFile(email).delete())
|
||||
log.error("Cannot delete file: '" + getStatusFile(email) + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param email
|
||||
* @param relayInfo A 0-length array means no relays were used, i.e. the email was sent directly to the recipient.
|
||||
* @param statusMessage
|
||||
*/
|
||||
public void updateStatus(Email email, int[] relayInfo, String statusMessage) {
|
||||
// TODO write out a new status file. filename is the msg id, statusMessage goes into the file.
|
||||
}
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.folder;
|
||||
|
||||
import i2p.bote.UniqueId;
|
||||
import i2p.bote.packet.DataPacket;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* This class stores new files under a random file name with the .pkt extension.
|
||||
*
|
||||
* @param <PacketType> The type of data stored in this folder
|
||||
*/
|
||||
public class PacketFolder<PacketType extends DataPacket> extends Folder<PacketType> {
|
||||
protected static final String PACKET_FILE_EXTENSION = ".pkt";
|
||||
|
||||
private Log log = new Log(PacketFolder.class);
|
||||
|
||||
public PacketFolder(File storageDir) {
|
||||
super(storageDir, PACKET_FILE_EXTENSION);
|
||||
}
|
||||
|
||||
public <T extends PacketType> void add(T packetToStore) {
|
||||
String filename = new UniqueId().toBase64() + PACKET_FILE_EXTENSION;
|
||||
add(packetToStore, filename);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param packetToStore
|
||||
* @param filename A filename relative to this folder's storage directory.
|
||||
*/
|
||||
protected void add(DataPacket packetToStore, String filename) {
|
||||
FileOutputStream outputStream = null;
|
||||
try {
|
||||
File file = new File(storageDir, filename);
|
||||
outputStream = new FileOutputStream(file);
|
||||
packetToStore.writeTo(outputStream);
|
||||
} catch (Exception e) {
|
||||
log.error("Can't save packet to file: <" + filename + ">", e);
|
||||
}
|
||||
finally {
|
||||
if (outputStream != null)
|
||||
try {
|
||||
outputStream.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Can't close file: <" + filename + ">", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(UniqueId packetId) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected PacketType createFolderElement(File file) throws Exception {
|
||||
return (PacketType)DataPacket.createPacket(file);
|
||||
}
|
||||
}
|
@ -1,172 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.network;
|
||||
|
||||
import i2p.bote.UniqueId;
|
||||
import i2p.bote.Util;
|
||||
import i2p.bote.email.EmailIdentity;
|
||||
import i2p.bote.folder.IncompleteEmailFolder;
|
||||
import i2p.bote.packet.EncryptedEmailPacket;
|
||||
import i2p.bote.packet.IndexPacket;
|
||||
import i2p.bote.packet.UnencryptedEmailPacket;
|
||||
import i2p.bote.packet.dht.DhtStorablePacket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Gets email packets from the DHT for one email identity. A separate thread is used for
|
||||
* each packet in order to speed things up, and because the packets are in different places
|
||||
* on the network.
|
||||
*/
|
||||
public class CheckEmailTask implements Callable<Boolean> {
|
||||
public static final int THREAD_STACK_SIZE = 64 * 1024; // TODO find a safe low value (default in 64-bit Java 1.6 = 1MByte)
|
||||
private static final int MAX_THREADS = 50;
|
||||
private static final ThreadFactory EMAIL_PACKET_TASK_THREAD_FACTORY = Util.createThreadFactory("EmailPktTask", THREAD_STACK_SIZE);
|
||||
|
||||
private Log log = new Log(CheckEmailTask.class);
|
||||
private EmailIdentity identity;
|
||||
private DHT dht;
|
||||
private PeerManager peerManager;
|
||||
private IncompleteEmailFolder incompleteEmailFolder;
|
||||
private I2PAppContext appContext;
|
||||
private ExecutorService executor;
|
||||
|
||||
public CheckEmailTask(EmailIdentity identity, DHT dht, PeerManager peerManager, IncompleteEmailFolder incompleteEmailFolder, I2PAppContext appContext) {
|
||||
this.identity = identity;
|
||||
this.dht = dht;
|
||||
this.peerManager = peerManager;
|
||||
this.incompleteEmailFolder = incompleteEmailFolder;
|
||||
this.appContext = appContext;
|
||||
executor = Executors.newFixedThreadPool(MAX_THREADS, EMAIL_PACKET_TASK_THREAD_FACTORY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if a new email was created in the inbox as a result
|
||||
* of receiving an email packet.
|
||||
*/
|
||||
@Override
|
||||
public Boolean call() {
|
||||
Collection<Hash> emailPacketKeys = findEmailPacketKeys();
|
||||
|
||||
Collection<Future<Boolean>> results = new ArrayList<Future<Boolean>>();
|
||||
for (Hash dhtKey: emailPacketKeys) {
|
||||
Future<Boolean> result = executor.submit(new EmailPacketTask(dhtKey));
|
||||
results.add(result);
|
||||
}
|
||||
|
||||
boolean newEmail = false;
|
||||
for (Future<Boolean> result: results)
|
||||
try {
|
||||
if (result.get())
|
||||
newEmail = true;
|
||||
} catch (ExecutionException e) {
|
||||
log.error("Can't retrieve email packet.", e);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted while checking for mail.", e);
|
||||
}
|
||||
return newEmail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the DHT for new index packets and returns the DHT keys contained in them.
|
||||
* @return A <code>Collection</code> containing zero or more elements
|
||||
*/
|
||||
private Collection<Hash> findEmailPacketKeys() {
|
||||
log.debug("Querying the DHT for index packets with key " + identity.getHash());
|
||||
Collection<DhtStorablePacket> packets = dht.findAll(identity.getHash(), IndexPacket.class);
|
||||
|
||||
// build an Collection of index packets
|
||||
Collection<IndexPacket> indexPackets = new ArrayList<IndexPacket>();
|
||||
for (DhtStorablePacket packet: packets)
|
||||
if (packet instanceof IndexPacket)
|
||||
indexPackets.add((IndexPacket)packet);
|
||||
else
|
||||
log.error("DHT returned packet of class " + packet.getClass().getSimpleName() + ", expected IndexPacket.");
|
||||
|
||||
IndexPacket mergedPacket = new IndexPacket(indexPackets);
|
||||
log.debug("Found " + mergedPacket.getDhtKeys().size() + " Email Packet keys.");
|
||||
return mergedPacket.getDhtKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the DHT for an email packet, adds the packet to the {@link IncompleteEmailFolder},
|
||||
* and deletes the packet from the DHT.
|
||||
*/
|
||||
private class EmailPacketTask implements Callable<Boolean> {
|
||||
private Hash dhtKey;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dhtKey The DHT key of the email packet to retrieve
|
||||
*/
|
||||
public EmailPacketTask(Hash dhtKey) {
|
||||
this.dhtKey = dhtKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if a new email was created in the inbox as a result
|
||||
* of receiving an email packet.
|
||||
*/
|
||||
@Override
|
||||
public Boolean call() {
|
||||
boolean emailCompleted = false;
|
||||
DhtStorablePacket packet = dht.findOne(dhtKey, EncryptedEmailPacket.class);
|
||||
if (packet instanceof EncryptedEmailPacket) {
|
||||
EncryptedEmailPacket emailPacket = (EncryptedEmailPacket)packet;
|
||||
try {
|
||||
UnencryptedEmailPacket decryptedPacket = emailPacket.decrypt(identity, appContext);
|
||||
emailCompleted = incompleteEmailFolder.addEmailPacket(decryptedPacket);
|
||||
sendDeleteRequest(dhtKey, decryptedPacket.getVerificationDeletionKey());
|
||||
}
|
||||
catch (DataFormatException e) {
|
||||
log.error("Can't decrypt email packet: " + emailPacket, e);
|
||||
// TODO propagate error message to UI
|
||||
}
|
||||
}
|
||||
else
|
||||
if (packet != null)
|
||||
log.error("DHT returned packet of class " + packet.getClass().getSimpleName() + ", expected EmailPacket.");
|
||||
return emailCompleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a delete request to the DHT.
|
||||
* @param dhtKey The DHT key of the email packet that is to be deleted
|
||||
* @param deletionKey The deletion key for the email packet
|
||||
*/
|
||||
private void sendDeleteRequest(Hash dhtKey, UniqueId deletionKey) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
@ -1,422 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.network;
|
||||
|
||||
import i2p.bote.UniqueId;
|
||||
import i2p.bote.packet.CommunicationPacket;
|
||||
import i2p.bote.packet.DataPacket;
|
||||
import i2p.bote.packet.RelayPacket;
|
||||
import i2p.bote.packet.RelayRequest;
|
||||
import i2p.bote.packet.ResponsePacket;
|
||||
import i2p.bote.packet.StatusCode;
|
||||
import i2p.bote.service.I2PBoteThread;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.client.datagram.I2PDatagramMaker;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import com.nettgryppa.security.HashCash;
|
||||
|
||||
/**
|
||||
* All outgoing I2P traffic goes through this class.
|
||||
* Packets are sent at a rate no greater than specified by the
|
||||
* <CODE>maxBandwidth</CODE> property.
|
||||
*
|
||||
* The packet queue is FIFO with the exception of delayed packets.
|
||||
* The packet with the highest index in the queue is always the next
|
||||
* to be sent.
|
||||
*/
|
||||
public class I2PSendQueue extends I2PBoteThread implements PacketListener {
|
||||
private Log log = new Log(I2PSendQueue.class);
|
||||
private I2PSession i2pSession;
|
||||
private PacketQueue packetQueue;
|
||||
private Map<UniqueId, ScheduledPacket> outstandingRequests; // sent packets that haven't been responded to
|
||||
private Set<PacketBatch> runningBatches;
|
||||
private int maxBandwidth;
|
||||
private Collection<SendQueuePacketListener> sendQueueListeners;
|
||||
private Map<SendQueuePacketListener, Long> timeoutValues;
|
||||
|
||||
/**
|
||||
* @param i2pSession
|
||||
* @param i2pReceiver
|
||||
*/
|
||||
public I2PSendQueue(I2PSession i2pSession, I2PPacketDispatcher i2pReceiver) {
|
||||
super("I2PSendQueue");
|
||||
|
||||
this.i2pSession = i2pSession;
|
||||
i2pReceiver.addPacketListener(this);
|
||||
packetQueue = new PacketQueue();
|
||||
outstandingRequests = new ConcurrentHashMap<UniqueId, ScheduledPacket>();
|
||||
runningBatches = new ConcurrentHashSet<PacketBatch>();
|
||||
sendQueueListeners = Collections.synchronizedCollection(new ArrayList<SendQueuePacketListener>());
|
||||
timeoutValues = new HashMap<SendQueuePacketListener, Long>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a packet behind the last undelayed packet.
|
||||
* @param packet
|
||||
* @param destination
|
||||
*/
|
||||
public void send(CommunicationPacket packet, Destination destination) {
|
||||
send(packet, destination, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a packet behind the last undelayed packet and waits for up to
|
||||
* <code>timeoutSeconds</code> for a response.
|
||||
* @param packet
|
||||
* @param destination
|
||||
* @param timeoutMilliSeconds
|
||||
* @return The response packet, or <code>null</code> if a timeout occurred.
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public CommunicationPacket sendAndWait(CommunicationPacket packet, Destination destination, long timeoutMilliSeconds) throws InterruptedException {
|
||||
ScheduledPacket scheduledPacket = new ScheduledPacket(packet, destination, 0);
|
||||
|
||||
long scheduleTime = System.currentTimeMillis();
|
||||
packetQueue.add(scheduledPacket);
|
||||
scheduledPacket.awaitSending();
|
||||
outstandingRequests.put(packet.getPacketId(), scheduledPacket);
|
||||
scheduledPacket.awaitResponse(timeoutMilliSeconds, TimeUnit.MILLISECONDS);
|
||||
|
||||
if (log.shouldLog(Log.DEBUG)) {
|
||||
long responseTime = scheduledPacket.getResponseTime();
|
||||
String responseInfo = responseTime<0?"timed out":"took " + (responseTime-scheduledPacket.getSentTime()) + "ms.";
|
||||
log.debug("Packet with id " + packet.getPacketId() + " was queued for " + (scheduledPacket.getSentTime()-scheduleTime) + " ms, response " + responseInfo);
|
||||
}
|
||||
|
||||
return scheduledPacket.getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a packet for sending at or after a certain time.
|
||||
* @param packet
|
||||
* @param destination
|
||||
* @param earliestSendTime
|
||||
*/
|
||||
public void send(CommunicationPacket packet, Destination destination, long earliestSendTime) {
|
||||
packetQueue.add(new ScheduledPacket(packet, destination, earliestSendTime));
|
||||
}
|
||||
|
||||
public void sendRelayRequest(RelayPacket relayPacket, HashCash hashCash, long earliestSendTime) {
|
||||
RelayRequest relayRequest = new RelayRequest(hashCash, relayPacket);
|
||||
ScheduledPacket scheduledPacket = new ScheduledPacket(relayRequest, relayPacket.getNextDestination(), earliestSendTime);
|
||||
packetQueue.add(scheduledPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Response Packet to a {@link Destination}, with the status code "OK".
|
||||
* @param packet
|
||||
* @param destination
|
||||
* @param requestPacketId The packet id of the packet we're responding to
|
||||
*/
|
||||
public void sendResponse(DataPacket packet, Destination destination, UniqueId requestPacketId) {
|
||||
sendResponse(packet, destination, StatusCode.OK, requestPacketId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Response Packet to a {@link Destination}.
|
||||
* @param packet
|
||||
* @param destination
|
||||
* @param statusCode
|
||||
* @param requestPacketId The packet id of the packet we're responding to
|
||||
*/
|
||||
public void sendResponse(DataPacket packet, Destination destination, StatusCode statusCode, UniqueId requestPacketId) {
|
||||
send(new ResponsePacket(packet, statusCode, requestPacketId), destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a batch of packets, each to its own destination.
|
||||
* Replies to the batch's packets are received until {@link remove(PacketBatch)} is called.
|
||||
* @param batch
|
||||
*/
|
||||
public void send(PacketBatch batch) {
|
||||
log.debug("Adding a batch containing " + batch.getPacketCount() + " packets.");
|
||||
runningBatches.add(batch);
|
||||
batch.initializeSentSignal();
|
||||
for (PacketBatchItem batchItem: batch) {
|
||||
ScheduledPacket scheduledPacket = new ScheduledPacket(batchItem.getPacket(), batchItem.getDestination(), 0, batch);
|
||||
packetQueue.add(scheduledPacket);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(PacketBatch batch) {
|
||||
runningBatches.remove(batch);
|
||||
}
|
||||
|
||||
public int getQueueLength() {
|
||||
return packetQueue.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum outgoing bandwidth in kbits/s
|
||||
* @param maxBandwidth
|
||||
*/
|
||||
public void setMaxBandwidth(int maxBandwidth) {
|
||||
this.maxBandwidth = maxBandwidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum outgoing bandwidth in kbits/s
|
||||
* @return
|
||||
*/
|
||||
public int getMaxBandwidth() {
|
||||
return maxBandwidth;
|
||||
}
|
||||
|
||||
public void addSendQueueListener(SendQueuePacketListener listener) {
|
||||
addSendQueueListener(listener, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link SendQueuePacketListener} that is automatically removed after
|
||||
* <code>timeout</code> milliseconds.
|
||||
* @param listener
|
||||
* @param timeout
|
||||
*/
|
||||
public void addSendQueueListener(SendQueuePacketListener listener, Long timeout) {
|
||||
sendQueueListeners.add(listener);
|
||||
timeoutValues.put(listener, timeout);
|
||||
}
|
||||
|
||||
public void removeSendQueueListener(SendQueuePacketListener listener) {
|
||||
sendQueueListeners.remove(listener);
|
||||
timeoutValues.remove(listener);
|
||||
}
|
||||
|
||||
private void firePacketListeners(CommunicationPacket packet) {
|
||||
for (SendQueuePacketListener listener: sendQueueListeners)
|
||||
listener.packetSent(packet);
|
||||
}
|
||||
|
||||
// Implementation of PacketListener
|
||||
@Override
|
||||
public void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime) {
|
||||
if (packet instanceof ResponsePacket) {
|
||||
log.debug("Response Packet received: Packet Id = " + packet.getPacketId() + " Sender = " + sender);
|
||||
|
||||
UniqueId packetId = packet.getPacketId();
|
||||
|
||||
for (PacketBatch batch: runningBatches)
|
||||
if (batch.contains(packetId))
|
||||
batch.addResponse(((ResponsePacket)packet).getPayload());
|
||||
|
||||
// Remove the original request if it is on the list, and notify objects waiting on a response
|
||||
ScheduledPacket requestPacket = outstandingRequests.remove(packetId);
|
||||
if (requestPacket != null) {
|
||||
requestPacket.setResponse(packet);
|
||||
requestPacket.decrementResponseLatch();
|
||||
}
|
||||
|
||||
// If there is a packet file for the original packet, it can be deleted now.
|
||||
// Use the cached filename if the packet was in the cache; otherwise, find the file by packet Id
|
||||
/* ScheduledPacket cachedPacket = recentlySentCache.remove(packetId);
|
||||
if (cachedPacket != null)
|
||||
fileManager.removePacketFile(cachedPacket);
|
||||
else
|
||||
fileManager.removePacketFile(packetId);*/
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
I2PDatagramMaker datagramMaker = new I2PDatagramMaker(i2pSession);
|
||||
|
||||
while (true) {
|
||||
ScheduledPacket scheduledPacket;
|
||||
try {
|
||||
scheduledPacket = packetQueue.take();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
log.warn("Interrupted while waiting for new packets.", e);
|
||||
break;
|
||||
}
|
||||
CommunicationPacket i2pBotePacket = scheduledPacket.data;
|
||||
|
||||
// wait long enough so rate <= maxBandwidth;
|
||||
if (maxBandwidth > 0) {
|
||||
int packetSizeBits = i2pBotePacket.getSize() * 8;
|
||||
int maxBWBitsPerSecond = maxBandwidth * 1024;
|
||||
long waitTimeMsecs = 1000L * packetSizeBits / maxBWBitsPerSecond;
|
||||
if (System.currentTimeMillis()+waitTimeMsecs < scheduledPacket.earliestSendTime)
|
||||
waitTimeMsecs = scheduledPacket.earliestSendTime;
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(waitTimeMsecs);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
log.warn("Interrupted while waiting to send packet.", e);
|
||||
}
|
||||
}
|
||||
|
||||
PacketBatch batch = scheduledPacket.batch;
|
||||
boolean isBatchPacket = batch != null;
|
||||
log.debug("Sending " + (isBatchPacket?"":"non-") + "batch packet: [" + i2pBotePacket + "] to peer: " + scheduledPacket.destination.toBase64());
|
||||
|
||||
byte[] replyableDatagram = datagramMaker.makeI2PDatagram(i2pBotePacket.toByteArray());
|
||||
try {
|
||||
i2pSession.sendMessage(scheduledPacket.destination, replyableDatagram);
|
||||
|
||||
// set sentTime; update queue+cache, update countdown latch, fire packet listeners
|
||||
scheduledPacket.data.setSentTime(System.currentTimeMillis());
|
||||
packetQueue.remove(scheduledPacket);
|
||||
if (isBatchPacket)
|
||||
batch.decrementSentLatch();
|
||||
scheduledPacket.decrementSentLatch();
|
||||
firePacketListeners(scheduledPacket.data);
|
||||
|
||||
log.debug("Packet sent. Send queue length is now " + packetQueue.size());
|
||||
if (isBatchPacket)
|
||||
log.debug(" Batch has " + batch.getPacketCount() + " packets total, " + batch.getUnsentPacketCount() + " waiting to be sent.");
|
||||
}
|
||||
catch (I2PSessionException sessExc) {
|
||||
log.error("Can't send packet.", sessExc);
|
||||
// pause to avoid CPU hogging if the error doesn't go away
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
}
|
||||
catch (InterruptedException intrExc) {
|
||||
log.error("Interrupted while sleeping after a send error.", intrExc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PacketQueue extends LinkedList<ScheduledPacket> {
|
||||
public void add(ScheduledPacket packet, long earliestSendTime) {
|
||||
int index = getInsertionIndex(packet, earliestSendTime);
|
||||
add(index, packet);
|
||||
}
|
||||
|
||||
private int getInsertionIndex(ScheduledPacket packet, long earliestSendTime) {
|
||||
if (isEmpty())
|
||||
return 0;
|
||||
|
||||
// do a linear search (binary search isn't a good fit for LinkedList)
|
||||
// TODO using foreach would be more efficient
|
||||
for (int i=0; i<size(); i++)
|
||||
if (get(i).earliestSendTime <= earliestSendTime)
|
||||
return i;
|
||||
|
||||
return size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Not synchronized because there should only be one {@link I2PSendQueue}.
|
||||
* @throws InterruptedException
|
||||
* @return
|
||||
*/
|
||||
public ScheduledPacket take() throws InterruptedException {
|
||||
while (isEmpty())
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
return pollLast();
|
||||
}
|
||||
}
|
||||
|
||||
private class ScheduledPacket implements Comparable<ScheduledPacket> {
|
||||
CommunicationPacket data;
|
||||
Destination destination;
|
||||
long earliestSendTime;
|
||||
PacketBatch batch; // the batch this packet belongs to, or null if not part of a batch
|
||||
long sentTime;
|
||||
long responseTime;
|
||||
CountDownLatch sentSignal;
|
||||
CountDownLatch responseSignal;
|
||||
CommunicationPacket response;
|
||||
|
||||
public ScheduledPacket(CommunicationPacket packet, Destination destination, long earliestSendTime) {
|
||||
this(packet, destination, earliestSendTime, null);
|
||||
}
|
||||
|
||||
public ScheduledPacket(CommunicationPacket packet, Destination destination, long earliestSendTime, PacketBatch batch) {
|
||||
this.data = packet;
|
||||
this.destination = destination;
|
||||
this.earliestSendTime = earliestSendTime;
|
||||
this.batch = batch;
|
||||
this.sentSignal = new CountDownLatch(1);
|
||||
this.responseSignal = new CountDownLatch(1);
|
||||
this.responseTime = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ScheduledPacket anotherPacket) {
|
||||
return new Long(earliestSendTime).compareTo(anotherPacket.earliestSendTime);
|
||||
}
|
||||
|
||||
public void decrementSentLatch() {
|
||||
sentTime = System.currentTimeMillis();
|
||||
sentSignal.countDown();
|
||||
}
|
||||
|
||||
public void awaitSending() throws InterruptedException {
|
||||
sentSignal.await();
|
||||
}
|
||||
|
||||
public long getSentTime() {
|
||||
return sentTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time at which a response packet was received, or -1 if no response.
|
||||
* @return
|
||||
*/
|
||||
public long getResponseTime() {
|
||||
return responseTime;
|
||||
}
|
||||
|
||||
public void decrementResponseLatch() {
|
||||
responseTime = System.currentTimeMillis();
|
||||
responseSignal.countDown();
|
||||
}
|
||||
|
||||
public void awaitResponse(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
responseSignal.await(timeout, unit);
|
||||
}
|
||||
|
||||
public void setResponse(CommunicationPacket response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this packet was sent via <code>sendAndWait</code>, and a response packet has been
|
||||
* received (i.e., a packet with the same packet id), this method returns the response packet.
|
||||
* Otherwise, <code>null</code> is returned.
|
||||
* @return
|
||||
*/
|
||||
public CommunicationPacket getResponse() {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,344 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.network.kademlia;
|
||||
|
||||
import i2p.bote.network.I2PSendQueue;
|
||||
import i2p.bote.network.PacketListener;
|
||||
import i2p.bote.packet.CommunicationPacket;
|
||||
import i2p.bote.service.I2PBoteThread;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.RandomSource;
|
||||
|
||||
// TODO if a sibling times out, refill the sibling table
|
||||
public class BucketManager extends I2PBoteThread implements PacketListener {
|
||||
private static final int INTERVAL = 5 * 60 * 1000;
|
||||
private static final int PING_TIMEOUT = 20 * 1000;
|
||||
|
||||
private Log log = new Log(BucketManager.class);
|
||||
private I2PSendQueue sendQueue;
|
||||
private List<KBucket> kBuckets;
|
||||
private KBucket siblingBucket; // TODO [ordered furthest away to closest in terms of hash distance to local node]
|
||||
private Hash localDestinationHash;
|
||||
|
||||
public BucketManager(I2PSendQueue sendQueue, Collection<KademliaPeer> initialPeers, Hash localDestinationHash) {
|
||||
super("BucketMgr");
|
||||
this.sendQueue = sendQueue;
|
||||
this.localDestinationHash = localDestinationHash;
|
||||
kBuckets = Collections.synchronizedList(new ArrayList<KBucket>());
|
||||
kBuckets.add(new KBucket(KBucket.MIN_HASH_VALUE, KBucket.MAX_HASH_VALUE, 0, true)); // this is the root bucket, so depth=0
|
||||
siblingBucket = new KBucket(KBucket.MIN_HASH_VALUE, KBucket.MAX_HASH_VALUE, 0, false);
|
||||
addAll(initialPeers);
|
||||
}
|
||||
|
||||
public void addAll(Collection<KademliaPeer> nodes) {
|
||||
for (KademliaPeer node: nodes)
|
||||
addOrUpdate(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a <code>{@link KademliaPeer}</code> to the sibling list or a bucket.
|
||||
* @param peer
|
||||
*/
|
||||
public void addOrUpdate(KademliaPeer peer) {
|
||||
Hash peerHash = peer.getDestinationHash();
|
||||
log.debug("Adding/updating peer: Hash = " + peerHash);
|
||||
|
||||
peer.resetStaleCounter();
|
||||
|
||||
synchronized(this) {
|
||||
if (!siblingBucket.isFull() || siblingBucket.contains(peer)) {
|
||||
siblingBucket.addOrUpdate(peer);
|
||||
getBucket(peerHash).remove(peer);
|
||||
}
|
||||
else if (isCloserSibling(peer)) {
|
||||
KademliaPeer ejectedPeer = siblingBucket.getMostDistantPeer(localDestinationHash);
|
||||
|
||||
addToBucket(ejectedPeer);
|
||||
siblingBucket.remove(ejectedPeer);
|
||||
|
||||
siblingBucket.addOrUpdate(peer);
|
||||
getBucket(peerHash).remove(peer);
|
||||
}
|
||||
else
|
||||
addToBucket(peer);
|
||||
}
|
||||
logBucketStats();
|
||||
|
||||
/* KademliaPeer ejectedPeer = addSibling(peer);
|
||||
// if the peer was added as a sibling, it may need to be removed from a bucket
|
||||
if (ejectedPeer != peer)
|
||||
getBucket(peerHash).remove(peer);
|
||||
// if the peer didn't get added as a sibling, try a bucket
|
||||
else
|
||||
addToBucket(peer);
|
||||
// if adding the peer to the list of siblings replaced another sibling, add the old sibling to a bucket
|
||||
if (ejectedPeer != null)
|
||||
addToBucket(ejectedPeer);*/
|
||||
|
||||
/*TODO synchronized(siblings) {
|
||||
if (siblings.isFull()) {
|
||||
KBucket bucket = getBucket(nodeHash);
|
||||
KademliaPeer mostDistantSibling = getMostDistantSibling();
|
||||
if (getDistance(node.getDestinationHash()) < getDistance(mostDistantSibling)) {
|
||||
bucket.addOrUpdate(mostDistantSibling);
|
||||
siblings.remove(mostDistantSibling);
|
||||
siblings.add(node);
|
||||
}
|
||||
else
|
||||
bucket.addOrUpdate(node);
|
||||
}
|
||||
else {
|
||||
siblings.add(node);
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
private void logBucketStats() {
|
||||
int numBuckets = kBuckets.size();
|
||||
int numPeers = getAllPeers().size();
|
||||
|
||||
log.debug("#buckets=" + numBuckets + "+sibBkt, #peers=" + numPeers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a <code>{@link KademliaPeer}</code> to the appropriate bucket.
|
||||
* @param peer
|
||||
*/
|
||||
private void addToBucket(KademliaPeer peer) {
|
||||
Hash nodeHash = peer.getDestinationHash();
|
||||
KBucket bucket = getBucket(nodeHash);
|
||||
KBucket newBucket = bucket.addOrSplit(peer);
|
||||
if (newBucket != null)
|
||||
kBuckets.add(newBucket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if a given peer is closer to the local node than at
|
||||
* least one sibling. In other words, test if <code>peer</code> should replace
|
||||
* an existing sibling.
|
||||
* @param peer
|
||||
* @return
|
||||
*/
|
||||
private boolean isCloserSibling(KademliaPeer peer) {
|
||||
BigInteger peerDistance = KademliaUtil.getDistance(peer, localDestinationHash);
|
||||
for (KademliaPeer sibling: siblingBucket) {
|
||||
BigInteger siblingDistance = KademliaUtil.getDistance(sibling.getDestinationHash(), localDestinationHash);
|
||||
if (peerDistance.compareTo(siblingDistance) < 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a peer to the sibling list if the list is not full, or if there is a node that can be
|
||||
* replaced.
|
||||
*
|
||||
* If <code>peer</code> replaced an existing sibling, that sibling is returned.
|
||||
* If <code>peer</code> could not be added to the list, <code>peer</code> is returned.
|
||||
* If the list was not full, <code>null</code> is returned.
|
||||
* @param peer
|
||||
* @return
|
||||
*/
|
||||
/* private KademliaPeer addSibling(KademliaPeer peer) {
|
||||
// no need to handle a replacement cache because the sibling bucket has none.
|
||||
KademliaPeer mostDistantSibling = siblingBucket.getMostDistantPeer(localDestinationHash);
|
||||
if (!siblingBucket.isFull()) {
|
||||
siblingBucket.add(peer);
|
||||
return null;
|
||||
}
|
||||
else if (new PeerDistanceComparator(localDestinationHash).compare(peer, mostDistantSibling) < 0) {
|
||||
siblingBucket.remove(mostDistantSibling);
|
||||
siblingBucket.add(peer);
|
||||
return mostDistantSibling;
|
||||
}
|
||||
else
|
||||
return peer;
|
||||
}*/
|
||||
|
||||
public void remove(KademliaPeer peer) {
|
||||
Hash nodeHash = peer.getDestinationHash();
|
||||
getBucket(nodeHash).remove(peer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a binary search for the index of the bucket whose key range contains a given {@link Hash}.
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
private int getBucketIndex(Hash key) {
|
||||
// initially, the search interval is 0..n-1
|
||||
int lowEnd = 0;
|
||||
int highEnd = kBuckets.size();
|
||||
|
||||
BigInteger keyValue = new BigInteger(key.getData());
|
||||
while (lowEnd < highEnd) {
|
||||
int centerIndex = (highEnd + lowEnd) / 2;
|
||||
if (keyValue.compareTo(kBuckets.get(centerIndex).getStartId()) < 0)
|
||||
highEnd = centerIndex;
|
||||
else if (keyValue.compareTo(kBuckets.get(centerIndex).getEndId()) > 0)
|
||||
lowEnd = centerIndex;
|
||||
else
|
||||
return centerIndex;
|
||||
}
|
||||
|
||||
log.error("This should never happen! No k-bucket found for hash: " + key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a binary search for the bucket whose key range contains a given {@link Hash}.
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
private KBucket getBucket(Hash key) {
|
||||
return kBuckets.get(getBucketIndex(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the <code>count</code> peers that are closest to a given key.
|
||||
* Less than <code>count</code> peers may be returned if there aren't
|
||||
* enough peers in the k-buckets.
|
||||
* @param key
|
||||
* @param count
|
||||
* @return
|
||||
*/
|
||||
public Collection<KademliaPeer> getClosestPeers(Hash key, int count) {
|
||||
Collection<KademliaPeer> closestPeers = new ConcurrentHashSet<KademliaPeer>();
|
||||
|
||||
// TODO don't put all peers in one huge list, only use two buckets at a time
|
||||
KademliaPeer[] allPeers = getAllPeersSortedByDistance(key);
|
||||
|
||||
for (int i=0; i<count && i<allPeers.length; i++)
|
||||
closestPeers.add(allPeers[i]);
|
||||
|
||||
return closestPeers;
|
||||
}
|
||||
|
||||
private KademliaPeer[] getAllPeersSortedByDistance(Hash key) {
|
||||
List<KademliaPeer> allPeers = getAllPeers();
|
||||
KademliaPeer[] peerArray = getAllPeers().toArray(new KademliaPeer[allPeers.size()]);
|
||||
Arrays.sort(peerArray, new PeerDistanceComparator(key));
|
||||
return peerArray;
|
||||
}
|
||||
|
||||
private List<KademliaPeer> getAllPeers() {
|
||||
List<KademliaPeer> allPeers = new ArrayList<KademliaPeer>();
|
||||
for (KBucket bucket: kBuckets)
|
||||
allPeers.addAll(bucket.getNodes());
|
||||
allPeers.addAll(siblingBucket.getNodes());
|
||||
return allPeers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all siblings of the local node (siblings are an S/Kademlia feature).
|
||||
* @return
|
||||
*/
|
||||
public List<KademliaPeer> getSiblings() {
|
||||
List<KademliaPeer> siblingDestinations = new ArrayList<KademliaPeer>();
|
||||
for (KademliaPeer sibling: siblingBucket)
|
||||
siblingDestinations.add(sibling);
|
||||
return siblingDestinations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of known Kademlia peers.
|
||||
* @return
|
||||
*/
|
||||
public int getPeerCount() {
|
||||
int count = 0;
|
||||
for (KBucket bucket: kBuckets)
|
||||
count += bucket.size();
|
||||
count += siblingBucket.size();
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* "refresh all k-buckets further away than the closest neighbor. This refresh is just a lookup of a random key that is within that k-bucket range."
|
||||
*/
|
||||
public void refreshAll() {
|
||||
for (KBucket bucket: kBuckets)
|
||||
refresh(bucket);
|
||||
}
|
||||
|
||||
private void refresh(KBucket bucket) {
|
||||
byte[] randomHash = new byte[Hash.HASH_LENGTH];
|
||||
for (int i=0; i<Hash.HASH_LENGTH; i++)
|
||||
randomHash[i] = (byte)RandomSource.getInstance().nextInt(256);
|
||||
Hash key = new Hash(randomHash);
|
||||
getClosestPeers(key, KademliaConstants.K);
|
||||
}
|
||||
|
||||
/* private void updatePeerList() {
|
||||
for (Destination peer: peers) {
|
||||
if (ping(peer))
|
||||
requestPeerList(peer);
|
||||
}
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!shutdownRequested()) {
|
||||
try {
|
||||
// TODO replicate();
|
||||
// TODO updatePeerList(); refresh every bucket to which we haven't performed a node lookup in the past hour. Refreshing means picking a random ID in the bucket's range and performing a node search for that ID.
|
||||
sleep(INTERVAL);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
log.debug("Thread '" + getName() + "' + interrupted", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PacketListener implementation
|
||||
@Override
|
||||
public void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime) {
|
||||
// any type of incoming packet updates the peer's record in the bucket/sibling list, or adds the peer to the bucket/sibling list
|
||||
addOrUpdate(new KademliaPeer(sender, receiveTime));
|
||||
}
|
||||
|
||||
private class PeerDistanceComparator implements Comparator<KademliaPeer> {
|
||||
private Hash reference;
|
||||
|
||||
PeerDistanceComparator(Hash reference) {
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(KademliaPeer peer1, KademliaPeer peer2) {
|
||||
BigInteger distance1 = KademliaUtil.getDistance(peer1.getDestinationHash(), reference);
|
||||
BigInteger distance2 = KademliaUtil.getDistance(peer2.getDestinationHash(), reference);
|
||||
return distance1.compareTo(distance2);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,219 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.network.kademlia;
|
||||
|
||||
import i2p.bote.UniqueId;
|
||||
import i2p.bote.network.I2PPacketDispatcher;
|
||||
import i2p.bote.network.I2PSendQueue;
|
||||
import i2p.bote.network.PacketListener;
|
||||
import i2p.bote.packet.CommunicationPacket;
|
||||
import i2p.bote.packet.DataPacket;
|
||||
import i2p.bote.packet.PeerList;
|
||||
import i2p.bote.packet.ResponsePacket;
|
||||
import i2p.bote.packet.dht.FindClosePeersPacket;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class ClosestNodesLookupTask implements Runnable {
|
||||
private static final int REQUEST_TIMEOUT = 30 * 1000;
|
||||
private static final int CLOSEST_NODES_LOOKUP_TIMEOUT = 2 * 60 * 1000; // the maximum amount of time a FIND_CLOSEST_NODES can take
|
||||
|
||||
private Log log = new Log(ClosestNodesLookupTask.class);
|
||||
private Hash key;
|
||||
private I2PPacketDispatcher i2pReceiver;
|
||||
private BucketManager bucketManager;
|
||||
private Random randomNumberGenerator;
|
||||
private I2PSendQueue sendQueue;
|
||||
private Set<Destination> responseReceived;
|
||||
private Set<KademliaPeer> notQueriedYet;
|
||||
private Set<FindClosePeersPacket> pendingRequests;
|
||||
|
||||
public ClosestNodesLookupTask(Hash key, I2PSendQueue sendQueue, I2PPacketDispatcher i2pReceiver, BucketManager bucketManager) {
|
||||
this.key = key;
|
||||
this.sendQueue = sendQueue;
|
||||
this.i2pReceiver = i2pReceiver;
|
||||
this.bucketManager = bucketManager;
|
||||
randomNumberGenerator = new Random(getTime());
|
||||
responseReceived = new TreeSet<Destination>(new HashDistanceComparator(key)); // nodes that have responded to a query; sorted by distance to the key
|
||||
notQueriedYet = new ConcurrentHashSet<KademliaPeer>(); // peers we haven't contacted yet
|
||||
pendingRequests = new ConcurrentHashSet<FindClosePeersPacket>(); // outstanding queries
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
log.debug("Looking up nodes closest to " + key);
|
||||
|
||||
PacketListener packetListener = new IncomingPacketHandler();
|
||||
i2pReceiver.addPacketListener(packetListener);
|
||||
|
||||
// prepare a list of close nodes (might need more than alpha if they don't all respond)
|
||||
notQueriedYet.addAll(bucketManager.getClosestPeers(key, KademliaConstants.S));
|
||||
|
||||
long startTime = getTime();
|
||||
while (true) {
|
||||
// send new requests if less than alpha are pending
|
||||
while (pendingRequests.size()<KademliaConstants.ALPHA && !notQueriedYet.isEmpty()) {
|
||||
KademliaPeer peer = selectRandom(notQueriedYet);
|
||||
notQueriedYet.remove(peer);
|
||||
FindClosePeersPacket packet = new FindClosePeersPacket(key);
|
||||
pendingRequests.add(packet);
|
||||
try {
|
||||
CommunicationPacket response = sendQueue.sendAndWait(packet, peer.getDestination(), REQUEST_TIMEOUT);
|
||||
if (response == null) // timeout occurred
|
||||
peer.incrementStaleCounter();
|
||||
else
|
||||
peer.resetStaleCounter();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
log.warn("Interrupted while waiting on a lookup request.", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (responseReceived.size() >= KademliaConstants.S)
|
||||
break;
|
||||
if (hasTimedOut(startTime, CLOSEST_NODES_LOOKUP_TIMEOUT)) {
|
||||
log.error("Lookup for closest nodes timed out.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
log.debug(responseReceived.size() + " nodes found.");
|
||||
|
||||
i2pReceiver.removePacketListener(packetListener);
|
||||
}
|
||||
|
||||
private KademliaPeer selectRandom(Collection<KademliaPeer> collection) {
|
||||
KademliaPeer[] array = new KademliaPeer[collection.size()];
|
||||
int index = randomNumberGenerator.nextInt(array.length);
|
||||
return collection.toArray(array)[index];
|
||||
}
|
||||
|
||||
private long getTime() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private boolean hasTimedOut(long startTime, long timeout) {
|
||||
return getTime() > startTime + timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return up to <code>s</code> peers.
|
||||
* @return
|
||||
*/
|
||||
public List<Destination> getResults() {
|
||||
List<Destination> resultsList = new ArrayList<Destination>();
|
||||
for (Destination destination: responseReceived)
|
||||
resultsList.add(destination);
|
||||
Collections.sort(resultsList, new HashDistanceComparator(key));
|
||||
|
||||
// trim the list to the k closest nodes
|
||||
if (resultsList.size() > KademliaConstants.S)
|
||||
resultsList = resultsList.subList(0, KademliaConstants.S);
|
||||
return resultsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if a set of peers contains a given peer.
|
||||
* @param peerSet
|
||||
* @param peerToFind
|
||||
* @return
|
||||
*/
|
||||
private boolean contains(Set<KademliaPeer> peerSet, KademliaPeer peerToFind) {
|
||||
Hash peerHash = peerToFind.getDestinationHash();
|
||||
for (KademliaPeer peer: peerSet)
|
||||
if (peer.getDestinationHash().equals(peerHash))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// compares two Destinations in terms of closeness to <code>reference</code>
|
||||
private class HashDistanceComparator implements Comparator<Destination> {
|
||||
private Hash reference;
|
||||
|
||||
public HashDistanceComparator(Hash reference) {
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
public int compare(Destination dest1, Destination dest2) {
|
||||
BigInteger dest1Distance = KademliaUtil.getDistance(dest1.calculateHash(), reference);
|
||||
BigInteger dest2Distance = KademliaUtil.getDistance(dest2.calculateHash(), reference);
|
||||
return dest1Distance.compareTo(dest2Distance);
|
||||
}
|
||||
};
|
||||
|
||||
private class IncomingPacketHandler implements PacketListener {
|
||||
@Override
|
||||
public void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime) {
|
||||
if (packet instanceof ResponsePacket) {
|
||||
ResponsePacket responsePacket = (ResponsePacket)packet;
|
||||
DataPacket payload = responsePacket.getPayload();
|
||||
if (payload instanceof PeerList)
|
||||
addPeers(responsePacket, (PeerList)payload, sender, receiveTime);
|
||||
}
|
||||
}
|
||||
|
||||
private void addPeers(ResponsePacket responsePacket, PeerList peerListPacket, Destination sender, long receiveTime) {
|
||||
log.debug("Peer List Packet received: #peers=" + peerListPacket.getPeers().size() + ", sender="+ sender);
|
||||
|
||||
// if the packet is in response to a pending request, update the three Sets
|
||||
FindClosePeersPacket request = getPacketById(pendingRequests, responsePacket.getPacketId()); // find the request the node list is in response to
|
||||
if (request != null) {
|
||||
// TODO make responseReceived and pendingRequests a parameter in the constructor?
|
||||
responseReceived.add(sender);
|
||||
Collection<KademliaPeer> peersReceived = peerListPacket.getPeers();
|
||||
for (KademliaPeer peer: peersReceived)
|
||||
if (contains(notQueriedYet, peer))
|
||||
notQueriedYet.add(peer);
|
||||
pendingRequests.remove(request);
|
||||
// TODO synchronize access to shortList and pendingRequests
|
||||
}
|
||||
else
|
||||
log.debug("No Find Close Nodes packet found for Peer List: " + peerListPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a packet that matches a given {@link UniqueId} from a {@link Collection} of packets, or
|
||||
* <code>null</code> if no match.
|
||||
* @param packets
|
||||
* @param packetId
|
||||
* @return
|
||||
*/
|
||||
private FindClosePeersPacket getPacketById(Collection<FindClosePeersPacket> packets, UniqueId packetId) {
|
||||
for (FindClosePeersPacket packet: packets)
|
||||
if (packetId.equals(packet.getPacketId()))
|
||||
return packet;
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,280 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.network.kademlia;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Peers are sorted by the time of the most recent communication: Index 0 = least recent,
|
||||
* index n-1 = most recent.
|
||||
*
|
||||
*/
|
||||
class KBucket implements Iterable<KademliaPeer> {
|
||||
static final BigInteger MIN_HASH_VALUE = BigInteger.ONE.negate().shiftLeft(Hash.HASH_LENGTH*8); // system-wide minimum hash value
|
||||
static final BigInteger MAX_HASH_VALUE = BigInteger.ONE.shiftLeft(Hash.HASH_LENGTH*8).subtract(BigInteger.ONE); // system-wide maximum hash value
|
||||
private static final int CAPACITY = KademliaConstants.K; // The maximum number of peers the bucket can hold
|
||||
|
||||
private Log log = new Log(KBucket.class);
|
||||
private BigInteger startId;
|
||||
private BigInteger endId;
|
||||
private List<KademliaPeer> peers; // the list is always kept sorted by "last seen" time
|
||||
private Set<KademliaPeer> replacementCache;
|
||||
private int depth;
|
||||
private boolean replacementCacheEnabled;
|
||||
|
||||
KBucket(BigInteger startId, BigInteger endId, int depth, boolean replacementCacheEnabled) {
|
||||
this.startId = startId;
|
||||
this.endId = endId;
|
||||
Comparator<KademliaPeer> peerComparator = createLastReceptionComparator();
|
||||
peers = new ArrayList<KademliaPeer>();
|
||||
replacementCache = new ConcurrentSkipListSet<KademliaPeer>(peerComparator);
|
||||
this.depth = depth;
|
||||
this.replacementCacheEnabled = replacementCacheEnabled;
|
||||
}
|
||||
|
||||
BigInteger getStartId() {
|
||||
return startId;
|
||||
}
|
||||
|
||||
BigInteger getEndId() {
|
||||
return endId;
|
||||
}
|
||||
|
||||
List<KademliaPeer> getNodes() {
|
||||
// TODO only return peers that are not locked
|
||||
return peers;
|
||||
}
|
||||
|
||||
void add(KademliaPeer node) {
|
||||
if (isFull())
|
||||
log.error("Error: adding a node to a full k-bucket. Bucket needs to be split first. Size=" + size() + ", capacity=" + CAPACITY);
|
||||
|
||||
peers.add(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a node to the bucket, splitting the bucket if necessary. If the bucket is split,
|
||||
* the newly created bucket is returned. Otherwise, <code>null</code> is returned.
|
||||
*
|
||||
* If the bucket is full but cannot be split, the new node is added to the replacement
|
||||
* cache and <code>null</code> is returned.
|
||||
* @param peer
|
||||
* @return
|
||||
*/
|
||||
synchronized KBucket addOrSplit(KademliaPeer peer) {
|
||||
if (!rangeContains(peer))
|
||||
log.error("Attempt to add a node whose hash is outside the bucket's range! Bucket start=" + startId + " Bucket end=" + endId + " peer hash=" + new BigInteger(peer.getDestinationHash().getData()));
|
||||
|
||||
if (isFull() && !contains(peer)) {
|
||||
if (canSplit(peer)) {
|
||||
KBucket newBucket = split(peer.getDestinationHash());
|
||||
if (rangeContains(peer))
|
||||
add(peer);
|
||||
else if (newBucket.rangeContains(peer))
|
||||
newBucket.add(peer);
|
||||
else
|
||||
log.error("After splitting a bucket, node is outside of both buckets' ranges.");
|
||||
return newBucket;
|
||||
}
|
||||
else {
|
||||
replacementCache.add(peer);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
addOrUpdate(peer);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the bucket should be split in order to make room for a new peer.
|
||||
* @return
|
||||
*/
|
||||
private boolean canSplit(KademliaPeer peer) {
|
||||
return depth%KademliaConstants.B!=0 || rangeContains(peer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a known peer, or adds the peer if it isn't known.
|
||||
* TODO If the bucket is full, the peer is added to the bucket's replacement cache.
|
||||
* @param destination
|
||||
* @return <code>true</code> if the peer was added (or replacement-cached),
|
||||
* <code>false</code> if it was updated.
|
||||
*/
|
||||
boolean addOrUpdate(KademliaPeer peer) {
|
||||
// TODO log an error if peer outside bucket's range
|
||||
// TODO handle stale peers
|
||||
// TODO manage replacement cache
|
||||
KademliaPeer existingPeer = getPeer(peer.getDestination());
|
||||
if (existingPeer == null) {
|
||||
add(peer);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
existingPeer.setLastReception(peer.getLastReception());
|
||||
// TODO move to end of list if lastReception is highest value, which it should be most of the time
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if a peer exists in the bucket.
|
||||
* @param peer
|
||||
* @return
|
||||
*/
|
||||
boolean contains(KademliaPeer peer) {
|
||||
return getPeer(peer.getDestination()) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the bucket's Id range contains the hash of a given
|
||||
* peer, regardless if the bucket contains the peer; <code>false</code> if the hash
|
||||
* is outside the range.
|
||||
* @param peer
|
||||
* @return
|
||||
*/
|
||||
private boolean rangeContains(KademliaPeer peer) {
|
||||
BigInteger peerHash = new BigInteger(peer.getDestinationHash().getData());
|
||||
return (startId.compareTo(peerHash)<=0 || endId.compareTo(peerHash)>0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a peer with a given I2P destination from the bucket, or <code>null</code> if the
|
||||
* peer isn't in the bucket.
|
||||
* @param destination
|
||||
* @return
|
||||
*/
|
||||
KademliaPeer getPeer(Destination destination) {
|
||||
for (KademliaPeer peer: peers)
|
||||
if (peer.getDestination().equals(destination))
|
||||
return peer;
|
||||
return null;
|
||||
}
|
||||
|
||||
KademliaPeer getClosestPeer(Hash key) {
|
||||
KademliaPeer closestPeer = null;
|
||||
BigInteger minDistance = MAX_HASH_VALUE;
|
||||
for (KademliaPeer peer: peers) {
|
||||
BigInteger distance = KademliaUtil.getDistance(key, peer.getDestination().calculateHash());
|
||||
if (distance.compareTo(minDistance) < 0) {
|
||||
closestPeer = peer;
|
||||
minDistance = distance;
|
||||
}
|
||||
}
|
||||
return closestPeer;
|
||||
}
|
||||
|
||||
/* KademliaPeer getMostDistantPeer(KademliaPeer node) {
|
||||
return getMostDistantPeer(node.getDestinationHash());
|
||||
}*/
|
||||
|
||||
KademliaPeer getMostDistantPeer(Hash key) {
|
||||
KademliaPeer mostDistantPeer = null;
|
||||
BigInteger maxDistance = BigInteger.ZERO;
|
||||
for (KademliaPeer peer: peers) {
|
||||
BigInteger distance = KademliaUtil.getDistance(key, peer.getDestination().calculateHash());
|
||||
if (distance.compareTo(maxDistance) > 0) {
|
||||
mostDistantPeer = peer;
|
||||
maxDistance = distance;
|
||||
}
|
||||
}
|
||||
return mostDistantPeer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<KademliaPeer> iterator() {
|
||||
return peers.iterator();
|
||||
}
|
||||
|
||||
void remove(Destination destination) {
|
||||
peers.remove(getPeer(destination));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a peer from the bucket. If the peer doesn't exist in the bucket, nothing happens.
|
||||
* @param node
|
||||
*/
|
||||
void remove(KademliaPeer node) {
|
||||
peers.remove(node);
|
||||
}
|
||||
|
||||
int size() {
|
||||
return peers.size();
|
||||
}
|
||||
|
||||
boolean isFull() {
|
||||
return size() >= CAPACITY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves half the nodes into a new bucket.
|
||||
* @return The new bucket
|
||||
*/
|
||||
KBucket split() {
|
||||
depth++;
|
||||
KBucket newBucket = new KBucket(startId, endId, depth, replacementCacheEnabled);
|
||||
for (int i=0; i<peers.size()/2; i++) {
|
||||
KademliaPeer peer = peers.get(i);
|
||||
newBucket.add(peer);
|
||||
remove(peer);
|
||||
}
|
||||
return newBucket;
|
||||
}
|
||||
|
||||
KBucket split(Hash hash) {
|
||||
return split(new BigInteger(hash.toBase64()));
|
||||
}
|
||||
|
||||
KBucket split(BigInteger pivot) {
|
||||
depth++;
|
||||
KBucket newBucket = new KBucket(startId, pivot.subtract(BigInteger.ONE), depth, replacementCacheEnabled);
|
||||
startId = pivot;
|
||||
for (KademliaPeer peer: peers) {
|
||||
BigInteger nodeId = new BigInteger(peer.getDestination().calculateHash().getData());
|
||||
if (nodeId.compareTo(pivot) >= 0) {
|
||||
newBucket.add(peer);
|
||||
remove(peer);
|
||||
}
|
||||
}
|
||||
return newBucket;
|
||||
}
|
||||
|
||||
private Comparator<KademliaPeer> createLastReceptionComparator() {
|
||||
return new Comparator<KademliaPeer>() {
|
||||
@Override
|
||||
public int compare(KademliaPeer peer1, KademliaPeer peer2) {
|
||||
return Long.valueOf(peer1.getLastReception()).compareTo(peer2.getLastReception());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -1,416 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* I2P-Bote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.network.kademlia;
|
||||
|
||||
import i2p.bote.network.DHT;
|
||||
import i2p.bote.network.DhtStorageHandler;
|
||||
import i2p.bote.network.I2PPacketDispatcher;
|
||||
import i2p.bote.network.I2PSendQueue;
|
||||
import i2p.bote.network.PacketBatch;
|
||||
import i2p.bote.network.PacketListener;
|
||||
import i2p.bote.packet.CommunicationPacket;
|
||||
import i2p.bote.packet.I2PBotePacket;
|
||||
import i2p.bote.packet.PeerList;
|
||||
import i2p.bote.packet.ResponsePacket;
|
||||
import i2p.bote.packet.StatusCode;
|
||||
import i2p.bote.packet.dht.DhtStorablePacket;
|
||||
import i2p.bote.packet.dht.FindClosePeersPacket;
|
||||
import i2p.bote.packet.dht.RetrieveRequest;
|
||||
import i2p.bote.packet.dht.StoreRequest;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import com.nettgryppa.security.HashCash;
|
||||
|
||||
/**
|
||||
* The main class of the Kademlia implementation. All the high-level Kademlia logic
|
||||
* is in here.
|
||||
*
|
||||
* Resources used:
|
||||
* [1] http://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf
|
||||
* [2] http://xlattice.sourceforge.net/components/protocol/kademlia/specs.html
|
||||
* [3] http://en.wikipedia.org/wiki/Kademlia
|
||||
* [4] http://www.barsoom.org/papers/infocom-2006-kad.pdf
|
||||
* [5] http://doc.tm.uka.de/SKademlia_2007.pdf
|
||||
* [6] OverSim (http://www.oversim.org/), which includes a S/Kademlia implementation
|
||||
*
|
||||
*/
|
||||
public class KademliaDHT implements DHT, PacketListener {
|
||||
private static final URL BUILT_IN_PEER_FILE = KademliaDHT.class.getResource("built-in-peers.txt");
|
||||
|
||||
private Log log = new Log(KademliaDHT.class);
|
||||
private Hash localDestinationHash;
|
||||
private I2PSendQueue sendQueue;
|
||||
private I2PPacketDispatcher i2pReceiver;
|
||||
private File peerFile;
|
||||
private Collection<KademliaPeer> initialPeers;
|
||||
private BucketManager bucketManager;
|
||||
private Map<Class<? extends DhtStorablePacket>, DhtStorageHandler> storageHandlers;
|
||||
|
||||
public KademliaDHT(Destination localDestination, I2PSendQueue sendQueue, I2PPacketDispatcher i2pReceiver, File peerFile) {
|
||||
localDestinationHash = localDestination.calculateHash();
|
||||
this.sendQueue = sendQueue;
|
||||
this.i2pReceiver = i2pReceiver;
|
||||
this.peerFile = peerFile;
|
||||
|
||||
initialPeers = Collections.synchronizedList(new ArrayList<KademliaPeer>());
|
||||
// Read the built-in peer file
|
||||
readPeers(BUILT_IN_PEER_FILE);
|
||||
// Read the updateable peer file
|
||||
readPeers(peerFile);
|
||||
|
||||
bucketManager = new BucketManager(sendQueue, initialPeers, localDestination.calculateHash());
|
||||
storageHandlers = new ConcurrentHashMap<Class<? extends DhtStorablePacket>, DhtStorageHandler>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the S nodes closest to a given key by querying peers.
|
||||
* This method blocks. It returns after <code>CLOSEST_NODES_LOOKUP_TIMEOUT+1</code> seconds at
|
||||
* the longest.
|
||||
*
|
||||
* The number of pending requests never exceeds ALPHA. According to [4], this is the most efficient.
|
||||
*
|
||||
* If there are less than <code>s</code> results after the kademlia lookup finishes, nodes from
|
||||
* the sibling list are used.
|
||||
*/
|
||||
private Collection<Destination> getClosestNodes(Hash key) {
|
||||
ClosestNodesLookupTask lookupTask = new ClosestNodesLookupTask(key, sendQueue, i2pReceiver, bucketManager);
|
||||
lookupTask.run();
|
||||
return lookupTask.getResults();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DhtStorablePacket findOne(Hash key, Class<? extends DhtStorablePacket> dataType) {
|
||||
Collection<DhtStorablePacket> results = find(key, dataType, false);
|
||||
if (results.isEmpty())
|
||||
return null;
|
||||
else
|
||||
return results.iterator().next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<DhtStorablePacket> findAll(Hash key, Class<? extends DhtStorablePacket> dataType) {
|
||||
return find(key, dataType, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStorageHandler(Class<? extends DhtStorablePacket> packetType, DhtStorageHandler storageHandler) {
|
||||
storageHandlers.put(packetType, storageHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumPeers() {
|
||||
return bucketManager.getPeerCount();
|
||||
}
|
||||
|
||||
private Collection<DhtStorablePacket> find(Hash key, Class<? extends DhtStorablePacket> dataType, boolean exhaustive) {
|
||||
final Collection<Destination> closeNodes = getClosestNodes(key);
|
||||
log.debug("Querying " + closeNodes.size() + " nodes for data type " + dataType + ", Kademlia key " + key);
|
||||
|
||||
final Collection<I2PBotePacket> receivedPackets = new ConcurrentHashSet<I2PBotePacket>(); // avoid adding duplicate packets
|
||||
|
||||
/* PacketListener packetListener = new PacketListener() {
|
||||
@Override
|
||||
public void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime) {
|
||||
// add packet to list of received packets if the packet is in response to a RetrieveRequest
|
||||
if (packet instanceof RetrieveRequest && closeNodes.contains(sender))
|
||||
receivedPackets.add(packet);
|
||||
}
|
||||
};
|
||||
i2pReceiver.addPacketListener(packetListener);*/
|
||||
|
||||
// Send the retrieve requests
|
||||
PacketBatch batch = new PacketBatch();
|
||||
for (Destination node: closeNodes)
|
||||
batch.putPacket(new RetrieveRequest(key, dataType), node);
|
||||
sendQueue.send(batch);
|
||||
try {
|
||||
batch.awaitSendCompletion();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
log.warn("Interrupted while waiting for Retrieve Requests to be sent.", e);
|
||||
}
|
||||
|
||||
// wait for replies
|
||||
try {
|
||||
if (exhaustive)
|
||||
TimeUnit.SECONDS.sleep(60); // TODO make a static field
|
||||
else
|
||||
batch.awaitFirstReply(60, TimeUnit.SECONDS); // TODO make a static field
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
log.warn("Interrupted while waiting for responses to Retrieve Requests.", e);
|
||||
}
|
||||
log.debug(batch.getResponses().size() + " response packets received for hash " + key + " and data type " + dataType);
|
||||
|
||||
sendQueue.remove(batch);
|
||||
// i2pReceiver.removePacketListener(packetListener);
|
||||
|
||||
ConcurrentHashSet<DhtStorablePacket> storablePackets = getStorablePackets(batch);
|
||||
DhtStorablePacket localResult = findLocally(key, dataType);
|
||||
if (localResult != null) {
|
||||
log.debug("Locally stored packet found for hash " + key + " and data type " + dataType);
|
||||
storablePackets.add(localResult);
|
||||
}
|
||||
return storablePackets;
|
||||
}
|
||||
|
||||
private DhtStorablePacket findLocally(Hash key, Class<? extends DhtStorablePacket> dataType) {
|
||||
DhtStorageHandler storageHandler = storageHandlers.get(dataType);
|
||||
if (storageHandler != null)
|
||||
return storageHandler.retrieve(key);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all <code>DhtStorablePacket</code> packets that have been received as a response to a send batch.
|
||||
* @param batch
|
||||
* @return
|
||||
*/
|
||||
private ConcurrentHashSet<DhtStorablePacket> getStorablePackets(PacketBatch batch) {
|
||||
ConcurrentHashSet<DhtStorablePacket> storablePackets = new ConcurrentHashSet<DhtStorablePacket>();
|
||||
for (I2PBotePacket packet: batch.getResponses())
|
||||
if (packet instanceof DhtStorablePacket)
|
||||
storablePackets.add((DhtStorablePacket)packet);
|
||||
return storablePackets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(DhtStorablePacket packet) throws NoSuchAlgorithmException {
|
||||
Hash key = packet.getDhtKey();
|
||||
|
||||
Collection<Destination> closeNodes = getClosestNodes(key);
|
||||
log.debug("Storing a " + packet.getClass().getSimpleName() + " with key " + key + " on " + closeNodes.size() + " nodes");
|
||||
|
||||
HashCash hashCash = HashCash.mintCash("", 1); // TODO
|
||||
StoreRequest storeRequest = new StoreRequest(hashCash, packet);
|
||||
PacketBatch batch = new PacketBatch();
|
||||
for (Destination node: closeNodes)
|
||||
batch.putPacket(storeRequest, node);
|
||||
sendQueue.send(batch);
|
||||
|
||||
try {
|
||||
batch.awaitSendCompletion();
|
||||
// TODO awaitAllResponses, repeat if necessary
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
log.warn("Interrupted while waiting for responses to Storage Requests to be sent.", e);
|
||||
}
|
||||
|
||||
sendQueue.remove(batch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
i2pReceiver.addPacketListener(this);
|
||||
bucketManager.start();
|
||||
bootstrap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutDown() {
|
||||
i2pReceiver.removePacketListener(this);
|
||||
bucketManager.requestShutdown();
|
||||
writePeersToFile(peerFile);
|
||||
}
|
||||
|
||||
private void bootstrap() {
|
||||
new BootstrapTask().start();
|
||||
}
|
||||
|
||||
private class BootstrapTask extends Thread {
|
||||
public BootstrapTask() {
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
log.debug("Bootstrap start");
|
||||
while (true) {
|
||||
for (KademliaPeer bootstrapNode: initialPeers) {
|
||||
bootstrapNode.setLastReception(-1);
|
||||
bucketManager.addOrUpdate(bootstrapNode);
|
||||
Collection<Destination> closestNodes = getClosestNodes(localDestinationHash);
|
||||
// if last reception time is not set, the node didn't respond, so remove it
|
||||
if (bootstrapNode.getLastReception() <= 0)
|
||||
bucketManager.remove(bootstrapNode);
|
||||
|
||||
if (closestNodes.isEmpty()) {
|
||||
log.debug("No response from bootstrap node " + bootstrapNode);
|
||||
bucketManager.remove(bootstrapNode);
|
||||
}
|
||||
else {
|
||||
bucketManager.refreshAll();
|
||||
log.info("Bootstrapping finished. Number of peers = " + bucketManager.getPeerCount());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log.warn("Can't bootstrap off any known peer, will retry shortly.");
|
||||
try {
|
||||
TimeUnit.MINUTES.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted while pausing after unsuccessful bootstrap attempt.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes all peers to a file in descending order of "last seen" time.
|
||||
* @param peerFile
|
||||
*/
|
||||
private void writePeersToFile(File peerFile) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void readPeers(URL url) {
|
||||
log.info("Reading peers from URL: '" + url + "'");
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = url.openStream();
|
||||
readPeers(stream);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Error reading peers from URL.", e);
|
||||
} finally {
|
||||
if (stream != null)
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
log.error("Can't close input stream.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readPeers(File peerFile) {
|
||||
log.info("Reading peers from file: '" + peerFile.getAbsolutePath() + "'");
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = new FileInputStream(peerFile);
|
||||
readPeers(stream);
|
||||
} catch (IOException e) {
|
||||
log.error("Error reading peers from file.", e);
|
||||
} finally {
|
||||
if (stream != null)
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
log.error("Can't close input stream.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads peer destinations from an <code>InputStream</code> and writes them to <code>initialPeers</code>.
|
||||
* @param inputStream
|
||||
*/
|
||||
private void readPeers(InputStream inputStream) throws IOException {
|
||||
BufferedReader inputBuffer = new BufferedReader(new InputStreamReader(inputStream));
|
||||
|
||||
int numPeersBefore = initialPeers.size();
|
||||
while (true) {
|
||||
String line = null;
|
||||
line = inputBuffer.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
|
||||
if (!line.startsWith("#"))
|
||||
try {
|
||||
Destination destination = new Destination(line);
|
||||
KademliaPeer peer = new KademliaPeer(destination, 0);
|
||||
|
||||
// don't add the local destination as a peer
|
||||
if (!peer.getDestinationHash().equals(localDestinationHash))
|
||||
initialPeers.add(peer);
|
||||
}
|
||||
catch (DataFormatException e) {
|
||||
log.error("Invalid destination key in line " + line, e);
|
||||
}
|
||||
}
|
||||
log.debug(initialPeers.size()-numPeersBefore + " peers read.");
|
||||
}
|
||||
|
||||
private void sendPeerList(FindClosePeersPacket packet, Destination destination) {
|
||||
// TODO don't include the requesting peer
|
||||
Collection<KademliaPeer> closestPeers = bucketManager.getClosestPeers(packet.getKey(), KademliaConstants.K);
|
||||
PeerList peerList = new PeerList(closestPeers);
|
||||
sendQueue.sendResponse(peerList, destination, packet.getPacketId());
|
||||
}
|
||||
|
||||
// PacketListener implementation
|
||||
@Override
|
||||
public void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime) {
|
||||
if (packet instanceof FindClosePeersPacket)
|
||||
sendPeerList((FindClosePeersPacket)packet, sender);
|
||||
else if (packet instanceof StoreRequest) {
|
||||
DhtStorablePacket packetToStore = ((StoreRequest)packet).getPacketToStore();
|
||||
if (packetToStore != null) {
|
||||
DhtStorageHandler storageHandler = storageHandlers.get(packetToStore.getClass());
|
||||
if (storageHandler != null)
|
||||
storageHandler.store(packetToStore);
|
||||
else
|
||||
log.warn("No storage handler found for type " + packetToStore.getClass().getSimpleName() + ".");
|
||||
}
|
||||
}
|
||||
else if (packet instanceof RetrieveRequest) {
|
||||
RetrieveRequest retrieveRequest = (RetrieveRequest)packet;
|
||||
DhtStorageHandler storageHandler = storageHandlers.get(retrieveRequest.getDataType());
|
||||
if (storageHandler != null) {
|
||||
DhtStorablePacket storedPacket = storageHandler.retrieve(retrieveRequest.getKey());
|
||||
// if requested packet found, send it to the requester
|
||||
if (storedPacket != null) {
|
||||
log.debug("Packet found for retrieve request: [" + retrieveRequest + "], replying to sender: [" + sender + "]");
|
||||
ResponsePacket response = new ResponsePacket(storedPacket, StatusCode.OK, retrieveRequest.getPacketId());
|
||||
sendQueue.send(response, sender);
|
||||
}
|
||||
else
|
||||
log.debug("No matching packet found for retrieve request: [" + retrieveRequest + "]");
|
||||
}
|
||||
else
|
||||
log.warn("No storage handler found for type " + packet.getClass().getSimpleName() + ".");
|
||||
}
|
||||
|
||||
// bucketManager is not registered as a PacketListener, so notify it here
|
||||
bucketManager.packetReceived(packet, sender, receiveTime);
|
||||
}
|
||||
}
|