Compare commits
826 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 |
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,45 +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/>.
|
||||
--%>
|
||||
|
||||
<%@ attribute name="dirName" required="true" description="The directory used by the folder" %>
|
||||
<%@ attribute name="displayName" required="true" description="The display name for the folder" %>
|
||||
|
||||
<%@ 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="numEmails" value="${ib:getMailFolder(dirName).numElements}"/>
|
||||
<c:set var="numNew" value="${ib:getMailFolder(dirName).numNewEmails}"/>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="${dirName eq 'Outbox'}">
|
||||
<a href="outbox.jsp" target="_parent" title="${numEmails} emails total, ${numNew} new">
|
||||
<img src="images/folder.png"/>${displayName}
|
||||
</a>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<a href="folder.jsp?path=${dirName}" target="_parent" title="${numEmails} emails total, ${numNew} new">
|
||||
<img src="images/folder.png"/>${displayName}
|
||||
</a>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
<c:if test="${numNew>0}">(${numNew})</c:if>
|
@@ -1,459 +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>JSP</body-content>
|
||||
<attribute>
|
||||
<name>sender</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>recipient</name>
|
||||
<info>
|
||||
Adds an email recipient. Must be inside an ib:sendEmail tag.
|
||||
</info>
|
||||
<tag-class>i2p.bote.web.RecipientTag</tag-class>
|
||||
<body-content>scriptless</body-content>
|
||||
<attribute>
|
||||
<name>type</name>
|
||||
<description>
|
||||
Can be any of the javax.mail.Message.RecipientType values
|
||||
(TO, CC, or BCC).
|
||||
</description>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>true</required>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>address</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>formatPlainText</name>
|
||||
<tag-class>i2p.bote.web.FormatPlainTextTag</tag-class>
|
||||
<body-content>empty</body-content>
|
||||
<attribute>
|
||||
<name>text</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>true</required>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>setEmailRead</name>
|
||||
<tag-class>i2p.bote.web.SetEmailReadTag</tag-class>
|
||||
<body-content>empty</body-content>
|
||||
<attribute>
|
||||
<name>folder</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>true</required>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>messageId</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>true</required>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>read</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>true</required>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>quote</name>
|
||||
<tag-class>i2p.bote.web.QuoteTag</tag-class>
|
||||
<body-content>empty</body-content>
|
||||
<attribute>
|
||||
<name>text</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>true</required>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>connect</name>
|
||||
<tag-class>i2p.bote.web.ConnectTag</tag-class>
|
||||
<body-content>empty</body-content>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>peerInfo</name>
|
||||
<tag-class>i2p.bote.web.PeerInfoTag</tag-class>
|
||||
<body-content>empty</body-content>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>localizedLanguageName</name>
|
||||
<tag-class>i2p.bote.web.LocalizedLanguageNameTag</tag-class>
|
||||
<body-content>empty</body-content>
|
||||
<attribute>
|
||||
<name>locale</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>true</required>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>message</name>
|
||||
<info>
|
||||
Same as fmt:message, except that message keys in
|
||||
the "en" locale translate to the message key itself.
|
||||
</info>
|
||||
<tag-class>i2p.bote.web.MessageTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
<attribute>
|
||||
<name>key</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>false</required>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>bundle</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>false</required>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>var</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>false</required>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>scope</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>false</required>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>hide</name>
|
||||
<description>
|
||||
If this parameter is set to true, and the user has chosen to hide
|
||||
the UI language from email recipients, the message is not translated.
|
||||
</description>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>false</required>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>param</name>
|
||||
<info>
|
||||
Same as fmt:param. Must be inside an ib:message tag.
|
||||
</info>
|
||||
<tag-class>i2p.bote.web.ParamTag</tag-class>
|
||||
<body-content>scriptless</body-content>
|
||||
<attribute>
|
||||
<name>value</name>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<required>false</required>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>saveConfiguration</name>
|
||||
<info>
|
||||
Saves the configuration to the config file.
|
||||
</info>
|
||||
<tag-class>i2p.bote.web.SaveConfigurationTag</tag-class>
|
||||
<body-content>empty</body-content>
|
||||
</tag>
|
||||
|
||||
<function>
|
||||
<name>getNetworkStatus</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
i2p.bote.network.NetworkStatus getNetworkStatus()
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>getIdentities</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
i2p.bote.email.Identities getIdentities()
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>saveIdentity</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
boolean saveIdentity(boolean, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean)
|
||||
</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>getIdentity</name>
|
||||
<description>
|
||||
Looks up an EmailIdentity by destination key.
|
||||
</description>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
i2p.bote.email.EmailIdentity getIdentity(java.lang.String)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>getCryptoImplementation</name>
|
||||
<description>
|
||||
Looks up a CryptoImplementation by ID number.
|
||||
</description>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
i2p.bote.crypto.CryptoImplementation getCryptoImplementation(int)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>getAddressBook</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
i2p.bote.addressbook.AddressBook getAddressBook()
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>saveContact</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
boolean saveContact(java.lang.String, java.lang.String)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>deleteContact</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
boolean deleteContact(java.lang.String)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>getContactName</name>
|
||||
<description>
|
||||
Looks up a name for an Email Destination in the address book.
|
||||
If the Email Destination does not exist in the address book,
|
||||
an empty string is returned.
|
||||
</description>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
java.lang.String getContactName(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>
|
||||
|
||||
<function>
|
||||
<name>getEmails</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
java.util.List getEmails(i2p.bote.folder.EmailFolder, i2p.bote.email.EmailAttribute, boolean)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>getShortSenderName</name>
|
||||
<description>
|
||||
Returns up to maxLength characters of a "sender" header field.
|
||||
</description>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
java.lang.String getShortSenderName(java.lang.String, int)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>getOneLocalRecipient</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
java.lang.String getOneLocalRecipient(i2p.bote.email.Email)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>getEmailStatus</name>
|
||||
<description>
|
||||
Returns the status of an email in the outbox. See OutboxProcessor.java.
|
||||
</description>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
java.lang.String getEmailStatus(i2p.bote.email.Email)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>deleteEmail</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
java.lang.boolean deleteEmail(java.lang.String, java.lang.String)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>getNameAndDestination</name>
|
||||
<description>
|
||||
Returns a string containing the locally known name of the Email Destination
|
||||
in the String argument, and the Base64-encoded Email Destination.
|
||||
If the Email Destination is not locally known (if it doesn't exist in the
|
||||
address book or any of the identities), the address is returned unaltered.
|
||||
</description>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
java.lang.String getNameAndDestination(java.lang.String)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>extractName</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
java.lang.String extractName(java.lang.String)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>extractEmailDestination</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
i2p.bote.email.EmailDestination extractEmailDestination(java.lang.String)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>getSortedRecipientParams</name>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
java.util.Map getSortedRecipientParams(java.util.Map)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>mergeRecipientFields</name>
|
||||
<description>
|
||||
This method is used by newEmail.jsp. It adds the values of all
|
||||
selectedContact keys as recipient addresses, and returns a List
|
||||
containing the old recipient addresses as well as the new ones.
|
||||
If the "action" parameter is set to "addRecipientField", a blank
|
||||
recipient address is added additionally.
|
||||
If the action is "removeRecipient" followed by a number, the
|
||||
recipient field with that index is removed.
|
||||
</description>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
java.util.List mergeRecipientFields(javax.servlet.ServletRequest)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>isKnown</name>
|
||||
<description>
|
||||
Returns true if a string contains a Base64-encoded Email Destination that
|
||||
is either in the address book or among the Email Identities.
|
||||
</description>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
boolean isKnown(java.lang.String)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<name>escapeQuotes</name>
|
||||
<description>
|
||||
Converts all double and single quotes in a string to HTML entities,
|
||||
which makes the string safe to use in hidden form fields.
|
||||
</description>
|
||||
<function-class>i2p.bote.web.JSPHelper</function-class>
|
||||
<function-signature>
|
||||
java.lang.String escapeQuotes(java.lang.String)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
<tag-file>
|
||||
<name>printWithLineBreaks</name>
|
||||
<path>/WEB-INF/tags/printWithLineBreaks.tag</path>
|
||||
</tag-file>
|
||||
|
||||
<tag-file>
|
||||
<name>folderLink</name>
|
||||
<path>/WEB-INF/tags/folderLink.tag</path>
|
||||
</tag-file>
|
||||
|
||||
<tag-file>
|
||||
<name>address</name>
|
||||
<path>/WEB-INF/tags/address.tag</path>
|
||||
</tag-file>
|
||||
|
||||
<tag-file>
|
||||
<name>emailDate</name>
|
||||
<path>/WEB-INF/tags/emailDate.tag</path>
|
||||
</tag-file>
|
||||
|
||||
<tag-file>
|
||||
<name>copyParams</name>
|
||||
<path>/WEB-INF/tags/copyParams.tag</path>
|
||||
</tag-file>
|
||||
|
||||
</taglib>
|
@@ -1,29 +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.jsp</welcome-file>
|
||||
</welcome-file-list>
|
||||
|
||||
<listener>
|
||||
<listener-class>i2p.bote.web.ServiceInitializer</listener-class>
|
||||
</listener>
|
||||
|
||||
<filter>
|
||||
<filter-name>CharsetFilter</filter-name>
|
||||
<filter-class>i2p.bote.web.CharsetFilter</filter-class>
|
||||
<init-param>
|
||||
<param-name>requestEncoding</param-name>
|
||||
<param-value>UTF-8</param-value>
|
||||
</init-param>
|
||||
</filter>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>CharsetFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
</web-app>
|
@@ -1,97 +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="getStatus.jsp"/>
|
||||
|
||||
<c:if test="${param.checkMail == 1}">
|
||||
<ib:checkForMail/>
|
||||
</c:if>
|
||||
|
||||
<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;url=buttonFrame.jsp" />
|
||||
</c:if>
|
||||
</head>
|
||||
|
||||
<body style="background-color: transparent; margin: 0px;">
|
||||
|
||||
<c:set var="disable" value=""/>
|
||||
<c:if test="${connStatus == DELAY}">
|
||||
<c:set var="disable" value="disabled="disabled""/>
|
||||
</c:if>
|
||||
|
||||
<table><tr>
|
||||
<td>
|
||||
<c:if test="${checkingForMail}">
|
||||
<div class="checkmail">
|
||||
<img src="images/wait.gif"/><ib:message key="Checking for mail..."/>
|
||||
</div>
|
||||
</c:if>
|
||||
<c:if test="${!checkingForMail}">
|
||||
<div class="checkmail">
|
||||
<c:choose>
|
||||
<c:when test="${empty ib:getIdentities().all}">
|
||||
<c:set var="url" value="noIdentities.jsp"/>
|
||||
<c:set var="frame" value="target="_parent""/>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<c:set var="link" value="buttonFrame.jsp"/>
|
||||
<c:set var="frame" value=""/>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
<form action="${url}" ${frame} method="GET">
|
||||
<input type="hidden" name="checkMail" value="1"/>
|
||||
<button type="submit" value="Check Mail/>" ${disable}><ib:message key="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 (parent.document.getElementById('inboxFlag'))
|
||||
parent.location.href = 'folder.jsp?path=Inbox';
|
||||
</script>
|
||||
</c:if>
|
||||
</c:if>
|
||||
</td>
|
||||
<td>
|
||||
<form action="newEmail.jsp" target="_top" method="GET">
|
||||
<button type="submit" value="New" ${disable}><ib:message key="New"/></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr></table>
|
||||
|
||||
</body>
|
||||
</html>
|
@@ -1,150 +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}">
|
||||
<ib:message key="New Email Identity" var="title" scope="request"/>
|
||||
<ib:message key="Create" var="commitAction"/>
|
||||
<c:set var="publicName" value="${param.publicName}"/>
|
||||
<c:set var="description" value="${param.description}"/>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<ib:message key="Edit Email Identity" var="title" scope="request"/>
|
||||
<ib:message key="Save" var="commitAction"/>
|
||||
<c:set var="key" value="${param.key}"/>
|
||||
<c:set var="identity" value="${ib:getIdentity(key)}"/>
|
||||
<c:set var="publicName" value="${ib:escapeQuotes(identity.publicName)}"/>
|
||||
<c:set var="description" value="${ib:escapeQuotes(identity.description)}"/>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
<jsp:include page="header.jsp"/>
|
||||
|
||||
<div class="errorMessage">
|
||||
${fn:escapeXml(param.errorMessage)}
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<form name="form" action="saveIdentity.jsp">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="font-weight: bold;"><ib:message key="Public Name:"/></div>
|
||||
<div style="font-size: 0.8em;"><ib:message key="(required field, shown to recipients)"/></div>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" size="40" name="publicName" value="${publicName}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="font-weight: bold;"><ib:message key="Description:"/></div>
|
||||
<div style="font-size: 0.8em;"><ib:message key="(optional, kept private)"/></div>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" size="40" name="description" value="${description}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="font-weight: bold;"><ib:message key="Email Address:"/></div>
|
||||
<div style="font-size: 0.8em;"><ib:message key="(optional)"/></div>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" size="40" name="emailAddress" value="${param.emailAddress}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="font-weight: bold;"><ib:message key="Encryption:"/></div>
|
||||
<c:if test="${param.new}">
|
||||
<div style="font-size: 0.8em;"><ib:message key="(If unsure, leave the default)"/></div>
|
||||
</c:if>
|
||||
</td>
|
||||
<c:if test="${param.new}">
|
||||
<td>
|
||||
<c:set var="selectedCryptoImplId" value="${param.cryptoImpl}"/>
|
||||
<c:if test="${empty param.cryptoImpl}">
|
||||
<c:set var="selectedCryptoImplId" value="2"/>
|
||||
</c:if>
|
||||
<select name="cryptoImpl">
|
||||
<jsp:useBean id="jspHelperBean" class="i2p.bote.web.JSPHelper"/>
|
||||
<c:forEach items="${jspHelperBean.cryptoImplementations}" var="cryptoImpl">
|
||||
<c:set var="selected" value=""/>
|
||||
<c:if test="${selectedCryptoImplId eq cryptoImpl.id}">
|
||||
<c:set var="selected" value=" selected"/>
|
||||
</c:if>
|
||||
<option value="${cryptoImpl.id}"${selected}>
|
||||
${cryptoImpl.name}
|
||||
</option>
|
||||
</c:forEach>
|
||||
</select>
|
||||
</td>
|
||||
</c:if>
|
||||
<c:if test="${not param.new}">
|
||||
<td>
|
||||
<c:set var="cryptoImpl" value="${ib:getCryptoImplementation(identity.cryptoImpl.id)}"/>
|
||||
${cryptoImpl.name}
|
||||
</td>
|
||||
</c:if>
|
||||
</tr>
|
||||
<c:if test="${!param.new}">
|
||||
<tr>
|
||||
<td style="font-weight: bold; vertical-align: top;">
|
||||
<ib:message key="Email Destination:"/>
|
||||
</td>
|
||||
<td>
|
||||
<textarea cols="64" rows="9" readonly="yes" wrap="soft" class="destinationtextarea">${param.key}</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-weight: bold; vertical-align: top;">
|
||||
<ib:message key="Default Identity:"/>
|
||||
</td>
|
||||
<td>
|
||||
<c:if test="${param.isDefault}">
|
||||
<c:set var="checked" value="checked"/>
|
||||
</c:if>
|
||||
<input type="checkbox" name="isDefault" ${checked}/>
|
||||
</td>
|
||||
</tr>
|
||||
</c:if>
|
||||
</table>
|
||||
<input type="hidden" name="new" value="${param.new}"/>
|
||||
<input type="hidden" name="key" value="${param.key}"/>
|
||||
<button name="action" value="${commitAction}">${commitAction}</button>
|
||||
<button name="action" value="cancel"/><ib:message key="Cancel"/></button>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript" language="JavaScript">
|
||||
document.forms['form'].elements['publicName'].focus();
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<jsp:include page="footer.jsp"/>
|
@@ -1,174 +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" %>
|
||||
|
||||
<%
|
||||
pageContext.setAttribute("FROM", i2p.bote.email.EmailAttribute.FROM, PageContext.PAGE_SCOPE);
|
||||
pageContext.setAttribute("TO", i2p.bote.email.EmailAttribute.TO, PageContext.PAGE_SCOPE);
|
||||
pageContext.setAttribute("SUBJECT", i2p.bote.email.EmailAttribute.SUBJECT, PageContext.PAGE_SCOPE);
|
||||
pageContext.setAttribute("DATE", i2p.bote.email.EmailAttribute.DATE, PageContext.PAGE_SCOPE);
|
||||
%>
|
||||
|
||||
<c:set var="title" value="${param.path}" scope="request"/>
|
||||
<jsp:include page="header.jsp"/>
|
||||
|
||||
<c:set var="folderName" value="${param.path}"/>
|
||||
<c:if test="${empty folderName}">
|
||||
<c:set var="folderName" value="Inbox"/>
|
||||
</c:if>
|
||||
<c:if test="${folderName == 'Inbox'}">
|
||||
<div id="inboxFlag"></div>
|
||||
</c:if>
|
||||
|
||||
<c:set var="sortcolumn" value="${DATE}"/>
|
||||
<c:if test="${!empty param.sortcolumn}">
|
||||
<c:set var="sortcolumn" value="${param.sortcolumn}"/>
|
||||
</c:if>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="${empty param.descending}">
|
||||
<%-- Use the default sort direction: descending for date, ascending for everything else --%>
|
||||
<c:if test="${sortcolumn eq DATE}">
|
||||
<c:set var="descending" value="true"/>
|
||||
</c:if>
|
||||
<c:if test="${sortcolumn ne DATE}">
|
||||
<c:set var="descending" value="false"/>
|
||||
</c:if>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<%-- Set the sort direction depending on param.descending --%>
|
||||
<c:set var="descending" value="false"/>
|
||||
<c:if test="${param.descending}">
|
||||
<c:set var="descending" value="true"/>
|
||||
</c:if>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
<c:if test="${!descending}">
|
||||
<c:set var="sortIndicator" value="▴"/>
|
||||
<c:set var="reverseSortOrder" value="&descending=true"/>
|
||||
</c:if>
|
||||
<c:if test="${descending}">
|
||||
<c:set var="sortIndicator" value="▾"/>
|
||||
<c:set var="reverseSortOrder" value="&descending=false"/>
|
||||
</c:if>
|
||||
|
||||
<div class="main">
|
||||
<div class="folder">
|
||||
<table class="table">
|
||||
<c:set var="folder" value="${ib:getMailFolder(folderName)}"/>
|
||||
<tr>
|
||||
<th style="width: 100px;">
|
||||
<c:set var="sortLink" value="folder.jsp?path=${param.path}&sortcolumn=${FROM}"/>
|
||||
<c:if test="${sortcolumn eq FROM}">
|
||||
<c:set var="sortLink" value="${sortLink}${reverseSortOrder}"/>
|
||||
<c:set var="fromColumnIndicator" value=" ${sortIndicator}"/>
|
||||
</c:if>
|
||||
<a href="${sortLink}"><ib:message key="From"/>${fromColumnIndicator}</a>
|
||||
</th>
|
||||
<th style="width: 30px; text-align: center;"><ib:message key="Unkn."/></th>
|
||||
<th style="width: 20px; text-align: center;"><ib:message key="Sig"/></th>
|
||||
<th style="width: 100px;">
|
||||
<c:set var="sortLink" value="folder.jsp?path=${param.path}&sortcolumn=${TO}"/>
|
||||
<c:if test="${sortcolumn eq TO}">
|
||||
<c:set var="sortLink" value="${sortLink}${reverseSortOrder}"/>
|
||||
<c:set var="toColumnIndicator" value=" ${sortIndicator}"/>
|
||||
</c:if>
|
||||
<a href="${sortLink}"><ib:message key="To"/>${toColumnIndicator}</a>
|
||||
</th>
|
||||
<th style="width: 150px;">
|
||||
<c:set var="sortLink" value="folder.jsp?path=${param.path}&sortcolumn=${SUBJECT}"/>
|
||||
<c:if test="${sortcolumn eq SUBJECT}">
|
||||
<c:set var="sortLink" value="${sortLink}${reverseSortOrder}"/>
|
||||
<c:set var="subjectColumnIndicator" value=" ${sortIndicator}"/>
|
||||
</c:if>
|
||||
<a href="${sortLink}"><ib:message key="Subject"/>${subjectColumnIndicator}</a>
|
||||
</th>
|
||||
<th style="width: 100px;">
|
||||
<c:set var="sortLink" value="folder.jsp?path=${param.path}&sortcolumn=${DATE}"/>
|
||||
<c:if test="${sortcolumn eq DATE}">
|
||||
<c:set var="sortLink" value="${sortLink}${reverseSortOrder}"/>
|
||||
<c:set var="dateColumnIndicator" value=" ${sortIndicator}"/>
|
||||
</c:if>
|
||||
<a href="${sortLink}"><ib:message key="Sent"/>${dateColumnIndicator}</a>
|
||||
</th>
|
||||
<th style="width: 20px;"></th>
|
||||
</tr>
|
||||
|
||||
<c:forEach items="${ib:getEmails(folder, sortcolumn, descending)}" var="email" varStatus="status">
|
||||
<c:set var="sender" value="${ib:getNameAndDestination(email.sender)}"/>
|
||||
<c:if test="${empty sender}">
|
||||
<ib:message key="Anonymous" var="sender"/>
|
||||
</c:if>
|
||||
|
||||
<c:if test="${email.signatureValid}">
|
||||
<c:set var="signature" value="<div style='color: green;'>✔</div>"/>
|
||||
</c:if>
|
||||
<c:if test="${!email.signatureValid}">
|
||||
<c:set var="signature" value="<div style='color: red;'>✘</div>"/>
|
||||
</c:if>
|
||||
|
||||
<c:set var="recipient" value="${ib:getNameAndDestination(ib:getOneLocalRecipient(email))}"/>
|
||||
|
||||
<c:set var="subject" value="${email.subject}"/>
|
||||
<c:if test="${empty subject}">
|
||||
<ib:message key="(No subject)" var="subject"/>
|
||||
</c:if>
|
||||
|
||||
<c:set var="mailUrl" value="showEmail.jsp?folder=${folderName}&messageID=${email.messageID}"/>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="${email.new}"><c:set var="fontWeight" value="bold"/></c:when>
|
||||
<c:otherwise><c:set var="fontWeight" value="normal"/></c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
<c:set var="class" value=""/>
|
||||
<c:if test="${status.index%2 != 0}">
|
||||
<c:set var="class" value=" class=\"alttablecell\""/>
|
||||
</c:if>
|
||||
|
||||
<tr>
|
||||
<td><div${class}><a href="${mailUrl}" style="font-weight: ${fontWeight}">${fn:escapeXml(sender)}</a></div></td>
|
||||
<td><div${class} style="text-align: center;">${!ib:isKnown(email.sender) ? '✗' : ' '}</div></td>
|
||||
<td><div${class} style="text-align: center;"><c:out value="${signature}" escapeXml="false"/></div></td>
|
||||
<td><div${class}><a href="${mailUrl}" style="font-weight: ${fontWeight}">${fn:escapeXml(recipient)}</a></div></td>
|
||||
<td><div${class}><a href="${mailUrl}" style="font-weight: ${fontWeight}">${fn:escapeXml(subject)}</a></div></td>
|
||||
<td><div${class}><a href="${mailUrl}" style="font-weight: ${fontWeight}"><ib:emailDate email="${email}" timeStyle="short"/></a></div></td>
|
||||
<td>
|
||||
<div${class}>
|
||||
<a href="deleteEmail.jsp?folder=${folderName}&messageID=${email.messageID}">
|
||||
<img src="images/delete.png" alt="<ib:message key='Delete'/>" title="<ib:message key='Delete this email'/>"/></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<jsp:include page="footer.jsp"/>
|
@@ -1,92 +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="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
|
||||
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
|
||||
|
||||
<fmt:requestEncoding value="UTF-8"/>
|
||||
|
||||
<jsp:useBean id="jspHelperBean" class="i2p.bote.web.JSPHelper"/>
|
||||
<c:set var="configuration" value="${jspHelperBean.configuration}"/>
|
||||
<fmt:setLocale value="${configuration.language}" scope="request"/>
|
||||
|
||||
<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" />
|
||||
<c:if test="${!empty title}">
|
||||
<title>${title} <ib:message key="- I2P-Bote"/></title>
|
||||
</c:if>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="titlebar" style="cursor:pointer" onclick="document.location='.'">
|
||||
<div class="title"><ib:message key="I2P-Bote"/></div>
|
||||
<br/>
|
||||
<div class="subtitle"><ib:message key="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">
|
||||
<iframe src="folders.jsp" width="100%" height="250px" scrolling="no" frameborder="0" allowtransparency="true"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="menubox">
|
||||
<h2><ib:message key="Addresses"/></h2>
|
||||
<a href="identities.jsp"><ib:message key="Identities"/></a><br/>
|
||||
<a href="addressBook.jsp"><ib:message key="Address Book"/></a><br/>
|
||||
Public Address Directory<br/>
|
||||
</div>
|
||||
|
||||
<div class="menubox">
|
||||
<h2><ib:message key="Configuration"/></h2>
|
||||
<a href="settings.jsp"><ib:message key="Settings"/></a><br/>
|
||||
</div>
|
||||
|
||||
<div class="menubox">
|
||||
<h2><a href="network.jsp"><ib:message key="Network Status"/></a></h2>
|
||||
<iframe src="statusFrame.jsp" width="100%" height="40px" scrolling="no" frameborder="0" allowtransparency="true"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="menubox">
|
||||
<h2><ib:message key="Help"/></h2>
|
||||
<ib:message key="User Guide"/><br/>
|
||||
<a href="about.jsp"><ib:message key="About"/></a><br/>
|
||||
</div>
|
||||
|
||||
<div class="infoMessage">
|
||||
${fn:escapeXml(param.infoMessage)}
|
||||
</div>
|
||||
|
||||
<div class="errorMessage">
|
||||
${fn:escapeXml(param.errorMessage)}
|
||||
</div>
|
@@ -1,365 +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 {
|
||||
font-size: 8pt;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.folderbox {
|
||||
font-size: 8pt;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.emailtext {
|
||||
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: #cce2ff;
|
||||
-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;
|
||||
}
|
||||
|
||||
button[disabled] {
|
||||
border: 1px outset #999;
|
||||
background: #ddf !important;
|
||||
color: #666;
|
||||
-moz-box-shadow: inset 0px 0px 0px 1px #999;
|
||||
}
|
||||
|
||||
.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] {
|
||||
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;
|
||||
}
|
||||
|
||||
.widetextfield {
|
||||
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: 9pt/130% "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
|
||||
min-height: 100px;
|
||||
border: 1px solid #001;
|
||||
}
|
||||
|
||||
.destinationtextarea {
|
||||
width: 100%;
|
||||
background: #eef;
|
||||
background-color: transparent;
|
||||
color: #003;
|
||||
border: none;
|
||||
font: 8pt "Lucida Console", "DejaVu Sans Mono", Courier, mono;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
.table {
|
||||
table-layout: fixed;
|
||||
width: 800px;
|
||||
}
|
||||
|
||||
.table td, th {
|
||||
padding-left: 10px;
|
||||
background: url('images/lightbluetile.png');
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.table td:first-child, th:first-child {
|
||||
padding-left: 0px;
|
||||
background: url('images/lightbluetile.png');
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.alttablecell
|
||||
{
|
||||
background: url('images/lightbluetile-alt.png');
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#address form span {
|
||||
display: none;
|
||||
}
|
||||
#address form:hover span {
|
||||
display: block;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
wbr:after { content: "\00200B" } /* For Opera */
|
@@ -1,104 +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:set var="title" value="Identities" scope="request"/>
|
||||
<jsp:include page="header.jsp"/>
|
||||
|
||||
<div class="main">
|
||||
<h2>
|
||||
<ib:message key="Email Identities"/>
|
||||
</h2>
|
||||
|
||||
<c:set var="identities" value="${ib:getIdentities().all}"/>
|
||||
<c:if test="${empty identities}">
|
||||
<ib:message key="No email identities are defined."/>
|
||||
</c:if>
|
||||
|
||||
<table class="table">
|
||||
<c:if test="${!empty identities}">
|
||||
<tr>
|
||||
<th style="width: 20px;"><ib:message key="Def."/></th>
|
||||
<th><ib:message key="Public Name"/></th>
|
||||
<th><ib:message key="Description"/></th>
|
||||
<th><ib:message key="Email Address"/></th>
|
||||
<th><ib:message key="Email Destination"/></th>
|
||||
<th style="width: 20px"></th>
|
||||
</tr>
|
||||
</c:if>
|
||||
<c:forEach items="${identities}" var="identity" varStatus="loopStatus">
|
||||
<c:set var="class" value=""/>
|
||||
<c:if test="${loopStatus.index%2 != 0}">
|
||||
<c:set var="class" value=" class=\"alttablecell\""/>
|
||||
</c:if>
|
||||
|
||||
<tr>
|
||||
<td style="width: 20px; text-align: right;">
|
||||
<div${class}>
|
||||
<c:if test="${identity.default}">
|
||||
<img src="images/asterisk.png"/>
|
||||
</c:if>
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 100px;">
|
||||
<div${class}>
|
||||
<jsp:useBean id="jspHelperBean" class="i2p.bote.web.JSPHelper"/>
|
||||
<%-- Insert a random number into the request string so others can't see contacts or identities using the CSS history hack --%>
|
||||
<a href="editIdentity.jsp?rnd=${jspHelperBean.randomNumber}&new=false&key=${identity.key}&cryptoImpl=${identity.cryptoImpl}&publicName=${ib:escapeQuotes(identity.publicName)}&description=${ib:escapeQuotes(identity.description)}&emailAddress=${ib:escapeQuotes(identity.emailAddress)}&isDefault=${identity.default}">
|
||||
${identity.publicName}
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 150px;">
|
||||
<div${class}>
|
||||
${identity.description}
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 150px;">
|
||||
<div${class}>
|
||||
${identity.emailAddress}
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 100px;">
|
||||
<div${class}>
|
||||
${identity.key}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="deleteIdentity.jsp?key=${identity.key}"><img src="images/delete.png" alt="<ib:message key='Delete'/>" title="<ib:message key='Delete this identity'/>"/></a>
|
||||
</td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</table>
|
||||
|
||||
<p/>
|
||||
<form action="editIdentity.jsp?new=true" method="POST">
|
||||
<button type="submit" value="New"><ib:message key="New Identity"/></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<jsp:include page="footer.jsp"/>
|
Before Width: | Height: | Size: 209 B |
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: 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,159 +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" %>
|
||||
|
||||
<%--
|
||||
Valid actions:
|
||||
<default> - show the "new email" form
|
||||
send - send an email using the request data
|
||||
addToAddrBook - add a recipient to the address book and return here
|
||||
lookup - add one or more address book entries as recipients and return here
|
||||
addRecipientField - add a recipient field
|
||||
removeRecipient<i> - remove the recipient field with index i
|
||||
|
||||
Other parameters:
|
||||
new - true for new contact, false for existing contact
|
||||
--%>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="${param.action eq 'send'}">
|
||||
<jsp:forward page="sendEmail.jsp"/>
|
||||
</c:when>
|
||||
<c:when test="${param.action eq 'addToAddrBook'}">
|
||||
<c:set var="destparam" value="${param.destparamname}"/>
|
||||
<jsp:forward page="editContact.jsp">
|
||||
<jsp:param name="new" value="true"/>
|
||||
<jsp:param name="destination" value="${ib:escapeQuotes(param[destparam])}"/>
|
||||
<jsp:param name="forwardUrl" value="newEmail.jsp"/>
|
||||
<jsp:param name="backUrl" value="newEmail.jsp"/>
|
||||
<jsp:param name="paramsToCopy" value="recipient*,to*,cc*,bcc*,replyto*,subject,message,forwardUrl,backUrl,paramsToCopy"/>
|
||||
</jsp:forward>
|
||||
</c:when>
|
||||
<c:when test="${param.action eq 'lookup'}">
|
||||
<jsp:forward page="addressBook.jsp">
|
||||
<jsp:param name="select" value="true"/>
|
||||
<jsp:param name="forwardUrl" value="newEmail.jsp"/>
|
||||
<jsp:param name="paramsToCopy" value="recipient*,to*,cc*,bcc*,replyto*,subject,message,forwardUrl"/>
|
||||
</jsp:forward>
|
||||
</c:when>
|
||||
</c:choose>
|
||||
|
||||
<ib:message key="New Email" var="title"scope="request"/>
|
||||
<jsp:include page="header.jsp"/>
|
||||
|
||||
<div class="main">
|
||||
<form action="newEmail.jsp" method="post">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<ib:message key="From:"/>
|
||||
</td>
|
||||
<td>
|
||||
<select name="sender">
|
||||
<option value="anonymous"><ib:message key="Anonymous"/></option>
|
||||
<c:forEach items="${ib:getIdentities().all}" var="identity">
|
||||
<c:set var="selected" value=""/>
|
||||
<c:if test="${fn:contains(param.sender, identity.key)}">
|
||||
<c:set var="selected" value=" selected"/>
|
||||
</c:if>
|
||||
<c:if test="${empty param.sender && identity.default}">
|
||||
<c:set var="selected" value=" selected"/>
|
||||
</c:if>
|
||||
<option value="${identity.publicName} <${identity.key}>"${selected}>
|
||||
${identity.publicName}
|
||||
<c:if test="${!empty identity.description}"> - ${identity.description}</c:if>
|
||||
</option>
|
||||
</c:forEach>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<%-- Add an address line for each recipient --%>
|
||||
<c:set var="recipients" value="${ib:mergeRecipientFields(pageContext.request)}"/>
|
||||
<c:forEach var="recipient" items="${recipients}" varStatus="status">
|
||||
<c:set var="recipientField" value="recipient${status.index}"/>
|
||||
<tr><td>
|
||||
<c:set var="recipientTypeField" value="recipientType${status.index}"/>
|
||||
<c:set var="recipientType" value="${param[recipientTypeField]}"/>
|
||||
<select name="recipientType${status.index}">
|
||||
<c:set var="toSelected" value="${recipientType eq 'to' ? ' selected' : ''}"/>
|
||||
<c:set var="ccSelected" value="${recipientType eq 'cc' ? ' selected' : ''}"/>
|
||||
<c:set var="bccSelected" value="${recipientType eq 'bcc' ? ' selected' : ''}"/>
|
||||
<c:set var="replytoSelected" value="${recipientType eq 'replyto' ? ' selected' : ''}"/>
|
||||
<option value="to"${toSelected}><ib:message key="To:"/></option>
|
||||
<option value="cc"${ccSelected}><ib:message key="CC:"/></option>
|
||||
<option value="bcc"${bccSelected}><ib:message key="BCC:"/></option>
|
||||
<option value="replyto"${replytoSelected}><ib:message key="Reply To:"/></option>
|
||||
</select>
|
||||
</td><td>
|
||||
<input type="text" size="80" name="${recipientField}" value="${ib:escapeQuotes(recipient.address)}"/>
|
||||
<c:choose>
|
||||
<c:when test="${status.last}">
|
||||
<input type="hidden" name="destparamname" value="${recipientField}"/>
|
||||
<button type="submit" name="action" value="addToAddrBook">➔<img src="images/addressbook.gif"/></button>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<button type="submit" name="action" value="removeRecipient${status.index}">-</button>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</td></tr>
|
||||
</c:forEach>
|
||||
|
||||
<tr>
|
||||
<td/>
|
||||
<td style="text-align: right;">
|
||||
<button type="submit" name="action" value="addRecipientField">+</button>
|
||||
<button type="submit" name="action" value="lookup"><ib:message key="Addr. Book..."/></button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top"><br/><ib:message key="Subject:"/></td>
|
||||
<td><input class="widetextfield" type="text" size="80" name="subject" value="${ib:escapeQuotes(param.subject)}"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top"><br/><ib:message key="Message:"/></td>
|
||||
<td>
|
||||
<textarea rows="30" cols="80" name="message"><c:if test="${!empty param.quoteMsgId}">
|
||||
<%-- The following lines are not indented because the indentation would show up as blank chars on the textarea --%>
|
||||
<c:set var="origEmail" value="${ib:getEmail(param.quoteMsgFolder, param.quoteMsgId)}"/>
|
||||
<ib:message key="{0} wrote:" hide="true">
|
||||
<ib:param value="${ib:getShortSenderName(origEmail.sender, 50)}"></ib:param>
|
||||
</ib:message><ib:quote text="${fn:escapeXml(origEmail.text)}"/></c:if><c:if test="${empty param.quoteMsgId}">${fn:escapeXml(param.message)}</c:if></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=3 align="center">
|
||||
<button type="submit" name="action" value="send"><ib:message key="Send"/></button>
|
||||
<button type="submit" name="action" disabled="disabled"><ib:message key="Save"/></button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<jsp:include page="footer.jsp"/>
|
@@ -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" %>
|
||||
|
||||
<c:set var="title" value="No Identity" scope="request"/>
|
||||
<jsp:include page="header.jsp"/>
|
||||
|
||||
<div class="main">
|
||||
<h2><ib:message key="No Email Identity Defined"/></h2>
|
||||
<p>
|
||||
<ib:message>
|
||||
In order to receive email from other people, you need to create an email identity
|
||||
first.
|
||||
</ib:message>
|
||||
</p><p>
|
||||
<ib:message>
|
||||
Every email identity is associated with an email destination. Anybody can send email
|
||||
to the email destination, but only the identity holder can read it.
|
||||
</ib:message>
|
||||
<br/>
|
||||
<ib:message>
|
||||
In a sense, email identities are the equivalent to traditional email accounts -
|
||||
except that there is no provider that can read all your email because I2P-Bote
|
||||
stores all emails encrypted on the network.
|
||||
</ib:message>
|
||||
</p><p>
|
||||
<ib:message>
|
||||
I2P-Bote automatically decrypts emails sent to you, using the email identity you
|
||||
created. Email identities are stored in a file named identities.txt; never give
|
||||
this file to anyone or they will be able to read all your emails.
|
||||
</ib:message>
|
||||
<br/>
|
||||
<ib:message>
|
||||
Do give out the email destination so others can contact you.
|
||||
</ib:message>
|
||||
</p><p>
|
||||
<ib:message>
|
||||
It is easy to create multiple identities for different purposes, or different
|
||||
contacts.
|
||||
</ib:message>
|
||||
</p><p>
|
||||
<ib:message>
|
||||
When you create a new email identity, you can choose the type of encryption that
|
||||
will be used by the email identity. While all encryption algorithms offered by
|
||||
I2P-Bote provide a high level of privacy, the length of an email destination
|
||||
depends on the encryption used. The higher the number of bits, the longer the
|
||||
email destination.
|
||||
</ib:message>
|
||||
<br/>
|
||||
<ib:message>
|
||||
Note that the same is not true for encryption strength. 256-bit elliptic curve
|
||||
encryption is equivalent in strength to 3072-bit ElGamal. 521-bit elliptic curve
|
||||
is equivalent to 15360-bit ElGamal.
|
||||
</ib:message>
|
||||
<form action="editIdentity.jsp?new=true" method="POST">
|
||||
<button type="submit" value="New"><ib:message key="Create a New Email Identity"/></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<jsp:include page="footer.jsp"/>
|
@@ -1,128 +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)}"/>
|
||||
|
||||
<c:if test="${fn:toLowerCase(param.folder) ne 'outbox'}">
|
||||
<ib:setEmailRead folder="${ib:getMailFolder(param.folder)}" messageId="${param.messageID}" read="true"/>
|
||||
</c:if>
|
||||
|
||||
<c:set var="title" value="${email.subject}" scope="request"/>
|
||||
<jsp:include page="header.jsp"/>
|
||||
|
||||
<div class="emailtext">
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top"><strong><ib:message key="From:"/></strong></td>
|
||||
<td>
|
||||
<c:if test="${email.anonymous}">
|
||||
<ib:message key="Anonymous"/>
|
||||
</c:if>
|
||||
<c:if test="${!email.anonymous}">
|
||||
<ib:address address="${email.sender}"/>
|
||||
</c:if>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top"><strong><ib:message key="Signature:"/></strong></td>
|
||||
<td>
|
||||
<c:if test="${email.signatureValid}"><ib:message key="Valid"/></c:if>
|
||||
<c:if test="${!email.signatureValid}"><div style="color: red;"><ib:message key="Invalid or missing"/></div></c:if>
|
||||
</td>
|
||||
</tr>
|
||||
<c:forEach var="replyToAddress" varStatus="status" items="${email.replyToAddresses}">
|
||||
<tr>
|
||||
<td valign="top"><strong><ib:message key="Reply To:"/></strong></td>
|
||||
<td><ib:address address="${replyToAddress}"/></td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
<c:forEach var="toAddress" varStatus="status" items="${email.toAddresses}">
|
||||
<tr>
|
||||
<td valign="top"><strong><ib:message key="To:"/></strong></td>
|
||||
<td><ib:address address="${toAddress}"/></td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
<c:forEach var="ccAddress" varStatus="status" items="${email.CCAddresses}">
|
||||
<tr>
|
||||
<td valign="top"><strong><ib:message key="CC:"/></strong></td>
|
||||
<td><ib:address address="${ccAddress}"/></td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
<c:forEach var="bccAddress" varStatus="status" items="${email.BCCAddresses}">
|
||||
<tr>
|
||||
<td valign="top"><strong><ib:message key="BCC:"/></strong></td>
|
||||
<td><ib:address address="${bccAddress}"/></td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
<tr>
|
||||
<td valign="top"><strong><ib:message key="Sent:"/></strong></td>
|
||||
<td><ib:emailDate email="${email}" timeStyle="full"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top"><strong><ib:message key="Subject:"/></strong></td>
|
||||
<td>${fn:escapeXml(email.subject)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top"><strong><ib:message key="Message:"/></strong></td>
|
||||
<td><ib:formatPlainText text="${fn:escapeXml(email.text)}"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<table><tr>
|
||||
<td>
|
||||
<form action="newEmail.jsp" method="post">
|
||||
<c:set var="replyDisabled" value="${email.anonymous ? 'disabled="disabled"' : ''}"/>
|
||||
<button type="submit"${replyDisabled}><ib:message key="Reply"/></button>
|
||||
<input type="hidden" name="sender" value="${ib:escapeQuotes(ib:getOneLocalRecipient(email))}"/>
|
||||
<input type="hidden" name="recipient0" value="${ib:escapeQuotes(email.sender)}"/>
|
||||
|
||||
<ib:message key="Re:" var="responsePrefix" hide="true"/>
|
||||
<c:set var="responsePrefix" value="${responsePrefix} "/>
|
||||
<c:if test="${fn:startsWith(email.subject, responsePrefix)}">
|
||||
<c:set var="responsePrefix" value=""/>
|
||||
</c:if>
|
||||
<input type="hidden" name="subject" value="${responsePrefix}${ib:escapeQuotes(email.subject)}"/>
|
||||
|
||||
<input type="hidden" name="quoteMsgFolder" value="${param.folder}"/>
|
||||
<input type="hidden" name="quoteMsgId" value="${param.messageID}"/>
|
||||
</form>
|
||||
</td><td>
|
||||
<form action="deleteEmail.jsp" method="post">
|
||||
<button type="submit"><ib:message key="Delete"/></button>
|
||||
<input type="hidden" name="folder" value="${param.folder}"/>
|
||||
<input type="hidden" name="messageID" value="${email.messageID}"/>
|
||||
</form>
|
||||
</td>
|
||||
</tr></table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<jsp:include page="footer.jsp"/>
|
@@ -1,59 +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="getStatus.jsp"/>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<link rel="stylesheet" href="i2pbote.css" />
|
||||
<meta http-equiv="refresh" content="20" />
|
||||
</head>
|
||||
|
||||
<body style="background-color: transparent; margin: 0px;">
|
||||
|
||||
<div class="statusbox">
|
||||
<c:choose>
|
||||
<c:when test="${connStatus == NOT_STARTED}"><img src="images/redsquare.png"/> <ib:message key="Not Started"/></c:when>
|
||||
<c:when test="${connStatus == DELAY}"><img src="images/yellowsquare.png"/> <ib:message key="Waiting 3 Minutes..."/><br/>
|
||||
<div style="text-align: center">
|
||||
<%-- When the connect button is clicked, refresh the entire page so the buttons in buttonFrame.jsp are enabled --%>
|
||||
<form action="connect.jsp" target="_top" method="GET">
|
||||
<button type="submit"><ib:message key="Connect Now"/></button>
|
||||
</form>
|
||||
</div>
|
||||
</c:when>
|
||||
<c:when test="${connStatus == CONNECTING}"><img src="images/yellowsquare.png"/> <ib:message key="Connecting..."/></c:when>
|
||||
<c:when test="${connStatus == CONNECTED}"><img src="images/greensquare.png"/> <ib:message key="Connected"/></c:when>
|
||||
<c:when test="${connStatus == ERROR}"><img src="images/redsquare.png"/> <ib:message key="Error"/></c:when>
|
||||
<c:otherwise> <ib:message key="Unknown Status"/></c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
813
build.xml
@@ -1,46 +1,200 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="help" name="I2P-Bote">
|
||||
<property name="i2pbase" value="../i2p"/>
|
||||
<property name="i2plib" value="${i2pbase}/core/java/build"/>
|
||||
<property name="jettylib" value="${i2pbase}/apps/jetty/jettylib"/>
|
||||
<property name="antlib" value="${jettylib}"/>
|
||||
<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="WebContent/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="${java.class.path}" />
|
||||
<pathelement location="${i2plib}/i2p.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="${jstllib}/jstl.jar" />
|
||||
<pathelement location="${jstllib}/standard.jar" />
|
||||
<pathelement location="${lib}/mailapi.jar" />
|
||||
<pathelement location="${lib}/bcprov-ecc-jdk16-145.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>
|
||||
|
||||
<pathconvert property="manifest.classpath" pathsep=" ">
|
||||
<path refid="cp" />
|
||||
<mapper>
|
||||
<flattenmapper />
|
||||
</mapper>
|
||||
</pathconvert>
|
||||
|
||||
|
||||
<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=" $I2P must point to an I2P installation." />
|
||||
<echo message=" all: war + plugin" />
|
||||
<echo message=" src: Zips up the source code" />
|
||||
<echo message=" junit: Runs all unit tests" />
|
||||
<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, war, plugin" />
|
||||
<target name="all" depends="clean, src, war, plugin" />
|
||||
|
||||
<target name="compile">
|
||||
<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"
|
||||
srcdir="./src/main/java"
|
||||
debug="true"
|
||||
deprecation="on"
|
||||
source="1.6" target="1.6"
|
||||
includeAntRuntime="false"
|
||||
destdir="./ant_build/classes"
|
||||
classpathref="cp"
|
||||
failonerror="true" />
|
||||
@@ -62,36 +216,44 @@
|
||||
<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.6" target="1.6"
|
||||
includeAntRuntime="false"
|
||||
destdir="ant_build/classes"
|
||||
srcdir="./ant_build/jspjava"
|
||||
includes="**/*.java"
|
||||
classpathref="jspcp"
|
||||
failonerror="true" />
|
||||
<copy file="WebContent/WEB-INF/web.xml" tofile="ant_build/web.xml" />
|
||||
<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,bundle">
|
||||
<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/built-in-peers.txt" />
|
||||
<fileset dir="WebContent" includes="index.html"/>
|
||||
<fileset dir="WebContent" includes="*.css"/>
|
||||
<fileset dir="WebContent/" includes="*.xml"/>
|
||||
<webinf dir="WebContent/WEB-INF/tlds" includes="*.tld"/>
|
||||
<lib file="${lib}/mailapi.jar"/>
|
||||
<lib file="${lib}/bcprov-ecc-jdk16-145.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:"/>
|
||||
@@ -100,9 +262,43 @@
|
||||
</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.
|
||||
- Only the initial install includes mailapi.jar.
|
||||
- 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.
|
||||
@@ -112,9 +308,9 @@
|
||||
<mkdir dir="plugin/plugin.tmp/console/webapps" />
|
||||
<copy file="plugin/webapps.config" todir="plugin/plugin.tmp/console/" />
|
||||
|
||||
<!-- run i2pbote-plugin.war through pack200 -->
|
||||
<!-- 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" failifexecutionfails="true">
|
||||
<exec executable="pack200" failonerror="true">
|
||||
<arg value="--no-gzip"/>
|
||||
<arg value="--effort=9"/>
|
||||
<arg value="--modification-time=latest"/>
|
||||
@@ -122,103 +318,331 @@
|
||||
<arg value="plugin/plugin.tmp/console/webapps/i2pbote.jar"/>
|
||||
</exec>
|
||||
<delete file="plugin/plugin.tmp/console/webapps/i2pbote.jar" />
|
||||
<!-- run bcprov-ecc-jdk16-145.jar through pack200 -->
|
||||
<exec executable="pack200" failifexecutionfails="true">
|
||||
<exec executable="pack200" failonerror="true">
|
||||
<arg value="--no-gzip"/>
|
||||
<arg value="--effort=9"/>
|
||||
<arg value="--modification-time=latest"/>
|
||||
<arg value="plugin/plugin.tmp/lib/bcprov-ecc-jdk16-145.jar.pack"/>
|
||||
<arg value="${lib}/bcprov-ecc-jdk16-145.jar"/>
|
||||
<arg value="plugin/plugin.tmp/lib/i2pbote.jar.pack"/>
|
||||
<arg value="${lib}/i2pbote.jar"/>
|
||||
</exec>
|
||||
|
||||
<delete file="plugin/plugin.tmp/lib/mailapi.jar" />
|
||||
<delete file="plugin/plugin.tmp/lib/mailapi.jar.pack" />
|
||||
<!-- 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" failifexecutionfails="true" output="plugin/plugin.tmp/plugin.config" append="true">
|
||||
<arg value="version=${version}-b${build.number}" />
|
||||
<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" failifexecutionfails="true" output="plugin/plugin.tmp/plugin.config" append="true">
|
||||
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.tmp/plugin.config" append="true">
|
||||
<arg value="update-only=true" />
|
||||
</exec>
|
||||
<exec executable="${makeplugin}" failifexecutionfails="true" >
|
||||
<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 -->
|
||||
<exec executable="pack200" failifexecutionfails="true">
|
||||
<arg value="--no-gzip"/>
|
||||
<arg value="--effort=9"/>
|
||||
<arg value="--modification-time=latest"/>
|
||||
<arg value="plugin/plugin.tmp/lib/mailapi.jar.pack"/>
|
||||
<arg value="${lib}/mailapi.jar"/>
|
||||
</exec>
|
||||
<copy file="plugin/plugin.config" todir="plugin/plugin.tmp" overwrite="true" />
|
||||
<exec executable="echo" osfamily="unix" failifexecutionfails="true" output="plugin/plugin.tmp/plugin.config" append="true">
|
||||
<arg value="version=${version}-b${build.number}" />
|
||||
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.tmp/plugin.config" append="true">
|
||||
<arg value="version=${version}" />
|
||||
</exec>
|
||||
<exec executable="${makeplugin}" >
|
||||
<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,precompilejsp,bundle">
|
||||
<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="**/*.class" />
|
||||
<classes dir="src" includes="i2p/bote/network/built-in-peers.txt" />
|
||||
<fileset dir="WebContent" includes="index.html"/>
|
||||
<fileset dir="WebContent" includes="*.css"/>
|
||||
<fileset dir="WebContent/" includes="*.xml"/>
|
||||
<webinf dir="WebContent/WEB-INF/tlds" includes="*.tld"/>
|
||||
<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"/>
|
||||
<zipfileset dir="src/main/webapp/html" prefix="html"/>
|
||||
<zipfileset dir="src/main/webapp/themes" prefix="themes"/>
|
||||
</war>
|
||||
</target>
|
||||
|
||||
<target name="bundle">
|
||||
|
||||
<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 fai
|
||||
l property to true -->
|
||||
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="./bundle-messages.sh" />
|
||||
<arg value="src/build/scripts/bundle-messages.sh" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="mac" failifexecutionfails="false" >
|
||||
<arg value="./bundle-messages.sh" />
|
||||
<arg value="src/build/scripts/bundle-messages.sh" />
|
||||
</exec>
|
||||
<exec executable="cmd" osfamily="windows" failifexecutionfails="false" >
|
||||
<arg value="/c" />
|
||||
<arg value="bundle-messages.bat" />
|
||||
<exec executable="sh" osfamily="windows" failifexecutionfails="false" >
|
||||
<arg value="src/build/scripts/bundle-messages.sh" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="poupdate" depends="compilejspstrings">
|
||||
<!-- 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" failifexecutionfails="true" >
|
||||
<arg value="./bundle-messages.sh" />
|
||||
<exec executable="sh" osfamily="unix" failonerror="true" >
|
||||
<arg value="src/build/scripts/bundle-messages.sh" />
|
||||
<arg value="-p" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="mac" failifexecutionfails="true" >
|
||||
<arg value="./bundle-messages.sh" />
|
||||
<exec executable="sh" osfamily="mac" failonerror="true" >
|
||||
<arg value="src/build/scripts/bundle-messages.sh" />
|
||||
<arg value="-p" />
|
||||
</exec>
|
||||
<exec executable="cmd" osfamily="windows" failifexecutionfails="true" >
|
||||
<arg value="/c" />
|
||||
<arg value="bundle-messages.bat" />
|
||||
<exec executable="sh" osfamily="windows" failifexecutionfails="true" >
|
||||
<arg value="src/build/scripts/bundle-messages.sh" />
|
||||
<arg value="-p" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="compilejspstrings" depends="compile">
|
||||
<!-- compiles the Java code in the ant directory -->
|
||||
<target name="compileantclasses" depends="compile">
|
||||
<javac
|
||||
srcdir="./ant"
|
||||
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"
|
||||
@@ -226,20 +650,35 @@ l property to true -->
|
||||
</target>
|
||||
|
||||
<target name="junit" depends="compile">
|
||||
<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.jar"/>
|
||||
<pathelement location="${lib}/${junit.filename}"/>
|
||||
<pathelement location="${lib}/${mockito.filename}"/>
|
||||
<pathelement path="${jmockcp}"/>
|
||||
<pathelement location="./ant_build/classes"/>
|
||||
<path refid="cp"/>
|
||||
</path>
|
||||
|
||||
<javac
|
||||
srcdir="./test"
|
||||
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="junitcp"/>
|
||||
@@ -258,12 +697,129 @@ l property to true -->
|
||||
<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="./ant"
|
||||
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"
|
||||
@@ -278,22 +834,17 @@ l property to true -->
|
||||
<echo message="I2P-Bote version is ${version}"/>
|
||||
</target>
|
||||
|
||||
<!-- depends on compile b/c it gets the app version from I2PBote.class -->
|
||||
<target name="src" depends="getversion">
|
||||
<property name="subdir" value="i2pbote-${version}-src"/>
|
||||
<zip destfile="src.zip">
|
||||
<zipfileset dir="src" prefix="${subdir}/src"/>
|
||||
<zipfileset dir="WebContent" prefix="${subdir}/WebContent" followsymlinks="false"/>
|
||||
<zipfileset dir="test" prefix="${subdir}/test"/>
|
||||
<zipfileset dir="ant" prefix="${subdir}/ant"/>
|
||||
<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="bundle-messages.sh" prefix="${subdir}"/>
|
||||
<zipfileset file="jsp2po.sh" prefix="${subdir}"/>
|
||||
<zipfileset file="src/build/scripts/bundle-messages.sh" prefix="${subdir}"/>
|
||||
<zipfileset file="history.txt" prefix="${subdir}"/>
|
||||
<zipfileset file="license.txt" prefix="${subdir}"/>
|
||||
</zip>
|
||||
@@ -304,13 +855,81 @@ l property to true -->
|
||||
</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
997
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
|
93
history.txt
@@ -1,6 +1,99 @@
|
||||
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
|
||||
|
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
@@ -2,5 +2,9 @@ LICENSES
|
||||
--------
|
||||
|
||||
This software is licensed under the GPL version 3 (see licenses/GPLv3.txt),
|
||||
except for bcprov-ecc-jdk16-145.jar which is licensed under the
|
||||
Bouncy Castle License (see licenses/BouncyCastle.txt).
|
||||
except for:
|
||||
|
||||
- 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).
|
||||
|
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
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>
|
@@ -1,12 +1,44 @@
|
||||
name=i2pbote
|
||||
signer=HungryHobo@mail.i2p
|
||||
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
|
||||
websiteURL=http://tjgidoycrw6s3guetge3kvrvynppqjmvqsosmtbmgqasa6vmsf6a.b32.i2p/
|
||||
updateURL=http://tjgidoycrw6s3guetge3kvrvynppqjmvqsosmtbmgqasa6vmsf6a.b32.i2p/i2pbote-update.xpi2p
|
||||
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
|
||||
author=HungryHobo@mail.i2p
|
||||
min-i2p-version=0.9.18
|
||||
author=str4d@mail.i2p
|
||||
|
@@ -1 +1 @@
|
||||
webapps.i2pbote.classpath=$I2P/lib/jstl.jar,$I2P/lib/standard.jar,$PLUGIN/lib/mailapi.jar,$PLUGIN/lib/bcprov-ecc-jdk16-145.jar
|
||||
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
|
||||
|
@@ -34,7 +34,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Extracts strings from <ib:message> tags and prints them
|
||||
* 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 {
|
||||
@@ -51,8 +51,8 @@ public class JspStrings {
|
||||
List<PoEntry> poEntries = processDirectory(new File(args[0]));
|
||||
removeDuplicates(poEntries);
|
||||
|
||||
for (PoEntry poEntry: poEntries)
|
||||
print(poEntry);
|
||||
for (PoEntry poEntry: poEntries)
|
||||
print(poEntry);
|
||||
}
|
||||
|
||||
static void removeDuplicates(List<PoEntry> poEntries) {
|
||||
@@ -114,64 +114,79 @@ public class JspStrings {
|
||||
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>();
|
||||
// Process all tags of the format <ib:message>foobar</ib:message>
|
||||
String[] noAttributeTags = fileContents.split("<ib:message>");
|
||||
List<String> msgKeys = (extractFromTagBody(noAttributeTags));
|
||||
entries.addAll(PoEntry.create(msgKeys, file));
|
||||
// Process all tags of the format <ib:message key="foobar".../>, as well as
|
||||
// <ib:message key="foo {0}"><ib:param value="bar"/></ib:message>
|
||||
String[] attributeTags = fileContents.split("<ib:message ");
|
||||
msgKeys = extractFromKeyAttribute(attributeTags);
|
||||
entries.addAll(PoEntry.create(msgKeys, file));
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/** Extracts the text between each pair of <ib:message> and </ib:message> tags */
|
||||
static List<String> extractFromTagBody(String[] strings) {
|
||||
List<String> keys = new ArrayList<String>();
|
||||
for (int i=1; i<strings.length; i++) {
|
||||
String element = strings[i];
|
||||
int endIdx = element.indexOf("</ib:message");
|
||||
if (endIdx < 0) {
|
||||
System.err.println("No \"</ib:message\" end tag in string: <" + element + ">");
|
||||
continue;
|
||||
}
|
||||
String str = element.substring(0, endIdx);
|
||||
// convert multiple whitespaces in a row to a single space, remove leading and trailing whitespace
|
||||
str = Util.removeExtraWhitespace(str);
|
||||
keys.add(str);
|
||||
}
|
||||
return keys;
|
||||
/**
|
||||
* 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 values of all "key" attributes */
|
||||
static List<String> extractFromKeyAttribute(String[] strings) {
|
||||
/**
|
||||
* 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();
|
||||
|
||||
List<String> keys = new ArrayList<String>();
|
||||
for (int i=1; i<strings.length; i++) {
|
||||
String element = strings[i];
|
||||
int startIdxSQ = element.indexOf(keyAttrSQ);
|
||||
int endIdxSQ = element.indexOf("'", startIdxSQ+keyAttrLen);
|
||||
int startIdxDQ = element.indexOf(keyAttrDQ);
|
||||
int endIdxDQ = element.indexOf("\"", startIdxDQ+keyAttrLen);
|
||||
|
||||
String message;
|
||||
if (startIdxSQ>=0 && (startIdxSQ<startIdxDQ || startIdxDQ<0) && endIdxSQ>=0)
|
||||
message = element.substring(startIdxSQ+keyAttrLen, endIdxSQ);
|
||||
else if (startIdxDQ>=0 && (startIdxDQ<startIdxSQ || startIdxSQ<0) && endIdxDQ>=0)
|
||||
message = element.substring(startIdxDQ+keyAttrLen, endIdxDQ);
|
||||
else {
|
||||
System.err.println("Expected a string containing key=\"...\" or key='...', got " + element);
|
||||
continue;
|
||||
}
|
||||
keys.add(message);
|
||||
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;
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
/** 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 {
|
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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Update messages_xx.po and messages_xx.class files,
|
||||
# from both java and jsp sources.
|
||||
@@ -21,15 +22,29 @@ 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"
|
||||
JSPPATHS="WebContent"
|
||||
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
|
||||
@@ -65,7 +80,7 @@ do
|
||||
fi
|
||||
|
||||
# extract strings from jsp files
|
||||
$JAVA -cp ant_build/classes i2p.bote.ant.JspStrings WebContent >> ${i}t
|
||||
$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'
|
||||
@@ -86,15 +101,19 @@ do
|
||||
touch $i
|
||||
fi
|
||||
|
||||
echo "Generating ${CLASS}_$LG ResourceBundle..."
|
||||
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
|
||||
# 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
|
@@ -1,453 +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.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.Translate;
|
||||
|
||||
public class Configuration {
|
||||
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 DHT_PEER_FILE_NAME = "dht_peers.txt";
|
||||
private static final String RELAY_PEER_FILE_NAME = "relay_peers.txt";
|
||||
private static final String IDENTITIES_FILE_NAME = "identities.txt";
|
||||
private static final String ADDRESS_BOOK_FILE_NAME = "addressBook.txt";
|
||||
private static final String MESSAGE_ID_CACHE_FILE = "msgidcache.txt";
|
||||
private static final String OUTBOX_DIR = "outbox"; // relative to I2P_BOTE_SUBDIR
|
||||
private static final String RELAY_PKT_SUBDIR = "relay_pkt"; // relative to I2P_BOTE_SUBDIR
|
||||
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
|
||||
private static final String SENT_FOLDER_DIR = "sent"; // relative to I2P_BOTE_SUBDIR
|
||||
private static final String TRASH_FOLDER_DIR = "trash"; // 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";
|
||||
private static final String PARAMETER_AUTO_MAIL_CHECK = "autoMailCheckEnabled";
|
||||
private static final String PARAMETER_MAIL_CHECK_INTERVAL = "mailCheckInterval";
|
||||
private static final String PARAMETER_LANGUAGE = "locale";
|
||||
private static final String PARAMETER_HIDE_LOCALE = "hideLocale";
|
||||
private static final String PARAMETER_INCLUDE_SENT_TIME = "includeSentTime";
|
||||
private static final String PARAMETER_MESSAGE_ID_CACHE_SIZE = "messageIdCacheSize";
|
||||
private static final String PARAMETER_RELAY_REDUNDANCY = "relayRedundancy";
|
||||
private static final String PARAMETER_RELAY_MIN_DELAY = "relayMinDelay";
|
||||
private static final String PARAMETER_RELAY_MAX_DELAY = "relayMaxDelay";
|
||||
private static final String PARAMETER_NUM_STORE_HOPS = "numSendHops";
|
||||
private static final String PARAMETER_GATEWAY_DESTINATION = "gatewayDestination";
|
||||
private static final String PARAMETER_GATEWAY_ENABLED = "gatewayEnabled";
|
||||
|
||||
// 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 static final boolean DEFAULT_AUTO_MAIL_CHECK = true;
|
||||
private static final int DEFAULT_MAIL_CHECK_INTERVAL = 30; // in minutes
|
||||
private static final String DEFAULT_LANGUAGE = Locale.getDefault().getLanguage();
|
||||
private static final boolean DEFAULT_HIDE_LOCALE = true;
|
||||
private static final boolean DEFAULT_INCLUDE_SENT_TIME = true;
|
||||
private static final int DEFAULT_MESSAGE_ID_CACHE_SIZE = 1000; // the maximum number of message IDs to cache
|
||||
private static final int DEFAULT_RELAY_REDUNDANCY = 5; // lower than the DHT redundancy because only the highest-uptime peers are used for relaying
|
||||
private static final int DEFAULT_RELAY_MIN_DELAY = 120; // in minutes
|
||||
private static final int DEFAULT_RELAY_MAX_DELAY = 600; // in minutes
|
||||
private static final int DEFAULT_NUM_STORE_HOPS = 0;
|
||||
private static final String DEFAULT_GATEWAY_DESTINATION = "";
|
||||
private static final boolean DEFAULT_GATEWAY_ENABLED = true;
|
||||
|
||||
private Log log = new Log(Configuration.class);
|
||||
private Properties properties;
|
||||
private File i2pBoteDir;
|
||||
private File configFile;
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
properties = new Properties();
|
||||
|
||||
// get the I2PBote directory and make sure it exists
|
||||
i2pBoteDir = getI2PBoteDirectory();
|
||||
if (!i2pBoteDir.exists() && !i2pBoteDir.mkdirs())
|
||||
log.error("Cannot create directory: <" + i2pBoteDir.getAbsolutePath() + ">");
|
||||
|
||||
// read the configuration file
|
||||
configFile = new File(i2pBoteDir, CONFIG_FILE_NAME);
|
||||
boolean configurationLoaded = false;
|
||||
if (configFile.exists()) {
|
||||
log.info("Loading config file <" + configFile.getAbsolutePath() + ">");
|
||||
|
||||
try {
|
||||
DataHelper.loadProps(properties, 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.");
|
||||
|
||||
// Apply the language setting if there is one in the config file
|
||||
setLanguage(getLanguage());
|
||||
}
|
||||
|
||||
public File getDestinationKeyFile() {
|
||||
return new File(i2pBoteDir, DEST_KEY_FILE_NAME);
|
||||
}
|
||||
|
||||
public File getDhtPeerFile() {
|
||||
return new File(i2pBoteDir, DHT_PEER_FILE_NAME);
|
||||
}
|
||||
|
||||
public File getRelayPeerFile() {
|
||||
return new File(i2pBoteDir, RELAY_PEER_FILE_NAME);
|
||||
}
|
||||
|
||||
public File getIdentitiesFile() {
|
||||
return new File(i2pBoteDir, IDENTITIES_FILE_NAME);
|
||||
}
|
||||
|
||||
public File getAddressBookFile() {
|
||||
return new File(i2pBoteDir, ADDRESS_BOOK_FILE_NAME);
|
||||
}
|
||||
|
||||
public File getMessageIdCacheFile() {
|
||||
return new File(i2pBoteDir, MESSAGE_ID_CACHE_FILE);
|
||||
}
|
||||
|
||||
public File getOutboxDir() {
|
||||
return new File(i2pBoteDir, OUTBOX_DIR);
|
||||
}
|
||||
|
||||
public File getRelayPacketDir() {
|
||||
return new File(i2pBoteDir, RELAY_PKT_SUBDIR);
|
||||
}
|
||||
|
||||
public File getSentFolderDir() {
|
||||
return new File(i2pBoteDir, SENT_FOLDER_DIR);
|
||||
}
|
||||
|
||||
public File getTrashFolderDir() {
|
||||
return new File(i2pBoteDir, TRASH_FOLDER_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
|
||||
*/
|
||||
public void save() {
|
||||
log.debug("Saving config file <" + configFile.getAbsolutePath() + ">");
|
||||
try {
|
||||
DataHelper.storeProps(properties, configFile);
|
||||
} catch (IOException e) {
|
||||
log.error("Cannot save configuration to file <" + configFile.getAbsolutePath() + ">", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
public void setAutoMailCheckEnabled(boolean enabled) {
|
||||
properties.setProperty(PARAMETER_AUTO_MAIL_CHECK, new Boolean(enabled).toString());
|
||||
}
|
||||
|
||||
public boolean isAutoMailCheckEnabled() {
|
||||
return getBooleanParameter(PARAMETER_AUTO_MAIL_CHECK, DEFAULT_AUTO_MAIL_CHECK);
|
||||
}
|
||||
|
||||
public void setMailCheckInterval(int minutes) {
|
||||
properties.setProperty(PARAMETER_MAIL_CHECK_INTERVAL, Integer.valueOf(minutes).toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of minutes the application should wait before automatically
|
||||
* checking for mail.
|
||||
* @return
|
||||
*/
|
||||
public int getMailCheckInterval() {
|
||||
return getIntParameter(PARAMETER_MAIL_CHECK_INTERVAL, DEFAULT_MAIL_CHECK_INTERVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the UI language.
|
||||
* @param languageCode A two-letter language code such as "en" or "de", or null for the system default.
|
||||
*/
|
||||
public void setLanguage(String languageCode) {
|
||||
if (languageCode == null) {
|
||||
properties.remove(PARAMETER_LANGUAGE);
|
||||
System.clearProperty(Translate.PROP_LANG);
|
||||
}
|
||||
else {
|
||||
properties.setProperty(PARAMETER_LANGUAGE, languageCode);
|
||||
|
||||
// Set the language used by i2p.bote.Util._().
|
||||
// This may interfere with other I2P apps.
|
||||
// Unfortunately, there is no setProperty() in I2PAppContext.
|
||||
System.setProperty(Translate.PROP_LANG, languageCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the two-letter language code for the current locale.
|
||||
* @return
|
||||
*/
|
||||
public String getLanguage() {
|
||||
return properties.getProperty(PARAMETER_LANGUAGE, DEFAULT_LANGUAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current locale.
|
||||
* @return
|
||||
*/
|
||||
public Locale getLocale() {
|
||||
return new Locale(getLanguage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all locales for which a translation exists.
|
||||
* @return
|
||||
*/
|
||||
public Locale[] getAllLocales() {
|
||||
return I2PBote.getInstance().getAllLocales();
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls whether strings that are added to outgoing email, like "Re:" or "Fwd:",
|
||||
* are translated or not.
|
||||
* If <code>hideLocale</code> is <code>false</code>, the UI language is used.
|
||||
* If <code>hideLocale</code> is <code>true</code>, the strings are left untranslated
|
||||
* (which means they are in English).
|
||||
* @param hideLocale
|
||||
*/
|
||||
public void setHideLocale(boolean hideLocale) {
|
||||
properties.setProperty(PARAMETER_HIDE_LOCALE, Boolean.valueOf(hideLocale).toString());
|
||||
}
|
||||
|
||||
public boolean getHideLocale() {
|
||||
return getBooleanParameter(PARAMETER_HIDE_LOCALE, DEFAULT_HIDE_LOCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls whether the send time is included in outgoing emails.
|
||||
* @param includeSentTime
|
||||
*/
|
||||
public void setIncludeSentTime(boolean includeSentTime) {
|
||||
properties.setProperty(PARAMETER_INCLUDE_SENT_TIME, Boolean.valueOf(includeSentTime).toString());
|
||||
}
|
||||
|
||||
public boolean getIncludeSentTime() {
|
||||
return getBooleanParameter(PARAMETER_INCLUDE_SENT_TIME, DEFAULT_INCLUDE_SENT_TIME);
|
||||
}
|
||||
|
||||
public int getMessageIdCacheSize() {
|
||||
return getIntParameter(PARAMETER_MESSAGE_ID_CACHE_SIZE, DEFAULT_MESSAGE_ID_CACHE_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of relay chains that should be used per Relay Request.
|
||||
* @return
|
||||
*/
|
||||
public int getRelayRedundancy() {
|
||||
return getIntParameter(PARAMETER_RELAY_REDUNDANCY, DEFAULT_RELAY_REDUNDANCY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum amount of time that a Relay Request is delayed.
|
||||
* @param minDelay The minimum delay in minutes
|
||||
*/
|
||||
public void setRelayMinDelay(int minDelay) {
|
||||
properties.setProperty(PARAMETER_RELAY_MIN_DELAY, Integer.valueOf(minDelay).toString());
|
||||
}
|
||||
|
||||
public int getRelayMinDelay() {
|
||||
return getIntParameter(PARAMETER_RELAY_MIN_DELAY, DEFAULT_RELAY_MIN_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum amount of time that a Relay Request is delayed.
|
||||
* @param minDelay The minimum delay in minutes
|
||||
*/
|
||||
public void setRelayMaxDelay(int maxDelay) {
|
||||
properties.setProperty(PARAMETER_RELAY_MAX_DELAY, Integer.valueOf(maxDelay).toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum amount of time in minutes that a Relay Request is delayed.
|
||||
* @return
|
||||
*/
|
||||
public int getRelayMaxDelay() {
|
||||
return getIntParameter(PARAMETER_RELAY_MAX_DELAY, DEFAULT_RELAY_MAX_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of relays that should be used when sending a DHT store request.
|
||||
* @return
|
||||
*/
|
||||
public void setNumStoreHops(int numHops) {
|
||||
properties.setProperty(PARAMETER_NUM_STORE_HOPS, Integer.valueOf(numHops).toString());
|
||||
}
|
||||
|
||||
public int getNumStoreHops() {
|
||||
return getIntParameter(PARAMETER_NUM_STORE_HOPS, DEFAULT_NUM_STORE_HOPS);
|
||||
}
|
||||
|
||||
public void setGatewayDestination(String destination) {
|
||||
properties.setProperty(PARAMETER_GATEWAY_DESTINATION, destination);
|
||||
}
|
||||
|
||||
public String getGatewayDestination() {
|
||||
return properties.getProperty(PARAMETER_GATEWAY_DESTINATION, DEFAULT_GATEWAY_DESTINATION);
|
||||
}
|
||||
|
||||
public void setGatewayEnabled(boolean enable) {
|
||||
properties.setProperty(PARAMETER_GATEWAY_ENABLED, Boolean.valueOf(enable).toString());
|
||||
}
|
||||
|
||||
public boolean isGatewayEnabled() {
|
||||
return getBooleanParameter(PARAMETER_GATEWAY_ENABLED, DEFAULT_GATEWAY_ENABLED);
|
||||
}
|
||||
|
||||
private boolean getBooleanParameter(String parameterName, boolean defaultValue) {
|
||||
String stringValue = properties.getProperty(parameterName);
|
||||
if ("true".equalsIgnoreCase(stringValue) || "yes".equalsIgnoreCase(stringValue) || "on".equalsIgnoreCase(stringValue) || "1".equals(stringValue))
|
||||
return true;
|
||||
else if ("false".equalsIgnoreCase(stringValue) || "no".equalsIgnoreCase(stringValue) || "off".equalsIgnoreCase(stringValue) || "0".equals(stringValue))
|
||||
return false;
|
||||
else if (stringValue == null)
|
||||
return defaultValue;
|
||||
else {
|
||||
log.warn("<" + stringValue + "> is not a legal value for the boolean parameter <" + parameterName + ">");
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
private int getIntParameter(String parameterName, int defaultValue) {
|
||||
String stringValue = properties.getProperty(parameterName);
|
||||
if (stringValue == null)
|
||||
return defaultValue;
|
||||
else
|
||||
try {
|
||||
return new Integer(stringValue);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
log.warn("Can't convert value <" + stringValue + "> for parameter <" + parameterName + "> to int, using default.");
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,585 +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.addressbook.AddressBook;
|
||||
import i2p.bote.email.Email;
|
||||
import i2p.bote.email.EmailIdentity;
|
||||
import i2p.bote.email.Identities;
|
||||
import i2p.bote.folder.EmailFolder;
|
||||
import i2p.bote.folder.EmailPacketFolder;
|
||||
import i2p.bote.folder.IncompleteEmailFolder;
|
||||
import i2p.bote.folder.IndexPacketFolder;
|
||||
import i2p.bote.folder.MessageIdCache;
|
||||
import i2p.bote.folder.Outbox;
|
||||
import i2p.bote.folder.RelayPacketFolder;
|
||||
import i2p.bote.folder.TrashFolder;
|
||||
import i2p.bote.locale.Locales;
|
||||
import i2p.bote.network.BanList;
|
||||
import i2p.bote.network.BannedPeer;
|
||||
import i2p.bote.network.CheckEmailTask;
|
||||
import i2p.bote.network.DHT;
|
||||
import i2p.bote.network.DhtPeerStats;
|
||||
import i2p.bote.network.I2PPacketDispatcher;
|
||||
import i2p.bote.network.I2PSendQueue;
|
||||
import i2p.bote.network.NetworkStatus;
|
||||
import i2p.bote.network.RelayPacketHandler;
|
||||
import i2p.bote.network.RelayPeer;
|
||||
import i2p.bote.network.kademlia.KademliaDHT;
|
||||
import i2p.bote.packet.EncryptedEmailPacket;
|
||||
import i2p.bote.packet.IndexPacket;
|
||||
import i2p.bote.service.AutoMailCheckTask;
|
||||
import i2p.bote.service.I2PBoteThread;
|
||||
import i2p.bote.service.OutboxListener;
|
||||
import i2p.bote.service.OutboxProcessor;
|
||||
import i2p.bote.service.POP3Service;
|
||||
import i2p.bote.service.RelayPacketSender;
|
||||
import i2p.bote.service.RelayPeerManager;
|
||||
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.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
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. It is implemented as a singleton.
|
||||
*/
|
||||
public class I2PBote {
|
||||
public static final int PROTOCOL_VERSION = 3;
|
||||
private static final String APP_VERSION = "0.2.4";
|
||||
private static final int STARTUP_DELAY = 3; // the number of minutes to wait before connecting to I2P (this gives the router time to get ready)
|
||||
private static volatile I2PBote instance;
|
||||
|
||||
private Log log = new Log(I2PBote.class);
|
||||
private I2PClient i2pClient;
|
||||
private I2PSession i2pSession;
|
||||
private Configuration configuration;
|
||||
private Identities identities;
|
||||
private AddressBook addressBook;
|
||||
private I2PSendQueue sendQueue;
|
||||
private Outbox outbox; // stores outgoing emails for all local users
|
||||
private EmailFolder inbox; // stores incoming emails for all local users
|
||||
private EmailFolder sentFolder;
|
||||
private TrashFolder trashFolder;
|
||||
private RelayPacketFolder relayPacketFolder; // stores email packets we're forwarding for other machines
|
||||
private IncompleteEmailFolder incompleteEmailFolder; // stores email packets addressed to a local user
|
||||
private EmailPacketFolder emailDhtStorageFolder; // stores email 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 AutoMailCheckTask autoMailCheckTask;
|
||||
private ExpirationThread expirationThread;
|
||||
private RelayPacketSender relayPacketSender; // reads packets stored in the relayPacketFolder and sends them
|
||||
private DHT dht;
|
||||
private RelayPeerManager peerManager;
|
||||
private ThreadFactory mailCheckThreadFactory;
|
||||
private ExecutorService mailCheckExecutor;
|
||||
private Collection<Future<Boolean>> pendingMailCheckTasks;
|
||||
private long lastMailCheckTime;
|
||||
private ConnectTask connectTask;
|
||||
|
||||
private I2PBote() {
|
||||
Thread.currentThread().setName("I2PBoteMain");
|
||||
|
||||
I2PAppContext appContext = new I2PAppContext();
|
||||
appContext.addShutdownTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
shutDown();
|
||||
}
|
||||
});
|
||||
i2pClient = I2PClientFactory.createClient();
|
||||
configuration = new Configuration();
|
||||
|
||||
mailCheckThreadFactory = Util.createThreadFactory("ChkMailTask", CheckEmailTask.THREAD_STACK_SIZE);
|
||||
|
||||
identities = new Identities(configuration.getIdentitiesFile());
|
||||
addressBook = new AddressBook(configuration.getAddressBookFile());
|
||||
initializeFolderAccess();
|
||||
|
||||
// The rest of the initialization happens in ConnectTask because it needs an I2PSession.
|
||||
// It is done in the background so we don't block the webapp thread.
|
||||
connectTask = new ConnectTask();
|
||||
connectTask.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes objects for accessing emails and packet files on the filesystem.
|
||||
*/
|
||||
private void initializeFolderAccess() {
|
||||
inbox = new EmailFolder(configuration.getInboxDir());
|
||||
outbox = new Outbox(configuration.getOutboxDir());
|
||||
sentFolder = new EmailFolder(configuration.getSentFolderDir());
|
||||
trashFolder = new TrashFolder(configuration.getTrashFolderDir());
|
||||
relayPacketFolder = new RelayPacketFolder(configuration.getRelayPacketDir());
|
||||
MessageIdCache messageIdCache = new MessageIdCache(configuration.getMessageIdCacheFile(), configuration.getMessageIdCacheSize());
|
||||
incompleteEmailFolder = new IncompleteEmailFolder(configuration.getIncompleteDir(), messageIdCache, inbox);
|
||||
emailDhtStorageFolder = new EmailPacketFolder(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("inbound.nickname", "I2P-Bote");
|
||||
sessionProperties.setProperty("outbound.nickname", "I2P-Bote");
|
||||
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();
|
||||
FileReader fileReader = null;
|
||||
try {
|
||||
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);
|
||||
}
|
||||
finally {
|
||||
if (fileReader != null)
|
||||
try {
|
||||
fileReader.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.debug("Error closing file: <" + destinationKeyFile.getAbsolutePath() + ">" + 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.info("Local destination key = " + localDestination.toBase64());
|
||||
log.info("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();
|
||||
sendQueue = new I2PSendQueue(i2pSession, dispatcher);
|
||||
relayPacketSender = new RelayPacketSender(sendQueue, relayPacketFolder);
|
||||
|
||||
dht = new KademliaDHT(sendQueue, dispatcher, configuration.getDhtPeerFile());
|
||||
|
||||
dht.setStorageHandler(EncryptedEmailPacket.class, emailDhtStorageFolder);
|
||||
dht.setStorageHandler(IndexPacket.class, indexPacketDhtStorageFolder);
|
||||
//TODO dht.setStorageHandler(AddressPacket.class, );
|
||||
|
||||
peerManager = new RelayPeerManager(sendQueue, getLocalDestination(), configuration.getRelayPeerFile());
|
||||
|
||||
dispatcher.addPacketListener(emailDhtStorageFolder);
|
||||
dispatcher.addPacketListener(indexPacketDhtStorageFolder);
|
||||
dispatcher.addPacketListener(new RelayPacketHandler(relayPacketFolder, dht, i2pSession));
|
||||
dispatcher.addPacketListener(peerManager);
|
||||
|
||||
expirationThread = new ExpirationThread();
|
||||
expirationThread.addExpirationListener(emailDhtStorageFolder);
|
||||
expirationThread.addExpirationListener(indexPacketDhtStorageFolder);
|
||||
expirationThread.addExpirationListener(relayPacketSender);
|
||||
|
||||
outboxProcessor = new OutboxProcessor(dht, outbox, peerManager, relayPacketFolder, configuration);
|
||||
outboxProcessor.addOutboxListener(new OutboxListener() {
|
||||
/** Moves sent emails to the "sent" folder */
|
||||
@Override
|
||||
public void emailSent(Email email) {
|
||||
try {
|
||||
outbox.setNew(email, false); // this prevents OutboxProcessor from sending the email again if it can't be moved for some reason
|
||||
log.debug("Moving email [" + email + "] to the \"sent\" folder.");
|
||||
outbox.move(email, sentFolder);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Cannot move email from outbox to sent folder: " + email, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
autoMailCheckTask = new AutoMailCheckTask(configuration.getMailCheckInterval());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
if (!keyFile.renameTo(oldKeyFile))
|
||||
log.error("Cannot rename destination key file <" + keyFile.getAbsolutePath() + "> to <" + oldKeyFile.getAbsolutePath() + ">");
|
||||
}
|
||||
else
|
||||
if (!keyFile.createNewFile())
|
||||
log.error("Cannot create destination key file: <" + keyFile.getAbsolutePath() + ">");
|
||||
|
||||
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 Configuration getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public static String getAppVersion() {
|
||||
return APP_VERSION;
|
||||
}
|
||||
|
||||
public Locale[] getAllLocales() {
|
||||
return Locales.ALL_LOCALES;
|
||||
}
|
||||
|
||||
public Identities getIdentities() {
|
||||
return identities;
|
||||
}
|
||||
|
||||
public AddressBook getAddressBook() {
|
||||
return addressBook;
|
||||
}
|
||||
|
||||
public Destination getLocalDestination() {
|
||||
return i2pSession.getMyDestination();
|
||||
}
|
||||
|
||||
public void sendEmail(Email email) throws Exception {
|
||||
email.checkAddresses();
|
||||
outbox.add(email);
|
||||
outboxProcessor.checkForEmail();
|
||||
}
|
||||
|
||||
public synchronized void checkForMail() {
|
||||
if (!isCheckingForMail()) {
|
||||
if (identities.size() <= 0)
|
||||
log.info("Not checking for mail because no identities are defined.");
|
||||
else
|
||||
log.info("Checking mail for " + identities.size() + " Email Identities...");
|
||||
|
||||
lastMailCheckTime = System.currentTimeMillis();
|
||||
pendingMailCheckTasks = Collections.synchronizedCollection(new ArrayList<Future<Boolean>>());
|
||||
mailCheckExecutor = Executors.newFixedThreadPool(configuration.getMaxConcurIdCheckMail(), mailCheckThreadFactory);
|
||||
for (EmailIdentity identity: identities) {
|
||||
Callable<Boolean> checkMailTask = new CheckEmailTask(identity, dht, peerManager, sendQueue, incompleteEmailFolder, emailDhtStorageFolder, indexPacketDhtStorageFolder);
|
||||
Future<Boolean> task = mailCheckExecutor.submit(checkMailTask);
|
||||
pendingMailCheckTasks.add(task);
|
||||
}
|
||||
mailCheckExecutor.shutdown(); // finish all tasks, then shut down
|
||||
}
|
||||
else
|
||||
log.info("Not checking for mail because the last mail check hasn't finished.");
|
||||
}
|
||||
|
||||
public synchronized long getLastMailCheckTime() {
|
||||
return lastMailCheckTime;
|
||||
}
|
||||
|
||||
public synchronized boolean isCheckingForMail() {
|
||||
if (mailCheckExecutor == null)
|
||||
return false;
|
||||
|
||||
return !mailCheckExecutor.isTerminated();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (pendingMailCheckTasks == null)
|
||||
return false;
|
||||
if (isCheckingForMail())
|
||||
return false;
|
||||
|
||||
try {
|
||||
for (Future<Boolean> result: pendingMailCheckTasks)
|
||||
if (result.get(1, TimeUnit.MILLISECONDS)) {
|
||||
pendingMailCheckTasks = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Error while checking whether new mail has arrived.", e);
|
||||
}
|
||||
|
||||
pendingMailCheckTasks = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public EmailFolder getInbox() {
|
||||
return inbox;
|
||||
}
|
||||
|
||||
public Outbox getOutbox() {
|
||||
return outbox;
|
||||
}
|
||||
|
||||
public EmailFolder getSentFolder() {
|
||||
return sentFolder;
|
||||
}
|
||||
|
||||
public EmailFolder getTrashFolder() {
|
||||
return trashFolder;
|
||||
}
|
||||
|
||||
public boolean moveToTrash(EmailFolder sourceFolder, String messageId) {
|
||||
return sourceFolder.move(messageId, trashFolder);
|
||||
}
|
||||
|
||||
public int getNumDhtPeers() {
|
||||
if (dht == null)
|
||||
return 0;
|
||||
else
|
||||
return dht.getNumPeers();
|
||||
}
|
||||
|
||||
public DhtPeerStats getDhtStats() {
|
||||
if (dht == null)
|
||||
return null;
|
||||
else
|
||||
return dht.getPeerStats();
|
||||
}
|
||||
|
||||
public Set<RelayPeer> getRelayPeers() {
|
||||
return peerManager.getAllPeers();
|
||||
}
|
||||
|
||||
public Collection<BannedPeer> getBannedPeers() {
|
||||
return BanList.getInstance().getAll();
|
||||
}
|
||||
|
||||
private void startAllServices() {
|
||||
peerManager.start();
|
||||
outboxProcessor.start();
|
||||
dht.start();
|
||||
relayPacketSender.start();
|
||||
// smtpService.start();
|
||||
// pop3Service.start();
|
||||
sendQueue.start();
|
||||
autoMailCheckTask.start();
|
||||
expirationThread.start();
|
||||
}
|
||||
|
||||
private void stopAllServices() {
|
||||
if (connectTask != null)
|
||||
connectTask.requestShutdown();
|
||||
if (dht != null) dht.requestShutdown();
|
||||
if (outboxProcessor != null) outboxProcessor.requestShutdown();
|
||||
if (relayPacketSender != null) relayPacketSender.requestShutdown();
|
||||
if (smtpService != null) smtpService.requestShutdown();
|
||||
if (pop3Service != null) pop3Service.requestShutdown();
|
||||
if (sendQueue != null) sendQueue.requestShutdown();
|
||||
if (mailCheckExecutor != null) mailCheckExecutor.shutdown();
|
||||
if (pendingMailCheckTasks != null)
|
||||
for (Future<Boolean> mailCheckTask: pendingMailCheckTasks)
|
||||
mailCheckTask.cancel(false);
|
||||
if (autoMailCheckTask != null) autoMailCheckTask.requestShutdown();
|
||||
if (expirationThread != null) expirationThread.requestShutdown();
|
||||
if (peerManager != null) peerManager.requestShutdown();
|
||||
|
||||
long deadline = System.currentTimeMillis() + 1000 * 60; // the time at which any background threads that are still running are killed
|
||||
if (dht != null)
|
||||
try {
|
||||
dht.awaitShutdown(deadline - System.currentTimeMillis());
|
||||
}
|
||||
catch(InterruptedException e) {
|
||||
log.error("Interrupted while waiting for DHT shutdown.", e);
|
||||
}
|
||||
join(outboxProcessor, deadline);
|
||||
join(relayPacketSender, deadline);
|
||||
join(smtpService, deadline);
|
||||
join(pop3Service, deadline);
|
||||
join(sendQueue, deadline);
|
||||
if (mailCheckExecutor != null) mailCheckExecutor.shutdownNow();
|
||||
join(autoMailCheckTask, deadline);
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (mailCheckExecutor!=null && currentTime<deadline)
|
||||
try {
|
||||
mailCheckExecutor.awaitTermination(deadline-currentTime, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted while waiting for mailCheckExecutor to exit", e);
|
||||
}
|
||||
try {
|
||||
if (i2pSession != null)
|
||||
i2pSession.destroySession();
|
||||
} catch (I2PSessionException e) {
|
||||
log.error("Can't destroy I2P session.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void join(Thread thread, long until) {
|
||||
if (thread == null)
|
||||
return;
|
||||
long timeout = System.currentTimeMillis() - until;
|
||||
if (timeout > 0)
|
||||
try {
|
||||
thread.join(timeout);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted while waiting for thread <" + thread.getName() + "> to exit", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the network, skipping the connect delay.
|
||||
* If the delay time has already passed, calling this method has no effect.
|
||||
*/
|
||||
public void connectNow() {
|
||||
connectTask.startSignal.countDown();
|
||||
}
|
||||
|
||||
public NetworkStatus getNetworkStatus() {
|
||||
if (!connectTask.isDone())
|
||||
return connectTask.getNetworkStatus();
|
||||
else if (dht != null)
|
||||
return dht.isReady()?NetworkStatus.CONNECTED:NetworkStatus.CONNECTING;
|
||||
else
|
||||
return NetworkStatus.ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits <code>STARTUP_DELAY</code> milliseconds or until <code>startSignal</code>
|
||||
* is triggered from outside this class, then sets up an I2P session and everything
|
||||
* that depends on it.
|
||||
*/
|
||||
private class ConnectTask extends I2PBoteThread {
|
||||
volatile NetworkStatus status = NetworkStatus.NOT_STARTED;
|
||||
CountDownLatch startSignal = new CountDownLatch(1);
|
||||
CountDownLatch doneSignal = new CountDownLatch(1);
|
||||
|
||||
protected ConnectTask() {
|
||||
super("ConnectTask");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
public NetworkStatus getNetworkStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
try {
|
||||
return doneSignal.await(0, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestShutdown() {
|
||||
super.requestShutdown();
|
||||
startSignal.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
status = NetworkStatus.DELAY;
|
||||
try {
|
||||
startSignal.await(STARTUP_DELAY, TimeUnit.MINUTES);
|
||||
status = NetworkStatus.CONNECTING;
|
||||
initializeSession();
|
||||
initializeServices();
|
||||
startAllServices();
|
||||
doneSignal.countDown();
|
||||
} catch (Exception e) {
|
||||
status = NetworkStatus.ERROR;
|
||||
log.error("Can't initialize the application.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,260 +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.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
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.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.Translate;
|
||||
|
||||
public class Util {
|
||||
private static final String BUNDLE_NAME = "i2p.bote.locale.Messages";
|
||||
|
||||
private Util() { }
|
||||
|
||||
public static byte[] readBytes(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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a <code>URL</code> and reads one line at a time.
|
||||
* Returns the lines as a <code>List</code> of <code>String</code>s,
|
||||
* or an empty <code>List</code> if an error occurred.
|
||||
* @param url
|
||||
* @return
|
||||
* @see readLines(File)
|
||||
*/
|
||||
public static List<String> readLines(URL url) {
|
||||
Log log = new Log(Util.class);
|
||||
log.info("Reading URL: <" + url + ">");
|
||||
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = url.openStream();
|
||||
return readLines(stream);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Error reading URL.", e);
|
||||
return Collections.emptyList();
|
||||
} finally {
|
||||
if (stream != null)
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
log.error("Can't close input stream.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a <code>File</code> and reads one line at a time.
|
||||
* Returns the lines as a <code>List</code> of <code>String</code>s,
|
||||
* or an empty <code>List</code> if an error occurred.
|
||||
* @param file
|
||||
* @return
|
||||
* @see readLines(URL)
|
||||
*/
|
||||
public static List<String> readLines(File file) {
|
||||
Log log = new Log(Util.class);
|
||||
log.info("Reading file: <" + file.getAbsolutePath() + ">");
|
||||
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = new FileInputStream(file);
|
||||
return readLines(stream);
|
||||
} catch (IOException e) {
|
||||
log.error("Error reading file.", e);
|
||||
return Collections.emptyList();
|
||||
} finally {
|
||||
if (stream != null)
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
log.error("Can't close input stream.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an <code>InputStream</code> and reads one line at a time.
|
||||
* Returns the lines as a <code>List</code> of <code>String</code>s.
|
||||
* or an empty <code>List</code> if an error occurred.
|
||||
* @param url
|
||||
* @return
|
||||
* @see readLines(URL)
|
||||
*/
|
||||
private static List<String> readLines(InputStream inputStream) throws IOException {
|
||||
Log log = new Log(Util.class);
|
||||
|
||||
BufferedReader inputBuffer = new BufferedReader(new InputStreamReader(inputStream));
|
||||
List<String> lines = new ArrayList<String>();
|
||||
|
||||
int linesRead = 0;
|
||||
while (true) {
|
||||
String line = null;
|
||||
line = inputBuffer.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
lines.add(line);
|
||||
}
|
||||
|
||||
log.info(linesRead + " lines read.");
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an I2P destination with a null certificate from 384 bytes that
|
||||
* are read from a <code>ByteBuffer</code>.
|
||||
* @param buffer
|
||||
* @return
|
||||
* @throws DataFormatException
|
||||
*/
|
||||
public static Destination createDestination(ByteBuffer buffer) throws DataFormatException {
|
||||
byte[] bytes = new byte[388];
|
||||
// read 384 bytes, leave the last 3 bytes zero
|
||||
buffer.get(bytes, 0, 384);
|
||||
Destination peer = new Destination();
|
||||
peer.readBytes(bytes, 0);
|
||||
return peer;
|
||||
}
|
||||
|
||||
public static ThreadFactory createThreadFactory(final String threadName, final int stackSize) {
|
||||
return new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable runnable) {
|
||||
return new Thread(Thread.currentThread().getThreadGroup(), runnable, threadName, stackSize);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a thread-safe <code>Iterable</code> from a thread-unsafe one.
|
||||
* Modifications to the old <code>Iterable</code> will not affect the
|
||||
* new one.
|
||||
* @param <E>
|
||||
* @param iterable
|
||||
* @return
|
||||
*/
|
||||
public static <E> Iterable<E> synchronizedCopy(Iterable<E> iterable) {
|
||||
synchronized(iterable) {
|
||||
Collection<E> collection = new ArrayList<E>();
|
||||
for (E element: iterable)
|
||||
collection.add(element);
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>i</code>-th element of a <code>Collection</code>'s <code>Iterator</code>.
|
||||
* @param <E>
|
||||
* @param collection
|
||||
* @param i
|
||||
* @return
|
||||
*/
|
||||
public static <E> E get(Collection<E> collection, int i) {
|
||||
for (E element: collection) {
|
||||
if (i == 0)
|
||||
return element;
|
||||
i--;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String _(String messageKey) {
|
||||
return Translate.getString(messageKey, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
|
||||
}
|
||||
|
||||
public static String _(String messageKey, Object parameter) {
|
||||
return Translate.getString(messageKey, parameter, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
|
||||
}
|
||||
|
||||
public static String _(String messageKey, Object parameter1, Object parameter2) {
|
||||
return Translate.getString(messageKey, parameter1, parameter2, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all whitespace at the beginning and the end of a string,
|
||||
* and replaces multiple whitespace characters with a single space.
|
||||
* @param string
|
||||
* @return
|
||||
*/
|
||||
public static String removeExtraWhitespace(String string) {
|
||||
if (string == null)
|
||||
return null;
|
||||
return string.trim().replaceAll("\\s+", " ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes whitespace from the beginning and end of an address.
|
||||
* Also removes angle brackets if the address begins and ends
|
||||
* with an angle bracket.
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
public static String fixAddress(String address) {
|
||||
if (address == null)
|
||||
return null;
|
||||
|
||||
address = address.trim();
|
||||
if (address.startsWith("<") && address.endsWith(">"))
|
||||
address = address.substring(1, address.length()-1);
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
public static UniqueId zeroId() {
|
||||
return new UniqueId(new byte[UniqueId.LENGTH], 0);
|
||||
}
|
||||
|
||||
public static String toBase32(Hash hash) {
|
||||
return Base32.encode(hash.toByteArray());
|
||||
}
|
||||
}
|
@@ -1,177 +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.addressbook;
|
||||
|
||||
import i2p.bote.email.EmailDestination;
|
||||
|
||||
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.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Implements the private address book. Holds a set of <code>Addresses</code>.
|
||||
*/
|
||||
public class AddressBook implements Iterable<Contact> {
|
||||
private Log log = new Log(AddressBook.class);
|
||||
private File addressFile;
|
||||
private List<Contact> contacts;
|
||||
|
||||
/**
|
||||
* Reads an <code>AddressBook</code> from a text file. Each contact is defined
|
||||
* by one line that contains an Email Destination and a name, separated by a
|
||||
* tab character.
|
||||
* @param addressFile
|
||||
*/
|
||||
public AddressBook(File addressFile) {
|
||||
this.addressFile = addressFile;
|
||||
contacts = Collections.synchronizedList(new ArrayList<Contact>());
|
||||
|
||||
if (!addressFile.exists()) {
|
||||
log.debug("Address file does not exist: <" + addressFile.getAbsolutePath() + ">");
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Reading address book from <" + addressFile.getAbsolutePath() + ">");
|
||||
BufferedReader input = null;
|
||||
try {
|
||||
input = new BufferedReader(new FileReader(addressFile));
|
||||
|
||||
while (true) {
|
||||
String line = input.readLine();
|
||||
if (line == null) // EOF
|
||||
break;
|
||||
|
||||
String[] fields = line.split("\\t", 2);
|
||||
try {
|
||||
EmailDestination destination = new EmailDestination(fields[0]);
|
||||
String name = null;
|
||||
if (fields.length > 1)
|
||||
name = fields[1];
|
||||
contacts.add(new Contact(destination, name));
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
log.error("Not a valid Email Destination: <" + fields[0] + ">", e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Can't read address book.", e);
|
||||
}
|
||||
finally {
|
||||
if (input != null)
|
||||
try {
|
||||
input.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Error closing input stream.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void save() throws IOException {
|
||||
BufferedWriter writer = new BufferedWriter(new FileWriter(addressFile));
|
||||
try {
|
||||
for (Contact contact: contacts) {
|
||||
writer.write(contact.toBase64());
|
||||
writer.write("\t");
|
||||
writer.write(contact.getName());
|
||||
writer.newLine();
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Can't save address book to file <" + addressFile.getAbsolutePath() + ">.", e);
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void add(Contact contact) {
|
||||
contacts.add(contact);
|
||||
}
|
||||
|
||||
public void remove(String destination) {
|
||||
Contact contact = get(destination);
|
||||
if (contact != null)
|
||||
contacts.remove(contact);
|
||||
}
|
||||
|
||||
public Contact get(int i) {
|
||||
return contacts.get(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up an {@link Contact} by its Base64 key. If none is found,
|
||||
* <code>null</code> is returned.
|
||||
* @param destination
|
||||
* @return
|
||||
*/
|
||||
public Contact get(String destination) {
|
||||
if (destination==null || destination.isEmpty())
|
||||
return null;
|
||||
|
||||
for (Contact contact: contacts)
|
||||
if (destination.equals(contact.toBase64()))
|
||||
return contact;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if a given Base64-encoded Email Destination
|
||||
* is in the address book.
|
||||
* @param base64dest
|
||||
* @return
|
||||
*/
|
||||
public boolean contains(String base64dest) {
|
||||
if (base64dest == null)
|
||||
return false;
|
||||
|
||||
for (Contact contact: contacts)
|
||||
if (base64dest.equals(contact.toBase64()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public Collection<Contact> getAll() {
|
||||
return contacts;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return contacts.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Contact> iterator() {
|
||||
return contacts.iterator();
|
||||
}
|
||||
}
|
@@ -1,62 +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.crypto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class CryptoFactory {
|
||||
private static List<CryptoImplementation> instances;
|
||||
|
||||
public static CryptoImplementation getInstance(int id) {
|
||||
if (instances == null)
|
||||
init();
|
||||
|
||||
for (CryptoImplementation instance: instances)
|
||||
if (instance.getId() == id)
|
||||
return instance;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<CryptoImplementation> getInstances() {
|
||||
if (instances == null)
|
||||
init();
|
||||
|
||||
return instances;
|
||||
}
|
||||
|
||||
private static void init() {
|
||||
instances = Collections.synchronizedList(new ArrayList<CryptoImplementation>());
|
||||
instances.add(new ElGamal2048_DSA1024());
|
||||
try {
|
||||
instances.add(new ECDH256_ECDSA256());
|
||||
instances.add(new ECDH521_ECDSA521());
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log log = new Log(CryptoFactory.class);
|
||||
log.error("Error creating ECDH256_ECDSA256 or ECDH521_ECDSA521.", e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,68 +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.addressbook.AddressBook;
|
||||
import i2p.bote.addressbook.Contact;
|
||||
|
||||
/**
|
||||
* This class is used for adding/replacing names in email addresses with
|
||||
* local names (address book entries and email identities).
|
||||
*/
|
||||
public class AddressDisplayFilter {
|
||||
private Identities identities;
|
||||
private AddressBook addressBook;
|
||||
|
||||
public AddressDisplayFilter(Identities identities, AddressBook addressBook) {
|
||||
this.identities = identities;
|
||||
this.addressBook = addressBook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the name associated with a Base64-encoded Email Destination,
|
||||
* in the address book and the local identities, and returns a string
|
||||
* that contains the name and the Base64-encoded destination.
|
||||
* If <code>address</code> already contains a name, it is replaced with
|
||||
* the one from the address book or identities.
|
||||
* If no name is found in the address book or the identities, or if
|
||||
* <code>address</code> does not contain a valid Email Destination,
|
||||
* <code>address</code> is returned.
|
||||
* @param address A Base64-encoded Email Destination, and optionally a name
|
||||
* @return
|
||||
*/
|
||||
public String getNameAndDestination(String address) {
|
||||
String base64dest = EmailDestination.extractBase64Dest(address);
|
||||
if (base64dest != null) {
|
||||
// try the address book
|
||||
Contact contact = addressBook.get(base64dest);
|
||||
if (contact != null)
|
||||
return contact.getName() + "<" + contact.toBase64() + ">";
|
||||
|
||||
// if no address book entry, try the email identities
|
||||
EmailIdentity identity = identities.get(base64dest);
|
||||
if (identity != null)
|
||||
return identity.getPublicName() + "<" + identity.toBase64() + ">";
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
}
|
@@ -1,542 +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 static i2p.bote.Util._;
|
||||
import i2p.bote.I2PBote;
|
||||
import i2p.bote.UniqueId;
|
||||
import i2p.bote.Util;
|
||||
import i2p.bote.crypto.CryptoFactory;
|
||||
import i2p.bote.crypto.CryptoImplementation;
|
||||
import i2p.bote.packet.UnencryptedEmailPacket;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PrivateKey;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.mail.Address;
|
||||
import javax.mail.Header;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import com.nettgryppa.security.HashCash;
|
||||
|
||||
public class Email extends MimeMessage {
|
||||
private static final int MAX_BYTES_PER_PACKET = 30 * 1024;
|
||||
private static final String SIGNATURE_HEADER = "X-I2PBote-Signature"; // contains the sender's base64-encoded signature
|
||||
private static final String SIGNATURE_VALID_HEADER = "X-I2PBote-Sig-Valid"; // contains the string "true" or "false"
|
||||
private static final String[] HEADER_WHITELIST = new String[] {
|
||||
"From", "Sender", "To", "CC", "BCC", "Reply-To", "Subject", "Date", "MIME-Version", "Content-Type",
|
||||
"Content-Transfer-Encoding", "In-Reply-To", "X-HashCash", "X-Priority", SIGNATURE_HEADER
|
||||
};
|
||||
private static final String[] ADDRESS_HEADERS = new String[] {"From", "Sender", "To", "CC", "BCC", "Reply-To"};
|
||||
|
||||
private static Log log = new Log(Email.class);
|
||||
private UniqueId messageId;
|
||||
private boolean isNew = true;
|
||||
|
||||
public Email() {
|
||||
super(Session.getDefaultInstance(new Properties()));
|
||||
messageId = new UniqueId();
|
||||
}
|
||||
|
||||
public Email(File file) throws FileNotFoundException, MessagingException {
|
||||
this(new FileInputStream(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Email object from an InputStream containing a MIME email.
|
||||
*
|
||||
* @param inputStream
|
||||
* @throws MessagingException
|
||||
*/
|
||||
private Email(InputStream inputStream) throws MessagingException {
|
||||
super(Session.getDefaultInstance(new Properties()), inputStream);
|
||||
messageId = new UniqueId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Email object from a byte array containing a MIME email.
|
||||
*
|
||||
* @param bytes
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public Email(byte[] bytes) throws MessagingException {
|
||||
super(Session.getDefaultInstance(new Properties()), new ByteArrayInputStream(bytes));
|
||||
messageId = new UniqueId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the sender is anonymous.
|
||||
* @return
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public boolean isAnonymous() throws MessagingException {
|
||||
Address sender = getSender();
|
||||
return sender==null || "Anonymous".equalsIgnoreCase(sender.toString());
|
||||
}
|
||||
|
||||
public String getOneFromAddress() throws MessagingException {
|
||||
Address[] fromAddresses = getFrom();
|
||||
if (fromAddresses==null || fromAddresses.length==0)
|
||||
return null;
|
||||
else
|
||||
return fromAddresses[0].toString();
|
||||
}
|
||||
|
||||
public String getOneRecipient() throws MessagingException {
|
||||
Address[] recipients = getAllRecipients();
|
||||
if (recipients==null || recipients.length==0)
|
||||
return null;
|
||||
else
|
||||
return recipients[0].toString();
|
||||
}
|
||||
|
||||
public void setHashCash(HashCash hashCash) throws MessagingException {
|
||||
setHeader("X-HashCash", hashCash.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all headers that are not on the whitelist, and initializes some
|
||||
* basic header fields.
|
||||
* Called by <code>saveChanges()</code>, see JavaMail JavaDoc.
|
||||
* @throws MessagingException
|
||||
*/
|
||||
@Override
|
||||
public void updateHeaders() throws MessagingException {
|
||||
super.updateHeaders();
|
||||
scrubHeaders();
|
||||
removeRecipientNames();
|
||||
|
||||
// Set the send time if the "Include sent time" config setting is enabled;
|
||||
// otherwise, remove the send time field.
|
||||
if (I2PBote.getInstance().getConfiguration().getIncludeSentTime()) {
|
||||
// Set the "Date" field in UTC time, using the English locale.
|
||||
long currentTime = new Date().getTime();
|
||||
long timeZoneOffset = TimeZone.getDefault().getOffset(currentTime);
|
||||
currentTime -= timeZoneOffset;
|
||||
DateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy kk:mm:ss +0000", Locale.ENGLISH); // always use UTC for outgoing mail
|
||||
setHeader("Date", formatter.format(currentTime));
|
||||
}
|
||||
else
|
||||
removeHeader("Date");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a digital signature of the email and stores it in the
|
||||
* <code>SIGNATURE_HEADER</code> header field. It also removes the
|
||||
* <code>SIGNATURE_VALID_FLAG</code> header.
|
||||
* The signature is computed over the stream representation of the
|
||||
* email, minus the signature header if it is present.
|
||||
* The signature includes the ID number of the {@link CryptoImplementation}
|
||||
* used (signature lengths can be different for the same algorithm).
|
||||
* @param senderIdentity
|
||||
* @throws MessagingException
|
||||
* @throws GeneralSecurityException
|
||||
*/
|
||||
private void sign(EmailIdentity senderIdentity) throws MessagingException, GeneralSecurityException {
|
||||
removeHeader(SIGNATURE_HEADER); // make sure there is no existing signature which would make the new signature invalid
|
||||
removeHeader(SIGNATURE_VALID_HEADER); // remove the signature validity flag before signing
|
||||
CryptoImplementation cryptoImpl = senderIdentity.getCryptoImpl();
|
||||
PrivateKey signingKey = senderIdentity.getPrivateSigningKey();
|
||||
byte[] signature = cryptoImpl.sign(toByteArray(), signingKey);
|
||||
setHeader(SIGNATURE_HEADER, cryptoImpl.getId() + "_" + Base64.encode(signature));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the signature and sets the <code>SIGNATURE_VALID_FLAG</code>
|
||||
* header field accordingly.
|
||||
*/
|
||||
public void setSignatureFlag() {
|
||||
try {
|
||||
removeHeader(SIGNATURE_VALID_HEADER); // remove the signature validity flag before verifying
|
||||
boolean valid = verifySignature();
|
||||
setHeader(SIGNATURE_VALID_HEADER, Boolean.valueOf(valid).toString());
|
||||
} catch (MessagingException e) {
|
||||
log.error("Cannot get header field: " + SIGNATURE_VALID_HEADER, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the email contains a valid signature. If the
|
||||
* <code>SIGNATURE_VALID_FLAG</code> is present, its value is used.
|
||||
* If not, the value of the <code>SIGNATURE_HEADER</code> header
|
||||
* field is verified (which is more CPU intensive).
|
||||
* @return
|
||||
*/
|
||||
public boolean isSignatureValid() {
|
||||
try {
|
||||
String[] sigValidFlag = getHeader(SIGNATURE_VALID_HEADER);
|
||||
if (sigValidFlag==null || sigValidFlag.length==0)
|
||||
return verifySignature();
|
||||
else
|
||||
return "true".equalsIgnoreCase(sigValidFlag[0]);
|
||||
} catch (MessagingException e) {
|
||||
log.error("Cannot get header field: " + SIGNATURE_VALID_HEADER, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the <code>SIGNATURE_HEADER</code> header field
|
||||
* contains a valid signature.
|
||||
* @return
|
||||
*/
|
||||
private boolean verifySignature() {
|
||||
String[] signatureHeaders;
|
||||
try {
|
||||
signatureHeaders = getHeader(SIGNATURE_HEADER);
|
||||
} catch (MessagingException e) {
|
||||
log.error("Cannot get header field: " + SIGNATURE_HEADER, e);
|
||||
return false;
|
||||
}
|
||||
if (signatureHeaders==null || signatureHeaders.length<=0)
|
||||
return false;
|
||||
String signatureHeader = signatureHeaders[0];
|
||||
|
||||
// the crypto implementation ID is the number before the underscore
|
||||
int _index = signatureHeader.indexOf('_');
|
||||
if (_index < 0)
|
||||
return false;
|
||||
String cryptoImplIdString = signatureHeader.substring(0, _index);
|
||||
int cryptoImplId = 0;
|
||||
try {
|
||||
cryptoImplId = Integer.valueOf(cryptoImplIdString);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
CryptoImplementation cryptoImpl = CryptoFactory.getInstance(cryptoImplId);
|
||||
|
||||
// the actual signature is everything after the underscore
|
||||
String base64Signature = signatureHeader.substring(_index + 1);
|
||||
try {
|
||||
removeHeader(SIGNATURE_HEADER);
|
||||
byte[] signature = Base64.decode(base64Signature);
|
||||
EmailDestination senderDestination = new EmailDestination(getSender().toString());
|
||||
return cryptoImpl.verify(toByteArray(), signature, senderDestination.getPublicSigningKey());
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot verify email signature. Email: [" + this + "]", e);
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
setHeader(SIGNATURE_HEADER, signatureHeader);
|
||||
} catch (MessagingException e) {
|
||||
log.error("Cannot set signature header field.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if one or more address fields contain an invalid
|
||||
* Email Destination. If all addresses are valid, nothing happens.
|
||||
* @throws MessagingException
|
||||
* @throws DataFormatException
|
||||
*/
|
||||
public void checkAddresses() throws MessagingException, DataFormatException {
|
||||
Collection<Header> headers = getAllAddressHeaders();
|
||||
for (Header header: headers) {
|
||||
String address = header.getValue();
|
||||
if (!"Sender".equalsIgnoreCase(header.getName()) || !isAnonymous()) { // don't validate if this is the "sender" field and the sender is anonymous
|
||||
boolean validEmailDest = false;
|
||||
try {
|
||||
new EmailDestination(address);
|
||||
validEmailDest = true;
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
log.debug("Address contains no email destination: <" + address + ">, message: " + e.getLocalizedMessage());
|
||||
}
|
||||
try {
|
||||
String adrString = address.toString();
|
||||
new InternetAddress(adrString, true);
|
||||
// InternetAddress accepts addresses without a domain, so check that there is a '.' after the '@'
|
||||
if (adrString.indexOf('@') >= adrString.indexOf('.'))
|
||||
throw new DataFormatException(_("Invalid address: {0}", address));
|
||||
validEmailDest = true;
|
||||
} catch (AddressException e) {
|
||||
log.debug("Address contains no external email address: <" + address + ">, message: " + e.getLocalizedMessage());
|
||||
}
|
||||
if (!validEmailDest)
|
||||
throw new DataFormatException(_("Address doesn't contain an Email Destination or an external address: {0}", address));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void fixAddresses() throws MessagingException {
|
||||
List<Header> addressHeaders = getAllAddressHeaders();
|
||||
for (String headerName: ADDRESS_HEADERS)
|
||||
removeHeader(headerName);
|
||||
for (Header header: addressHeaders) {
|
||||
String fixedAddress = Util.fixAddress(header.getValue());
|
||||
addHeader(header.getName(), fixedAddress);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Header> getAllAddressHeaders() throws MessagingException {
|
||||
@SuppressWarnings("unchecked")
|
||||
Enumeration<Header> addressHeaders = (Enumeration<Header>)getMatchingHeaders(ADDRESS_HEADERS);
|
||||
return Collections.list(addressHeaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all "Reply To" addresses (usually zero or one).
|
||||
* Unlike {@link getReplyTo()}, this method does not return
|
||||
* the "From" address if there is no "Reply To" address.
|
||||
* @return
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public String[] getReplyToAddresses() throws MessagingException {
|
||||
return getHeader("Reply-To");
|
||||
}
|
||||
|
||||
public Address[] getToAddresses() throws MessagingException {
|
||||
return getRecipients(RecipientType.TO);
|
||||
}
|
||||
|
||||
public Address[] getCCAddresses() throws MessagingException {
|
||||
return getRecipients(RecipientType.CC);
|
||||
}
|
||||
|
||||
public Address[] getBCCAddresses() throws MessagingException {
|
||||
return getRecipients(RecipientType.BCC);
|
||||
}
|
||||
|
||||
private byte[] toByteArray() throws MessagingException {
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
try {
|
||||
writeTo(byteStream);
|
||||
} catch (IOException e) {
|
||||
throw new MessagingException("Cannot write email to ByteArrayOutputStream.", e);
|
||||
}
|
||||
return byteStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all mail headers except the ones in <code>HEADER_WHITELIST</code>.
|
||||
* @throws MessagingException
|
||||
*/
|
||||
private void scrubHeaders() throws MessagingException {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Header> nonMatchingHeaders = Collections.list(getNonMatchingHeaders(HEADER_WHITELIST));
|
||||
for (Header header: nonMatchingHeaders)
|
||||
if (header != null) {
|
||||
log.debug("Removing all instances of non-whitelisted header <" + header.getName() + ">");
|
||||
removeHeader(header.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes everything but the email destination from all recipient fields,
|
||||
* in order to keep local contact names private.
|
||||
* @throws MessagingException
|
||||
*/
|
||||
private void removeRecipientNames() throws MessagingException {
|
||||
removeRecipientNames("To");
|
||||
removeRecipientNames("CC");
|
||||
removeRecipientNames("BCC");
|
||||
}
|
||||
|
||||
private void removeRecipientNames(String headerName) throws MessagingException {
|
||||
String[] headerValues = getHeader(headerName);
|
||||
removeHeader(headerName);
|
||||
if (headerValues != null)
|
||||
for (String recipient: headerValues) {
|
||||
String dest = EmailDestination.extractBase64Dest(recipient);
|
||||
if (dest != null)
|
||||
addHeader(headerName, dest);
|
||||
// If there is no email destination, assume it is an external address and don't change it
|
||||
else
|
||||
addHeader(headerName, recipient);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param messageIdString Must be a 44-character Base64-encoded string.
|
||||
*/
|
||||
public void setMessageID(String messageIdString) {
|
||||
this.messageId = new UniqueId(messageIdString);
|
||||
}
|
||||
|
||||
public void setMessageID(UniqueId messageId) {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessageID() {
|
||||
return messageId.toBase64();
|
||||
}
|
||||
|
||||
public void setNew(boolean isNew) {
|
||||
this.isNew = isNew;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the email is unread (incoming mail), or
|
||||
* if it has not been sent yet (outgoing mail).
|
||||
* @return
|
||||
*/
|
||||
public boolean isNew() {
|
||||
return isNew;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
try {
|
||||
return getContent().toString();
|
||||
} catch (Exception e) {
|
||||
String errorMsg = "Error reading email content.";
|
||||
log.error(errorMsg, e);
|
||||
return errorMsg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates headers, signs the email, and converts it into one or more email packets.
|
||||
* If an error occurs, an empty <code>Collection</code> is returned.
|
||||
*
|
||||
* @param senderIdentity The sender's Email Identity, or <code>null</code> for anonymous emails
|
||||
* @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 MessagingException
|
||||
* @throws GeneralSecurityException
|
||||
*/
|
||||
public Collection<UnencryptedEmailPacket> createEmailPackets(EmailIdentity senderIdentity, String bccToKeep) throws MessagingException, GeneralSecurityException {
|
||||
ArrayList<UnencryptedEmailPacket> packets = new ArrayList<UnencryptedEmailPacket>();
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
String[] bccHeaders = null;
|
||||
try {
|
||||
bccHeaders = getHeader("BCC");
|
||||
saveChanges();
|
||||
if (bccToKeep!=null && isBCC(bccToKeep))
|
||||
setHeader("BCC", bccToKeep); // set bccToKeep and remove any other existing BCC addresses
|
||||
else
|
||||
removeHeader("BCC");
|
||||
if (!isAnonymous())
|
||||
sign(senderIdentity);
|
||||
writeTo(outputStream);
|
||||
} catch (IOException e) {
|
||||
throw new MessagingException("Can't write to ByteArrayOutputStream.", e);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new GeneralSecurityException("Can't sign email.", e);
|
||||
} finally {
|
||||
// restore the BCC headers
|
||||
removeHeader("BCC");
|
||||
if (bccHeaders != null)
|
||||
for (String bccAddress: bccHeaders)
|
||||
addHeader("BCC", bccAddress);
|
||||
}
|
||||
byte[] emailArray = outputStream.toByteArray();
|
||||
|
||||
// calculate packet count
|
||||
int numPackets = (emailArray.length+MAX_BYTES_PER_PACKET-1) / MAX_BYTES_PER_PACKET;
|
||||
|
||||
int packetIndex = 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);
|
||||
UnencryptedEmailPacket packet = new UnencryptedEmailPacket(messageId, packetIndex, numPackets, block);
|
||||
packets.add(packet);
|
||||
packetIndex++;
|
||||
blockStart += blockSize;
|
||||
}
|
||||
}
|
||||
|
||||
return packets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if <code>address</code> is a BCC address.
|
||||
* @param address
|
||||
* @return
|
||||
* @throws MessagingException
|
||||
*/
|
||||
private boolean isBCC(String address) throws MessagingException {
|
||||
String[] bccAddresses = getHeader("BCC");
|
||||
if (bccAddresses == null)
|
||||
return false;
|
||||
|
||||
for (String bccAddress: bccAddresses)
|
||||
if (bccAddress.equals(address))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder("MsgId: ").append(getMessageID());
|
||||
try {
|
||||
result = result.append("Sender: ").append(getSender());
|
||||
result = result.append("Recipients: ");
|
||||
for (Address recipient: getAllRecipients()) {
|
||||
if (result.length() > 1000) {
|
||||
result = result.append("...");
|
||||
break;
|
||||
}
|
||||
if (result.length() > 0)
|
||||
result = result.append(", ");
|
||||
String recipientAddress = recipient.toString();
|
||||
if (recipientAddress.length() > 20)
|
||||
result = result.append(recipientAddress).append("...");
|
||||
else
|
||||
result = result.append(recipientAddress);
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
log.error("Error getting sender or recipients.");
|
||||
result.append("#Error#");
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
@@ -1,145 +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.crypto.CryptoFactory;
|
||||
import i2p.bote.crypto.CryptoImplementation;
|
||||
import i2p.bote.crypto.PrivateKeyPair;
|
||||
import i2p.bote.crypto.PublicKeyPair;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
|
||||
public class EmailIdentity extends EmailDestination {
|
||||
private PrivateKey privateEncryptionKey;
|
||||
private PrivateKey privateSigningKey;
|
||||
private String publicName;
|
||||
private String description; // optional
|
||||
private String emailAddress; // optional
|
||||
private boolean isDefault;
|
||||
|
||||
/**
|
||||
* Creates a random <code>EmailIdentity</code>.
|
||||
* @param cryptoImpl
|
||||
* @throws GeneralSecurityException
|
||||
*/
|
||||
public EmailIdentity(CryptoImplementation cryptoImpl) throws GeneralSecurityException {
|
||||
super();
|
||||
this.cryptoImpl = cryptoImpl;
|
||||
KeyPair encryptionKeys = cryptoImpl.generateEncryptionKeyPair();
|
||||
KeyPair signingKeys = cryptoImpl.generateSigningKeyPair();
|
||||
|
||||
publicEncryptionKey = encryptionKeys.getPublic();
|
||||
privateEncryptionKey = encryptionKeys.getPrivate();
|
||||
publicSigningKey = signingKeys.getPublic();
|
||||
privateSigningKey = signingKeys.getPrivate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <code>EmailIdentity</code> from a Base64-encoded string.
|
||||
* The format can be any format supported by one of the {@link CryptoImplementation}s.
|
||||
* @param base64Key
|
||||
* @throws GeneralSecurityException
|
||||
*/
|
||||
public EmailIdentity(String base64Key) throws GeneralSecurityException {
|
||||
// find the crypto implementation for this key length
|
||||
for (CryptoImplementation cryptoImpl: CryptoFactory.getInstances()) {
|
||||
int base64Length = cryptoImpl.getBase64CompleteKeySetLength(); // length of an email identity with this CryptoImplementation
|
||||
if (base64Key.length() == base64Length) {
|
||||
this.cryptoImpl = cryptoImpl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cryptoImpl == null)
|
||||
throw new InvalidKeyException("Not a valid Email Identity: <" + base64Key + ">");
|
||||
|
||||
PublicKeyPair publicKeys = cryptoImpl.createPublicKeyPair(base64Key);
|
||||
String base64PrivateKeys = base64Key.substring(cryptoImpl.getBase64PublicKeyPairLength()); // the two private keys start after the two public keys
|
||||
PrivateKeyPair privateKeys = cryptoImpl.createPrivateKeyPair(base64PrivateKeys);
|
||||
|
||||
publicEncryptionKey = publicKeys.encryptionKey;
|
||||
privateEncryptionKey = privateKeys.encryptionKey;
|
||||
publicSigningKey = publicKeys.signingKey;
|
||||
privateSigningKey = privateKeys.signingKey;
|
||||
}
|
||||
|
||||
public PrivateKey getPrivateEncryptionKey() {
|
||||
return privateEncryptionKey;
|
||||
}
|
||||
|
||||
public PrivateKey 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;
|
||||
}
|
||||
|
||||
public void setDefault(boolean isDefault) {
|
||||
this.isDefault = isDefault;
|
||||
}
|
||||
|
||||
public boolean isDefault() {
|
||||
return isDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the two key pairs (public + private) as one Base64-encoded string.
|
||||
* @return
|
||||
* @throws GeneralSecurityException
|
||||
*/
|
||||
public String getFullKey() throws GeneralSecurityException {
|
||||
PublicKeyPair publicKeys = new PublicKeyPair(publicEncryptionKey, publicSigningKey);
|
||||
PrivateKeyPair privateKeys = new PrivateKeyPair(privateEncryptionKey, privateSigningKey);
|
||||
|
||||
String pubKeys = cryptoImpl.toBase64(publicKeys);
|
||||
String privKeys = cryptoImpl.toBase64(privateKeys);
|
||||
return pubKeys + privKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getKey() + " address=<" + getEmailAddress() + "> identity name=<" + getDescription() + "> visible name=<" + getPublicName() + ">";
|
||||
}
|
||||
}
|
@@ -1,285 +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.security.GeneralSecurityException;
|
||||
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.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;
|
||||
|
||||
/**
|
||||
* Constructs <code>Identities</code> from a text file. Each identity is defined
|
||||
* by one line that contains two to four tab-separated fields:
|
||||
* Email Destination key, Public Name, Description, and Email Address.
|
||||
* The first two are mandatory, the last two are optional.
|
||||
*
|
||||
* Additionally, the file can set a default Email Destination by including a
|
||||
* line that starts with "Default ", followed by an Email Destination key.
|
||||
* The destination key must match one of the Email Destinations defined in
|
||||
* the file.
|
||||
* @param identitiesFile
|
||||
*/
|
||||
public Identities(File identitiesFile) {
|
||||
this.identitiesFile = identitiesFile;
|
||||
identities = Collections.synchronizedList(new ArrayList<EmailIdentity>());
|
||||
String defaultIdentityString = null;
|
||||
|
||||
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;
|
||||
|
||||
if (line.toLowerCase().startsWith("default"))
|
||||
defaultIdentityString = line.substring("default ".length());
|
||||
else {
|
||||
EmailIdentity identity = parse(line);
|
||||
if (identity != null)
|
||||
identities.add(identity);
|
||||
}
|
||||
}
|
||||
|
||||
// set the default identity; if none defined, make the first one the default
|
||||
EmailIdentity defaultIdentity = get(defaultIdentityString);
|
||||
if (defaultIdentity != null)
|
||||
defaultIdentity.setDefault(true);
|
||||
else if (!identities.isEmpty())
|
||||
identities.get(0).setDefault(true);
|
||||
} catch (Exception e) {
|
||||
log.error("Error reading the 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) {
|
||||
String[] fields = emailIdentityString.split("\\t", 4);
|
||||
if (fields.length < 2) {
|
||||
log.debug("Unparseable email identity: <" + emailIdentityString + ">");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
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;
|
||||
} catch (GeneralSecurityException e) {
|
||||
log.debug("Invalid email identity: <" + fields[0] + ">");
|
||||
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
|
||||
* @throws GeneralSecurityException
|
||||
*/
|
||||
private String toFileFormat(EmailIdentity identity) throws GeneralSecurityException {
|
||||
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, GeneralSecurityException {
|
||||
String newLine = System.getProperty("line.separator");
|
||||
Writer writer = new BufferedWriter(new FileWriter(identitiesFile));
|
||||
try {
|
||||
EmailIdentity defaultIdentity = getDefault();
|
||||
if (defaultIdentity != null)
|
||||
writer.write("Default " + defaultIdentity.getKey() + newLine);
|
||||
|
||||
for (EmailIdentity identity: identities)
|
||||
writer.write(toFileFormat(identity) + newLine);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Can't save email identities to file <" + identitiesFile.getAbsolutePath() + ">.", e);
|
||||
throw e;
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
log.error("Can't save email identities to file <" + identitiesFile.getAbsolutePath() + ">.", e);
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void add(EmailIdentity identity) {
|
||||
if (identities.isEmpty())
|
||||
identity.setDefault(true);
|
||||
identities.add(identity);
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
EmailIdentity identity = get(key);
|
||||
if (identity != null) {
|
||||
identities.remove(identity);
|
||||
|
||||
// if we deleted the default identity, set a new default
|
||||
if (identity.isDefault() && !identities.isEmpty())
|
||||
identities.get(0).setDefault(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default identity. Assumes this <code>Identities</code> already
|
||||
* contains <code>defaultIdentity</code>.
|
||||
* @return
|
||||
*/
|
||||
public void setDefault(EmailIdentity defaultIdentity) {
|
||||
// clear the old default
|
||||
for (EmailIdentity identity: identities)
|
||||
identity.setDefault(false);
|
||||
|
||||
defaultIdentity.setDefault(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default identity, or <code>null</code> if no default is set.
|
||||
* @return
|
||||
*/
|
||||
public EmailIdentity getDefault() {
|
||||
for (EmailIdentity identity: identities)
|
||||
if (identity.isDefault())
|
||||
return identity;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public EmailIdentity get(int i) {
|
||||
return identities.get(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up an {@link EmailIdentity} that has the same public encryption key and the
|
||||
* same public signing key as a given {@link EmailDestination}.
|
||||
* Returns <code>null</code> if nothing is found.
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public EmailIdentity get(EmailDestination destination) {
|
||||
for (EmailIdentity identity: identities)
|
||||
if (identity.getPublicEncryptionKey().equals(destination.getPublicEncryptionKey())
|
||||
&& identity.getPublicSigningKey().equals(destination.getPublicSigningKey()))
|
||||
return identity;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up an {@link EmailIdentity} by its Base64 key (the two public keys, to be
|
||||
* more precise).
|
||||
* Returns <code>null</code> if nothing is found.
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public EmailIdentity get(String key) {
|
||||
for (EmailIdentity identity: identities)
|
||||
if (identity.getKey().equals(key))
|
||||
return identity;
|
||||
return null;
|
||||
}
|
||||
|
||||
public Collection<EmailIdentity> getAll() {
|
||||
return identities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if any of the <code>Identities</code> matches
|
||||
* the two public keys of a given {@link EmailDestination}
|
||||
* (which can be an <code>EmailIdentity</code>).
|
||||
* @param destination A base64-encoded Email Destination
|
||||
* @return
|
||||
*/
|
||||
public boolean contains(String base64dest) {
|
||||
if (base64dest == null)
|
||||
return false;
|
||||
|
||||
for (EmailIdentity identity: identities)
|
||||
if (base64dest.equals(identity.toBase64()))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return identities.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<EmailIdentity> iterator() {
|
||||
return identities.iterator();
|
||||
}
|
||||
}
|
@@ -1,338 +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.email.AddressDisplayFilter;
|
||||
import i2p.bote.email.Email;
|
||||
import i2p.bote.email.EmailAttribute;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.mail.MessagingException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Stores emails in a directory on the file system. Each email is stored in one file.
|
||||
* Filenames are in the format <code><N, O>_<message ID>.mail</code>, where
|
||||
* N is for new (unread or unsent) and O is for old (read or sent).
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores an email in the folder. If an email with the same
|
||||
* message ID exists already, nothing happens.
|
||||
* @param email
|
||||
* @throws IOException
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public void add(Email email) throws IOException, MessagingException {
|
||||
// check if an email exists already with that message id
|
||||
if (getEmailFile(email.getMessageID()) != null) {
|
||||
log.debug("Not storing email because there is an existing one with the same message ID: <" + email.getMessageID()+ ">");
|
||||
return;
|
||||
}
|
||||
|
||||
// write out the email file
|
||||
File emailFile = getEmailFile(email);
|
||||
log.info("Mail folder <" + storageDir + ">: storing email file: <" + emailFile.getAbsolutePath() + ">");
|
||||
OutputStream emailOutputStream = null;
|
||||
try {
|
||||
emailOutputStream = new FileOutputStream(emailFile);
|
||||
email.writeTo(emailOutputStream);
|
||||
}
|
||||
finally {
|
||||
if (emailOutputStream != null)
|
||||
try {
|
||||
emailOutputStream.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Can't close file: <" + emailFile + ">", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all emails in the folder, in the order specified by <code>sortColumn</code>.
|
||||
*/
|
||||
public List<Email> getElements(AddressDisplayFilter displayFilter, EmailAttribute sortColumn, boolean descending) {
|
||||
Comparator<Email> comparator = new EmailComparator(sortColumn, displayFilter);
|
||||
if (descending)
|
||||
comparator = Collections.reverseOrder(comparator);
|
||||
|
||||
List<Email> emails = getElements();
|
||||
Collections.sort(emails, comparator);
|
||||
return emails;
|
||||
}
|
||||
|
||||
/**
|
||||
* A <code>Comparator</code> for sorting emails by a given {@link EmailAttribute}.
|
||||
* If <code>attribute</code> is <code>null</code>, the date field is used.
|
||||
*/
|
||||
private class EmailComparator implements Comparator<Email> {
|
||||
private EmailAttribute attribute;
|
||||
private AddressDisplayFilter displayFilter;
|
||||
|
||||
public EmailComparator(EmailAttribute attribute, AddressDisplayFilter displayFilter) {
|
||||
if (attribute == null)
|
||||
attribute = EmailAttribute.DATE;
|
||||
this.attribute = attribute;
|
||||
this.displayFilter = displayFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(Email email1, Email email2) {
|
||||
@SuppressWarnings("unchecked") Comparable value1 = 0;
|
||||
@SuppressWarnings("unchecked") Comparable value2 = 0;
|
||||
|
||||
try {
|
||||
switch(attribute) {
|
||||
case DATE:
|
||||
value1 = email1.getSentDate();
|
||||
value2 = email2.getSentDate();
|
||||
break;
|
||||
case FROM:
|
||||
value1 = displayFilter.getNameAndDestination(email1.getOneFromAddress());
|
||||
value2 = displayFilter.getNameAndDestination(email2.getOneFromAddress());
|
||||
break;
|
||||
case TO:
|
||||
value1 = displayFilter.getNameAndDestination(email1.getOneRecipient());
|
||||
value2 = displayFilter.getNameAndDestination(email2.getOneRecipient());
|
||||
break;
|
||||
case SUBJECT:
|
||||
value1 = email1.getSubject();
|
||||
value2 = email2.getSubject();
|
||||
break;
|
||||
default:
|
||||
log.error("Unknown email attribute type: " + attribute);
|
||||
}
|
||||
|
||||
return nullSafeCompare(value1, value2);
|
||||
}
|
||||
catch (MessagingException e) {
|
||||
log.error("Can't read the " + attribute + " attribute from an email", e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private int nullSafeCompare(Comparable value1, Comparable value2) {
|
||||
if (value1 == null) {
|
||||
if (value2 == null)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
if (value2 == null)
|
||||
return 1;
|
||||
else {
|
||||
if (value1 instanceof String)
|
||||
return ((String)value1).compareToIgnoreCase((String)value2);
|
||||
else
|
||||
return value1.compareTo(value2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@link UniqueId}.
|
||||
* @param messageId
|
||||
* @return
|
||||
*/
|
||||
public Email getEmail(String messageId) {
|
||||
File file = getEmailFile(messageId);
|
||||
if (file == null)
|
||||
return null;
|
||||
try {
|
||||
return createFolderElement(file);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Can't read email from file: <" + file.getAbsolutePath() + ">", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves an email from this folder to another. May not work if the
|
||||
* two folders are on different filesystems, or if a file with the
|
||||
* same name exists already.
|
||||
* @param email
|
||||
* @param newFolder
|
||||
* @return true if successful, false if not
|
||||
*/
|
||||
public boolean move(Email email, EmailFolder newFolder) {
|
||||
File emailFile = getEmailFile(email);
|
||||
if (emailFile == null) {
|
||||
log.error("Cannot move email [" + email + "] to folder [" + newFolder + "]: email file doesn't exist.");
|
||||
return false;
|
||||
}
|
||||
File newFile = new File(newFolder.getStorageDirectory(), emailFile.getName());
|
||||
boolean success = emailFile.renameTo(newFile);
|
||||
if (!success)
|
||||
log.error("Cannot move <" + emailFile.getAbsolutePath() + "> to <" + newFile.getAbsolutePath() + ">");
|
||||
return success;
|
||||
}
|
||||
|
||||
/** @see move(Email, EmailFolder) */
|
||||
public boolean move(String messageId, EmailFolder newFolder) {
|
||||
Email email = getEmail(messageId);
|
||||
if (email == null) {
|
||||
log.error("Cannot move email: Email with message ID " + messageId + " not found in folder <" + getStorageDirectory() + ">.");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return move(email, newFolder);
|
||||
}
|
||||
|
||||
private File getEmailFile(Email email) {
|
||||
return getEmailFile(email.getMessageID(), email.isNew());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file in the file system for a given message ID, or <code>null</code> if
|
||||
* none exists.
|
||||
* @param messageId
|
||||
* @return
|
||||
*/
|
||||
private File getEmailFile(String messageId) {
|
||||
// try new email
|
||||
File newEmailFile = getEmailFile(messageId, true);
|
||||
if (newEmailFile.exists())
|
||||
return newEmailFile;
|
||||
|
||||
// try old email
|
||||
File oldEmailFile = getEmailFile(messageId, false);
|
||||
if (oldEmailFile.exists())
|
||||
return oldEmailFile;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private File getEmailFile(String messageId, boolean newIndicator) {
|
||||
return new File(storageDir, (newIndicator?'N':'O') + "_" + messageId + EMAIL_FILE_EXTENSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Folder.getNumElements()
|
||||
* @return
|
||||
*/
|
||||
public int getNumNewEmails() {
|
||||
int numNew = 0;
|
||||
for (File file: getFilenames())
|
||||
if (isNew(file))
|
||||
numNew++;
|
||||
|
||||
return numNew;
|
||||
}
|
||||
|
||||
private boolean isNew(File file) {
|
||||
switch (file.getName().charAt(0)) {
|
||||
case 'N':
|
||||
return true;
|
||||
case 'O':
|
||||
return false;
|
||||
default:
|
||||
throw new IllegalArgumentException("Illegal email filename, doesn't start with N or O: <" + file.getAbsolutePath() + ">");
|
||||
}
|
||||
}
|
||||
|
||||
public void setNew(Email email, boolean isNew) {
|
||||
String messageId = email.getMessageID();
|
||||
if (messageId != null) {
|
||||
boolean success = setNew(messageId, isNew);
|
||||
if (success)
|
||||
email.setNew(isNew);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags an email "new" (if <code>isNew</code> is <code>true</code>) or
|
||||
* "old" (if <code>isNew</code> is <code>false</code>).
|
||||
* @param messageId
|
||||
* @param isNew
|
||||
* @return true if the email file was renamed, false if something went wrong
|
||||
*/
|
||||
public boolean setNew(String messageId, boolean isNew) {
|
||||
File file = getEmailFile(messageId);
|
||||
if (file != null) {
|
||||
char newIndicator = isNew?'N':'O'; // the new start character
|
||||
String newFilename = newIndicator + file.getName().substring(1);
|
||||
File newFile = new File(file.getParentFile(), newFilename);
|
||||
boolean success = file.renameTo(newFile);
|
||||
if (!success)
|
||||
log.error("Cannot rename <" + file.getAbsolutePath() + "> to <" + newFile.getAbsolutePath() + ">");
|
||||
return success;
|
||||
}
|
||||
else {
|
||||
log.error("No email found for message Id: <" + messageId + ">");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an email with a given message ID.
|
||||
* @param messageId
|
||||
* @return <code>true</code> if the email was deleted, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean delete(String messageId) {
|
||||
File emailFile = getEmailFile(messageId);
|
||||
if (emailFile != null)
|
||||
return emailFile.delete();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public void delete(Email email) {
|
||||
if (!getEmailFile(email).delete())
|
||||
log.error("Cannot delete file: '" + getEmailFile(email) + "'");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Email createFolderElement(File file) throws Exception {
|
||||
Email email = new Email(file);
|
||||
email.setNew(isNew(file));
|
||||
|
||||
String messageIdString = file.getName().substring(2, 46);
|
||||
email.setMessageID(messageIdString);
|
||||
|
||||
return email;
|
||||
}
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
package i2p.bote.folder;
|
||||
|
||||
/** Interface for folders whose content expires */
|
||||
public interface ExpirationListener {
|
||||
final static long EXPIRATION_TIME_MILLISECONDS = 100 * 24 * 3600 * 1000L; // keep for up to 100 days
|
||||
|
||||
/** Deletes any expired content */
|
||||
void deleteExpired();
|
||||
}
|
@@ -1,134 +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.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <T> The type of objects the folder can store.
|
||||
*/
|
||||
public abstract class Folder<T> 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 int getNumElements() {
|
||||
return getFilenames().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of all files in the folder.
|
||||
* @return
|
||||
*/
|
||||
public File[] getFilenames() {
|
||||
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
|
||||
// sort files by date, newest first
|
||||
Arrays.sort(files, new Comparator<File>() {
|
||||
@Override
|
||||
public int compare(File f1, File f2) {
|
||||
return (int)Math.signum(f2.lastModified() - f1.lastModified());
|
||||
}
|
||||
});
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
public List<T> getElements() {
|
||||
List<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 = getFilenames();
|
||||
log.debug(files.length + " files with the extension '" + fileExtension + "' found in '" + storageDir + "'.");
|
||||
|
||||
return new Iterator<T>() {
|
||||
int nextIndex = 0;
|
||||
File currentFile;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return nextIndex < files.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
currentFile = files[nextIndex];
|
||||
nextIndex++;
|
||||
String filePath = currentFile.getAbsolutePath();
|
||||
log.debug("Reading file: '" + filePath + "'");
|
||||
try {
|
||||
T nextElement = createFolderElement(currentFile);
|
||||
return nextElement;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Can't create a FolderElement from file: " + filePath, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (currentFile == null)
|
||||
throw new IllegalStateException("remove() was called before next()");
|
||||
if (!currentFile.delete())
|
||||
log.error("Can't delete file: <" + currentFile.getAbsolutePath() + ">");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract T createFolderElement(File file) throws Exception;
|
||||
}
|
@@ -1,76 +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.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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a packet to a file in the folder. If the file already exists, it is overwritten.
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected PacketType createFolderElement(File file) throws Exception {
|
||||
return (PacketType)DataPacket.createPacket(file);
|
||||
}
|
||||
}
|
@@ -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.UniqueId;
|
||||
import i2p.bote.packet.RelayDataPacket;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Random;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* A <code>PacketFolder</code> that uses filenames that consist of
|
||||
* the packet's scheduled send time and a random part.
|
||||
*/
|
||||
public class RelayPacketFolder extends PacketFolder<RelayDataPacket> {
|
||||
private final Log log = new Log(RelayPacketFolder.class);
|
||||
private Random random;
|
||||
|
||||
public RelayPacketFolder(File storageDir) {
|
||||
super(storageDir);
|
||||
random = new Random();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a <code>RelayDataPacket</code> in the folder.
|
||||
* @param packet
|
||||
*/
|
||||
public void add(RelayDataPacket packet) {
|
||||
while (true) {
|
||||
UniqueId id = new UniqueId();
|
||||
long minDelay = packet.getMinimumDelay();
|
||||
long maxDelay = packet.getMaximumDelay();
|
||||
// generate a random time between minDelay and maxDelay milliseconds in the future
|
||||
long sendTime;
|
||||
if (minDelay == maxDelay)
|
||||
sendTime = System.currentTimeMillis() + minDelay;
|
||||
else
|
||||
sendTime = System.currentTimeMillis() + minDelay + Math.abs(random.nextLong()) % Math.abs(maxDelay-minDelay);
|
||||
|
||||
String filename = sendTime + "_" + id.toBase32() + PACKET_FILE_EXTENSION;
|
||||
|
||||
File file = new File(storageDir, filename);
|
||||
if (!file.exists()) {
|
||||
add(packet, filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RelayDataPacket createFolderElement(File file) throws Exception {
|
||||
RelayDataPacket packet = super.createFolderElement(file);
|
||||
try {
|
||||
long sendTime = getSendTime(file.getName());
|
||||
packet.setSendTime(sendTime);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
log.error("Invalid send time in filename: <" + file.getAbsolutePath() + ">", e);
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private long getSendTime(String filename) throws NumberFormatException {
|
||||
String[] parts = filename.split("_");
|
||||
return Long.valueOf(parts[0]);
|
||||
}
|
||||
}
|
@@ -1,80 +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 net.i2p.data.Destination;
|
||||
|
||||
public class RelayPeer extends Destination {
|
||||
private long requestsSent;
|
||||
private long responsesReceived;
|
||||
|
||||
/**
|
||||
* Creates a new <code>RelayPeer</code> with the <code>requestsSent</code> and
|
||||
* <code>responsesReceived</code> number set to <code>0</code>.
|
||||
* @param destination
|
||||
*/
|
||||
public RelayPeer(Destination destination) {
|
||||
this(destination, 0, 0);
|
||||
}
|
||||
|
||||
public RelayPeer(Destination destination, long requestsSent, long responsesReceived) {
|
||||
// initialize the Destination part of the RelayPeer
|
||||
setCertificate(destination.getCertificate());
|
||||
setSigningPublicKey(destination.getSigningPublicKey());
|
||||
setPublicKey(destination.getPublicKey());
|
||||
|
||||
// initialize RelayPeer-specific fields
|
||||
this.requestsSent = requestsSent;
|
||||
this.responsesReceived = responsesReceived;
|
||||
}
|
||||
|
||||
public synchronized long getRequestsSent() {
|
||||
return requestsSent;
|
||||
}
|
||||
|
||||
public synchronized long getResponsesReceived() {
|
||||
return responsesReceived;
|
||||
}
|
||||
|
||||
/** Increments the number of requests made to this <code>RelayPeer</code> by one */
|
||||
public synchronized void requestSent() {
|
||||
requestsSent++;
|
||||
}
|
||||
|
||||
/** Increments the number of responses received from this <code>RelayPeer</code> by one */
|
||||
public synchronized void responseReceived() {
|
||||
responsesReceived++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the percentage of requests sent to this peer for which
|
||||
* a response was received.
|
||||
* If no request has been sent to the peer, <code>0</code> is returned.
|
||||
* @return
|
||||
*/
|
||||
public synchronized int getReachability() {
|
||||
if (requestsSent == 0)
|
||||
return 0;
|
||||
else
|
||||
return (int)(100L * responsesReceived / requestsSent);
|
||||
}
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
# Each line in this file should be a 516-byte I2P destination key
|
||||
# in Base64 format. Lines beginning with # are ignored.
|
||||
AHZwTj~5kmXod-2jFj1RamSFMBijtEnp9x3FWrF19uJQQga~rH4OaAtqdrHBeKfSBwugWEQMI8~shhBpk3H~fgQEd24RntmItdFzCsdeKz~0WnYqvhZo-ZryXk6J08wTPX9hX8BWuJSTgQyJo7WvDEqxx2AxQUIDyWVPld7VdfrpNEFWTQPNo0YxQWqSlnhSZr~hIVEV6hRsRgAP89Il8tnRa0f2YqZI7Ac2p8btVTkXMExVfLqZ2Y6J71K53oJ1mXZCISGUVAJhRWhdl~QKT8x-V5~5iSLoGG0514E4KOGc1ViKhl4YvhfCVXezm-oVDT8e9QTgy1pYjk9U88ng7hg~vjlCx8g2F3zTbZMaMZch4K50j7cRHFxvWA47EiEw~AtlEWhzTyQY2HMfQE-Hj7CJISdoaPk3ehvpkSDPdLWDSeQ~7rSYujWBAH~KI1DViRElS-1oJ3rPLqNTA2IwywbbaCs0LnAX9NrTnwIx4X6~kr7Re8DLVw5Gn-vjsrYjAAAA
|
||||
CYnzmb1axrzpDXEX9haV7qBT-jGxFmLmkV913DgPqgJ1lVcSfpIrTe4ETMsRRjd2JMvGJxBYznENH8oTlitVptNAe8HjXYxjPqCRkHeh3QCk9nZALTWxjGy90DUHytIytPwRYqIZlLneiePhVFgV-HXOne~Bsv-mT5zrfDIDNhkJlmyXq5vLcvTkqp-eDEMr6iC4njCNKjzHDxsTvwwecYSmzFDY2f0SMZhrmL3qKSofMVPwpEVwkBX7LF50TSOfPvIEVgX7uPKI~avIOrShksSdJ0ksDQkw5Q66tOp-yrgxh--FnQYnblfLMBhI5UyWuifV3HqGqctjjHfW2mEOVpIoarjgbdmN9knThRBlgC8gs7AffoYuKs~GbIsE4PpnJI2rS3skBfPoNcmBtUF2bfQZ6gb3zk8NH7alXWMciF3wXyLka2FBuQKQQz4fUuraVzQu0COncv0hpmV5CIO7HWTs8pdem6~r0NydoRF3wrGj8xDeySTVJfmDfXCKhi0GAAAA
|
@@ -1,207 +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 static i2p.bote.network.kademlia.KademliaConstants.REPLICATE_INTERVAL;
|
||||
import static i2p.bote.network.kademlia.KademliaConstants.REPLICATE_VARIANCE;
|
||||
import i2p.bote.network.DhtStorageHandler;
|
||||
import i2p.bote.network.I2PPacketDispatcher;
|
||||
import i2p.bote.network.I2PSendQueue;
|
||||
import i2p.bote.network.PacketListener;
|
||||
import i2p.bote.packet.CommunicationPacket;
|
||||
import i2p.bote.packet.DeleteRequest;
|
||||
import i2p.bote.packet.dht.DhtStorablePacket;
|
||||
import i2p.bote.packet.dht.StoreRequest;
|
||||
import i2p.bote.service.I2PBoteThread;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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 basic algorithm goes like this:
|
||||
*
|
||||
* 1. Do a lookup for the local node ID to refresh the s-bucket
|
||||
* 2. For each locally stored DHT item (index packet entries and email packets) that has not been deleted:
|
||||
* a. Store the entry on the k closest nodes, based on info from local buckets
|
||||
* b. If at least one peer responds with a valid delete request,
|
||||
* - delete it locally (this is already handled by code outside this class), and
|
||||
* - send a delete request to the nodes that didn't respond (which they
|
||||
* won't if they don't know the packet has been deleted)
|
||||
* c. Otherwise, replication for that entry is finished.
|
||||
*/
|
||||
public class ReplicateThread extends I2PBoteThread implements PacketListener {
|
||||
private static final int WAIT_TIME_SECONDS = 5; // amount of time to wait after sending <number of nodes> store requests
|
||||
|
||||
private final Log log = new Log(ReplicateThread.class);
|
||||
private Destination localDestination;
|
||||
private I2PSendQueue sendQueue;
|
||||
private I2PPacketDispatcher i2pReceiver;
|
||||
private BucketManager bucketManager;
|
||||
private CountDownLatch startSignal;
|
||||
private Random rng;
|
||||
private long nextReplicationTime;
|
||||
private Map<DhtStorageHandler, Set<Hash>> upToDateKeys; // all DHT keys that have been re-stored since the last replication
|
||||
private Map<Destination, DeleteRequest> receivedDeleteRequests; // null when not replicating
|
||||
|
||||
public ReplicateThread(Destination localDestination, I2PSendQueue sendQueue, I2PPacketDispatcher i2pReceiver, BucketManager bucketManager, CountDownLatch startSignal) {
|
||||
super("ReplicateThd");
|
||||
this.localDestination = localDestination;
|
||||
this.sendQueue = sendQueue;
|
||||
this.i2pReceiver = i2pReceiver;
|
||||
this.bucketManager = bucketManager;
|
||||
this.startSignal = startSignal;
|
||||
rng = new Random();
|
||||
upToDateKeys = new ConcurrentHashMap<DhtStorageHandler, Set<Hash>>();
|
||||
}
|
||||
|
||||
void addDhtStoreToReplicate(DhtStorageHandler dhtStore) {
|
||||
upToDateKeys.put(dhtStore, new ConcurrentHashSet<Hash>());
|
||||
}
|
||||
|
||||
private long randomTime(long min, long max) {
|
||||
if (min < max)
|
||||
return min + rng.nextLong() % (max-min);
|
||||
else
|
||||
return min;
|
||||
}
|
||||
|
||||
private void replicate() throws InterruptedException {
|
||||
log.debug("Replicating DHT data...");
|
||||
|
||||
// refresh peers close to the local dest (essentially a refresh of the s-bucket)
|
||||
ClosestNodesLookupTask lookupTask = new ClosestNodesLookupTask(localDestination.calculateHash(), sendQueue, i2pReceiver, bucketManager);
|
||||
lookupTask.run();
|
||||
List<Destination> closestNodes = lookupTask.getResults();
|
||||
closestNodes.remove(localDestination);
|
||||
|
||||
HashCash hashCash;
|
||||
try {
|
||||
hashCash = HashCash.mintCash("", 1); // TODO
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
log.error("Can't generate HashCash for replication requests, aborting replication.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
receivedDeleteRequests = new ConcurrentHashMap<Destination, DeleteRequest>();
|
||||
|
||||
// 3 possible ways of sending out the store requests:
|
||||
//
|
||||
// 1. Send the first store request to all k nodes, send the next store request to all k nodes, etc.
|
||||
// Downside: If the first node responds with a delete request, all other store requests are sent unnecessarily;
|
||||
// this can be fixed by waiting after each request, but replication will take much longer
|
||||
// (numNodes*numPackets*waitTime).
|
||||
// 2. Send all packets to the first node, wait for responses, send all packets to the second node, wait, etc.
|
||||
// Downside: Nodes are bombarded with packets
|
||||
// 3. Send packet 0 to node 0, send packet 1 to node 1, etc. until packet k-1 is sent to node k-1. Wait for
|
||||
// responses, then send packet 0 to node 1, send packet 1 to node 2, etc. until packet k-1 is sent to node k.
|
||||
// If the node index would exceed the number of nodes, subtract the number of nodes.
|
||||
// Repeat [number of nodes] times.
|
||||
// This method avoids the downsides of (1) and (2).
|
||||
//
|
||||
// For now, method (2) is used because it is easier to implement than (3).
|
||||
//
|
||||
for (Destination node: closestNodes) {
|
||||
for (DhtStorageHandler dhtStore: upToDateKeys.keySet()) {
|
||||
Iterator<? extends DhtStorablePacket> packetIterator = dhtStore.individualPackets();
|
||||
while (packetIterator.hasNext()) {
|
||||
DhtStorablePacket packet = packetIterator.next();
|
||||
StoreRequest request = new StoreRequest(hashCash, packet);
|
||||
sendQueue.send(request, node);
|
||||
}
|
||||
}
|
||||
TimeUnit.SECONDS.sleep(WAIT_TIME_SECONDS);
|
||||
|
||||
DeleteRequest delRequest = receivedDeleteRequests.get(node);
|
||||
if (delRequest != null)
|
||||
// send the delete request to all other nodes
|
||||
for (Destination otherNode: closestNodes)
|
||||
sendQueue.send(delRequest, otherNode);
|
||||
}
|
||||
|
||||
for (DhtStorageHandler dhtStore: upToDateKeys.keySet())
|
||||
upToDateKeys.get(dhtStore).clear();
|
||||
receivedDeleteRequests = null;
|
||||
|
||||
log.debug("Replication finished.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs the <code>ReplicationThread</code> that a packet has been stored in a local folder.
|
||||
* @param folder
|
||||
* @param packet
|
||||
*/
|
||||
public void packetStored(DhtStorageHandler folder, DhtStorablePacket packet) {
|
||||
Set<Hash> keys = upToDateKeys.get(folder);
|
||||
keys.add(packet.getDhtKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// wait for startSignal
|
||||
while (!shutdownRequested()) {
|
||||
try {
|
||||
if (startSignal.await(1, TimeUnit.SECONDS))
|
||||
break;
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Replication thread interrupted!", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Replicate on startup and every REPLICATE_INTERVAL seconds after that
|
||||
nextReplicationTime = System.currentTimeMillis();
|
||||
while (!shutdownRequested()) {
|
||||
try {
|
||||
replicate();
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted while replicating!", e);
|
||||
return;
|
||||
}
|
||||
long waitTime = randomTime(REPLICATE_INTERVAL-REPLICATE_VARIANCE, REPLICATE_INTERVAL+REPLICATE_VARIANCE);
|
||||
nextReplicationTime += waitTime;
|
||||
log.debug("Next replication at " + new Date(nextReplicationTime));
|
||||
awaitShutdownRequest(waitTime, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
// PacketListener implementation
|
||||
@Override
|
||||
public void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime) {
|
||||
if (packet instanceof DeleteRequest && receivedDeleteRequests!=null)
|
||||
receivedDeleteRequests.put(sender, (DeleteRequest)packet);
|
||||
}
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
package i2p.bote.packet;
|
||||
|
||||
public abstract class DeleteRequest extends CommunicationPacket {
|
||||
|
||||
protected DeleteRequest() {
|
||||
}
|
||||
|
||||
protected DeleteRequest(byte[] data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
public abstract Class<? extends I2PBotePacket> getDataType();
|
||||
}
|
@@ -1,130 +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.packet;
|
||||
|
||||
import i2p.bote.I2PBote;
|
||||
import i2p.bote.packet.dht.FindClosePeersPacket;
|
||||
import i2p.bote.packet.dht.RetrieveRequest;
|
||||
import i2p.bote.packet.dht.StoreRequest;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public abstract class I2PBotePacket {
|
||||
private static final int MAX_DATAGRAM_SIZE = 31 * 1024;
|
||||
private static final Log log = new Log(I2PBotePacket.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Class<? extends I2PBotePacket>[] ALL_PACKET_TYPES = new Class[] {
|
||||
RelayDataPacket.class, RelayRequest.class, ResponsePacket.class, RetrieveRequest.class, StoreRequest.class,
|
||||
FindClosePeersPacket.class, PeerListRequest.class, PeerList.class,
|
||||
EncryptedEmailPacket.class, UnencryptedEmailPacket.class, EmailPacketDeleteRequest.class,
|
||||
IndexPacket.class, IndexPacketDeleteRequest.class, DeletionInfoPacket.class
|
||||
};
|
||||
|
||||
private int protocolVersion;
|
||||
|
||||
/**
|
||||
* Creates a new <code>I2PBotePacket</code> with the current protocol version.
|
||||
*/
|
||||
protected I2PBotePacket() {
|
||||
protocolVersion = I2PBote.PROTOCOL_VERSION;
|
||||
}
|
||||
|
||||
protected I2PBotePacket(int protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public abstract byte[] toByteArray();
|
||||
|
||||
/**
|
||||
* Returns the size of the packet in bytes.
|
||||
* @return
|
||||
*/
|
||||
// TODO rename to getPacketSize
|
||||
// TODO override in subclasses to avoid calling toByteArray by adding field lengths
|
||||
public int getSize() {
|
||||
return toByteArray().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>false</code> if this packet can't fit into an I2P datagram.
|
||||
* @return
|
||||
*/
|
||||
public boolean isTooBig() {
|
||||
return getSize() > MAX_DATAGRAM_SIZE;
|
||||
}
|
||||
|
||||
protected char getPacketTypeCode(Class<? extends I2PBotePacket> dataType) {
|
||||
return dataType.getAnnotation(TypeCode.class).value();
|
||||
}
|
||||
|
||||
public char getPacketTypeCode() {
|
||||
return getPacketTypeCode(getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an error if the packet type of the packet instance is not correct
|
||||
* @param packetTypeCode
|
||||
*/
|
||||
protected void checkPacketType(char packetTypeCode) {
|
||||
if (getPacketTypeCode() != packetTypeCode)
|
||||
log.error("Packet type code of class " + getClass().getSimpleName() + " should be " + getPacketTypeCode() + ", is <" + packetTypeCode + ">");
|
||||
}
|
||||
|
||||
protected void checkPacketType(byte packetTypeCode) {
|
||||
checkPacketType((char)packetTypeCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the I2P-Bote network protocol used by this packet.
|
||||
* @return
|
||||
*/
|
||||
public int getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Hash} from bytes read from a {@link ByteBuffer}.
|
||||
* No check is done to make sure the buffer has enough bytes available.
|
||||
*/
|
||||
protected Hash readHash(ByteBuffer buffer) {
|
||||
byte[] bytes = new byte[Hash.HASH_LENGTH];
|
||||
buffer.get(bytes);
|
||||
return new Hash(bytes);
|
||||
}
|
||||
|
||||
protected static Class<? extends I2PBotePacket> decodePacketTypeCode(char packetTypeCode) {
|
||||
for (Class<? extends I2PBotePacket> packetType: ALL_PACKET_TYPES)
|
||||
if (packetType.getAnnotation(TypeCode.class).value() == packetTypeCode)
|
||||
return packetType;
|
||||
|
||||
log.debug("Invalid type code for I2PBotePacket: <" + packetTypeCode + ">");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Type=" + getClass().getSimpleName() + ", code=<" + getPacketTypeCode() + ">, sizeBytes=" + getSize();
|
||||
}
|
||||
}
|
@@ -1,191 +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.packet;
|
||||
|
||||
import i2p.bote.Util;
|
||||
import i2p.bote.service.RelayPeerManager;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* A <code>RelayDataPacket</code> contains a {@link RelayRequest},
|
||||
* an I2P destination to send it to, and a time window for the send time.
|
||||
*/
|
||||
@TypeCode('R')
|
||||
public class RelayDataPacket extends DataPacket {
|
||||
private Log log = new Log(RelayDataPacket.class);
|
||||
private long minDelay;
|
||||
private long maxDelay;
|
||||
private long sendTime;
|
||||
private Destination nextDestination;
|
||||
private RelayRequest request;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param nextDestination The I2P destination to send the packet to
|
||||
* @param minDelayMilliseconds In milliseconds
|
||||
* @param maxDelayMilliseconds In milliseconds
|
||||
* @param request
|
||||
*/
|
||||
public RelayDataPacket(Destination nextDestination, long minDelayMilliseconds, long maxDelayMilliseconds, RelayRequest request) {
|
||||
this.nextDestination = nextDestination;
|
||||
this.minDelay = minDelayMilliseconds;
|
||||
this.maxDelay = maxDelayMilliseconds;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public RelayDataPacket(byte[] data) throws DataFormatException, MalformedDataPacketException {
|
||||
super(data);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data, HEADER_LENGTH, data.length-HEADER_LENGTH);
|
||||
|
||||
minDelay = buffer.getInt() * 1000L;
|
||||
maxDelay = buffer.getInt() * 1000L;
|
||||
nextDestination = Util.createDestination(buffer);
|
||||
|
||||
int requestDataLength = buffer.getShort();
|
||||
byte[] requestBytes = new byte[requestDataLength];
|
||||
buffer.get(requestBytes);
|
||||
request = new RelayRequest(requestBytes);
|
||||
|
||||
if (buffer.hasRemaining())
|
||||
log.debug("Relay Packet has " + buffer.remaining() + " extra bytes.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <code>RelayDataPacket</code> containing <code>numHops</code> nested
|
||||
* <code>RelayRequest</code>s, each containing a <code>RelayDataPacket</code>.
|
||||
* Returns <code>null</code> if <code>numHops</code> is <code>0</code>.
|
||||
*
|
||||
* If <code>numHops = 1</code>, the finished packet looks as follows (outermost to innermost):
|
||||
*
|
||||
* Unencrypted RelayDataPacket
|
||||
* RelayRequest
|
||||
* Encrypted DataPacket
|
||||
*
|
||||
* For <code>numHops = 2</code>:
|
||||
*
|
||||
* Unencrypted RelayDataPacket
|
||||
* RelayRequest
|
||||
* Encrypted RelayDataPacket
|
||||
* RelayRequest
|
||||
* Encrypted DataPacket
|
||||
*
|
||||
* Each additional hop adds an encrypted <code>RelayDataPacket</code> and a <code>RelayRequest</code>.
|
||||
* @param payload
|
||||
* @param peerManager
|
||||
* @param numHops
|
||||
* @param minDelayMilliseconds
|
||||
* @param maxDelayMilliseconds
|
||||
* @return
|
||||
*/
|
||||
public static RelayDataPacket create(DataPacket payload, RelayPeerManager peerManager, int numHops, long minDelayMilliseconds, long maxDelayMilliseconds) {
|
||||
List<Destination> relayPeers = peerManager.getRandomPeers(numHops);
|
||||
|
||||
Log log = new Log(RelayDataPacket.class);
|
||||
if (log.shouldLog(Log.DEBUG)) {
|
||||
StringBuilder debugMsg = new StringBuilder("Creating relay chain: [");
|
||||
for (int i=relayPeers.size()-1; i>=0; i--) {
|
||||
debugMsg.append(relayPeers.get(i).calculateHash().toBase64().substring(0, 8));
|
||||
if (i > 0)
|
||||
debugMsg.append("... --> ");
|
||||
}
|
||||
debugMsg.append("...]");
|
||||
log.debug(debugMsg.toString());
|
||||
}
|
||||
|
||||
DataPacket dataPacket = payload;
|
||||
for (Iterator<Destination> iterator=relayPeers.iterator(); iterator.hasNext();) {
|
||||
Destination relayPeer = iterator.next();
|
||||
RelayRequest request = new RelayRequest(dataPacket, relayPeer);
|
||||
dataPacket = new RelayDataPacket(relayPeer, minDelayMilliseconds, maxDelayMilliseconds, request);
|
||||
}
|
||||
return (RelayDataPacket)dataPacket;
|
||||
}
|
||||
|
||||
public Destination getNextDestination() {
|
||||
return nextDestination;
|
||||
}
|
||||
|
||||
/** Returns the minimum delay time for this packet in milliseconds */
|
||||
public long getMinimumDelay() {
|
||||
return minDelay;
|
||||
}
|
||||
|
||||
/** Returns the maximum delay time for this packet in milliseconds */
|
||||
public long getMaximumDelay() {
|
||||
return maxDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param The time the packet is scheduled for sending, in milliseconds since 1-1-1970
|
||||
*/
|
||||
public void setSendTime(long sendTime) {
|
||||
this.sendTime = sendTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The time the packet is scheduled for sending, in milliseconds since 1-1-1970
|
||||
*/
|
||||
public long getSendTime() {
|
||||
return sendTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>RelayRequest</code> this packet contains.
|
||||
* @return
|
||||
*/
|
||||
public RelayRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
|
||||
DataOutputStream dataStream = new DataOutputStream(arrayOutputStream);
|
||||
|
||||
try {
|
||||
writeHeader(dataStream);
|
||||
dataStream.writeInt((int)(minDelay/1000));
|
||||
dataStream.writeInt((int)(maxDelay/1000));
|
||||
// write the first 384 bytes (the two public keys)
|
||||
dataStream.write(nextDestination.toByteArray(), 0, 384);
|
||||
byte[] requestBytes = request.toByteArray();
|
||||
dataStream.writeShort(requestBytes.length);
|
||||
dataStream.write(requestBytes);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Can't write to ByteArrayOutputStream.", e);
|
||||
}
|
||||
|
||||
return arrayOutputStream.toByteArray();
|
||||
}
|
||||
}
|
@@ -1,140 +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.packet;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import com.nettgryppa.security.HashCash;
|
||||
|
||||
/**
|
||||
* A <code>RelayRequest</code> contains an encrypted {@link RelayDataPacket} or {@link DhtStorablePacket}.
|
||||
*/
|
||||
@TypeCode('Y')
|
||||
public class RelayRequest extends CommunicationPacket {
|
||||
private static final int PADDED_SIZE = 16; // pad to the length of an AES-256 block (not to be confused with the AES key size)
|
||||
|
||||
private Log log = new Log(RelayRequest.class);
|
||||
private HashCash hashCash;
|
||||
private byte[] payload; // a DataPacket
|
||||
|
||||
/**
|
||||
* Creates a <code>RelayRequest</code> that contains an encrypted <code>DataPacket</code>.
|
||||
* @param payload
|
||||
* @param destination
|
||||
*/
|
||||
public RelayRequest(DataPacket payload, Destination destination) {
|
||||
try {
|
||||
hashCash = HashCash.mintCash("", 1); // TODO
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
log.error("Cannot create HashCash.", e);
|
||||
}
|
||||
this.payload = encrypt(payload, destination);
|
||||
}
|
||||
|
||||
public RelayRequest(byte[] data) throws MalformedDataPacketException {
|
||||
super(data);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data, HEADER_LENGTH, data.length-HEADER_LENGTH);
|
||||
|
||||
int hashCashLength = buffer.getShort();
|
||||
byte[] hashCashData = new byte[hashCashLength];
|
||||
buffer.get(hashCashData);
|
||||
try {
|
||||
hashCash = new HashCash(new String(hashCashData));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
log.error("Cannot create HashCash.", e);
|
||||
}
|
||||
|
||||
int payloadLength = buffer.getShort();
|
||||
payload = new byte[payloadLength];
|
||||
buffer.get(payload);
|
||||
|
||||
if (buffer.hasRemaining())
|
||||
log.debug("Storage Request Packet has " + buffer.remaining() + " extra bytes.");
|
||||
}
|
||||
|
||||
public HashCash getHashCash() {
|
||||
return hashCash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the payload packet, i.e. the data that is being relayed.
|
||||
* @return
|
||||
* @throws DataFormatException
|
||||
* @throws MalformedDataPacketException
|
||||
*/
|
||||
public DataPacket getStoredPacket(I2PSession i2pSession) throws DataFormatException, MalformedDataPacketException {
|
||||
return decrypt(i2pSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
|
||||
DataOutputStream dataStream = new DataOutputStream(byteArrayStream);
|
||||
|
||||
try {
|
||||
writeHeader(dataStream);
|
||||
String hashCashString = hashCash.toString();
|
||||
dataStream.writeShort(hashCashString.length());
|
||||
dataStream.write(hashCashString.getBytes());
|
||||
|
||||
dataStream.writeShort(payload.length);
|
||||
dataStream.write(payload);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Can't write to ByteArrayOutputStream.", e);
|
||||
}
|
||||
return byteArrayStream.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] encrypt(DataPacket dataPacket, Destination destination) {
|
||||
PublicKey publicKey = destination.getPublicKey();
|
||||
I2PAppContext appContext = I2PAppContext.getGlobalContext();
|
||||
SessionKey sessionKey = appContext.sessionKeyManager().createSession(publicKey);
|
||||
byte[] data = dataPacket.toByteArray();
|
||||
return appContext.elGamalAESEngine().encrypt(data, publicKey, sessionKey, PADDED_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the <code>RelayDataPacket</code> inside this packet.
|
||||
* @throws DataFormatException
|
||||
* @throws MalformedDataPacketException
|
||||
*/
|
||||
private DataPacket decrypt(I2PSession i2pSession) throws DataFormatException, MalformedDataPacketException {
|
||||
PrivateKey privateKey = i2pSession.getDecryptionKey();
|
||||
I2PAppContext appContext = I2PAppContext.getGlobalContext();
|
||||
byte[] decryptedData = appContext.elGamalAESEngine().decrypt(payload, privateKey, appContext.sessionKeyManager());
|
||||
return DataPacket.createPacket(decryptedData);
|
||||
}
|
||||
}
|