Compare commits
845 Commits
i2p-0.9.10
...
i2p-0.9.17
Author | SHA1 | Date | |
---|---|---|---|
7ab6708a3c | |||
7010d9b524 | |||
947a3a2181 | |||
0ff87ef8cb | |||
ec20150ffd | |||
30876a9cd3 | |||
be8832e87f | |||
5999690665 | |||
285fa6cbc9 | |||
9700f30c35 | |||
a38bd0b5cf | |||
5f2b620819 | |||
fd47cb88de | |||
77e7982e74 | |||
04cd1cedda | |||
3ef89f49e7 | |||
2a681608b5 | |||
a52c06a6c6 | |||
49b8a65ad9 | |||
9781cb72ac | |||
f7e83fb839 | |||
ce2a2cf684 | |||
c88fa70f82 | |||
f76744a0c0 | |||
15137d9b62 | |||
279e102d7a | |||
7f72830ec8 | |||
2caaad95ec | |||
09b995aca6 | |||
a0bf8433e4 | |||
9104bd7304 | |||
2f2aa7f5a8 | |||
0773a30578 | |||
962f5efe6b | |||
6dc3cd9650 | |||
397ae536f9 | |||
f19ec4bd44 | |||
fd7e549915 | |||
7a7ae77c83 | |||
1a9fb381ed | |||
ae7bfceafb | |||
a961843aa6 | |||
43c6a4ddac | |||
ae1d5648d5 | |||
2d3e8e0c4e | |||
4691fc69d5 | |||
cb87f9f307 | |||
5f1e5bc271 | |||
1c6d5ad2db | |||
555189f123 | |||
049044b827 | |||
cfd9e2c3ab | |||
a0b457b9a1 | |||
23f24c7d39 | |||
6112cc5a38 | |||
7deb8c1bcb | |||
76e4b49d9d | |||
5ae267a8a2 | |||
f524351041 | |||
893d1bb45f | |||
945988dfb7 | |||
62698664a2 | |||
0d2892c75d | |||
ecc72e6825 | |||
531d520ceb | |||
4e72e150ad | |||
b28628b8e1 | |||
702830ad0e | |||
6ca0c54ba7 | |||
634bf5f7e3 | |||
2284c963af | |||
ad2052395f | |||
e9a1dbf8f1 | |||
703b21e89b | |||
36ea2cca6b | |||
8b2cf770a5 | |||
7d6d801943 | |||
d705b43f3a | |||
a18ed194cb | |||
a3e4293fd8 | |||
df144d8434 | |||
bab1e05235 | |||
a1fdd41b0e | |||
60d9c1651a | |||
ec1380dfa1 | |||
798275608e | |||
0f2affd414 | |||
7695b51d89 | |||
fb99122d83 | |||
6d53838e20 | |||
ec3fd9a7d7 | |||
304f2ebb7b | |||
4976e84488 | |||
2ebacb1b9b | |||
d085f9ea66 | |||
e275117569 | |||
eae277fb77 | |||
d7130c15cc | |||
937a17c5dd | |||
b6234e1d5e | |||
7955b8ae71 | |||
a36ef62358 | |||
bcbda3cd27 | |||
239fe518a9 | |||
44d6e117d5 | |||
8a12b7cb41 | |||
4d4308c486 | |||
abcdcf2e8c | |||
44b753d1e5 | |||
83b3f242a9 | |||
86c43f4734 | |||
be0cb84f97 | |||
3bea7f5ad5 | |||
266a20d55e | |||
d2c6a80d24 | |||
cd51fbc2a6 | |||
73256f6030 | |||
e081f94d9f | |||
e96cc09d75 | |||
d87178fec3 | |||
28ad95f892 | |||
8270a92a44 | |||
088290c544 | |||
6685acfef4 | |||
1d1a05ee7b | |||
bbeb429a59 | |||
55af588c2c | |||
80d0313fe5 | |||
853d309960 | |||
564400597a | |||
1c2b6fc00e | |||
7b6f32e5b2 | |||
dd4acc88a1 | |||
6e566f6e3d | |||
fff7fbe121 | |||
a50afeb5d5 | |||
49eeb99d43 | |||
bfd51097c9 | |||
a21e3cd842 | |||
0f298cf48e | |||
4b074b8dcf | |||
2c79853ffe | |||
72f57255f0 | |||
1053bc8bb0 | |||
4835e6fcb9 | |||
7ec02a1620 | |||
10993cc6ef | |||
81409369bc | |||
edb8590da8 | |||
3a7bfd28fc | |||
36fdb4ee2f | |||
b06f772647 | |||
012cc740d6 | |||
1407cff49d | |||
3331e1c152 | |||
591f48856d | |||
479b9691fd | |||
0e48557b48 | |||
3fae6f06dd | |||
7639c24bfe | |||
39fd1c3ab8 | |||
abf9dbae6d | |||
98062f830a | |||
c259347917 | |||
9c4558d891 | |||
9e7e2948e3 | |||
43430da25f | |||
eca7ac21a0 | |||
bc463f6d0b | |||
ec2708a1fd | |||
082922de01 | |||
ea02a7c70b | |||
ab7e25bd52 | |||
d4876dd25e | |||
89ee0bbab4 | |||
06ae882064 | |||
6517fe7515 | |||
d510aad2ab | |||
3db297de95 | |||
8688f26f15 | |||
85d38e7af2 | |||
0448348154 | |||
ceab4f1ffc | |||
3781b8db09 | |||
7a450c526c | |||
c1e8ea0e4a | |||
f248a33eaa | |||
67fb4e7007 | |||
0a41052f3f | |||
a7763a08dc | |||
fcfb471a8a | |||
b9e383130e | |||
cd2159b873 | |||
e492d5e0cf | |||
9a0f6490ba | |||
5183b44d8b | |||
156d86835a | |||
eab4397b0f | |||
d808b999c6 | |||
603b345405 | |||
682534f468 | |||
42eb43f713 | |||
0817b58b9d | |||
9ab766375d | |||
6c2799fe53 | |||
ef81a575cd | |||
cce0d94fbb | |||
22b5203334 | |||
d4be5abe67 | |||
9985a02efc | |||
41c2c60ab0 | |||
f285364f46 | |||
1c5e9b7fe3 | |||
12cc501e25 | |||
49118b8bf1 | |||
09dfea7dea | |||
00bd469f8e | |||
18e7e56a6c | |||
34d14a720b | |||
c4d7f9924f | |||
80d6921a66 | |||
3c95144b83 | |||
84ad155ab8 | |||
330a5ddd0f | |||
3b2f1d35c4 | |||
0f1036b0e1 | |||
86935f10a8 | |||
1078c42a14 | |||
09cf973712 | |||
5af749a226 | |||
f84b86a752 | |||
ca7873eda7 | |||
f87ebaf214 | |||
a9802eb6a7 | |||
5d5a68cb3e | |||
c6b1f5053f | |||
1d2e01c8cd | |||
0c5c18a767 | |||
6826ba05e7 | |||
053ce88743 | |||
4a216c57d4 | |||
03cec7fd5a | |||
1238001add | |||
fa1c077fdd | |||
8a7c3390f5 | |||
2302aee819 | |||
a72866ee6a | |||
0f7a3dba87 | |||
5decf18eb5 | |||
c318760398 | |||
629eff20dd | |||
f6e508ca14 | |||
588ab86abb | |||
387629372b | |||
0a01700e3e | |||
59504deb7f | |||
8ee660c238 | |||
176c106427 | |||
ed4fe56e7e | |||
310cd54aa0 | |||
3217b4ac90 | |||
7bf1949061 | |||
51f9d6d421 | |||
ddb32c65fb | |||
c5c158e983 | |||
f83007e038 | |||
2b9a368b18 | |||
6ad6974452 | |||
308923448b | |||
04ad7de2e1 | |||
54563b0b42 | |||
593779b54f | |||
34d3704680 | |||
613f90bcf7 | |||
6ff500f7cb | |||
c79e33896e | |||
68b15aadca | |||
819504f08f | |||
e65ec2a589 | |||
1bc355b8fd | |||
d76164679f | |||
efebecfc67 | |||
7b64586c87 | |||
0fe15b8e1d | |||
a1cb00b5a3 | |||
5041d819a9 | |||
02ab6eac62 | |||
d7feab116f | |||
4f9e13d0f6 | |||
d0b0e6a58e | |||
a12f898096 | |||
c921ecca05 | |||
975378b224 | |||
915e003355 | |||
51e45d128a | |||
57650ef058 | |||
dee6e16e6c | |||
c860674613 | |||
33b7f08d5c | |||
66bbe21a87 | |||
51995cc428 | |||
a3e3a305ce | |||
15facc72b3 | |||
3839c8d1c0 | |||
d5edcbc6e1 | |||
eb97ef4cb2 | |||
9c38e1e191 | |||
7c3d3b4128 | |||
367cea4b1f | |||
a63bfeaeec | |||
41672dde64 | |||
3b18cb7eca | |||
c9ce1751c1 | |||
4ba40b340a | |||
e3be1d1a04 | |||
6fa2a416be | |||
fdb54c315b | |||
c7de4e46c1 | |||
22a7757461 | |||
0bacbbc553 | |||
fbdc535287 | |||
03d8314842 | |||
fe4d98f0df | |||
b1d60122a3 | |||
64ec9f6a00 | |||
2d6f71dc12 | |||
c9e20c5d23 | |||
381f494754 | |||
506419964b | |||
35bb8c5348 | |||
79fe799aeb | |||
2878a6487e | |||
d4722e0d2c | |||
9655e79d26 | |||
d1a2e24f0e | |||
086381d958 | |||
7187f6f714 | |||
e10e05166f | |||
b0f8d84a7f | |||
0e9ceba057 | |||
fe3059f0ab | |||
bd566f52cf | |||
b7e0dabe61 | |||
2d2348f671 | |||
b28eb708a4 | |||
bf9c4b2346 | |||
d33aa097fe | |||
8673c232b6 | |||
d3ea5d2122 | |||
370d2555c7 | |||
5332cee3e8 | |||
1246e1c498 | |||
d6b0b1b93c | |||
1e0c970c95 | |||
db9f49c7d4 | |||
1603353ae8 | |||
6753d23309 | |||
ca5755b0fd | |||
2c8223274d | |||
f0dd09cf9c | |||
4746d9eb80 | |||
99401c5639 | |||
58578d9020 | |||
af575d6c95 | |||
e9c8748c0b | |||
08409d016b | |||
42bfbfc60b | |||
c7c087d964 | |||
89764c12e7 | |||
bd45d5483f | |||
328d7d0008 | |||
cca5bef8c1 | |||
ce4874d825 | |||
9b408b67ef | |||
c3bf100082 | |||
b282ccd890 | |||
f38b741813 | |||
3a899d52d1 | |||
a2567b0ee2 | |||
4b0019c732 | |||
5d21738410 | |||
df81006b42 | |||
2c7006e9bd | |||
b1caa8d5a3 | |||
8b2ffada10 | |||
0998738e94 | |||
c04062bbdf | |||
0c7a3a3a39 | |||
f364a83f4f | |||
9dabc75866 | |||
2c185ea76c | |||
39e859c368 | |||
2cfe5e678a | |||
d48991f71f | |||
dfbe3c4eb1 | |||
b8170a544b | |||
4e7f92ec89 | |||
292683268b | |||
dc14abd4d3 | |||
dd782f08f7 | |||
d57dc9a8a2 | |||
4e463d57ce | |||
e0c0cc8b63 | |||
cc50d47376 | |||
4da7548caa | |||
91a676cb36 | |||
48a32fb3b8 | |||
845b45a57d | |||
7b7a620999 | |||
82217d5ebc | |||
b95ec70d7d | |||
aa3d3670a4 | |||
8198c83982 | |||
75ff7987b8 | |||
9c87685c02 | |||
ce2bb85440 | |||
43fdff2292 | |||
06525adf3d | |||
cff4210dfd | |||
d855c5de50 | |||
f1a738340f | |||
9827c48527 | |||
367d68e552 | |||
1498ed361e | |||
91bc16ce05 | |||
861a1e26d7 | |||
a5b2f9a5e9 | |||
9550484037 | |||
b33284bb8e | |||
ce2694e8fb | |||
44073732e2 | |||
fef591412e | |||
f191e50b14 | |||
3379432e5f | |||
bb9129b61b | |||
0fc3029aaa | |||
d8c8586ccf | |||
38a4728283 | |||
7888705b01 | |||
31938f49d6 | |||
c95ed2ea96 | |||
b5ed247a53 | |||
22aff49747 | |||
e4430f05e4 | |||
1047691c64 | |||
f3180b3f6f | |||
616866cc9e | |||
58512b8230 | |||
ca4555c496 | |||
bc99bc7206 | |||
8f2dc67430 | |||
1420c773a6 | |||
888ef37808 | |||
690b40ed77 | |||
986de4c1d6 | |||
01da32364f | |||
8b1abc08db | |||
69e56f8f6b | |||
b611d0238a | |||
c987a9735d | |||
3b9549c2c1 | |||
2dcc9b7a1e | |||
3e54b5d544 | |||
8845ce6e1c | |||
ff189e796c | |||
89c07ac969 | |||
a8e878f894 | |||
1f8f3eb4d9 | |||
8fd2a05bf9 | |||
002d057c92 | |||
ab44488e4c | |||
653ffbc82e | |||
95fd0291e3 | |||
2a269ff1a9 | |||
83ccfb4596 | |||
e968828916 | |||
ed85a2b82b | |||
e692e21dc9 | |||
662fe3ebc2 | |||
1bf8fd92e4 | |||
4dd8a6421a | |||
884b285bf5 | |||
cb340152df | |||
299a44e7eb | |||
40e5bcbdbb | |||
d328e78727 | |||
1bcb9b24b6 | |||
3c1c130bf0 | |||
df3442563c | |||
331b1fa742 | |||
b97a53177e | |||
633b71ba19 | |||
f3dd42143d | |||
7c79f5d5e5 | |||
af5c0bd8a7 | |||
c07bfe34ab | |||
49681415e2 | |||
190e8c01b7 | |||
6ae86f7d81 | |||
0aeb3ca75e | |||
1d3e12abb7 | |||
5e8428ef6c | |||
19e3064529 | |||
f9dbd74ad8 | |||
ff837cf66e | |||
e0914c358e | |||
0e9bb23c7b | |||
7ff5d36f07 | |||
5a3eab0c7c | |||
c28f707f55 | |||
ef96c88719 | |||
faa2435e33 | |||
0537a221d3 | |||
99c5a1978f | |||
d106f483a1 | |||
dee84e70ae | |||
09995b77b4 | |||
06894f9f0b | |||
73943b1a08 | |||
b573dab05f | |||
a766eca283 | |||
a65edbef92 | |||
7479aa235e | |||
4167cd955b | |||
d1bd893a7b | |||
2467856011 | |||
d32b4e9f24 | |||
1acd5caaa8 | |||
f69b757305 | |||
d2db41bc89 | |||
f3b4377ee1 | |||
551a8091a9 | |||
f994590ad7 | |||
8371b8f806 | |||
5d04f8db89 | |||
06de347373 | |||
2bf2eb482e | |||
a93666cd36 | |||
dbb7eb3d88 | |||
39169f0450 | |||
df71308664 | |||
e393f82eb7 | |||
8480a204ea | |||
197be5f60f | |||
5621e9b390 | |||
485d785e0b | |||
8d71d496be | |||
738bae46d2 | |||
d519228efb | |||
72c404c4d4 | |||
d2e3547a2e | |||
8d9790fd77 | |||
cd91a6b2a4 | |||
e165c1805d | |||
292b0a81c0 | |||
b9e9c07d95 | |||
837bf9eafe | |||
cfdbef05c5 | |||
47b10e9771 | |||
1b5a2ddef3 | |||
c161649ed5 | |||
c9b5c03e1b | |||
c4c04d7ec5 | |||
b4e03fa969 | |||
1cdcf1cb0a | |||
56b6992ca8 | |||
2beaea4a86 | |||
2dc97b160a | |||
a014918c0d | |||
fb9a4eb87a | |||
cd83c48526 | |||
5b2766ddfb | |||
f912b01137 | |||
b0db4e4fff | |||
649f76fb06 | |||
91408cbdce | |||
97c1ba2d02 | |||
284802bfa5 | |||
48b6e0693e | |||
67ea2f3717 | |||
e9e535cb94 | |||
7822b5c3ac | |||
767bd05ce1 | |||
36ebe19cd7 | |||
943ea957a2 | |||
04a3673366 | |||
1dfbe73b73 | |||
87889bb322 | |||
aa0616d7c5 | |||
611ff6357e | |||
91d7a0ab98 | |||
f5661da595 | |||
d867f9f36e | |||
55d92fc9f2 | |||
2e2d3c39e6 | |||
3cd01acb73 | |||
02c0ddb3d3 | |||
ce397f5858 | |||
3f56ce206d | |||
8a2308b411 | |||
04cabf40b5 | |||
4e0c4f6f98 | |||
75bd235eb2 | |||
05236b093a | |||
260ebe512c | |||
c2dab16c8c | |||
945d455f33 | |||
c8f8f6ff34 | |||
0d4f597a59 | |||
775047fbc2 | |||
1e4b43314c | |||
b365817c99 | |||
bbb04774d1 | |||
1823e5e641 | |||
4d2dc1c8e8 | |||
6986f90bf8 | |||
b43ebd2486 | |||
611f991fdd | |||
7bf3ea5200 | |||
490727b401 | |||
49f4f3398d | |||
b84682fdc9 | |||
b9491b269b | |||
b70cbb28b2 | |||
673c14287a | |||
b4a0ffdbbd | |||
3b2e5bded2 | |||
67eb3cc140 | |||
5a683149ab | |||
b75ad1ca5a | |||
552ab31559 | |||
4abfde4047 | |||
6ecfedba37 | |||
43883a90d2 | |||
3930113f00 | |||
029198c213 | |||
493788f4f8 | |||
028776de88 | |||
705de68aa3 | |||
eb96a74e32 | |||
614f34c6b4 | |||
f77a3c7f56 | |||
6de81d41d2 | |||
7ac9dc5542 | |||
2195c2fe98 | |||
3f35e927dd | |||
5ec659513b | |||
1039a4b7a0 | |||
88899c1233 | |||
d429514a3a | |||
b2c6fcbb73 | |||
3b1e030b39 | |||
e097a1caeb | |||
d6b09f8bab | |||
6d46344171 | |||
24e807b238 | |||
fdf6f5d51f | |||
4b938a02e7 | |||
44a5740a04 | |||
8d73b2e838 | |||
e1fc6893b4 | |||
7487ab8848 | |||
e675416b8c | |||
24a133fe67 | |||
0570feda6f | |||
b206665c72 | |||
8a6fb132f5 | |||
6992ca8b98 | |||
fd916a7646 | |||
90cd68900e | |||
3f865edb4f | |||
2e8681de2c | |||
2d85b98c20 | |||
d28d6efb79 | |||
43d84a5f07 | |||
e4d57f62bb | |||
a974268e7b | |||
1695af7011 | |||
682c4cd0b8 | |||
0f6d039391 | |||
d6233a8798 | |||
4f12e81dbb | |||
ab612d0088 | |||
3fa7bb9dc5 | |||
22b3d4d70b | |||
bd6c588c74 | |||
6c202e8f1d | |||
24e6750529 | |||
af7ce8e18e | |||
c73f0eeeb5 | |||
c68769cf7f | |||
3e639a319d | |||
1bbb79f5b1 | |||
84e6991374 | |||
5d1796bb6f | |||
bfba732f76 | |||
738c5ed14e | |||
beed080390 | |||
3624d66c12 | |||
2cca2781fd | |||
31d485299c | |||
a39f667c2f | |||
5283fc923e | |||
c57552f4e9 | |||
96b4c6b219 | |||
51911bd9a8 | |||
1f5926e4e9 | |||
d6a02a13ad | |||
e282491798 | |||
2b0dfed012 | |||
9d80aff977 | |||
a0724dc009 | |||
8c820bb237 | |||
3fdc964eac | |||
597662d0dc | |||
17c80c29e6 | |||
5d0bfc63fa | |||
0c449f8b8e | |||
36b6baa33e | |||
2c049878c6 | |||
81c58c1796 | |||
36a3edf612 | |||
4b6fd3d387 | |||
f777696e14 | |||
c9c181c14a | |||
a62b7a4374 | |||
9d7a9c9895 | |||
5d6a1c5e35 | |||
c48266fdc4 | |||
895d54d36d | |||
ba0e1a3aa9 | |||
6ec665db50 | |||
7f4c52cf42 | |||
37728e38c9 | |||
18b4a2427b | |||
3102970540 | |||
c679091afd | |||
91cdf85772 | |||
aab8b10adf | |||
5bcfe1ec72 | |||
4209c291ba | |||
7c5dc7fa55 | |||
c6dfb8744a | |||
6e0ca92041 | |||
2f7eb56790 | |||
8c98ef7328 | |||
45997fd1d5 | |||
6a3e5ec620 | |||
18cbf3d253 | |||
4df6a6f47b | |||
5542406f3d | |||
a9fceae181 | |||
c79ff0dc09 | |||
e2fc5c6957 | |||
5667a6647f | |||
b70d616083 | |||
75fa2b1809 | |||
66d9017d58 | |||
53efb7119a | |||
8b946bb56b | |||
8ed34e3edf | |||
d8fef53aef | |||
6af82f2a9a | |||
36b2547ca4 | |||
72e96cdd23 | |||
a2ba9bbdb1 | |||
f6d9a6917f | |||
2e91890401 | |||
1956068698 | |||
855cae0a45 | |||
ef3a12f01a | |||
37bf750ab9 | |||
090a790a9d | |||
8ef3bb3d82 | |||
388019249d | |||
91d1364832 | |||
2ec1d8484f | |||
9a01fdf57c | |||
deec84713f | |||
0d028122a6 | |||
4998f86efe | |||
839bd51bc8 | |||
936f2bb317 | |||
0cd774273c | |||
fe391ff29f | |||
001b127258 | |||
7c00a5165f | |||
e1c3e2c1c7 | |||
e9b3577eec | |||
3622501471 | |||
b7207fd29f | |||
4dc1241d2f | |||
c59603d31b | |||
3ab149a399 | |||
99f28519fb | |||
05aa88b4e8 | |||
887f953efb | |||
5e16c42e4e | |||
2cea7cdb30 | |||
e60da8e8ff | |||
be12995753 | |||
c30419107f | |||
2cacded182 | |||
ec22a1dafc | |||
434bf13be9 | |||
236df32f30 | |||
28575dbdae | |||
5b9d669d79 | |||
b2f4fde7e5 | |||
9eefe1e935 | |||
b91f041ad7 | |||
ba96f72899 | |||
5d322245d8 | |||
47712a39ac | |||
6b16907e40 | |||
18146daad8 | |||
0c326f989e | |||
e0a499dd0f | |||
0f862124fe | |||
d00be4ceee | |||
ec8354860e | |||
f9144f2fbf | |||
5d2ff5e648 | |||
ce475d2cd6 | |||
72bd1fe91b | |||
747d833392 | |||
3427464de6 | |||
9ca625a64e | |||
9e87fd9b13 | |||
576984badc | |||
c20c697126 | |||
b16e66d39a | |||
0bc6c23ac9 | |||
17e63b054c | |||
0fae0640d6 | |||
d054e12952 | |||
fba209ca7d | |||
41e071efe5 | |||
e8e239616f | |||
5842e25205 | |||
a2e7fa8b7b | |||
30ccf1b334 | |||
5219791673 | |||
cc97a19d3c | |||
01b153488a |
48
.gitignore
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
# Just to try and prevent some noob disasters.
|
||||
# Use git add -f foo.jar to ignore this ignore list
|
||||
|
||||
# generated release files
|
||||
*.exe
|
||||
*.[gx]z
|
||||
*.bz2
|
||||
*.[rwjt]ar
|
||||
*.sig
|
||||
*.su[d23]
|
||||
*.deb
|
||||
*.zip
|
||||
*.torrent
|
||||
|
||||
*~
|
||||
web-fragment.xml
|
||||
web-out.xml
|
||||
*.out
|
||||
|
||||
# Temporary/build dirs
|
||||
build/
|
||||
pkg-temp/
|
||||
classes/
|
||||
dist/
|
||||
/installer/resources/locale/mo
|
||||
/tmp
|
||||
/apps/jetty/jettylib
|
||||
*_jsp.java
|
||||
*.class
|
||||
|
||||
# Debian-related
|
||||
/debian/copyright
|
||||
/debian/changelog
|
||||
.pc/
|
||||
|
||||
# Build property overrides
|
||||
/override.properties
|
||||
|
||||
# Reporting
|
||||
*.fba
|
||||
sloccount.sc
|
||||
/reports/
|
||||
|
||||
# Don't allow patches
|
||||
*.(diff|patch)
|
||||
|
||||
# but allow debian/patches
|
||||
!/debian/patches/*.(patch|diff)
|
@ -16,10 +16,12 @@ _jsp\.java$
|
||||
\.sig$
|
||||
\.sud$
|
||||
\.su2$
|
||||
.\su3$
|
||||
\.tar$
|
||||
\.war$
|
||||
.\deb$
|
||||
\.zip$
|
||||
\.torrent$
|
||||
^\.
|
||||
~$
|
||||
web-fragment.xml
|
||||
@ -31,13 +33,14 @@ web-out.xml
|
||||
/build
|
||||
/classes
|
||||
/dist
|
||||
/mo
|
||||
^installer/resources/locale/mo
|
||||
/tmp
|
||||
^apps/jetty/jettylib
|
||||
|
||||
# Debian-related
|
||||
^debian/copyright
|
||||
^debian/changelog
|
||||
^.pc/
|
||||
|
||||
# Build property overrides
|
||||
override.properties
|
||||
|
62
.tx/config
@ -12,10 +12,13 @@ trans.it = apps/i2ptunnel/locale/messages_it.po
|
||||
trans.ja = apps/i2ptunnel/locale/messages_ja.po
|
||||
trans.nb = apps/i2ptunnel/locale/messages_nb.po
|
||||
trans.nl = apps/i2ptunnel/locale/messages_nl.po
|
||||
trans.nn = apps/i2ptunnel/locale/messages_nn.po
|
||||
trans.pl = apps/i2ptunnel/locale/messages_pl.po
|
||||
trans.pt = apps/i2ptunnel/locale/messages_pt.po
|
||||
trans.pt_BR = apps/i2ptunnel/locale/messages_pt_BR.po
|
||||
trans.ro = apps/i2ptunnel/locale/messages_ro.po
|
||||
trans.ru_RU = apps/i2ptunnel/locale/messages_ru.po
|
||||
trans.sk = apps/i2ptunnel/locale/messages_sk.po
|
||||
trans.sv_SE = apps/i2ptunnel/locale/messages_sv.po
|
||||
trans.uk_UA = apps/i2ptunnel/locale/messages_uk.po
|
||||
trans.vi = apps/i2ptunnel/locale/messages_vi.po
|
||||
@ -38,6 +41,7 @@ trans.pt = apps/i2ptunnel/locale-proxy/messages_pt.po
|
||||
trans.pt_BR = apps/i2ptunnel/locale-proxy/messages_pt_BR.po
|
||||
trans.ro = apps/i2ptunnel/locale-proxy/messages_ro.po
|
||||
trans.ru_RU = apps/i2ptunnel/locale-proxy/messages_ru.po
|
||||
trans.sk = apps/i2ptunnel/locale-proxy/messages_sk.po
|
||||
trans.sv_SE = apps/i2ptunnel/locale-proxy/messages_sv.po
|
||||
trans.uk_UA = apps/i2ptunnel/locale-proxy/messages_uk.po
|
||||
trans.vi = apps/i2ptunnel/locale-proxy/messages_vi.po
|
||||
@ -78,22 +82,28 @@ trans.ar = apps/routerconsole/locale-news/messages_ar.po
|
||||
trans.de = apps/routerconsole/locale-news/messages_de.po
|
||||
trans.es = apps/routerconsole/locale-news/messages_es.po
|
||||
trans.fr = apps/routerconsole/locale-news/messages_fr.po
|
||||
trans.ja = apps/routerconsole/locale-news/messages_ja.po
|
||||
trans.he = apps/routerconsole/locale-news/messages_he.po
|
||||
trans.it = apps/routerconsole/locale-news/messages_it.po
|
||||
trans.ja = apps/routerconsole/locale-news/messages_ja.po
|
||||
trans.ko = apps/routerconsole/locale-news/messages_ko.po
|
||||
trans.nb = apps/routerconsole/locale-news/messages_nb.po
|
||||
trans.nl = apps/routerconsole/locale-news/messages_nl.po
|
||||
trans.pl = apps/routerconsole/locale-news/messages_pl.po
|
||||
trans.pt = apps/routerconsole/locale-news/messages_pt.po
|
||||
trans.pt_BR = apps/routerconsole/locale-news/messages_pt_BR.po
|
||||
trans.ro = apps/routerconsole/locale-news/messages_ro.po
|
||||
trans.ru_RU = apps/routerconsole/locale-news/messages_ru.po
|
||||
trans.sk = apps/routerconsole/locale-news/messages_sk.po
|
||||
trans.sv_SE = apps/routerconsole/locale-news/messages_sv.po
|
||||
trans.tr_TR = apps/routerconsole/locale-news/messages_tr.po
|
||||
trans.uk_UA = apps/routerconsole/locale-news/messages_uk.po
|
||||
trans.zh_CN = apps/routerconsole/locale-news/messages_zh.po
|
||||
|
||||
[I2P.countries]
|
||||
type = PO
|
||||
source_file = apps/routerconsole/locale-countries/messages_en.po
|
||||
source_lang = en
|
||||
trans.ca = apps/routerconsole/locale-countries/messages_ca.po
|
||||
trans.da = apps/routerconsole/locale-countries/messages_da.po
|
||||
trans.de = apps/routerconsole/locale-countries/messages_de.po
|
||||
trans.el = apps/routerconsole/locale-countries/messages_el.po
|
||||
@ -111,7 +121,10 @@ trans.pt = apps/routerconsole/locale-countries/messages_pt.po
|
||||
trans.pt_BR = apps/routerconsole/locale-countries/messages_pt_BR.po
|
||||
trans.ro = apps/routerconsole/locale-countries/messages_ro.po
|
||||
trans.ru_RU = apps/routerconsole/locale-countries/messages_ru.po
|
||||
trans.sk = apps/routerconsole/locale-countries/messages_sk.po
|
||||
trans.sq = apps/routerconsole/locale-countries/messages_sq.po
|
||||
trans.sv_SE = apps/routerconsole/locale-countries/messages_sv.po
|
||||
trans.uk_UA = apps/routerconsole/locale-countries/messages_uk.po
|
||||
trans.tr_TR = apps/routerconsole/locale-countries/messages_tr.po
|
||||
trans.vi = apps/routerconsole/locale-countries/messages_vi.po
|
||||
trans.zh_CN = apps/routerconsole/locale-countries/messages_zh.po
|
||||
@ -130,8 +143,10 @@ trans.nb = apps/i2psnark/locale/messages_nb.po
|
||||
trans.nl = apps/i2psnark/locale/messages_nl.po
|
||||
trans.pl = apps/i2psnark/locale/messages_pl.po
|
||||
trans.pt = apps/i2psnark/locale/messages_pt.po
|
||||
trans.pt_BR = apps/i2psnark/locale/messages_pt_bR.po
|
||||
trans.ro = apps/i2psnark/locale/messages_ro.po
|
||||
trans.ru_RU = apps/i2psnark/locale/messages_ru.po
|
||||
trans.sk = apps/i2psnark/locale/messages_sk.po
|
||||
trans.sv_SE = apps/i2psnark/locale/messages_sv.po
|
||||
trans.vi = apps/i2psnark/locale/messages_vi.po
|
||||
trans.zh_CN = apps/i2psnark/locale/messages_zh.po
|
||||
@ -156,6 +171,7 @@ trans.pt_BR = apps/susidns/locale/messages_pt_BR.po
|
||||
trans.ro = apps/susidns/locale/messages_ro.po
|
||||
trans.ru_RU = apps/susidns/locale/messages_ru.po
|
||||
trans.sv_SE = apps/susidns/locale/messages_sv.po
|
||||
trans.tr_TR = apps/susidns/locale/messages_tr.po
|
||||
trans.uk_UA = apps/susidns/locale/messages_uk.po
|
||||
trans.vi = apps/susidns/locale/messages_vi.po
|
||||
trans.zh_CN = apps/susidns/locale/messages_zh.po
|
||||
@ -178,6 +194,7 @@ trans.pl = apps/desktopgui/locale/messages_pl.po
|
||||
trans.pt_BR = apps/desktopgui/locale/messages_pt_BR.po
|
||||
trans.ro = apps/desktopgui/locale/messages_ro.po
|
||||
trans.ru_RU = apps/desktopgui/locale/messages_ru.po
|
||||
trans.sk = apps/desktopgui/locale/messages_sk.po
|
||||
trans.sv_SE = apps/desktopgui/locale/messages_sv.po
|
||||
trans.uk_UA = apps/desktopgui/locale/messages_uk.po
|
||||
trans.tr_TR = apps/desktopgui/locale/messages_tr.po
|
||||
@ -188,11 +205,13 @@ trans.zh_CN = apps/desktopgui/locale/messages_zh.po
|
||||
source_file = apps/susimail/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.cs = apps/susimail/locale/messages_cs.po
|
||||
trans.da = apps/susimail/locale/messages_da.po
|
||||
trans.de = apps/susimail/locale/messages_de.po
|
||||
trans.es = apps/susimail/locale/messages_es.po
|
||||
trans.fr = apps/susimail/locale/messages_fr.po
|
||||
trans.hu = apps/susimail/locale/messages_hu.po
|
||||
trans.it = apps/susimail/locale/messages_it.po
|
||||
trans.ja = apps/susimail/locale/messages_ja.po
|
||||
trans.nl = apps/susimail/locale/messages_nl.po
|
||||
trans.ru_RU = apps/susimail/locale/messages_ru.po
|
||||
trans.sv_SE = apps/susimail/locale/messages_sv.po
|
||||
@ -214,10 +233,13 @@ trans.es = debian/po/es.po
|
||||
trans.fr = debian/po/fr.po
|
||||
trans.it = debian/po/it.po
|
||||
trans.hu = debian/po/hu.po
|
||||
trans.ja = debian/po/ja.po
|
||||
trans.ko = debian/po/ko.po
|
||||
trans.pl = debian/po/pl.po
|
||||
trans.pt = debian/po/pt.po
|
||||
trans.ro = debian/po/ro.po
|
||||
trans.ru_RU = debian/po/ru.po
|
||||
trans.sk = debian/po/sk.po
|
||||
trans.sv_SE = debian/po/sv.po
|
||||
trans.uk_UA = debian/po/uk.po
|
||||
trans.tr_TR = debian/po/tr.po
|
||||
@ -230,14 +252,50 @@ trans.de = installer/resources/locale/po/messages_de.po
|
||||
trans.es = installer/resources/locale/po/messages_es.po
|
||||
trans.fr = installer/resources/locale/po/messages_fr.po
|
||||
trans.it = installer/resources/locale/po/messages_it.po
|
||||
trans.pl = installer/resources/locale/po/messages_pl.po
|
||||
trans.ja = installer/resources/locale/po/messages_ja.po
|
||||
trans.pl = installer/resources/locale/po/messages_pl.po
|
||||
trans.pt = installer/resources/locale/po/messages_pt.po
|
||||
trans.pt_BR = installer/resources/locale/po/messages_pt_BR.po
|
||||
trans.ro = installer/resources/locale/po/messages_ro.po
|
||||
trans.sv_SE = installer/resources/locale/po/messages_sv.po
|
||||
trans.ru_RU = installer/resources/locale/po/messages_ru.po
|
||||
trans.sk = installer/resources/locale/po/messages_sk.po
|
||||
trans.sv_SE = installer/resources/locale/po/messages_sv.po
|
||||
trans.tr_TR = installer/resources/locale/po/messages_tr.po
|
||||
trans.zh_CN = installer/resources/locale/po/messages_zh.po
|
||||
|
||||
[I2P.getopt]
|
||||
source_file = core/java/src/gnu/getopt/MessagesBundle.properties
|
||||
source_lang = en
|
||||
type = PROPERTIES
|
||||
trans.cs = core/java/src/gnu/getopt/MessagesBundle_cs.properties
|
||||
trans.de = core/java/src/gnu/getopt/MessagesBundle_de.properties
|
||||
trans.es = core/java/src/gnu/getopt/MessagesBundle_es.properties
|
||||
trans.fr = core/java/src/gnu/getopt/MessagesBundle_fr.properties
|
||||
trans.hu = core/java/src/gnu/getopt/MessagesBundle_hu.properties
|
||||
trans.it = core/java/src/gnu/getopt/MessagesBundle_it.properties
|
||||
trans.ja = core/java/src/gnu/getopt/MessagesBundle_ja.properties
|
||||
trans.nl = core/java/src/gnu/getopt/MessagesBundle_nl.properties
|
||||
trans.nb = core/java/src/gnu/getopt/MessagesBundle_nb.properties
|
||||
trans.pl = core/java/src/gnu/getopt/MessagesBundle_pl.properties
|
||||
trans.pt_BR = core/java/src/gnu/getopt/MessagesBundle_pt_BR.properties
|
||||
trans.ro = core/java/src/gnu/getopt/MessagesBundle_ro.properties
|
||||
trans.ru_RU = core/java/src/gnu/getopt/MessagesBundle_ru.properties
|
||||
trans.sk = core/java/src/gnu/getopt/MessagesBundle_sk.properties
|
||||
trans.zh_CN = core/java/src/gnu/getopt/MessagesBundle_zh.properties
|
||||
|
||||
[I2P.streaming]
|
||||
source_file = apps/ministreaming/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.de = apps/ministreaming/locale/messages_de.po
|
||||
trans.es = apps/ministreaming/locale/messages_es.po
|
||||
trans.fr = apps/ministreaming/locale/messages_fr.po
|
||||
trans.it = apps/ministreaming/locale/messages_it.po
|
||||
trans.nb = apps/ministreaming/locale/messages_nb.po
|
||||
trans.ru_RU = apps/ministreaming/locale/messages_ru.po
|
||||
trans.uk_UA = apps/ministreaming/locale/messages_uk.po
|
||||
trans.zh_CN = apps/ministreaming/locale/messages_zh.po
|
||||
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
|
@ -12,7 +12,7 @@ you may use:
|
||||
to configure the router.
|
||||
|
||||
If you're having trouble, swing by http://forum.i2p/, check the
|
||||
website at http://www.i2p2.de/, or get on irc://irc.freenode.net/#i2p
|
||||
website at https://geti2p.net/, or get on irc://irc.freenode.net/#i2p
|
||||
|
||||
I2P will create and store files and configuration data in the user directory
|
||||
~/.i2p/ on Linux and %APPDATA%\I2P\ on Windows. This directory is created
|
||||
@ -39,7 +39,10 @@ To uninstall I2P:
|
||||
rm -rf $I2PInstallDir ~/.i2p
|
||||
|
||||
Supported JVMs:
|
||||
Windows: Latest available from http://java.com/download (1.5+ supported)
|
||||
Linux: Latest available from http://java.com/download (1.5+ supported)
|
||||
FreeBSD: 1.5-compatible (NIO required)
|
||||
Other operating systems and JVMs: See https://trac.i2p2.de/wiki/java
|
||||
All platforms: Java 1.6 or higher required; 1.7 or higher recommended
|
||||
Windows: OpenJDK or Oracle from http://java.com/download
|
||||
Linux: OpenJDK or Oracle from http://java.com/download
|
||||
FreeBSD: OpenJDK or Oracle from http://java.com/download
|
||||
Raspberry Pi: Oracle 8 Early Access https://jdk8.java.net/download.html
|
||||
PowerPC: IBM SDK 7 http://www.ibm.com/developerworks/java/jdk/linux/download.html
|
||||
Other operating systems and JVMs: See https://trac.i2p2.de/wiki/java or https://geti2p.net/download
|
||||
|
15
LICENSE.txt
@ -36,7 +36,7 @@ Public domain except as listed below:
|
||||
Copyright (c) 2003, TheCrypto
|
||||
See licenses/LICENSE-ElGamalDSA.txt
|
||||
|
||||
SHA256 and HMAC-SHA256:
|
||||
SHA256 and HMAC:
|
||||
Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
|
||||
See licenses/LICENSE-SHA256.txt
|
||||
|
||||
@ -76,6 +76,11 @@ Public domain except as listed below:
|
||||
Copyright 2012 Hiroshi Nakamura <nahi@ruby-lang.org>
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
Getopt:
|
||||
Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com)
|
||||
See licenses/LICENSE-LGPLv2.1.txt
|
||||
|
||||
|
||||
Router (router.jar):
|
||||
Public domain except as listed below:
|
||||
UPnP.java:
|
||||
@ -132,7 +137,7 @@ Installer:
|
||||
|
||||
|
||||
|
||||
Java Service Wrapper Community Edition 32-bit 3.5.19:
|
||||
Java Service Wrapper Community Edition 32-bit 3.5.25:
|
||||
Copyright (C) 1999-2011 Tanuki Software, Ltd. All Rights Reserved.
|
||||
See licenses/LICENSE-Wrapper.txt
|
||||
|
||||
@ -177,7 +182,7 @@ Applications:
|
||||
By welterde.
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
Jetty 7.6.14.v20131031:
|
||||
Jetty 8.1.16.v20140903:
|
||||
See licenses/ABOUT-Jetty.html
|
||||
See licenses/NOTICE-Jetty.html
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
@ -243,8 +248,8 @@ Applications:
|
||||
Bundles systray4j-2.4.1:
|
||||
See licenses/LICENSE-LGPLv2.1.txt
|
||||
|
||||
Tomcat 6.0.37:
|
||||
Copyright 1999-2013 The Apache Software Foundation
|
||||
Tomcat 6.0.41:
|
||||
Copyright 1999-2014 The Apache Software Foundation
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
See licenses/NOTICE-Tomcat.txt
|
||||
|
||||
|
10
README.txt
@ -15,20 +15,20 @@ To build:
|
||||
ant installer-osx
|
||||
|
||||
Run 'ant' with no arguments to see other build options.
|
||||
See INSTALL.txt or http://www.i2p2.de/download.html for installation instructions.
|
||||
See INSTALL.txt or https://geti2p.net/download for installation instructions.
|
||||
|
||||
Documentation:
|
||||
http://www.i2p2.de/how
|
||||
https://geti2p.net/how
|
||||
API: run 'ant javadoc' then start at build/javadoc/index.html
|
||||
|
||||
Latest release:
|
||||
http://www.i2p2.de/download
|
||||
https://geti2p.net/download
|
||||
|
||||
To get development branch from source control:
|
||||
http://www.i2p2.de/newdevelopers
|
||||
https://geti2p.net/newdevelopers
|
||||
|
||||
FAQ:
|
||||
http://www.i2p2.de/faq
|
||||
https://geti2p.net/faq
|
||||
|
||||
Need help?
|
||||
IRC irc.freenode.net #i2p
|
||||
|
@ -1,30 +1,70 @@
|
||||
ou will need atleast monotone > = 0.41 to get the most recent build source
|
||||
and connect it to an already running i2p router.
|
||||
Getting the sources
|
||||
===================
|
||||
|
||||
OR:
|
||||
Monotone
|
||||
--------
|
||||
|
||||
You may download the actual "stable" source from
|
||||
http://code.google.com/p/i2p/downloads/list
|
||||
The bleeding-edge source code is available both within and outside of I2P. The
|
||||
I2P project uses Monotone to maintain the codebase.
|
||||
|
||||
You will need to follwing tools to build the i2p and i2p-base packages:
|
||||
Information on retrieving the source with monotone is available within I2P at
|
||||
http://i2p-projekt.i2p/monotone and from the internet at https://geti2p.net/monotone.
|
||||
|
||||
bash >= 3.1.017
|
||||
requiredbuilder >= 0.16.3 ( http://www.stabellini.net/requiredbuilder.html )
|
||||
jre >= 6u11
|
||||
jdk >= 6u11
|
||||
apache-ant >= 1.7.1
|
||||
perl >= 5.10.0
|
||||
python >= 2.5.2
|
||||
Monotone 1.0 Slackbuilds are available at http://slackbuilds.org/.
|
||||
|
||||
Reccomended:
|
||||
monotone >= 0.41 ( http://pkgs.dr.ea.ms )
|
||||
Git
|
||||
---
|
||||
|
||||
See also:
|
||||
Git is also an option to retrieve the I2P source code. That said, the I2P
|
||||
Monotone servers are *the* authoritative source.
|
||||
|
||||
i2p/readme.txt
|
||||
Public Git repositories hosting the I2P source and managed by I2P project team members include
|
||||
|
||||
AND
|
||||
- https://github.com/i2p/i2p.i2p
|
||||
- http://git.repo.i2p/w/i2p.i2p.git (mirrored from Github)
|
||||
- http://sourceforge.net/p/i2p/code/
|
||||
|
||||
i2p-base/readme.txt
|
||||
|
||||
for information and handy tips.
|
||||
Tarball
|
||||
-------
|
||||
|
||||
The latest stable release is always available from the I2P homepage at
|
||||
https://geti2p.net/get/.
|
||||
|
||||
|
||||
This SlackBuild
|
||||
===============
|
||||
|
||||
Requirements
|
||||
-------------
|
||||
|
||||
The following are needed to build the i2p package:
|
||||
|
||||
* jre >= 6
|
||||
* jdk >= 6
|
||||
* gettext
|
||||
* apache-ant >= 1.7.1
|
||||
|
||||
If you don't care about bundling the translations, the gettext requirement can
|
||||
be avoided by adding -Drequire.gettext=false to the ant lines in
|
||||
i2p/i2p.SlackBuild
|
||||
|
||||
A JRE >= v6 is the only requirement to run I2P.
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
As the root user, run either $I2PSRC/Slackware/i2p/i2p.SlackBuild or `ant slackpkg` to create a package
|
||||
in $I2PSRC/Slackware/i2p which can be installed using the Slackware packaging tools.
|
||||
|
||||
See also
|
||||
========
|
||||
|
||||
Please also take a look at
|
||||
|
||||
* i2p/README
|
||||
* eepget(1)
|
||||
* i2prouter(1)
|
||||
* http://i2p-projekt.i2p / https://geti2p.net
|
||||
|
||||
for additional information and tips.
|
||||
|
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<project basedir="." default="slackpkg">
|
||||
<target name="slackpkg">
|
||||
<echo message="Building Slackware package." />
|
||||
<exec executable="./i2p-base.SlackBuild">
|
||||
</exec>
|
||||
</target>
|
||||
</project>
|
@ -1,157 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
#
|
||||
# Now in the future we only need to look for '#I2P' and '#/I2P'
|
||||
# for modifications to rc.local and rc.local_shutdown.
|
||||
# I was a moron for not doing it this way in the first place :-) -- Sponge
|
||||
#
|
||||
#
|
||||
|
||||
touch /etc/rc.d/rc.local
|
||||
touch /etc/rc.d/rc.local_shutdown
|
||||
|
||||
echo
|
||||
echo -n "Check 1: /etc/rc.d/rc.local "
|
||||
I2PRCA=`grep -c /etc/rc.d/rc.local -e '/etc/rc.d/rc.i2p'`
|
||||
|
||||
if [ $I2PRCA -eq 0 ] ; then
|
||||
echo '#I2P' >> /etc/rc.d/rc.local
|
||||
echo '( cd /tmp ; rm -Rf i2p-*.tmp )' >> /etc/rc.d/rc.local
|
||||
echo "if [ -x /etc/rc.d/rc.i2p ] ; then" >> /etc/rc.d/rc.local
|
||||
echo " sh /etc/rc.d/rc.i2p start" >> /etc/rc.d/rc.local
|
||||
echo "fi" >> /etc/rc.d/rc.local
|
||||
echo '#/I2P' >> /etc/rc.d/rc.local
|
||||
echo "modified."
|
||||
else
|
||||
echo -n "looks OK so far,"
|
||||
# Fix old installs, or where people have modified.
|
||||
|
||||
echo -n " Check 1A: "
|
||||
I2PRCC=`grep -c /etc/rc.d/rc.local -e 'i2p-\*\.tmp'`
|
||||
|
||||
if [ $I2PRCC -eq 0 ] ; then
|
||||
DATA=$(cat /etc/rc.d/rc.local | sed -re 's/if \[ -x \/etc\/rc\.d\/rc\.i2p \] ; then/#I2P\n\( cd \/tmp ; rm -Rf i2p-*.tmp \)\nif \[ -x \/etc\/rc.d\/rc.i2p \] ; then/')
|
||||
echo "${DATA}" > /etc/rc.d/rc.local
|
||||
echo -n "additional modifications applied,"
|
||||
else
|
||||
echo -n "looks OK so far,"
|
||||
fi
|
||||
|
||||
echo -n " Check 1B: "
|
||||
I2PRCE=`grep -c /etc/rc.d/rc.local -e 'i2p-\*\.tmp'`
|
||||
if [ $I2PRCE -eq 0 ] ; then
|
||||
DATATOP=$(cat /etc/rc.d/rc.local | sed -n '0,/i2p-\*\.tmp/p' | sed '$d' )
|
||||
DATABOT=$(cat /etc/rc.d/rc.local | sed -n '/i2p-\*\.tmp/,$p' | sed -n '/^fi/,$p' | sed "1d")
|
||||
echo "${DATATOP}" > /etc/rc.d/rc.local
|
||||
echo '#I2P' >> /etc/rc.d/rc.local
|
||||
echo '( cd /tmp ; rm -Rf i2p-*.tmp )' >> /etc/rc.d/rc.local
|
||||
echo "if [ -x /etc/rc.d/rc.i2p ] ; then" >> /etc/rc.d/rc.local
|
||||
echo " sh /etc/rc.d/rc.i2p start" >> /etc/rc.d/rc.local
|
||||
echo "fi" >> /etc/rc.d/rc.local
|
||||
echo '#/I2P' >> /etc/rc.d/rc.local
|
||||
echo "${DATABOT}" >> /etc/rc.d/rc.local
|
||||
|
||||
echo -n "additional modifications applied,"
|
||||
else
|
||||
echo -n "looks ok so far,"
|
||||
fi
|
||||
echo -n " Check 1C: "
|
||||
I2PRCF=`grep -c /etc/rc.d/rc.local -e '#/I2P'`
|
||||
if [ $I2PRCF -eq 0 ] ; then
|
||||
DATATOP=$(cat /etc/rc.d/rc.local | sed -n '0,/^#I2P/p' | sed '$d' )
|
||||
DATABOT=$(cat /etc/rc.d/rc.local | sed -n '/^#I2P/,$p' | sed -n '/^fi/,$p' | sed "1d")
|
||||
echo "${DATATOP}" > /etc/rc.d/rc.local
|
||||
echo '#I2P' >> /etc/rc.d/rc.local
|
||||
echo '( cd /tmp ; rm -Rf i2p-*.tmp )' >> /etc/rc.d/rc.local
|
||||
echo "if [ -x /etc/rc.d/rc.i2p ] ; then" >> /etc/rc.d/rc.local
|
||||
echo " sh /etc/rc.d/rc.i2p start" >> /etc/rc.d/rc.local
|
||||
echo "fi" >> /etc/rc.d/rc.local
|
||||
echo '#/I2P' >> /etc/rc.d/rc.local
|
||||
echo "${DATABOT}" >> /etc/rc.d/rc.local
|
||||
|
||||
echo -n "additional modifications applied,"
|
||||
else
|
||||
echo -n "looks ok so far,"
|
||||
fi
|
||||
echo " Done."
|
||||
fi
|
||||
|
||||
echo -n "Check 2: /etc/rc.d/rc.local_shutdown "
|
||||
I2PRCB=`grep -c /etc/rc.d/rc.local_shutdown -e '/etc/rc.d/rc.i2p'`
|
||||
if [ $I2PRCB -eq 0 ] ; then
|
||||
echo "#I2P" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "if [ -x /etc/rc.d/rc.i2p ] ; then" >> /etc/rc.d/rc.local_shutdown
|
||||
echo " sh /etc/rc.d/rc.i2p stop" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "fi" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "#/I2P" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "modified."
|
||||
else
|
||||
echo -n "looks OK so far,"
|
||||
# Fix old installs
|
||||
|
||||
echo -n " Check 1A: "
|
||||
I2PRCG=`grep -c /etc/rc.d/rc.local_shutdown -e '#I2P'`
|
||||
if [ $I2PRCG -eq 0 ] ; then
|
||||
DATATOP=$(cat /etc/rc.d/rc.local_shutdown | sed -n '0,/^if \[ -x \/etc\/rc\.d\/rc\.i2p \] ; then/p' | sed '$d' )
|
||||
DATABOT=$(cat /etc/rc.d/rc.local_shutdown | sed -n '/^if \[ -x \/etc\/rc\.d\/rc\.i2p \] ; then/,$p' | sed -n '/^fi/,$p' | sed "1d")
|
||||
echo "${DATATOP}" > /etc/rc.d/rc.local_shutdown
|
||||
echo '#I2P' >> /etc/rc.d/rc.local_shutdown
|
||||
echo "if [ -x /etc/rc.d/rc.i2p ] ; then" >> /etc/rc.d/rc.local_shutdown
|
||||
echo " sh /etc/rc.d/rc.i2p stop" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "fi" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "#/I2P" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "${DATABOT}" >> /etc/rc.d/rc.local_shutdown
|
||||
echo -n "additional modifications applied,"
|
||||
else
|
||||
echo -n "looks OK so far,"
|
||||
fi
|
||||
echo -n " Check 1B: "
|
||||
I2PRCH=`grep -c /etc/rc.d/rc.local_shutdown -e '#/I2P'`
|
||||
if [ $I2PRCH -eq 0 ] ; then
|
||||
DATATOP=$(cat /etc/rc.d/rc.local_shutdown | sed -n '0,/^#I2P/p' | sed '$d' )
|
||||
DATABOT=$(cat /etc/rc.d/rc.local_shutdown | sed -n '/^#I2P/,$p' | sed -n '/^fi/,$p' | sed "1d")
|
||||
echo "${DATATOP}" > /etc/rc.d/rc.local_shutdown
|
||||
echo '#I2P' >> /etc/rc.d/rc.local_shutdown
|
||||
echo "if [ -x /etc/rc.d/rc.i2p ] ; then" >> /etc/rc.d/rc.local_shutdown
|
||||
echo " sh /etc/rc.d/rc.i2p stop" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "fi" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "#/I2P" >> /etc/rc.d/rc.local_shutdown
|
||||
echo "${DATABOT}" >> /etc/rc.d/rc.local_shutdown
|
||||
echo -n "additional modifications applied,"
|
||||
else
|
||||
echo -n "looks OK so far,"
|
||||
fi
|
||||
echo " Done."
|
||||
fi
|
||||
|
||||
if [ -f /etc/rc.d/rc.i2p ] ; then
|
||||
if [ -x /etc/rc.d/rc.i2p ] ; then
|
||||
chmod +x /etc/rc.d/rc.i2p.new
|
||||
fi
|
||||
# Hopefully get admin's attention.
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -e "\007" ; sleep 0.3
|
||||
echo "It apears that you already have /etc/rc.d/rc.i2p"
|
||||
echo "You should replace it with /etc/rc.d/rc.i2p.new as soon as possible"
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -ne "\007" ; sleep 0.3
|
||||
echo -e "\007" ; sleep 0.3
|
||||
else
|
||||
mv /etc/rc.d/rc.i2p.new /etc/rc.d/rc.i2p
|
||||
echo
|
||||
echo "Installation finished. The i2p start/stop script has been"
|
||||
echo "installed in /etc/rc.d . You should chmod +x"
|
||||
echo '/etc/rc.d/rc.i2p to start it on boot.'
|
||||
echo
|
||||
fi
|
||||
|
||||
exit
|
@ -1,57 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Heavily based on the Slackware 12.2 SlackBuild
|
||||
# Slackware build script for I2P
|
||||
#
|
||||
# PLEASE READ THIS:
|
||||
# How to start I2P:
|
||||
# After installpkg command, doinst.sh will execute a post-installation script
|
||||
# needed by I2P. After that you have to chmod +x /etc/rc.d/rc.i2p and start
|
||||
# I2P service with /etc/rc.d/rc.i2p start.
|
||||
#
|
||||
# Now tell your browser to user this proxy: localhost on port 4444 and open
|
||||
# this page: http://localhost:7657/index.jsp
|
||||
#
|
||||
# Here you can configure I2P, watch network status and navigate anonimously.
|
||||
# It's suggested to subscribe to various dns host, like i2host.i2p
|
||||
# For any additional information, visit i2host.i2p and forum.i2p
|
||||
#
|
||||
|
||||
CWD=$(pwd)
|
||||
TMP=/tmp
|
||||
PKG=/$TMP/package-base-i2p
|
||||
NAME=i2p-base
|
||||
VERSION=0.0.4
|
||||
BUILD=1sponge
|
||||
ARCH=noarch
|
||||
INSTALL_DIR=opt
|
||||
|
||||
# Less than slackware 13?
|
||||
SLKPLT=$(cat /etc/slackware-version | sed -re "s/(Slackware )([0-9]*)(.*)/\2/")
|
||||
if [ $SLKPLT -lt 13 ] ; then
|
||||
EXT=tgz
|
||||
else
|
||||
EXT=txz
|
||||
fi
|
||||
|
||||
rm -rf $PKG
|
||||
mkdir -p $PKG
|
||||
cd $PKG
|
||||
chown -R root:root .
|
||||
|
||||
mkdir -p $PKG/etc/rc.d
|
||||
mkdir -p $PKG/install
|
||||
sed "s|directory|/$INSTALL_DIR/i2p/i2prouter|g" "$CWD/rc.i2p_def" > $PKG/etc/rc.d/rc.i2p.new
|
||||
chmod 644 $PKG/etc/rc.d/rc.i2p.new
|
||||
cat "$CWD/doinst.sh" > $PKG/install/doinst.sh
|
||||
cat "$CWD/slack-desc" > $PKG/install/slack-desc
|
||||
|
||||
cd $PKG
|
||||
#
|
||||
# Not really that important to exec this
|
||||
# as there aren't any deps we don't know.
|
||||
#
|
||||
# requiredbuilder -v -y -s $CWD $PKG
|
||||
#
|
||||
cat "$CWD/slack-required" > $PKG/install/slack-required
|
||||
makepkg -l y -c n $CWD/${NAME}-$VERSION-$ARCH-$BUILD.$EXT
|
@ -1,68 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Start/stop i2p service.
|
||||
|
||||
i2p_start() {
|
||||
# Check if router is up first!
|
||||
/bin/su - -c "/bin/bash -l -c '( export PATH=\"$PATH:/usr/lib/java/bin:/usr/lib/java/jre/bin\" ; directory status )'" > /dev/null
|
||||
if [ $? -eq 0 ] ; then {
|
||||
# I2p is already running, so tell the user.
|
||||
echo "I2P is already running..."
|
||||
i2p_status
|
||||
}
|
||||
else
|
||||
{
|
||||
# Just in-case there are leftover junk in /tmp...
|
||||
rm -Rf `grep /tmp/hsperfdata_root/* -le i2p` /tmp/i2p-*.tmp /tmp/router.ping
|
||||
# Now that all junk is cleaned up, start.
|
||||
/bin/su - -c "/bin/bash -l -c '( export PATH=\"$PATH:/usr/lib/java/bin:/usr/lib/java/jre/bin\" ; directory start )'"
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
i2p_stop() {
|
||||
/bin/su - -c "/bin/bash -l -c '( export PATH=\"$PATH:/usr/lib/java/bin:/usr/lib/java/jre/bin\" ; directory stop )'"
|
||||
rm -Rf `grep /tmp/hsperfdata_root/* -le i2p` /tmp/i2p-*.tmp /tmp/router.ping
|
||||
}
|
||||
|
||||
i2p_restart() {
|
||||
# We want a FULL cycle here, not the wrappers idea of this!
|
||||
i2p_stop
|
||||
i2p_start
|
||||
}
|
||||
|
||||
i2p_status() {
|
||||
/bin/su - -c "/bin/bash -l -c '( export PATH=\"$PATH:/usr/lib/java/bin:/usr/lib/java/jre/bin\" ; directory status )'"
|
||||
}
|
||||
|
||||
i2p_console() {
|
||||
/bin/su - -c "/bin/bash -l -c '( export PATH=\"$PATH:/usr/lib/java/bin:/usr/lib/java/jre/bin\" ; directory console )'"
|
||||
}
|
||||
|
||||
i2p_dump() {
|
||||
/bin/su - -c "/bin/bash -l -c '( export PATH=\"$PATH:/usr/lib/java/bin:/usr/lib/java/jre/bin\" ; directory dump )'"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
'start')
|
||||
i2p_start
|
||||
;;
|
||||
'stop')
|
||||
i2p_stop
|
||||
;;
|
||||
'restart')
|
||||
i2p_restart
|
||||
;;
|
||||
'status')
|
||||
i2p_status
|
||||
;;
|
||||
'console')
|
||||
i2p_console
|
||||
;;
|
||||
'dump')
|
||||
i2p_dump
|
||||
;;
|
||||
*)
|
||||
echo "usage $0 start|stop|restart|status|console|dump"
|
||||
;;
|
||||
esac
|
||||
|
@ -1,10 +0,0 @@
|
||||
An rc file called rc.i2p has been placed into the /etc/rc.d directory.
|
||||
If you want to change installation dir, change the variable INSTALL_DIR
|
||||
on base-i2p.SlackBuild and rebuild the package. You also will need to do the
|
||||
same for the i2p package.
|
||||
|
||||
The install script will insert everything needed into /etc/rc.d/rc.local and
|
||||
into /etc/rc.d/rc.local_shutdown automatically.
|
||||
|
||||
If you want to start I2P at boot you have to chmod +x /etc/rc.d/rc.i2p
|
||||
|
@ -1,19 +0,0 @@
|
||||
# HOW TO EDIT THIS FILE:
|
||||
# The "handy ruler" below makes it easier to edit a package description. Line
|
||||
# up the first '|' above the ':' following the base package name, and the '|' on
|
||||
# the right side marks the last column you can put a character in. You must make
|
||||
# exactly 11 lines for the formatting to be correct. It's also customary to
|
||||
# leave one space after the ':'.
|
||||
|
||||
|-----handy-ruler------------------------------------------------------|
|
||||
base-i2p: base-i2p (I2P anonymizing network base config files)
|
||||
base-i2p:
|
||||
base-i2p: I2P is an anonymizing network, offering a simple layer that
|
||||
base-i2p: identity-sensitive applications can use to securely communicate. All
|
||||
base-i2p: data is wrapped with several layers of encryption, and the network is
|
||||
base-i2p: both distributed and dynamic, with no trusted parties.
|
||||
base-i2p: Many applications are available that interface with I2P, including
|
||||
base-i2p: mail, peer-peer file sharing, IRC chat, and others.
|
||||
base-i2p:
|
||||
base-i2p: This package provides the startup files.
|
||||
base-i2p:
|
@ -1 +0,0 @@
|
||||
bash >= 3.1.017
|
89
Slackware/i2p/README
Normal file
@ -0,0 +1,89 @@
|
||||
The I2P package will be installed to /opt/i2p.
|
||||
|
||||
To install to another location, set the variable INSTALL_DIR in i2p.SlackBuild
|
||||
and rebuild the package.
|
||||
|
||||
|
||||
Installing and Upgrading:
|
||||
=========================
|
||||
|
||||
I2P has an auto-update function but generally speaking packages are managed by
|
||||
packaging systems. Changing a package's files outside of the package system can
|
||||
cause problems. To upgrade when there's a new I2P release, retrieve the new
|
||||
source, re-run the SlackBuild, then use upgradepkg to update.
|
||||
|
||||
To ignore all this and upgrade "in-network", simply change the permissions of the installation
|
||||
directory (/opt/i2p by default). Something like the following would suffice:
|
||||
|
||||
chown -R USERNAME /opt/i2p
|
||||
|
||||
|
||||
Starting and using I2P
|
||||
======================
|
||||
|
||||
Using the initscript
|
||||
--------------------
|
||||
|
||||
To start I2P at boot, set the executable bit on /etc/rc.d/rc.i2p, add this
|
||||
script to rc.local*, and set the variable "RUN_AS_USER" in /etc/rc.d/rc.i2p.
|
||||
|
||||
Optionally, create a new user/group with limited rights to run I2P.
|
||||
|
||||
Something like the following would work to start I2P at system boot, running under the
|
||||
"i2p" account:
|
||||
|
||||
echo '[ -x /etc/rc.d/rc.i2p ] && /etc/rc.d/rc.i2p start' >> /etc/rc.d/rc.local
|
||||
echo '[ -x /etc/rc.d/rc.i2p ] && /etc/rc.d/rc.i2p stop' >> /etc/.rc.d/rc.local_shutdown
|
||||
sed -i .bak 's/^.*\(RUN_AS_USER\)=.*/\1=i2p/' /etc/rc.d/rc.i2p
|
||||
chmod 755 /etc/rc.d/rc.i2p
|
||||
|
||||
Unless running as a user named "i2psvc", I2P's config directory defaults to
|
||||
$HOME/.i2p. In the case of the "i2psvc" user, the default config directory is
|
||||
/var/lib/i2p/i2p-config.
|
||||
|
||||
It should not need to be said that using the "root" account is not recommended.
|
||||
|
||||
When running using the initscript, I2P will write its logs to /var/log/i2p.
|
||||
|
||||
Starting I2P "on-demand"
|
||||
------------------------
|
||||
|
||||
As with a normal installation, I2P can be started with "i2prouter start". The
|
||||
i2prouter and eepget scripts hve been installed to /usr/bin so they'll be
|
||||
accessible in the default system PATH.
|
||||
|
||||
When running using the i2prouter script, I2P will write its logs to $HOME/.i2p.
|
||||
|
||||
|
||||
Configuring your browser
|
||||
------------------------
|
||||
|
||||
In order to access eepSites (I2P-internal web sites) your web browser needs to
|
||||
be configured. Set the HTTP Proxy to 127.0.0.1 and port 4444. For more information, see
|
||||
https://geti2p.net/en/about/browser-config
|
||||
|
||||
The I2P router console is reachable at http://127.0.0.1:7657.
|
||||
|
||||
|
||||
Addressbook subscriptions
|
||||
-------------------------
|
||||
|
||||
Please see the FAQs at http://i2p-projekt.i2p/faq or https://geti2p.net/faq for information about
|
||||
the various addressbook services.
|
||||
|
||||
|
||||
Chatting on IRC
|
||||
---------------
|
||||
|
||||
I2P comes preconfigured with a tunnel pointing to the I2P-only IRC network,
|
||||
Irc2P. Signing on is easy, just connect to 127.0.0.1 on port 6668. Do not
|
||||
configure a proxy in your IRC client.
|
||||
|
||||
|
||||
Additional information
|
||||
======================
|
||||
|
||||
Within I2P: http://i2p-projekt.i2p/, http://forum.i2p/, http://zzz.i2p/, http://trac.i2p2.i2p/
|
||||
On the Internet: https://geti2p.net/, https://trac.i2p2.de
|
||||
Manpages: i2prouter(1), eepget(1)
|
||||
|
@ -2,7 +2,7 @@
|
||||
<project basedir="." default="slackpkg">
|
||||
<target name="slackpkg">
|
||||
<echo message="Building Slackware package." />
|
||||
<exec executable="./i2p.SlackBuild">
|
||||
</exec>
|
||||
<chmod file="./i2p.Slackbuild" perm="755" />
|
||||
<exec executable="./i2p.SlackBuild" failifexecutionfails="true" />
|
||||
</target>
|
||||
</project>
|
||||
|
@ -1,72 +1,60 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
# Abort on error or unset variables
|
||||
set -e
|
||||
set -u
|
||||
|
||||
# This is changed by i2p.SlackBuild
|
||||
INST_DIR=directory
|
||||
PKGNAME="%pkg"
|
||||
|
||||
( cd install
|
||||
config() {
|
||||
NEW="$1"
|
||||
OLD="$(dirname $NEW)/$(basename $NEW .new)"
|
||||
if [ ! -r $NEW ]; then
|
||||
# If we get here there's a flaw in the packaging. We'll just return so that
|
||||
# we don't emit a spurious error for the user. (It's not the user's problem).
|
||||
return
|
||||
fi
|
||||
|
||||
echo
|
||||
for i in *.config ; {
|
||||
if [ -f $INST_DIR/$i ] ; then
|
||||
echo "Please check ${INST_DIR}${i}, as there is a new version."
|
||||
cp $i $INST_DIR/$i.new
|
||||
else
|
||||
cp $i $INST_DIR/$i
|
||||
fi
|
||||
# If this file doesn't exist yet, drop the .new extension.
|
||||
if [ ! -r $OLD ]; then
|
||||
mv $NEW $OLD
|
||||
return
|
||||
elif [ "$(md5sum $OLD | cut -d' ' -f1)" = "$(md5sum $NEW | cut -d' ' -f1)" ]; then
|
||||
# If there are no differences in the files, remove the file with the .new extension.
|
||||
rm $NEW
|
||||
return
|
||||
fi
|
||||
# Alert the admin if they differ, but let's not be terribly obnoxious about it.
|
||||
echo "WARNING: The files $OLD and $NEW differ." >&2
|
||||
}
|
||||
|
||||
)
|
||||
# Unlike previous versions of the package, we install i2prouter and eepget to /usr/bin
|
||||
# to make them available within the system PATH.
|
||||
|
||||
( cd $INST_DIR
|
||||
if [ -f blocklist.txt ] ; then
|
||||
echo "Please check ${INST_DIR}blocklist.txt, as there is a new version."
|
||||
else
|
||||
mv blocklist.txt.new blocklist.txt
|
||||
fi
|
||||
)
|
||||
# Users might still want to /opt/i2p/i2prouter or /opt/i2p/eepget so we'll create symlinks
|
||||
# in the installation directory.
|
||||
ln -sf /usr/bin/eepget $INST_DIR
|
||||
ln -sf /usr/bin/i2prouter $INST_DIR
|
||||
(cd /usr/doc/$PKGNAME; ln -sf $INST_DIR/history.txt changelog)
|
||||
|
||||
( cd $INST_DIR/eepsite
|
||||
if [ -f jetty.xml ] ; then
|
||||
echo "Please check ${INST_DIR}/eepsite, as there are new files."
|
||||
else
|
||||
find $PKG/$INSTALL_DIR/i2p -name "*.xml.new" -exec sh -c 'mv "$0" "${0/.new}"' {} \;
|
||||
fi
|
||||
)
|
||||
|
||||
( cd $INST_DIR/eepsite/docroot
|
||||
if [ -f index.html ] ; then
|
||||
rm index.html.new
|
||||
else
|
||||
mv index.html.new index.html
|
||||
fi
|
||||
if [ -f favicon.ico ] ; then
|
||||
rm favicon.ico.new
|
||||
else
|
||||
mv favicon.ico.new favicon.ico
|
||||
fi
|
||||
)
|
||||
|
||||
echo
|
||||
echo "FINISHING I2P INSTALLATION. PLEASE WAIT."
|
||||
|
||||
cd $INST_DIR
|
||||
|
||||
|
||||
|
||||
OS_ARCH=`uname -m`
|
||||
X86_64=`echo "$OS_ARCH" | grep x86_64`
|
||||
if [ "X$X86_64" = "X" ]; then
|
||||
wrapperpath="./lib/wrapper/linux"
|
||||
if $(uname -m | grep -q '64'); then
|
||||
(cd $INST_DIR; ln -sf i2psvc-linux-x86-64 i2psvc)
|
||||
else
|
||||
wrapperpath="./lib/wrapper/linux64"
|
||||
(cd $INST_DIR; ln -sf i2psvc-linux-x86-32 i2psvc)
|
||||
fi
|
||||
cp $wrapperpath/libwrapper.so ./lib/
|
||||
cp $wrapperpath/wrapper.jar ./lib/
|
||||
cp $wrapperpath/i2psvc .
|
||||
rm -rf ./lib/wrapper
|
||||
chmod 744 ./i2psvc
|
||||
|
||||
echo
|
||||
echo "Installation finished."
|
||||
echo
|
||||
config /etc/rc.d/rc.i2p.new
|
||||
config $INST_DIR/wrapper.config.new
|
||||
|
||||
exit
|
||||
if [ -e /var/log/packages/i2p-base* ]; then
|
||||
echo "Warning: This package supercedes the 'i2p-base' package." >&2
|
||||
echo
|
||||
echo "You may want to 'removepkg i2p-base'" >&2
|
||||
echo "and check the contents of /etc/rc.d/rc.local*" >&2
|
||||
echo "for correctness" >&2
|
||||
fi
|
||||
|
||||
# Remove extraneous 'sh' from sponge's set-up
|
||||
sed -i 's|sh /etc/rc\.d/rc\.i2p|/etc/rc.d/rc.i2p|g' /etc/rc.d/rc.local*
|
||||
|
@ -3,135 +3,124 @@
|
||||
# Heavily based on the Slackware 12.2 SlackBuild
|
||||
# Slackware build script for I2P
|
||||
#
|
||||
# PLEASE READ THIS:
|
||||
# Probably you will never have to update I2P packages with upgradepkg,
|
||||
# just because I2P has an auto-update function.
|
||||
# Really you should not ever use any "upgrade" method.
|
||||
#
|
||||
# The correct way to upgrade is to:
|
||||
# 1: install the upgrade
|
||||
# 2: remove the old package
|
||||
#
|
||||
# It is a terrible shame that upgradepkg doesn't do this, infact,
|
||||
# it would actually be the correct way for *any* package!
|
||||
# Packages are generally prohibited from being updated outside
|
||||
# of the package manager; this I2P SlackBuild is no different.
|
||||
#
|
||||
# If you'd like to use the I2P "in-network" updates anyway, you'll need to
|
||||
# grant the user that I2P will run as write permission to the installation directory
|
||||
# (/opt/i2p by default).
|
||||
#
|
||||
# For safety's sake, a user's I2P config files will *never* be overwritten by any upgrade method.
|
||||
# In the future this SlackBuild may alert when a default config file is updated.
|
||||
##
|
||||
|
||||
BUILD=1sponge
|
||||
# Make sure makepkg and friends can be found
|
||||
PATH=$PATH:/sbin
|
||||
|
||||
# abort on error and unset variables (same as set -e and set -u, respectively)
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
if [ $(id -ur) -ne 0 ]; then
|
||||
echo "ERROR: SlackBuilds require root access." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUILD=1kytv
|
||||
# INSTALL_DIR is referenced from /, don't prefix it with a '/'
|
||||
INSTALL_DIR=opt
|
||||
NAME=i2p
|
||||
ARCH=noarch
|
||||
|
||||
# Less than slackware 13?
|
||||
SLKPLT=$(cat /etc/slackware-version | sed -re "s/(Slackware )([0-9]*)(.*)/\2/")
|
||||
if [ $SLKPLT -lt 13 ] ; then
|
||||
EXT=tgz
|
||||
else
|
||||
EXT=txz
|
||||
fi
|
||||
|
||||
|
||||
#
|
||||
# This mess is here due to the totally moronic way i2p does versioning.
|
||||
# We correct it here.
|
||||
#
|
||||
ROUTER=$(echo -ne "_")$(cat ../../router/java/src/net/i2p/router/RouterVersion.java | grep -e "public final static long BUILD" | cut -f2 -d"=" | cut -f1 -d";" | sed -re "s/ //g")
|
||||
if [ "$ROUTER" == "_" ] ; then
|
||||
ROUTER="_0"
|
||||
fi
|
||||
|
||||
#
|
||||
# That was the easy one, now for the tough one.
|
||||
#
|
||||
|
||||
CORE=$(cat ../../core/java/src/net/i2p/CoreVersion.java | grep -e "public final static String VERSION" | cut -f2 -d'"' | sed -re "s/ //g")
|
||||
CORE1=$(echo -n $CORE.x.x | sed -re "s/(.*)\.(.*)\.(.*)\.(.*)/\1/")
|
||||
CORE2=$(echo -n $CORE.x | sed -re "s/(.*)\.(.*)\.(.*)\.(.*)/\1/")
|
||||
|
||||
if [ "$CORE.x.x" == "$CORE1" ] ; then
|
||||
CORE=$(echo -ne $CORE".0.0")
|
||||
fi
|
||||
if [ "$CORE.x" == "$CORE2" ] ; then
|
||||
CORE=$(echo -ne $CORE".0")
|
||||
fi
|
||||
|
||||
VERSION=$(echo $CORE$ROUTER)
|
||||
#
|
||||
# Whew!
|
||||
# OK, let's build i2p
|
||||
#
|
||||
|
||||
CWD=$(pwd)
|
||||
CWD=$(readlink -m $(dirname $0))
|
||||
I2PSRC=$(readlink -m $CWD/../../)
|
||||
TMP=/tmp
|
||||
|
||||
PKG=$TMP/package-i2p
|
||||
rm -rf $PKG
|
||||
mkdir -p $PKG
|
||||
|
||||
cd $CWD/../../
|
||||
if [ -e "/etc/slackware-version" ]; then
|
||||
# Older than Slackware 13?
|
||||
SLACKVER=$(sed -e "s/Slackware\s\+\([0-9]\+\)\.\?\([0-9]\+\)\?/\1/" /etc/slackware-version)
|
||||
if [ $SLACKVER -lt 13 ] ; then
|
||||
EXT=tgz
|
||||
else
|
||||
EXT=txz
|
||||
fi
|
||||
else
|
||||
echo "ERROR: This script is only intended for use on Slackware systems.">&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract version strings
|
||||
I2PBUILD=$(sed -e '/^.\+long\s\+BUILD/!d' -e 's/^.\+long\s\+BUILD\s\+=\s\+\([0-9]\+\);/\1/' $I2PSRC/router/java/src/net/i2p/router/RouterVersion.java)
|
||||
# Thanks to user "ihavei2p" for the second awk statement
|
||||
# If the version is x.y, it'll be set to x.y.0. Otherwise the version string will be unchanged
|
||||
CORE=$(awk -F'"' '/static\s+String\s+VERSION/{print $2}' $I2PSRC/core/java/src/net/i2p/CoreVersion.java | \
|
||||
awk -F. '{ if (NF > 3) { print; exit } else if ($3 == "") { $3=0; print $1"."$2"."$3 } else print }')
|
||||
VERSION="${CORE}_${I2PBUILD}"
|
||||
|
||||
[ -d $PKG ] && rm -rf $PKG
|
||||
mkdir -p $PKG/$INSTALL_DIR $PKG/install
|
||||
|
||||
cd "$I2PSRC"
|
||||
ant distclean
|
||||
#ant dist
|
||||
ant tarball
|
||||
ant jbigi-linux-x86-only preppkg-unix
|
||||
|
||||
tar xjvf i2p.tar.bz2 -C $TMP
|
||||
chown -R root:root $I2PSRC/pkg-temp
|
||||
cp -a $I2PSRC/pkg-temp $PKG/$INSTALL_DIR/i2p
|
||||
|
||||
cd $TMP/i2p
|
||||
chown -R root:root .
|
||||
|
||||
mkdir -p $PKG/$INSTALL_DIR/
|
||||
cp -a ../i2p $PKG/$INSTALL_DIR/
|
||||
|
||||
mkdir -p $PKG/install
|
||||
|
||||
#############################################################################
|
||||
# Preconfigureation to make package smaller, and...
|
||||
# we keep as much as reasonable in the installation directory.
|
||||
# This makes the install map fairly well to the standard installation.
|
||||
# It also makes it easier to find the log and pid files!
|
||||
#############################################################################
|
||||
# $INSTALL_DIR is used by this SlackBuild.
|
||||
# [%$]INSTALL_PATH , [%$]SYSTEM_java_io_tmpdir, and [%$]USER_HOME have the correct paths set
|
||||
# by the IzPack installer.
|
||||
cd $PKG/$INSTALL_DIR/i2p
|
||||
for file in wrapper.config eepget i2prouter runplain.sh; do
|
||||
sed -i "s|[%$]INSTALL_PATH|/$INSTALL_DIR/i2p|g;s|[$%]SYSTEM_java_io_tmpdir|$TMP|g;s/[%$]USER_HOME/\$HOME/g" $file
|
||||
done
|
||||
mv wrapper.config wrapper.config.new
|
||||
|
||||
# wrapper.config $INSTALL_PATH and $SYSTEM_java_io_tmpdir
|
||||
sed "s|\$INSTALL_PATH|/$INSTALL_DIR/i2p|g" wrapper.config > a
|
||||
sed "s|\$SYSTEM_java_io_tmpdir|/$INSTALL_DIR/i2p|g" a > wrapper.config
|
||||
# eepget %INSTALL_PATH
|
||||
sed "s|\$INSTALL_PATH|/$INSTALL_DIR/i2p|g" eepget > a
|
||||
rm eepget
|
||||
mv a eepget
|
||||
# runplain.sh %INSTALL_PATH and %SYSTEM_java_io_tmpdir
|
||||
sed "s|%INSTALL_PATH|/$INSTALL_DIR/i2p|g" runplain.sh > a
|
||||
sed "s|%SYSTEM_java_io_tmpdir|/$INSTALL_DIR/i2p|g" a > runplain.sh
|
||||
# i2prouter %INSTALL_PATH and %SYSTEM_java_io_tmpdir
|
||||
sed "s|%INSTALL_PATH|/$INSTALL_DIR/i2p|g" i2prouter > a
|
||||
rm i2prouter
|
||||
mv a i2prouter
|
||||
sed "s|%SYSTEM_java_io_tmpdir|/$INSTALL_DIR/i2p|g" i2prouter > a
|
||||
sed "s|#ALLOW_ROOT=true|ALLOW_ROOT=true|g" a > i2prouter
|
||||
install -d $PKG/usr/bin
|
||||
install -d $PKG/usr/doc/$NAME-$VERSION
|
||||
install -d $PKG/etc/rc.d
|
||||
mv licenses LICENSE.txt -t $PKG/usr/doc/$NAME-$VERSION
|
||||
|
||||
chmod 744 ./i2prouter
|
||||
chmod 744 ./osid
|
||||
chmod 744 ./runplain.sh
|
||||
chmod 744 ./eepget
|
||||
chmod 744 ./scripts/i2pbench.sh
|
||||
chmod 744 ./scripts/i2ptest.sh
|
||||
rm -Rf ./lib/*.dll ./*.bat ./*.exe ./installer ./icons ./a postinstall.sh
|
||||
# runplain.sh will live in the installation directory. eepget and i2prouter will go to /usr/bin
|
||||
# with symlinks in INST_DIR (created in doinst.sh)
|
||||
install -m755 i2prouter $PKG/usr/bin
|
||||
install -m755 eepget $PKG/usr/bin
|
||||
chmod 755 ./runplain.sh
|
||||
|
||||
mv $PKG/$INSTALL_DIR/i2p/*.config $PKG/install
|
||||
mv $PKG/$INSTALL_DIR/i2p/blocklist.txt $PKG/$INSTALL_DIR/i2p/blocklist.txt.new
|
||||
find $PKG/$INSTALL_DIR/i2p -name "*.xml" -exec mv {} {}.new \;
|
||||
mv $PKG/$INSTALL_DIR/i2p/eepsite/docroot/index.html $PKG/$INSTALL_DIR/i2p/eepsite/docroot/index.html.new
|
||||
mv $PKG/$INSTALL_DIR/i2p/eepsite/docroot/favicon.ico $PKG/$INSTALL_DIR/i2p/eepsite/docroot/favicon.ico.new
|
||||
sed "s|directory|/$INSTALL_DIR/i2p/|g" $CWD/doinst.sh > $PKG/install/doinst.sh
|
||||
cat $CWD/slack-desc > $PKG/install/slack-desc
|
||||
if [ $INSTALL_DIR != 'opt' ]; then
|
||||
sed "s|\(The I2P package\)\s\+will be\s\+\(installed to\).+|\1 has been \2 $INSTALL_DIR/i2p|g" $CWD/README > $PKG/usr/doc/$NAME-$VERSION/README
|
||||
else
|
||||
sed "s|will be installed|has been installed|" $CWD/README > $PKG/usr/doc/$NAME-$VERSION/README
|
||||
fi
|
||||
|
||||
install -d $PKG/usr/man/man1
|
||||
gzip -9 man/*.1
|
||||
install -m644 man/*.1.gz $PKG/usr/man/man1
|
||||
rm -rf ./man
|
||||
|
||||
# We install all x86 wrapper binaries.
|
||||
# The i2prouter script will try to determine the OS (linux), the bits (32 VS 64) and should be able
|
||||
# to figure out the correct wrapper binary to use.
|
||||
|
||||
# However: In case the i2prouter script's detection fails, "$INST_DIR/i2psvc" will point to
|
||||
# what 'we' think the correct binary is.
|
||||
#
|
||||
# A good reason for installing all binaries: in case the user, for whatever reason, switches from an
|
||||
# x64 JRE to an x86 JRE, I2P should continue to work without needing to be reinstalled.
|
||||
install -m755 $I2PSRC/installer/lib/wrapper/linux/i2psvc ./i2psvc-linux-x86-32
|
||||
install -m644 $I2PSRC/installer/lib/wrapper/linux/libwrapper.so ./lib/libwrapper-linux-x86-32.so
|
||||
install -m755 $I2PSRC/installer/lib/wrapper/linux64/i2psvc ./i2psvc-linux-x86-64
|
||||
install -m644 $I2PSRC/installer/lib/wrapper/linux64/libwrapper.so ./lib/libwrapper-linux-x86-64.so
|
||||
install -m644 $I2PSRC/installer/lib/wrapper/all/wrapper.jar ./lib/wrapper.jar
|
||||
install -m644 $I2PSRC/build/jbigi.jar $PKG/$INSTALL_DIR/i2p/lib/jbigi.jar
|
||||
|
||||
rm -f ./postinstall.sh ./osid ./INSTALL-*.txt
|
||||
sed "s|directory|/$INSTALL_DIR/i2p|" $CWD/doinst.sh > $PKG/install/doinst.sh
|
||||
sed -i "s|%pkg|$NAME-$VERSION|" $PKG/install/doinst.sh
|
||||
sed "s|%INST_DIR|/$INSTALL_DIR/i2p|" $CWD/rc.i2p> $PKG/etc/rc.d/rc.i2p.new
|
||||
cp $CWD/slack-desc $PKG/install/slack-desc
|
||||
|
||||
cd $PKG
|
||||
#
|
||||
# requiredbuilder messes up REALLY bad, and thinks java is perl?!
|
||||
# It also did not catch the shell requirements! BOOOOOOOOOOO! HISSSSSSSS!
|
||||
#
|
||||
# requiredbuilder -v -y -s $CWD $PKG
|
||||
#
|
||||
cat $CWD/slack-required > $PKG/install/slack-required
|
||||
cp $CWD/slack-required $PKG/install/slack-required
|
||||
makepkg -l y -c n $CWD/${NAME}-$VERSION-$ARCH-$BUILD.$EXT
|
||||
|
185
Slackware/i2p/rc.i2p
Normal file
@ -0,0 +1,185 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Abort on errors
|
||||
set -e
|
||||
|
||||
##
|
||||
# This *must* be configured. Set this to the user that will run I2P.
|
||||
# Note: If you don't want I2P to start automatically at boot,
|
||||
# use "i2prouter start" as a non-root user to start I2P.
|
||||
#RUN_AS_USER=
|
||||
##
|
||||
|
||||
##
|
||||
# Set the locale as desired.
|
||||
# Note: this is not the same as the language shown in the I2P router console.
|
||||
# This affects the locale used in wrapper.log. For best results, use a
|
||||
# unicode enabled locale. This is especially important for foreign language torrents.
|
||||
#
|
||||
# If not set the user's configured locale will be used.
|
||||
#RCLOCALE=
|
||||
##
|
||||
|
||||
#####################################################
|
||||
# Nothing below this point should need to be edited #
|
||||
#####################################################
|
||||
# %INST_DIR is set by i2p.SlackBuild
|
||||
INSTALL_DIR="%INST_DIR"
|
||||
# Make sure the package is installed and that the wrapper can be found
|
||||
[ -d $INSTALL_DIR ] && [ -x $INSTALL_DIR/i2psvc ] || (echo "The I2P package is not installed" >&2 ; exit 1)
|
||||
|
||||
if [ -z $RUN_AS_USER ]; then
|
||||
echo "ERROR: RUN_AS_USER not configured in $0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $(id -ur) -ne 0 ]; then
|
||||
echo 'ERROR: You must be root to start this service.' >&2
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z $RCLOCALE ]; then
|
||||
if [ ! $(locale -a |grep -q "en_US\.utf8") ]; then
|
||||
RCLOCALE="en_US.utf8"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Abort this script if any referenced variables haven't been set
|
||||
set -u
|
||||
|
||||
if $(uname -m |grep -q '64'); then
|
||||
BITS='64'
|
||||
else
|
||||
BITS=''
|
||||
fi
|
||||
|
||||
PATH="$PATH:/usr/lib$BITS/java/bin:/usr/lib$BITS/java/jre/bin"
|
||||
RUN=/var/run/i2p
|
||||
PIDFILE="$RUN/i2p.pid"
|
||||
WRAPPER_CONF="$INSTALL_DIR/wrapper.config"
|
||||
WRAPPERLOG=/var/log/i2p/wrapper.log
|
||||
I2PTEMP="/tmp/i2p-daemon"
|
||||
DESC="The I2P daemon"
|
||||
JAVABINARY=$(awk -F'=' '/^ *wrapper\.java\.command/{print $2}' "$WRAPPER_CONF")
|
||||
|
||||
if [ ! $(which $JAVABINARY 2>/dev/null) ]; then
|
||||
for rc in /etc/profile.d/*jdk*.sh /etc/profile.d/*java*.sh; do
|
||||
[ -r $rc ] && . $rc
|
||||
done
|
||||
if [ ! $(which $JAVABINARY 2>/dev/null) ]; then
|
||||
echo "ERROR: Cannot find java. Please set the path to java in $WRAPPER_CONF" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
JAVA=$(which $JAVABINARY 2>/dev/null)
|
||||
|
||||
I2P_ARGS="$WRAPPER_CONF \
|
||||
wrapper.java.additional.1=-DloggerFilenameOverride=/var/log/i2p/log-router-@.txt \
|
||||
wrapper.java.additional.10=-Dwrapper.logfile=$WRAPPERLOG \
|
||||
wrapper.java.additional.11=-Di2p.dir.pid=$RUN \
|
||||
wrapper.java.additional.12=-Di2p.dir.temp=$I2PTEMP \
|
||||
wrapper.java.command=$JAVA \
|
||||
wrapper.logfile=$WRAPPERLOG \
|
||||
wrapper.pidfile=$PIDFILE \
|
||||
wrapper.daemonize=TRUE"
|
||||
|
||||
LC_ALL=$RCLOCALE
|
||||
LANG=$RCLOCALE
|
||||
export LC_ALL LANG
|
||||
|
||||
is_running() {
|
||||
if [ -r $PIDFILE ]; then
|
||||
PID="$(cat ${PIDFILE})" 2>/dev/null 2>&1
|
||||
if ! kill -0 $PID >/dev/null 2>&1; then
|
||||
rm "$PIDFILE"
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
start() {
|
||||
if is_running; then
|
||||
echo "ERROR: $DESC is already running." >&2
|
||||
fi
|
||||
|
||||
for DIR in $RUN $I2PTEMP; do
|
||||
test -d $DIR && rm -rf $DIR
|
||||
mkdir -p $DIR
|
||||
chown -R $RUN_AS_USER $DIR
|
||||
done
|
||||
[ -d /var/log/i2p ] || mkdir /var/log/i2p
|
||||
chown -R $RUN_AS_USER /var/log/i2p
|
||||
|
||||
echo -n "Starting $DESC..."
|
||||
TZ=UTC su $RUN_AS_USER -c "$INSTALL_DIR/i2psvc $I2P_ARGS"
|
||||
is_running
|
||||
echo "[pid: $PID]"
|
||||
}
|
||||
|
||||
stop(){
|
||||
if is_running; then
|
||||
echo -n "Stopping $DESC [pid: $PID] (this could take a while)."
|
||||
kill "$PID" >/dev/null 2>&1
|
||||
while kill -0 "$PID" > /dev/null 2>&1; do
|
||||
echo -n .
|
||||
sleep 1
|
||||
done
|
||||
rm -rf "$RUN" "$I2PTEMP"
|
||||
echo done.
|
||||
return 0
|
||||
else
|
||||
echo "$DESC is not running." >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Unset +u to allow the 'usage' text to be displayed
|
||||
set +u
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
status)
|
||||
if is_running; then
|
||||
echo "$DESC is running [pid: $PID]" >&2
|
||||
else
|
||||
echo "$DESC is not running." >&2
|
||||
fi
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
graceful)
|
||||
if is_running; then
|
||||
kill -HUP $PID
|
||||
echo "Graceful shutdown of $DESC initiated." >&2
|
||||
echo "This may take up to 11 minutes." >&2
|
||||
fi
|
||||
;;
|
||||
dump)
|
||||
if is_running; then
|
||||
kill -3 $PID
|
||||
echo "Threads dumped to $WRAPPERLOG" >&2
|
||||
else
|
||||
echo "$DESC is not running." >&2
|
||||
fi
|
||||
;;
|
||||
restart)
|
||||
if is_running; then
|
||||
stop
|
||||
start
|
||||
else
|
||||
echo "$DESC is not running." >&2
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 start|stop|status|restart|graceful|dump"
|
||||
;;
|
||||
esac
|
@ -1,47 +0,0 @@
|
||||
Building:
|
||||
The i2p package will be installed in /opt/i2p
|
||||
|
||||
If you want to change installation dir, change the variable INSTALL_DIR
|
||||
on i2p.SlackBuild and rebuild the package. You will also need to do the same
|
||||
in the base-i2p package.
|
||||
|
||||
Installation and Upgrade:
|
||||
Probably you will never have to update i2p packages. However if you do,
|
||||
be sure to installpkg first, then removepkg or custom config files can
|
||||
be lost with upgradepkg. I2P has an auto-update function. However using
|
||||
installpkg then removepkg lowers the demand on the I2P network as a
|
||||
whole, and is by far faster.
|
||||
|
||||
After installpkg command, doinst.sh will execute a postinstallation script
|
||||
needed by I2P. Be sure to also install the base-i2p package.
|
||||
|
||||
Optional:
|
||||
|
||||
chmod +x /etc/rc.d/rc.i2p only if you want it to start on boot and stop on
|
||||
shutdown.
|
||||
|
||||
How to start I2P:
|
||||
|
||||
Start I2P service with-
|
||||
sh /etc/rc.d/rc.i2p start
|
||||
|
||||
Now tell your browser to user this proxy: localhost on port 4444 and open
|
||||
this page: http://localhost:7657/index.jsp
|
||||
Here you can configure I2P, watch network status and navigate anonimously.
|
||||
It's suggested to subscribe to various addressbook hosts so that you can
|
||||
get to the many available eepsites and other service on I2P. These are not
|
||||
set up by default for security reasons.
|
||||
|
||||
Please see the faqs on http://www.i2p2.i2p/ or http://www.i2p2.de/ on how
|
||||
to subscribe to the various addressbook services.
|
||||
|
||||
To stop I2P:
|
||||
/etc/rc.d/rc.i2p stop
|
||||
|
||||
|
||||
For any additional information:
|
||||
|
||||
Within I2P- http://www.i2p2.i2p/, http://forum.i2p/, http://zzz.i2p
|
||||
|
||||
Internet (not reccomended!) - http://www.i2p2.de/, http://forum.i2p2.de/
|
||||
|
@ -6,7 +6,7 @@
|
||||
# leave one space after the ':'.
|
||||
|
||||
|-----handy-ruler----------------------------------------------------------|
|
||||
i2p: i2p (an anonymizing network)
|
||||
i2p: I2P (an anonymizing network)
|
||||
i2p:
|
||||
i2p: I2P is an anonymizing network, offering a simple layer that
|
||||
i2p: identity-sensitive applications can use to securely communicate. All
|
||||
@ -14,6 +14,6 @@ i2p: data is wrapped with several layers of encryption, and the network is
|
||||
i2p: both distributed and dynamic, with no trusted parties.
|
||||
i2p: Many applications are available that interface with I2P, including
|
||||
i2p: mail, peer-peer file sharing, IRC chat, and others.
|
||||
i2p: WARNING: To upgrade installpkg FIRST _THEN_ removepkg.
|
||||
i2p: For more information, see: http://www.i2p2.de/
|
||||
i2p:
|
||||
i2p: For more information, see: https://geti2p.net/
|
||||
i2p:
|
||||
|
@ -1,4 +1 @@
|
||||
jre >= 5
|
||||
i2p-base >= 0.0.1
|
||||
bash >= 3.1.017
|
||||
|
||||
jre >= 6
|
||||
|
@ -119,15 +119,16 @@ public class BOB implements Runnable, ClientApp {
|
||||
public final static String PROP_BOB_HOST = "BOB.host";
|
||||
public final static String PROP_CFG_VER = "BOB.CFG.VER";
|
||||
|
||||
/** unused when started via the ClientApp interface */
|
||||
private static BOB _bob;
|
||||
|
||||
private NamedDB database;
|
||||
private Properties props = new Properties();
|
||||
private AtomicBoolean spin = new AtomicBoolean(true);
|
||||
private final NamedDB database;
|
||||
private final Properties props = new Properties();
|
||||
private final AtomicBoolean spin = new AtomicBoolean(true);
|
||||
private static final String P_RUNNING = "RUNNING";
|
||||
private static final String P_STARTING = "STARTING";
|
||||
private static final String P_STOPPING = "STOPPING";
|
||||
private AtomicBoolean lock = new AtomicBoolean(false);
|
||||
private final AtomicBoolean lock = new AtomicBoolean(false);
|
||||
// no longer used.
|
||||
// private static int maxConnections = 0;
|
||||
|
||||
@ -143,8 +144,9 @@ public class BOB implements Runnable, ClientApp {
|
||||
* Stop BOB gracefully
|
||||
* @deprecated unused
|
||||
*/
|
||||
public static void stop() {
|
||||
_bob.shutdown(null);
|
||||
public synchronized static void stop() {
|
||||
if (_bob != null)
|
||||
_bob.shutdown(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,7 +191,7 @@ public class BOB implements Runnable, ClientApp {
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
public synchronized static void main(String[] args) {
|
||||
try {
|
||||
_bob = new BOB(I2PAppContext.getGlobalContext(), null, args);
|
||||
_bob.startup();
|
||||
@ -221,15 +223,17 @@ public class BOB implements Runnable, ClientApp {
|
||||
if (!cfg.isAbsolute()) {
|
||||
cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), configLocation);
|
||||
}
|
||||
FileInputStream fi = null;
|
||||
try {
|
||||
FileInputStream fi = new FileInputStream(cfg);
|
||||
fi = new FileInputStream(cfg);
|
||||
props.load(fi);
|
||||
fi.close();
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
_log.warn("Unable to load up the BOB config file " + cfg.getAbsolutePath() + ", Using defaults.", fnfe);
|
||||
save = true;
|
||||
} catch (IOException ioe) {
|
||||
_log.warn("IOException on BOB config file " + cfg.getAbsolutePath() + ", using defaults.", ioe);
|
||||
} finally {
|
||||
if (fi != null) try { fi.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
// Global router and client API configurations that are missing are set to defaults here.
|
||||
@ -276,13 +280,15 @@ public class BOB implements Runnable, ClientApp {
|
||||
if (!cfg.isAbsolute()) {
|
||||
cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), configLocation);
|
||||
}
|
||||
FileOutputStream fo = null;
|
||||
try {
|
||||
_log.warn("Writing new defaults file " + cfg.getAbsolutePath());
|
||||
FileOutputStream fo = new FileOutputStream(cfg);
|
||||
fo = new FileOutputStream(cfg);
|
||||
props.store(fo, cfg.getAbsolutePath());
|
||||
fo.close();
|
||||
} catch (IOException ioe) {
|
||||
_log.error("IOException on BOB config file " + cfg.getAbsolutePath(), ioe);
|
||||
} finally {
|
||||
if (fo != null) try { fo.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,8 +106,10 @@ public class UDPIOthread implements I2PSessionListener, Runnable {
|
||||
// _log.debug("Message available: id = " + msgId + " size = " + size);
|
||||
try {
|
||||
byte msg[] = session.receiveMessage(msgId);
|
||||
out.write(msg);
|
||||
out.flush();
|
||||
if (msg != null) {
|
||||
out.write(msg);
|
||||
out.flush();
|
||||
}
|
||||
} catch (I2PSessionException ise) {
|
||||
up = false;
|
||||
} catch (IOException ioe) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
<property name="jar" value="addressbook.jar"/>
|
||||
<property name="war" value="addressbook.war"/>
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="javac.version" value="1.6" />
|
||||
|
||||
<target name="init">
|
||||
<mkdir dir="${build}"/>
|
||||
@ -24,6 +25,18 @@
|
||||
<typefound name="depend" />
|
||||
</condition>
|
||||
<target name="depend" if="depend.available">
|
||||
<depend
|
||||
cache="../../build"
|
||||
srcdir="${src}"
|
||||
destdir="${build}" >
|
||||
<!-- Depend on classes instead of jars where available -->
|
||||
<classpath>
|
||||
<pathelement location="../../core/java/build/obj" />
|
||||
</classpath>
|
||||
</depend>
|
||||
</target>
|
||||
|
||||
<target name="dependServlet" if="depend.available">
|
||||
<depend
|
||||
cache="../../build"
|
||||
srcdir="${src}"
|
||||
@ -37,9 +50,22 @@
|
||||
</target>
|
||||
|
||||
<target name="compile" depends="init, depend">
|
||||
<javac debug="true" deprecation="on" source="1.5" target="1.5"
|
||||
<javac debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
|
||||
includeAntRuntime="false"
|
||||
srcdir="${src}" destdir="${build}">
|
||||
srcdir="${src}" destdir="${build}"
|
||||
excludes="net/i2p/addressbook/Servlet.java">
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
<classpath>
|
||||
<pathelement location="../../core/java/build/i2p.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="compileServlet" depends="init, dependServlet, compile">
|
||||
<javac debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
|
||||
includeAntRuntime="false"
|
||||
srcdir="${src}" destdir="${build}"
|
||||
includes="net/i2p/addressbook/Servlet.java">
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
<classpath>
|
||||
<pathelement location="../../core/java/build/i2p.jar" />
|
||||
@ -48,11 +74,11 @@
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<!-- unused for now, as we oddly ship addressbook as a .war -->
|
||||
<!-- unused for now (except for Android), as we oddly ship addressbook as a .war -->
|
||||
<target name="jar" depends="compile, changes">
|
||||
<jar basedir="${build}" destfile="${dist}/${jar}">
|
||||
<!-- set if unset -->
|
||||
<property name="workspace.changes" value="" />
|
||||
<jar basedir="${build}" destfile="${dist}/${jar}">
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="addressbook.Daemon"/>
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
@ -64,7 +90,7 @@
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="war" depends="compile, changes, warUpToDate" unless="war.uptodate">
|
||||
<target name="war" depends="compileServlet, changes, warUpToDate" unless="war.uptodate">
|
||||
<mkdir dir="${dist}/tmp"/>
|
||||
<mkdir dir="${dist}/tmp/WEB-INF"/>
|
||||
<mkdir dir="${dist}/tmp/WEB-INF/classes"/>
|
||||
|
@ -64,10 +64,11 @@ class ConfigParser {
|
||||
if (inputLine.startsWith(";")) {
|
||||
return "";
|
||||
}
|
||||
if (inputLine.split("#").length > 0) {
|
||||
return inputLine.split("#")[0];
|
||||
int hash = inputLine.indexOf('#');
|
||||
if (hash >= 0) {
|
||||
return inputLine.substring(0, hash);
|
||||
} else {
|
||||
return "";
|
||||
return inputLine;
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +92,7 @@ class ConfigParser {
|
||||
String inputLine;
|
||||
inputLine = input.readLine();
|
||||
while (inputLine != null) {
|
||||
inputLine = ConfigParser.stripComments(inputLine);
|
||||
inputLine = stripComments(inputLine);
|
||||
String[] splitLine = inputLine.split("=");
|
||||
if (splitLine.length == 2) {
|
||||
result.put(splitLine[0].trim().toLowerCase(Locale.US), splitLine[1].trim());
|
||||
@ -116,7 +117,7 @@ class ConfigParser {
|
||||
FileInputStream fileStream = new FileInputStream(file);
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(
|
||||
fileStream));
|
||||
Map<String, String> rv = ConfigParser.parse(input);
|
||||
Map<String, String> rv = parse(input);
|
||||
try {
|
||||
fileStream.close();
|
||||
} catch (IOException ioe) {}
|
||||
@ -136,7 +137,7 @@ class ConfigParser {
|
||||
public static Map<String, String> parse(String string) throws IOException {
|
||||
StringReader stringReader = new StringReader(string);
|
||||
BufferedReader input = new BufferedReader(stringReader);
|
||||
return ConfigParser.parse(input);
|
||||
return parse(input);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,7 +154,7 @@ class ConfigParser {
|
||||
public static Map<String, String> parse(File file, Map<String, String> map) {
|
||||
Map<String, String> result;
|
||||
try {
|
||||
result = ConfigParser.parse(file);
|
||||
result = parse(file);
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
if (!result.containsKey(entry.getKey()))
|
||||
result.put(entry.getKey(), entry.getValue());
|
||||
@ -161,7 +162,7 @@ class ConfigParser {
|
||||
} catch (IOException exp) {
|
||||
result = map;
|
||||
try {
|
||||
ConfigParser.write(result, file);
|
||||
write(result, file);
|
||||
} catch (IOException exp2) {
|
||||
}
|
||||
}
|
||||
@ -182,7 +183,7 @@ class ConfigParser {
|
||||
List<String> result = new LinkedList<String>();
|
||||
String inputLine = input.readLine();
|
||||
while (inputLine != null) {
|
||||
inputLine = ConfigParser.stripComments(inputLine).trim();
|
||||
inputLine = stripComments(inputLine).trim();
|
||||
if (inputLine.length() > 0) {
|
||||
result.add(inputLine);
|
||||
}
|
||||
@ -205,7 +206,7 @@ class ConfigParser {
|
||||
FileInputStream fileStream = new FileInputStream(file);
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(
|
||||
fileStream));
|
||||
List<String> rv = ConfigParser.parseSubscriptions(input);
|
||||
List<String> rv = parseSubscriptions(input);
|
||||
try {
|
||||
fileStream.close();
|
||||
} catch (IOException ioe) {}
|
||||
@ -224,7 +225,7 @@ class ConfigParser {
|
||||
public static List<String> parseSubscriptions(String string) throws IOException {
|
||||
StringReader stringReader = new StringReader(string);
|
||||
BufferedReader input = new BufferedReader(stringReader);
|
||||
return ConfigParser.parseSubscriptions(input);
|
||||
return parseSubscriptions(input);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -234,18 +235,30 @@ class ConfigParser {
|
||||
*
|
||||
* @param file
|
||||
* A File to attempt to parse.
|
||||
* @param list list of files to parse
|
||||
* @param list The default subscriptions to be saved and returned if the file cannot be read
|
||||
* @return A List consisting of one element for each line in file, or if
|
||||
* file cannot be read, list.
|
||||
*/
|
||||
public static List<String> parseSubscriptions(File file, List<String> list) {
|
||||
List<String> result;
|
||||
try {
|
||||
result = ConfigParser.parseSubscriptions(file);
|
||||
result = parseSubscriptions(file);
|
||||
// Fix up files that contain the old default
|
||||
// which was changed in 0.9.11
|
||||
if (result.remove(Daemon.OLD_DEFAULT_SUB)) {
|
||||
for (String sub : list) {
|
||||
if (!result.contains(sub))
|
||||
result.add(sub);
|
||||
}
|
||||
try {
|
||||
writeSubscriptions(result, file);
|
||||
// TODO log
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
} catch (IOException exp) {
|
||||
result = list;
|
||||
try {
|
||||
ConfigParser.writeSubscriptions(result, file);
|
||||
writeSubscriptions(result, file);
|
||||
} catch (IOException exp2) {
|
||||
}
|
||||
}
|
||||
@ -289,8 +302,7 @@ class ConfigParser {
|
||||
boolean success = false;
|
||||
if (!isWindows) {
|
||||
File tmp = SecureFile.createTempFile("temp-", ".tmp", file.getAbsoluteFile().getParentFile());
|
||||
ConfigParser
|
||||
.write(map, new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(tmp), "UTF-8")));
|
||||
write(map, new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(tmp), "UTF-8")));
|
||||
success = tmp.renameTo(file);
|
||||
if (!success) {
|
||||
tmp.delete();
|
||||
@ -299,8 +311,7 @@ class ConfigParser {
|
||||
}
|
||||
if (!success) {
|
||||
// hmm, that didn't work, try it the old way
|
||||
ConfigParser
|
||||
.write(map, new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8")));
|
||||
write(map, new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8")));
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,7 +348,7 @@ class ConfigParser {
|
||||
*/
|
||||
public static void writeSubscriptions(List<String> list, File file)
|
||||
throws IOException {
|
||||
ConfigParser.writeSubscriptions(list, new BufferedWriter(
|
||||
writeSubscriptions(list, new BufferedWriter(
|
||||
new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8")));
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,9 @@ public class Daemon {
|
||||
private static final Daemon _instance = new Daemon();
|
||||
private volatile boolean _running;
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String DEFAULT_SUB = "http://i2p-projekt.i2p/hosts.txt";
|
||||
/** @since 0.9.12 */
|
||||
static final String OLD_DEFAULT_SUB = "http://www.i2p2.i2p/hosts.txt";
|
||||
|
||||
/**
|
||||
* Update the router and published address books using remote data from the
|
||||
@ -253,7 +256,7 @@ public class Daemon {
|
||||
|
||||
List<String> defaultSubs = new LinkedList<String>();
|
||||
// defaultSubs.add("http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/i2p/hosts.txt");
|
||||
defaultSubs.add("http://www.i2p2.i2p/hosts.txt");
|
||||
defaultSubs.add(DEFAULT_SUB);
|
||||
|
||||
SubscriptionList subscriptions = new SubscriptionList(subscriptionFile,
|
||||
etagsFile, lastModifiedFile, lastFetchedFile, delay, defaultSubs, settings
|
||||
|
@ -41,7 +41,9 @@ import javax.servlet.http.HttpServletResponse;
|
||||
*
|
||||
*/
|
||||
public class Servlet extends HttpServlet {
|
||||
private DaemonThread thread;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private transient DaemonThread thread;
|
||||
//private String nonce;
|
||||
//private static final String PROP_NONCE = "addressbook.nonce";
|
||||
|
||||
|
@ -4,6 +4,15 @@
|
||||
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
|
||||
|
||||
<web-app>
|
||||
<filter>
|
||||
<filter-name>XSSFilter</filter-name>
|
||||
<filter-class>net.i2p.servlet.filters.XSSFilter</filter-class>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>XSSFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>addressbook</servlet-name>
|
||||
<servlet-class>net.i2p.addressbook.Servlet</servlet-class>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/i2p_router"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/i2p_sdk"/>
|
||||
<classpathentry kind="lib" path="/lib/wrapper/all/wrapper.jar"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/installer"/>
|
||||
<classpathentry kind="output" path="build"/>
|
||||
</classpath>
|
||||
|
@ -7,12 +7,13 @@
|
||||
<property name="jar" value="desktopgui.jar"/>
|
||||
<property name="resources" value="resources"/>
|
||||
<property name="javadoc" value="javadoc"/>
|
||||
<property name="javac.compilerargs" value=""/>
|
||||
<property name="javac.version" value="1.6" />
|
||||
<property name="require.gettext" value="true" />
|
||||
|
||||
<condition property="no.bundle">
|
||||
<isfalse value="${require.gettext}" />
|
||||
</condition>
|
||||
<property name="javac.compilerargs" value=""/>
|
||||
<property name="require.gettext" value="true" />
|
||||
|
||||
<target name="init">
|
||||
<mkdir dir="${build}"/>
|
||||
@ -27,7 +28,7 @@
|
||||
</target>
|
||||
|
||||
<target name="compile" depends="init">
|
||||
<javac debug="true" deprecation="on" source="1.5" target="1.5"
|
||||
<javac debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
|
||||
includeAntRuntime="false"
|
||||
srcdir="${src}" destdir="${build}">
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
|
@ -4,14 +4,15 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# testsubject67 <deborinha97@hotmail.com>, 2014
|
||||
# blueboy, 2013
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2011-03-03 18:29+0000\n"
|
||||
"PO-Revision-Date: 2013-11-23 16:31+0000\n"
|
||||
"Last-Translator: blueboy\n"
|
||||
"POT-Creation-Date: 2014-01-09 19:27+0000\n"
|
||||
"PO-Revision-Date: 2014-07-05 17:40+0000\n"
|
||||
"Last-Translator: testsubject67 <deborinha97@hotmail.com>\n"
|
||||
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/I2P/language/pt_BR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -33,11 +34,11 @@ msgstr "Conectando"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:26
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr ""
|
||||
msgstr "Lançar o navegador I2P "
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:50
|
||||
msgid "Configure desktopgui"
|
||||
msgstr ""
|
||||
msgstr "Configurar desktopgui"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:67
|
||||
msgid "Restart I2P"
|
||||
@ -47,10 +48,10 @@ msgstr "Reinicializar o roteador I2P"
|
||||
msgid "Stop I2P"
|
||||
msgstr "Interromper o roteador I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:43
|
||||
msgid "Tray icon configuration"
|
||||
msgstr ""
|
||||
msgstr "Configuração de ícone de bandeja"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:46
|
||||
msgid "Should tray icon be enabled?"
|
||||
msgstr ""
|
||||
msgstr "Ativar ícone de bandeja?"
|
||||
|
57
apps/desktopgui/locale/messages_sk.po
Normal file
@ -0,0 +1,57 @@
|
||||
# I2P
|
||||
# Copyright (C) 2009 The I2P Project
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Krantišek <jaksrn@gmail.com>, 2014
|
||||
# Svistwarrior273 <romanbeno273@gmail.com>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-01-09 19:27+0000\n"
|
||||
"PO-Revision-Date: 2014-04-20 09:56+0000\n"
|
||||
"Last-Translator: Svistwarrior273 <romanbeno273@gmail.com>\n"
|
||||
"Language-Team: Slovak (http://www.transifex.com/projects/p/I2P/language/sk/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: sk\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:23
|
||||
msgid "Start I2P"
|
||||
msgstr "Spustiť I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P sa spúšťa!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
|
||||
msgid "Starting"
|
||||
msgstr "Spúšťa sa"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:26
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Spustiť I2P prehliadač"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:50
|
||||
msgid "Configure desktopgui"
|
||||
msgstr "Nakonfigurovať desktopgui"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:67
|
||||
msgid "Restart I2P"
|
||||
msgstr "Reštartovať I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:85
|
||||
msgid "Stop I2P"
|
||||
msgstr "Zastaviť I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:43
|
||||
msgid "Tray icon configuration"
|
||||
msgstr "Konfigurácia ikony v lište"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:46
|
||||
msgid "Should tray icon be enabled?"
|
||||
msgstr "Mala by byť ikona v lište povolená?"
|
@ -3,20 +3,22 @@
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# <gribua@gmail.com>, 2011.
|
||||
# Translators:
|
||||
# Denis Blank <gribua@gmail.com>, 2011
|
||||
# LinuxChata, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: https://trac.i2p2.de/\n"
|
||||
"POT-Creation-Date: 2011-03-03 18:29+0000\n"
|
||||
"PO-Revision-Date: 2011-06-19 14:01+0000\n"
|
||||
"Last-Translator: Pharmasolin <gribua@gmail.com>\n"
|
||||
"Language-Team: Ukrainian (Ukraine) (http://www.transifex.net/projects/p/I2P/team/uk_UA/)\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-01-09 19:27+0000\n"
|
||||
"PO-Revision-Date: 2014-06-22 10:20+0000\n"
|
||||
"Last-Translator: LinuxChata\n"
|
||||
"Language-Team: Ukrainian (Ukraine) (http://www.transifex.com/projects/p/I2P/language/uk_UA/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: uk_UA\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:23
|
||||
msgid "Start I2P"
|
||||
@ -46,12 +48,10 @@ msgstr "Перезапустити I2P"
|
||||
msgid "Stop I2P"
|
||||
msgstr "Зупинити I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:43
|
||||
msgid "Tray icon configuration"
|
||||
msgstr "Настройка трей-іконки"
|
||||
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
|
||||
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:46
|
||||
msgid "Should tray icon be enabled?"
|
||||
msgstr "Чм повинна трей-іконка бути включена?"
|
||||
|
||||
|
||||
msgstr "Чи повинна трей-іконка бути включена?"
|
||||
|
@ -78,7 +78,7 @@ public class ConfigurationManager {
|
||||
* @return The value of a configuration: true if found, defaultValue if not found.
|
||||
*/
|
||||
public boolean getBooleanConfiguration(String arg, boolean defaultValue) {
|
||||
Boolean value = ((Boolean) booleanConfigurations.get("startWithI2P"));
|
||||
Boolean value = booleanConfigurations.get("startWithI2P");
|
||||
System.out.println(value);
|
||||
if(value != null) {
|
||||
return value;
|
||||
|
@ -17,29 +17,28 @@
|
||||
<classpath>
|
||||
<pathelement location="../../../core/java/build/obj" />
|
||||
<pathelement location="../../ministreaming/java/build/obj" />
|
||||
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-servlet.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-util.jar" />
|
||||
</classpath>
|
||||
</depend>
|
||||
</target>
|
||||
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="javac.version" value="1.6" />
|
||||
<property name="require.gettext" value="true" />
|
||||
|
||||
<condition property="no.bundle">
|
||||
<isfalse value="${require.gettext}" />
|
||||
</condition>
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="require.gettext" value="true" />
|
||||
|
||||
<target name="compile" depends="depend">
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/obj" />
|
||||
<javac
|
||||
srcdir="./src"
|
||||
debug="true" deprecation="on" source="1.5" target="1.5"
|
||||
debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
|
||||
destdir="./build/obj"
|
||||
includeAntRuntime="false"
|
||||
classpath="../../../core/java/build/i2p.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar:../../jetty/jettylib/jetty-servlet.jar:../../jetty/jettylib/jetty-util.jar:../../ministreaming/java/build/mstreaming.jar" >
|
||||
classpath="../../../core/java/build/i2p.jar:../../jetty/jettylib/javax.servlet.jar:../../ministreaming/java/build/mstreaming.jar" >
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
</javac>
|
||||
</target>
|
||||
@ -101,15 +100,15 @@
|
||||
<target name="war" depends="jar, bundle, warUpToDate, listChangedFiles" unless="war.uptodate" >
|
||||
<!-- set if unset -->
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
<copy todir="build/icons/.icons" >
|
||||
<fileset dir="../icons/" />
|
||||
<copy todir="build/resources/.resources" >
|
||||
<fileset dir="../resources/" />
|
||||
</copy>
|
||||
<!-- mime.properties must be in with the classes -->
|
||||
<copy file="../mime.properties" todir="build/obj/org/klomp/snark/web" />
|
||||
<war destfile="../i2psnark.war" webxml="../web.xml" >
|
||||
<!-- include only the web stuff, as of 0.7.12 the router will add i2psnark.jar to the classpath for the war -->
|
||||
<classes dir="./build/obj" includes="**/web/*" />
|
||||
<fileset dir="build/icons/" />
|
||||
<fileset dir="build/resources/" />
|
||||
<manifest>
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
@ -122,7 +121,7 @@
|
||||
|
||||
<target name="warUpToDate">
|
||||
<uptodate property="war.uptodate" targetfile="../i2psnark.war" >
|
||||
<srcfiles dir= "." includes="build/obj/org/klomp/snark/web/*.class ../icons/* ../web.xml" />
|
||||
<srcfiles dir= "." includes="build/obj/org/klomp/snark/web/*.class ../resources/**/* ../web.xml" />
|
||||
</uptodate>
|
||||
</target>
|
||||
|
||||
|
@ -54,7 +54,15 @@ public interface CompleteListener {
|
||||
*/
|
||||
public void gotPiece(Snark snark);
|
||||
|
||||
// not really listeners but the easiest way to get back to an optional SnarkManager
|
||||
/** not really listeners but the easiest way to get back to an optional SnarkManager */
|
||||
public long getSavedTorrentTime(Snark snark);
|
||||
public BitField getSavedTorrentBitField(Snark snark);
|
||||
/**
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public boolean getSavedPreserveNamesSetting(Snark snark);
|
||||
/**
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public long getSavedUploaded(Snark snark);
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ abstract class ExtensionHandler {
|
||||
public static final int ID_DHT = 3;
|
||||
/** not using the option bit since the compact format is different */
|
||||
public static final String TYPE_DHT = "i2p_dht";
|
||||
/** Pieces * SHA1 Hash length, + 25% extra for file names, benconding overhead, etc */
|
||||
/** Pieces * SHA1 Hash length, + 25% extra for file names, bencoding overhead, etc */
|
||||
private static final int MAX_METADATA_SIZE = Storage.MAX_PIECES * 20 * 5 / 4;
|
||||
private static final int PARALLEL_REQUESTS = 3;
|
||||
|
||||
|
@ -3,12 +3,15 @@ package org.klomp.snark;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PSession;
|
||||
@ -71,7 +74,6 @@ public class I2PSnarkUtil {
|
||||
private static final int EEPGET_CONNECT_TIMEOUT_SHORT = 5*1000;
|
||||
public static final int DEFAULT_STARTUP_DELAY = 3;
|
||||
public static final boolean DEFAULT_USE_OPENTRACKERS = true;
|
||||
public static final String DEFAULT_OPENTRACKERS = "http://tracker.welterde.i2p/a";
|
||||
public static final int DEFAULT_MAX_UP_BW = 8; //KBps
|
||||
public static final int MAX_CONNECTIONS = 16; // per torrent
|
||||
public static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond";
|
||||
@ -99,8 +101,7 @@ public class I2PSnarkUtil {
|
||||
_maxConnections = MAX_CONNECTIONS;
|
||||
_startupDelay = DEFAULT_STARTUP_DELAY;
|
||||
_shouldUseOT = DEFAULT_USE_OPENTRACKERS;
|
||||
// FIXME split if default has more than one
|
||||
_openTrackers = Collections.singletonList(DEFAULT_OPENTRACKERS);
|
||||
_openTrackers = Collections.emptyList();
|
||||
_shouldUseDHT = DEFAULT_USE_DHT;
|
||||
// This is used for both announce replies and .torrent file downloads,
|
||||
// so it must be available even if not connected to I2CP.
|
||||
@ -565,12 +566,12 @@ public class I2PSnarkUtil {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/** @param ot non-null */
|
||||
/** @param ot non-null list of announce URLs */
|
||||
public void setOpenTrackers(List<String> ot) {
|
||||
_openTrackers = ot;
|
||||
}
|
||||
|
||||
/** List of open trackers to use as backups
|
||||
/** List of open tracker announce URLs to use as backups
|
||||
* @return non-null, possibly unmodifiable, empty if disabled
|
||||
*/
|
||||
public List<String> getOpenTrackers() {
|
||||
@ -580,7 +581,22 @@ public class I2PSnarkUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* List of open trackers to use as backups even if disabled
|
||||
* Is this announce URL probably for an open tracker?
|
||||
*
|
||||
* @since 0.9.17
|
||||
*/
|
||||
public boolean isKnownOpenTracker(String url) {
|
||||
try {
|
||||
URL u = new URL(url);
|
||||
String host = u.getHost();
|
||||
return host != null && SnarkManager.KNOWN_OPENTRACKERS.contains(host);
|
||||
} catch (MalformedURLException mue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List of open tracker announce URLs to use as backups even if disabled
|
||||
* @return non-null
|
||||
* @since 0.9.4
|
||||
*/
|
||||
|
@ -66,7 +66,8 @@ class IdleChecker extends SimpleTimer2.TimedEvent {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Closing tunnels on idle");
|
||||
_util.disconnect();
|
||||
_mgr.addMessage(_util.getString("I2P tunnel closed."));
|
||||
_mgr.addMessage(_util.getString("No more torrents running.") + ' ' +
|
||||
_util.getString("I2P tunnel closed."));
|
||||
schedule(3 * CHECK_TIME);
|
||||
return;
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ class MagnetState {
|
||||
* @return true if this was the last piece
|
||||
* @throws NPE, IllegalArgumentException, IOException, ...
|
||||
*/
|
||||
public MetaInfo buildMetaInfo() throws Exception {
|
||||
private MetaInfo buildMetaInfo() throws Exception {
|
||||
// top map has nothing in it but the info map (no announce)
|
||||
Map<String, BEValue> map = new HashMap<String, BEValue>();
|
||||
InputStream is = new ByteArrayInputStream(metainfoBytes);
|
||||
|
@ -42,7 +42,7 @@ public class MagnetURI {
|
||||
name = util.getString("Magnet") + ' ' + ihash;
|
||||
String dn = getParam("dn", url);
|
||||
if (dn != null)
|
||||
name += " (" + Storage.filterName(dn) + ')';
|
||||
name += " (" + dn + ')';
|
||||
} else if (url.startsWith(MAGGOT)) {
|
||||
// maggot://0691e40aae02e552cfcb57af1dca56214680c0c5:0b557bbdf8718e95d352fbe994dec3a383e2ede7
|
||||
ihash = url.substring(MAGGOT.length()).trim();
|
||||
@ -82,7 +82,7 @@ public class MagnetURI {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return pretty name or null
|
||||
* @return pretty name or null, NOT HTML escaped
|
||||
*/
|
||||
public String getName() {
|
||||
return _name;
|
||||
@ -175,18 +175,25 @@ public class MagnetURI {
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode %xx encoding, convert to UTF-8 if necessary
|
||||
* Copied from i2ptunnel LocalHTTPServer
|
||||
* Decode %xx encoding, convert to UTF-8 if necessary.
|
||||
* Copied from i2ptunnel LocalHTTPServer.
|
||||
* Also converts '+' to ' ' so the dn parameter comes out right
|
||||
* These are coming in via a application/x-www-form-urlencoded form so
|
||||
* the pluses are in there...
|
||||
* hopefully any real + is encoded as %2B.
|
||||
*
|
||||
* @since 0.9.1
|
||||
*/
|
||||
private static String decode(String s) {
|
||||
if (!s.contains("%"))
|
||||
if (!(s.contains("%") || s.contains("+")))
|
||||
return s;
|
||||
StringBuilder buf = new StringBuilder(s.length());
|
||||
boolean utf8 = false;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c != '%') {
|
||||
if (c == '+') {
|
||||
buf.append(' ');
|
||||
} else if (c != '%') {
|
||||
buf.append(c);
|
||||
} else {
|
||||
try {
|
||||
|
@ -220,7 +220,9 @@ public class MetaInfo
|
||||
Object o = val.getValue();
|
||||
// Is it supposed to be a number or a string?
|
||||
// i2psnark does it as a string. BEP 27 doesn't say.
|
||||
// Transmission does numbers.
|
||||
// Transmission does numbers. So does libtorrent.
|
||||
// We handle both as of 0.9.9.
|
||||
// We switch to storing as number as of 0.9.14.
|
||||
privateTorrent = "1".equals(o) ||
|
||||
((o instanceof Number) && ((Number) o).intValue() == 1);
|
||||
} else {
|
||||
@ -515,9 +517,10 @@ public class MetaInfo
|
||||
|
||||
sha1.update(bs, off, length);
|
||||
byte[] hash = sha1.digest();
|
||||
for (int i = 0; i < 20; i++)
|
||||
for (int i = 0; i < 20; i++) {
|
||||
if (hash[i] != piece_hashes[20 * piece + i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -537,9 +540,10 @@ public class MetaInfo
|
||||
_log.warn("Error checking", ioe);
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < 20; i++)
|
||||
for (int i = 0; i < 20; i++) {
|
||||
if (hash[i] != piece_hashes[20 * piece + i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -621,7 +625,9 @@ public class MetaInfo
|
||||
info.put("name.utf-8", new BEValue(DataHelper.getUTF8(name_utf8)));
|
||||
// BEP 27
|
||||
if (privateTorrent)
|
||||
info.put("private", new BEValue(DataHelper.getUTF8("1")));
|
||||
// switched to number in 0.9.14
|
||||
//info.put("private", new BEValue(DataHelper.getUTF8("1")));
|
||||
info.put("private", new BEValue(Integer.valueOf(1)));
|
||||
|
||||
info.put("piece length", new BEValue(Integer.valueOf(piece_length)));
|
||||
info.put("pieces", new BEValue(piece_hashes));
|
||||
|
@ -57,8 +57,8 @@ public class Peer implements Comparable<Peer>
|
||||
private DataOutputStream dout;
|
||||
|
||||
/** running counters */
|
||||
private long downloaded;
|
||||
private long uploaded;
|
||||
private final AtomicLong downloaded = new AtomicLong();
|
||||
private final AtomicLong uploaded = new AtomicLong();
|
||||
|
||||
// Keeps state for in/out connections. Non-null when the handshake
|
||||
// was successful, the connection setup and runs
|
||||
@ -194,9 +194,8 @@ public class Peer implements Comparable<Peer>
|
||||
* Compares the PeerIDs.
|
||||
* @deprecated unused?
|
||||
*/
|
||||
public int compareTo(Peer o)
|
||||
public int compareTo(Peer p)
|
||||
{
|
||||
Peer p = (Peer)o;
|
||||
int rv = peerID.compareTo(p.peerID);
|
||||
if (rv == 0) {
|
||||
if (_id > p._id) return 1;
|
||||
@ -619,7 +618,7 @@ public class Peer implements Comparable<Peer>
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public void downloaded(int size) {
|
||||
downloaded += size;
|
||||
downloaded.addAndGet(size);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -627,7 +626,7 @@ public class Peer implements Comparable<Peer>
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public void uploaded(int size) {
|
||||
uploaded += size;
|
||||
uploaded.addAndGet(size);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -636,7 +635,7 @@ public class Peer implements Comparable<Peer>
|
||||
*/
|
||||
public long getDownloaded()
|
||||
{
|
||||
return downloaded;
|
||||
return downloaded.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -645,7 +644,7 @@ public class Peer implements Comparable<Peer>
|
||||
*/
|
||||
public long getUploaded()
|
||||
{
|
||||
return uploaded;
|
||||
return uploaded.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -653,8 +652,8 @@ public class Peer implements Comparable<Peer>
|
||||
*/
|
||||
public void resetCounters()
|
||||
{
|
||||
downloaded = 0;
|
||||
uploaded = 0;
|
||||
downloaded.set(0);
|
||||
uploaded.set(0);
|
||||
}
|
||||
|
||||
public long getInactiveTime() {
|
||||
|
@ -221,7 +221,8 @@ class PeerCheckerTask implements Runnable
|
||||
peer.keepAlive();
|
||||
// announce them to local tracker (TrackerClient does this too)
|
||||
if (dht != null && (_runCount % 5) == 0) {
|
||||
dht.announce(coordinator.getInfoHash(), peer.getPeerID().getDestHash());
|
||||
dht.announce(coordinator.getInfoHash(), peer.getPeerID().getDestHash(),
|
||||
peer.isCompleted());
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,7 +271,7 @@ class PeerCheckerTask implements Runnable
|
||||
|
||||
// announce ourselves to local tracker (TrackerClient does this too)
|
||||
if (dht != null && (_runCount % 16) == 0) {
|
||||
dht.announce(coordinator.getInfoHash());
|
||||
dht.announce(coordinator.getInfoHash(), coordinator.completed());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ class PeerConnectionOut implements Runnable
|
||||
nm = null;
|
||||
}
|
||||
|
||||
if (m == null && nm != null)
|
||||
if (nm != null)
|
||||
{
|
||||
m = nm;
|
||||
//SimpleTimer.getInstance().removeEvent(nm.expireEvent);
|
||||
|
@ -281,6 +281,14 @@ class PeerCoordinator implements PeerListener
|
||||
return uploaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial total of uploaded bytes of all peers (from a saved status)
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public void setUploaded(long up) {
|
||||
uploaded = up;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of downloaded bytes of all peers.
|
||||
*/
|
||||
@ -1202,22 +1210,23 @@ class PeerCoordinator implements PeerListener
|
||||
boolean skipped = false;
|
||||
for(Piece piece : wantedPieces) {
|
||||
if (piece.getId() == savedPiece) {
|
||||
if (peer.isCompleted() && piece.getPeerCount() > 1) {
|
||||
if (peer.isCompleted() && piece.getPeerCount() > 1 &&
|
||||
wantedPieces.size() > 2*END_GAME_THRESHOLD) {
|
||||
// Try to preserve rarest-first
|
||||
// by not requesting a partial piece that non-seeders also have
|
||||
// by not requesting a partial piece that at least two non-seeders also have
|
||||
// from a seeder
|
||||
boolean nonSeeds = false;
|
||||
int nonSeeds = 0;
|
||||
for (Peer pr : peers) {
|
||||
PeerState state = pr.state;
|
||||
if (state == null) continue;
|
||||
BitField bf = state.bitfield;
|
||||
if (bf == null) continue;
|
||||
if (bf.get(savedPiece) && !pr.isCompleted()) {
|
||||
nonSeeds = true;
|
||||
break;
|
||||
if (++nonSeeds > 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nonSeeds) {
|
||||
if (nonSeeds > 1) {
|
||||
skipped = true;
|
||||
break;
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ import org.klomp.snark.bencode.InvalidBEncodingException;
|
||||
* and the PeerID is not required.
|
||||
* Equality is now determined solely by the dest hash.
|
||||
*/
|
||||
class PeerID implements Comparable<PeerID>
|
||||
public class PeerID implements Comparable<PeerID>
|
||||
{
|
||||
private byte[] id;
|
||||
private Destination address;
|
||||
@ -52,6 +52,7 @@ class PeerID implements Comparable<PeerID>
|
||||
private boolean triedDestLookup;
|
||||
private final int hash;
|
||||
private final I2PSnarkUtil util;
|
||||
private String _toStringCache;
|
||||
|
||||
public PeerID(byte[] id, Destination address)
|
||||
{
|
||||
@ -216,13 +217,15 @@ class PeerID implements Comparable<PeerID>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the String "id@address" where id is the base64 encoded id
|
||||
* and address is the base64 dest (was the base64 hash of the dest) which
|
||||
* Returns the String "id@address" where id is the first 4 chars of the base64 encoded id
|
||||
* and address is the first 6 chars of the base64 dest (was the base64 hash of the dest) which
|
||||
* should match what the bytemonsoon tracker reports on its web pages.
|
||||
*/
|
||||
@Override
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if (_toStringCache != null)
|
||||
return _toStringCache;
|
||||
if (id == null || address == null)
|
||||
return "unkn@" + Base64.encode(destHash).substring(0, 6);
|
||||
int nonZero = 0;
|
||||
@ -232,7 +235,8 @@ class PeerID implements Comparable<PeerID>
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Base64.encode(id, nonZero, id.length-nonZero).substring(0,4) + "@" + address.toBase64().substring(0,6);
|
||||
_toStringCache = Base64.encode(id, nonZero, id.length-nonZero).substring(0,4) + "@" + address.toBase64().substring(0,6);
|
||||
return _toStringCache;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,8 +37,8 @@ class PeerMonitorTask implements Runnable
|
||||
|
||||
private final PeerCoordinator coordinator;
|
||||
|
||||
private long lastDownloaded = 0;
|
||||
private long lastUploaded = 0;
|
||||
//private long lastDownloaded = 0;
|
||||
//private long lastUploaded = 0;
|
||||
|
||||
PeerMonitorTask(PeerCoordinator coordinator)
|
||||
{
|
||||
|
@ -163,7 +163,7 @@ class PeerState implements DataLoader
|
||||
_log.debug(peer + " rcv bitfield");
|
||||
if (bitfield != null)
|
||||
{
|
||||
// XXX - Be liberal in what you except?
|
||||
// XXX - Be liberal in what you accept?
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Got unexpected bitfield message from " + peer);
|
||||
return;
|
||||
|
@ -27,13 +27,13 @@ import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFile;
|
||||
|
||||
/**
|
||||
* Main Snark program startup class.
|
||||
@ -69,8 +69,6 @@ public class Snark
|
||||
"Commands: 'info', 'list', 'quit'.";
|
||||
****/
|
||||
|
||||
// String indicating main activity
|
||||
String activity = "Not started";
|
||||
|
||||
/****
|
||||
private static class OOMListener implements I2PThread.OOMEventListener {
|
||||
@ -223,11 +221,11 @@ public class Snark
|
||||
private PeerCoordinator coordinator;
|
||||
private ConnectionAcceptor acceptor;
|
||||
private TrackerClient trackerclient;
|
||||
private String rootDataDir = ".";
|
||||
private final File rootDataDir;
|
||||
private final CompleteListener completeListener;
|
||||
private volatile boolean stopped;
|
||||
private volatile boolean starting;
|
||||
private byte[] id;
|
||||
private final byte[] id;
|
||||
private final byte[] infoHash;
|
||||
private String additionalTrackerURL;
|
||||
protected final I2PSnarkUtil _util;
|
||||
@ -236,15 +234,29 @@ public class Snark
|
||||
private volatile String trackerProblems;
|
||||
private volatile int trackerSeenPeers;
|
||||
private volatile boolean _autoStoppable;
|
||||
// String indicating main activity
|
||||
private volatile String activity = "Not started";
|
||||
private final long savedUploaded;
|
||||
|
||||
|
||||
/** from main() via parseArguments() single torrent */
|
||||
/**
|
||||
* from main() via parseArguments() single torrent
|
||||
*
|
||||
* @deprecated unused
|
||||
*/
|
||||
/****
|
||||
Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
|
||||
StorageListener slistener, CoordinatorListener clistener) {
|
||||
this(util, torrent, ip, user_port, slistener, clistener, null, null, null, true, ".");
|
||||
}
|
||||
****/
|
||||
|
||||
/** single torrent - via router */
|
||||
/**
|
||||
* single torrent - via router
|
||||
*
|
||||
* @deprecated unused
|
||||
*/
|
||||
/****
|
||||
public Snark(I2PAppContext ctx, Properties opts, String torrent,
|
||||
StorageListener slistener, boolean start, String rootDir) {
|
||||
this(new I2PSnarkUtil(ctx), torrent, null, -1, slistener, null, null, null, null, false, rootDir);
|
||||
@ -274,12 +286,30 @@ public class Snark
|
||||
if (start)
|
||||
this.startTorrent();
|
||||
}
|
||||
****/
|
||||
|
||||
/** multitorrent */
|
||||
/**
|
||||
* multitorrent
|
||||
*/
|
||||
public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
|
||||
StorageListener slistener, CoordinatorListener clistener,
|
||||
CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet,
|
||||
ConnectionAcceptor connectionAcceptor, boolean start, String rootDir)
|
||||
{
|
||||
this(util, torrent, ip, user_port, slistener, clistener, complistener,
|
||||
peerCoordinatorSet, connectionAcceptor, start, rootDir, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* multitorrent
|
||||
*
|
||||
* @param baseFile if null, use rootDir/torrentName; if non-null, use it instead
|
||||
* @since 0.9.11
|
||||
*/
|
||||
public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
|
||||
StorageListener slistener, CoordinatorListener clistener,
|
||||
CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet,
|
||||
ConnectionAcceptor connectionAcceptor, boolean start, String rootDir, File baseFile)
|
||||
{
|
||||
if (slistener == null)
|
||||
slistener = this;
|
||||
@ -291,7 +321,7 @@ public class Snark
|
||||
acceptor = connectionAcceptor;
|
||||
|
||||
this.torrent = torrent;
|
||||
this.rootDataDir = rootDir;
|
||||
this.rootDataDir = new File(rootDir);
|
||||
|
||||
stopped = true;
|
||||
activity = "Network setup";
|
||||
@ -317,7 +347,6 @@ public class Snark
|
||||
*/
|
||||
|
||||
// Figure out what the torrent argument represents.
|
||||
meta = null;
|
||||
File f = null;
|
||||
InputStream in = null;
|
||||
byte[] x_infoHash = null;
|
||||
@ -395,13 +424,22 @@ public class Snark
|
||||
try
|
||||
{
|
||||
activity = "Checking storage";
|
||||
storage = new Storage(_util, meta, slistener);
|
||||
boolean shouldPreserve = completeListener != null && completeListener.getSavedPreserveNamesSetting(this);
|
||||
if (baseFile == null) {
|
||||
String base = meta.getName();
|
||||
if (!shouldPreserve)
|
||||
base = Storage.filterName(base);
|
||||
if (_util.getFilesPublic())
|
||||
baseFile = new File(rootDataDir, base);
|
||||
else
|
||||
baseFile = new SecureFile(rootDataDir, base);
|
||||
}
|
||||
storage = new Storage(_util, baseFile, meta, slistener, shouldPreserve);
|
||||
if (completeListener != null) {
|
||||
storage.check(rootDataDir,
|
||||
completeListener.getSavedTorrentTime(this),
|
||||
storage.check(completeListener.getSavedTorrentTime(this),
|
||||
completeListener.getSavedTorrentBitField(this));
|
||||
} else {
|
||||
storage.check(rootDataDir);
|
||||
storage.check();
|
||||
}
|
||||
// have to figure out when to reopen
|
||||
// if (!start)
|
||||
@ -429,6 +467,7 @@ public class Snark
|
||||
trackerclient = new TrackerClient(meta, coordinator);
|
||||
*/
|
||||
|
||||
savedUploaded = (completeListener != null) ? completeListener.getSavedUploaded(this) : 0;
|
||||
if (start)
|
||||
startTorrent();
|
||||
}
|
||||
@ -453,7 +492,8 @@ public class Snark
|
||||
this.torrent = torrent;
|
||||
this.infoHash = ih;
|
||||
this.additionalTrackerURL = trackerURL;
|
||||
this.rootDataDir = rootDir;
|
||||
this.rootDataDir = rootDir != null ? new File(rootDir) : null; // null only for FetchAndAdd extension
|
||||
savedUploaded = 0;
|
||||
stopped = true;
|
||||
id = generateID();
|
||||
|
||||
@ -478,18 +518,13 @@ public class Snark
|
||||
|
||||
// Create a new ID and fill it with something random. First nine
|
||||
// zeros bytes, then three bytes filled with snark and then
|
||||
// sixteen random bytes.
|
||||
// eight random bytes.
|
||||
byte snark = (((3 + 7 + 10) * (1000 - 8)) / 992) - 17;
|
||||
byte[] rv = new byte[20];
|
||||
Random random = I2PAppContext.getGlobalContext().random();
|
||||
int i;
|
||||
for (i = 0; i < 9; i++)
|
||||
rv[i] = 0;
|
||||
rv[i++] = snark;
|
||||
rv[i++] = snark;
|
||||
rv[i++] = snark;
|
||||
while (i < 20)
|
||||
rv[i++] = (byte)random.nextInt(256);
|
||||
rv[9] = snark;
|
||||
rv[10] = snark;
|
||||
rv[11] = snark;
|
||||
I2PAppContext.getGlobalContext().random().nextBytes(rv, 12, 8);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -522,6 +557,7 @@ public class Snark
|
||||
_log.info("Starting PeerCoordinator, ConnectionAcceptor, and TrackerClient");
|
||||
activity = "Collecting pieces";
|
||||
coordinator = new PeerCoordinator(_util, id, infoHash, meta, storage, this, this);
|
||||
coordinator.setUploaded(savedUploaded);
|
||||
if (_peerCoordinatorSet != null) {
|
||||
// multitorrent
|
||||
_peerCoordinatorSet.add(coordinator);
|
||||
@ -548,7 +584,7 @@ public class Snark
|
||||
} else if (trackerclient.halted()) {
|
||||
if (storage != null) {
|
||||
try {
|
||||
storage.reopen(rootDataDir);
|
||||
storage.reopen();
|
||||
} catch (IOException ioe) {
|
||||
try { storage.close(); } catch (IOException ioee) {
|
||||
ioee.printStackTrace();
|
||||
@ -585,7 +621,7 @@ public class Snark
|
||||
pc.halt();
|
||||
Storage st = storage;
|
||||
if (st != null) {
|
||||
boolean changed = storage.isChanged();
|
||||
boolean changed = storage.isChanged() || getUploaded() != savedUploaded;
|
||||
try {
|
||||
storage.close();
|
||||
} catch (IOException ioe) {
|
||||
@ -739,7 +775,7 @@ public class Snark
|
||||
PeerCoordinator coord = coordinator;
|
||||
if (coord != null)
|
||||
return coord.getUploaded();
|
||||
return 0;
|
||||
return savedUploaded;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -763,6 +799,7 @@ public class Snark
|
||||
}
|
||||
|
||||
/**
|
||||
* Not HTML escaped.
|
||||
* @return String returned from tracker, or null if no error
|
||||
* @since 0.8.4
|
||||
*/
|
||||
@ -835,7 +872,7 @@ public class Snark
|
||||
}
|
||||
|
||||
/**
|
||||
* Bytes still wanted. DOES account for skipped files.
|
||||
* Bytes still wanted. DOES account for (i.e. does not include) skipped files.
|
||||
* FIXME -1 when not running.
|
||||
* @return exact value. or -1 if no storage yet or when not running.
|
||||
* @since 0.9.1
|
||||
@ -848,7 +885,7 @@ public class Snark
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not account for skipped files.
|
||||
* Does not account (i.e. includes) for skipped files.
|
||||
* @return number of pieces still needed (magnet mode or not), or -1 if unknown
|
||||
* @since 0.8.4
|
||||
*/
|
||||
@ -919,6 +956,7 @@ public class Snark
|
||||
* non-valid argument list. The given listeners will be
|
||||
* passed to all components that take one.
|
||||
*/
|
||||
/****
|
||||
private static Snark parseArguments(String[] args,
|
||||
StorageListener slistener,
|
||||
CoordinatorListener clistener)
|
||||
@ -933,6 +971,7 @@ public class Snark
|
||||
int i = 0;
|
||||
while (i < args.length)
|
||||
{
|
||||
****/
|
||||
/*
|
||||
if (args[i].equals("--debug"))
|
||||
{
|
||||
@ -954,7 +993,9 @@ public class Snark
|
||||
catch (NumberFormatException nfe) { }
|
||||
}
|
||||
}
|
||||
else */ if (args[i].equals("--port"))
|
||||
else */
|
||||
/****
|
||||
if (args[i].equals("--port"))
|
||||
{
|
||||
if (args.length - 1 < i + 1)
|
||||
usage("--port needs port number to listen on");
|
||||
@ -1060,6 +1101,7 @@ public class Snark
|
||||
System.out.println
|
||||
(" \tor (with --share) a file to share.");
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* Aborts program abnormally.
|
||||
@ -1103,9 +1145,15 @@ public class Snark
|
||||
*/
|
||||
public void gotMetaInfo(PeerCoordinator coordinator, MetaInfo metainfo) {
|
||||
try {
|
||||
String base = Storage.filterName(metainfo.getName());
|
||||
File baseFile;
|
||||
if (_util.getFilesPublic())
|
||||
baseFile = new File(rootDataDir, base);
|
||||
else
|
||||
baseFile = new SecureFile(rootDataDir, base);
|
||||
// The following two may throw IOE...
|
||||
storage = new Storage(_util, metainfo, this);
|
||||
storage.check(rootDataDir);
|
||||
storage = new Storage(_util, baseFile, metainfo, this, false);
|
||||
storage.check();
|
||||
// ... so don't set meta until here
|
||||
meta = metainfo;
|
||||
if (completeListener != null) {
|
||||
@ -1152,8 +1200,8 @@ public class Snark
|
||||
// System.out.println(); // We have all the disk space we need.
|
||||
}
|
||||
|
||||
private boolean allChecked = false;
|
||||
private boolean checking = false;
|
||||
private boolean allChecked;
|
||||
private boolean checking;
|
||||
//private boolean prechecking = true;
|
||||
|
||||
public void storageChecked(Storage storage, int num, boolean checked)
|
||||
@ -1230,6 +1278,7 @@ public class Snark
|
||||
*/
|
||||
final static int MIN_TOTAL_UPLOADERS = 4;
|
||||
final static int MAX_TOTAL_UPLOADERS = 10;
|
||||
|
||||
public boolean overUploadLimit(int uploaders) {
|
||||
if (_peerCoordinatorSet == null || uploaders <= 0)
|
||||
return false;
|
||||
|
@ -7,6 +7,7 @@ import java.io.FileOutputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -14,6 +15,7 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
@ -24,6 +26,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.update.*;
|
||||
@ -38,6 +42,7 @@ import net.i2p.util.SimpleTimer;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
import org.klomp.snark.dht.DHT;
|
||||
import org.klomp.snark.dht.KRPC;
|
||||
|
||||
/**
|
||||
* Manage multiple snarks
|
||||
@ -53,7 +58,10 @@ public class SnarkManager implements CompleteListener {
|
||||
/** used to prevent DirMonitor from deleting torrents that don't have a torrent file yet */
|
||||
private final Set<String> _magnets;
|
||||
private final Object _addSnarkLock;
|
||||
private /* FIXME final FIXME */ File _configFile;
|
||||
private File _configFile;
|
||||
private File _configDir;
|
||||
/** one lock for all config, files for simplicity */
|
||||
private final Object _configLock = new Object();
|
||||
private Properties _config;
|
||||
private final I2PAppContext _context;
|
||||
private final String _contextPath;
|
||||
@ -79,14 +87,22 @@ public class SnarkManager implements CompleteListener {
|
||||
public static final String PROP_UPLOADERS_TOTAL = "i2psnark.uploaders.total";
|
||||
public static final String PROP_UPBW_MAX = "i2psnark.upbw.max";
|
||||
public static final String PROP_DIR = "i2psnark.dir";
|
||||
public static final String PROP_META_PREFIX = "i2psnark.zmeta.";
|
||||
public static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
|
||||
public static final String PROP_META_PRIORITY_SUFFIX = ".priority";
|
||||
public static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet.";
|
||||
private static final String PROP_META_PREFIX = "i2psnark.zmeta.";
|
||||
private static final String PROP_META_STAMP = "stamp";
|
||||
private static final String PROP_META_BASE = "base";
|
||||
private static final String PROP_META_BITFIELD = "bitfield";
|
||||
private static final String PROP_META_PRIORITY = "priority";
|
||||
private static final String PROP_META_PRESERVE_NAMES = "preserveFileNames";
|
||||
private static final String PROP_META_UPLOADED = "uploaded";
|
||||
//private static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
|
||||
//private static final String PROP_META_PRIORITY_SUFFIX = ".priority";
|
||||
private static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet.";
|
||||
|
||||
private static final String CONFIG_FILE_SUFFIX = ".config";
|
||||
private static final String CONFIG_FILE = "i2psnark" + CONFIG_FILE_SUFFIX;
|
||||
public static final String PROP_FILES_PUBLIC = "i2psnark.filesPublic";
|
||||
public static final String PROP_AUTO_START = "i2snark.autoStart"; // oops
|
||||
public static final String PROP_OLD_AUTO_START = "i2snark.autoStart"; // oops
|
||||
public static final String PROP_AUTO_START = "i2psnark.autoStart"; // convert in migration to new config file
|
||||
public static final String DEFAULT_AUTO_START = "false";
|
||||
//public static final String PROP_LINK_PREFIX = "i2psnark.linkPrefix";
|
||||
//public static final String DEFAULT_LINK_PREFIX = "file:///";
|
||||
@ -107,10 +123,16 @@ public class SnarkManager implements CompleteListener {
|
||||
public static final int DEFAULT_STARTUP_DELAY = 3;
|
||||
public static final int DEFAULT_REFRESH_DELAY_SECS = 60;
|
||||
private static final int DEFAULT_PAGE_SIZE = 50;
|
||||
public static final String CONFIG_DIR_SUFFIX = ".d";
|
||||
private static final String SUBDIR_PREFIX = "s";
|
||||
private static final String B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~";
|
||||
|
||||
/**
|
||||
* "name", "announceURL=websiteURL" pairs
|
||||
* '=' in announceURL must be escaped as ,
|
||||
*
|
||||
* Please use host name, not b32 or full dest, in announce URL. Ensure in default hosts.txt.
|
||||
* Please use host name, not b32 or full dest, in website URL. Ensure in default hosts.txt.
|
||||
*/
|
||||
private static final String DEFAULT_TRACKERS[] = {
|
||||
// "Postman", "http://YRgrgTLGnbTq2aZOZDJQ~o6Uk5k6TK-OZtx0St9pb0G-5EGYURZioxqYG8AQt~LgyyI~NCj6aYWpPO-150RcEvsfgXLR~CxkkZcVpgt6pns8SRc3Bi-QSAkXpJtloapRGcQfzTtwllokbdC-aMGpeDOjYLd8b5V9Im8wdCHYy7LRFxhEtGb~RL55DA8aYOgEXcTpr6RPPywbV~Qf3q5UK55el6Kex-6VCxreUnPEe4hmTAbqZNR7Fm0hpCiHKGoToRcygafpFqDw5frLXToYiqs9d4liyVB-BcOb0ihORbo0nS3CLmAwZGvdAP8BZ7cIYE3Z9IU9D1G8JCMxWarfKX1pix~6pIA-sp1gKlL1HhYhPMxwyxvuSqx34o3BqU7vdTYwWiLpGM~zU1~j9rHL7x60pVuYaXcFQDR4-QVy26b6Pt6BlAZoFmHhPcAuWfu-SFhjyZYsqzmEmHeYdAwa~HojSbofg0TMUgESRXMw6YThK1KXWeeJVeztGTz25sL8AAAA.i2p/announce.php=http://tracker.postman.i2p/"
|
||||
@ -123,17 +145,40 @@ public class SnarkManager implements CompleteListener {
|
||||
// , "mastertracker", "http://VzXD~stRKbL3MOmeTn1iaCQ0CFyTmuFHiKYyo0Rd~dFPZFCYH-22rT8JD7i-C2xzYFa4jT5U2aqHzHI-Jre4HL3Ri5hFtZrLk2ax3ji7Qfb6qPnuYkuiF2E2UDmKUOppI8d9Ye7tjdhQVCy0izn55tBaB-U7UWdcvSK2i85sauyw3G0Gfads1Rvy5-CAe2paqyYATcDmGjpUNLoxbfv9KH1KmwRTNH6k1v4PyWYYnhbT39WfKMbBjSxVQRdi19cyJrULSWhjxaQfJHeWx5Z8Ev4bSPByBeQBFl2~4vqy0S5RypINsRSa3MZdbiAAyn5tr5slWR6QdoqY3qBQgBJFZppy-3iWkFqqKgSxCPundF8gdDLC5ddizl~KYcYKl42y9SGFHIukH-TZs8~em0~iahzsqWVRks3zRG~tlBcX2U3M2~OJs~C33-NKhyfZT7-XFBREvb8Szmd~p66jDxrwOnKaku-G6DyoQipJqIz4VHmY9-y5T8RrUcJcM-5lVoMpAAAA.i2p/announce.php=http://tracker.mastertracker.i2p/"
|
||||
// , "Galen", "http://5jpwQMI5FT303YwKa5Rd38PYSX04pbIKgTaKQsWbqoWjIfoancFdWCShXHLI5G5ofOb0Xu11vl2VEMyPsg1jUFYSVnu4-VfMe3y4TKTR6DTpetWrnmEK6m2UXh91J5DZJAKlgmO7UdsFlBkQfR2rY853-DfbJtQIFl91tbsmjcA5CGQi4VxMFyIkBzv-pCsuLQiZqOwWasTlnzey8GcDAPG1LDcvfflGV~6F5no9mnuisZPteZKlrv~~TDoXTj74QjByWc4EOYlwqK8sbU9aOvz~s31XzErbPTfwiawiaZ0RUI-IDrKgyvmj0neuFTWgjRGVTH8bz7cBZIc3viy6ioD-eMQOrXaQL0TCWZUelRwHRvgdPiQrxdYQs7ixkajeHzxi-Pq0EMm5Vbh3j3Q9kfUFW3JjFDA-MLB4g6XnjCbM5J1rC0oOBDCIEfhQkszru5cyLjHiZ5yeA0VThgu~c7xKHybv~OMXION7V8pBKOgET7ZgAkw1xgYe3Kkyq5syAAAA.i2p/tr/announce.php=http://galen.i2p/tr/"
|
||||
"Postman", "http://tracker2.postman.i2p/announce.php=http://tracker2.postman.i2p/"
|
||||
,"Welterde", "http://tracker.welterde.i2p/a=http://tracker.welterde.i2p/stats?mode=top5"
|
||||
// ,"Welterde", "http://tracker.welterde.i2p/a=http://tracker.welterde.i2p/stats?mode=top5"
|
||||
,"Diftracker", "http://diftracker.i2p/announce.php=http://diftracker.i2p/"
|
||||
// , "CRSTRACK", "http://b4G9sCdtfvccMAXh~SaZrPqVQNyGQbhbYMbw6supq2XGzbjU4NcOmjFI0vxQ8w1L05twmkOvg5QERcX6Mi8NQrWnR0stLExu2LucUXg1aYjnggxIR8TIOGygZVIMV3STKH4UQXD--wz0BUrqaLxPhrm2Eh9Hwc8TdB6Na4ShQUq5Xm8D4elzNUVdpM~RtChEyJWuQvoGAHY3ppX-EJJLkiSr1t77neS4Lc-KofMVmgI9a2tSSpNAagBiNI6Ak9L1T0F9uxeDfEG9bBSQPNMOSUbAoEcNxtt7xOW~cNOAyMyGydwPMnrQ5kIYPY8Pd3XudEko970vE0D6gO19yoBMJpKx6Dh50DGgybLQ9CpRaynh2zPULTHxm8rneOGRcQo8D3mE7FQ92m54~SvfjXjD2TwAVGI~ae~n9HDxt8uxOecAAvjjJ3TD4XM63Q9TmB38RmGNzNLDBQMEmJFpqQU8YeuhnS54IVdUoVQFqui5SfDeLXlSkh4vYoMU66pvBfWbAAAA.i2p/tracker/announce.php=http://crstrack.i2p/tracker/"
|
||||
// ,"Exotrack", "http://blbgywsjubw3d2zih2giokakhe3o2cko7jtte4risb3hohbcoyva.b32.i2p/announce.php=http://exotrack.i2p/"
|
||||
,"DgTrack", "http://w7tpbzncbcocrqtwwm3nezhnnsw4ozadvi2hmvzdhrqzfxfum7wa.b32.i2p/a=http://opentracker.dg2.i2p/"
|
||||
// The following is ECDSA_SHA256_P256
|
||||
,"TheBland", "http://s5ikrdyjwbcgxmqetxb3nyheizftms7euacuub2hic7defkh3xhq.b32.i2p/a=http://tracker.thebland.i2p/stats?mode=peer"
|
||||
};
|
||||
|
||||
/** URL. This is our equivalent to router.utorrent.com for bootstrap */
|
||||
public static final String DEFAULT_BACKUP_TRACKER = "http://opentracker.dg2.i2p/a";
|
||||
|
||||
/** URLs, comma-separated. Used for "announce to open trackers also" */
|
||||
private static final String DEFAULT_OPENTRACKERS = DEFAULT_BACKUP_TRACKER +
|
||||
(SigType.ECDSA_SHA256_P256.isAvailable() ? ",http://tracker.thebland.i2p/a" : "");
|
||||
|
||||
public static final Set<String> DEFAULT_TRACKER_ANNOUNCES;
|
||||
|
||||
/** host names for config form */
|
||||
public static final Set<String> KNOWN_OPENTRACKERS = new HashSet(Arrays.asList(new String[] {
|
||||
"tracker.welterde.i2p", "cfmqlafjfmgkzbt4r3jsfyhgsr5abgxryl6fnz3d3y5a365di5aa.b32.i2p",
|
||||
"opentracker.dg2.i2p", "w7tpbzncbcocrqtwwm3nezhnnsw4ozadvi2hmvzdhrqzfxfum7wa.b32.i2p",
|
||||
"tracker.thebland.i2p", "s5ikrdyjwbcgxmqetxb3nyheizftms7euacuub2hic7defkh3xhq.b32.i2p",
|
||||
"psi.i2p", "avviiexdngd32ccoy4kuckvc3mkf53ycvzbz6vz75vzhv4tbpk5a.b32.i2p",
|
||||
"opentracker.psi.i2p", "vmow3h54yljn7zvzbqepdddt5fmygijujycod2q6yznpy2rrzuwa.b32.i2p",
|
||||
"tracker.killyourtv.i2p", "5mpvzxfbd4rtped3c7ln4ddw52e7i7t56s36ztky4ustxtxrjdpa.b32.i2p",
|
||||
"opendiftracker.i2p", "bikpeyxci4zuyy36eau5ycw665dplun4yxamn7vmsastejdqtfoq.b32.i2p"
|
||||
}));
|
||||
|
||||
static {
|
||||
Set<String> ann = new HashSet();
|
||||
for (int i = 1; i < DEFAULT_TRACKERS.length; i += 2) {
|
||||
if (DEFAULT_TRACKERS[i-1].equals("TheBland") && !SigType.ECDSA_SHA256_P256.isAvailable())
|
||||
continue;
|
||||
String urls[] = DEFAULT_TRACKERS[i].split("=", 2);
|
||||
ann.add(urls[0]);
|
||||
}
|
||||
@ -167,9 +212,11 @@ public class SnarkManager implements CompleteListener {
|
||||
_messages = new LinkedBlockingQueue<String>();
|
||||
_util = new I2PSnarkUtil(_context, ctxName);
|
||||
String cfile = ctxName + CONFIG_FILE_SUFFIX;
|
||||
_configFile = new File(cfile);
|
||||
if (!_configFile.isAbsolute())
|
||||
_configFile = new File(_context.getConfigDir(), cfile);
|
||||
File configFile = new File(cfile);
|
||||
if (!configFile.isAbsolute())
|
||||
configFile = new File(_context.getConfigDir(), cfile);
|
||||
_configDir = migrateConfig(configFile);
|
||||
_configFile = new File(_configDir, CONFIG_FILE);
|
||||
_trackerMap = new ConcurrentHashMap<String, Tracker>(4);
|
||||
loadConfig(null);
|
||||
}
|
||||
@ -198,7 +245,9 @@ public class SnarkManager implements CompleteListener {
|
||||
public void timeReached() {
|
||||
if (!_running)
|
||||
return;
|
||||
_umgr = _context.updateManager();
|
||||
ClientAppManager cmgr = _context.clientAppManager();
|
||||
if (cmgr != null)
|
||||
_umgr = (UpdateManager) cmgr.getRegisteredApp(UpdateManager.APP_NAME);
|
||||
if (_umgr != null) {
|
||||
_uhandler = new UpdateHandler(_context, _umgr, SnarkManager.this);
|
||||
_umgr.register(_uhandler, UpdateType.ROUTER_SIGNED, UpdateMethod.TORRENT, 10);
|
||||
@ -237,7 +286,20 @@ public class SnarkManager implements CompleteListener {
|
||||
|
||||
private static final int MAX_MESSAGES = 100;
|
||||
|
||||
/**
|
||||
* Use if it does not include a link.
|
||||
* Escapes '<' and '>' before queueing
|
||||
*/
|
||||
public void addMessage(String message) {
|
||||
addMessageNoEscape(message.replace("<", "<").replace(">", ">"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Use if it includes a link.
|
||||
* Does not escape '<' and '>' before queueing
|
||||
* @since 0.9.14.1
|
||||
*/
|
||||
public void addMessageNoEscape(String message) {
|
||||
_messages.offer(message);
|
||||
while (_messages.size() > MAX_MESSAGES) {
|
||||
_messages.poll();
|
||||
@ -324,20 +386,179 @@ public class SnarkManager implements CompleteListener {
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate the old flat config file to the new config dir
|
||||
* containing the config file minus the per-torrent entries,
|
||||
* the dht file, and 64 subdirs for per-torrent config files
|
||||
* Caller must synch.
|
||||
*
|
||||
* @return the new config directory, non-null
|
||||
* @throws RuntimeException on creation fail
|
||||
* @since 0.9.15
|
||||
*/
|
||||
private File migrateConfig(File oldFile) {
|
||||
File dir = new SecureDirectory(oldFile + CONFIG_DIR_SUFFIX);
|
||||
if ((!dir.exists()) && (!dir.mkdirs())) {
|
||||
_log.error("Error creating I2PSnark config dir " + dir);
|
||||
throw new RuntimeException("Error creating I2PSnark config dir " + dir);
|
||||
}
|
||||
// move the DHT file as-is
|
||||
String oldName = oldFile.toString();
|
||||
if (oldName.endsWith(CONFIG_FILE_SUFFIX)) {
|
||||
String oldDHT = oldName.replace(CONFIG_FILE_SUFFIX, KRPC.DHT_FILE_SUFFIX);
|
||||
File oldDHTFile = new File(oldDHT);
|
||||
if (oldDHTFile.exists()) {
|
||||
File newDHTFile = new File(dir, "i2psnark" + KRPC.DHT_FILE_SUFFIX);
|
||||
FileUtil.rename(oldDHTFile, newDHTFile);
|
||||
}
|
||||
}
|
||||
if (!oldFile.exists())
|
||||
return dir;
|
||||
Properties oldProps = new Properties();
|
||||
try {
|
||||
DataHelper.loadProps(oldProps, oldFile);
|
||||
// a good time to fix this ancient typo
|
||||
String auto = (String) oldProps.remove(PROP_OLD_AUTO_START);
|
||||
if (auto != null)
|
||||
oldProps.setProperty(PROP_AUTO_START, auto);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error loading I2PSnark config " + oldFile, ioe);
|
||||
return dir;
|
||||
}
|
||||
// Gather the props for each torrent, removing them from config
|
||||
// old b64 of hash as key
|
||||
Map<String, Properties> configs = new HashMap<String, Properties>(16);
|
||||
for (Iterator<Map.Entry<Object, Object>> iter = oldProps.entrySet().iterator(); iter.hasNext(); ) {
|
||||
Map.Entry<Object, Object> e = iter.next();
|
||||
String k = (String) e.getKey();
|
||||
if (k.startsWith(PROP_META_PREFIX)) {
|
||||
iter.remove();
|
||||
String v = (String) e.getValue();
|
||||
try {
|
||||
k = k.substring(PROP_META_PREFIX.length());
|
||||
String h = k.substring(0, 28); // length of b64 of 160 bit infohash
|
||||
k = k.substring(29); // skip '.'
|
||||
Properties tprops = configs.get(h);
|
||||
if (tprops == null) {
|
||||
tprops = new OrderedProperties();
|
||||
configs.put(h, tprops);
|
||||
}
|
||||
if (k.equals(PROP_META_BITFIELD)) {
|
||||
// old config was timestamp,bitfield; split them
|
||||
int comma = v.indexOf(',');
|
||||
if (comma > 0 && v.length() > comma + 1) {
|
||||
tprops.put(PROP_META_STAMP, v.substring(0, comma));
|
||||
tprops.put(PROP_META_BITFIELD, v.substring(comma + 1));
|
||||
} else {
|
||||
// timestamp only??
|
||||
tprops.put(PROP_META_STAMP, v);
|
||||
}
|
||||
} else {
|
||||
tprops.put(k, v);
|
||||
}
|
||||
} catch (IndexOutOfBoundsException ioobe) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now make a config file for each torrent
|
||||
for (Map.Entry<String, Properties> e : configs.entrySet()) {
|
||||
String b64 = e.getKey();
|
||||
Properties props = e.getValue();
|
||||
if (props.isEmpty())
|
||||
continue;
|
||||
b64 = b64.replace('$', '=');
|
||||
byte[] ih = Base64.decode(b64);
|
||||
if (ih == null || ih.length != 20)
|
||||
continue;
|
||||
File cfg = configFile(dir, ih);
|
||||
if (!cfg.exists()) {
|
||||
File subdir = cfg.getParentFile();
|
||||
if (!subdir.exists())
|
||||
subdir.mkdirs();
|
||||
try {
|
||||
DataHelper.storeProps(props, cfg);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error storing I2PSnark config " + cfg, ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
// now store in new location, minus the zmeta entries
|
||||
File newFile = new File(dir, CONFIG_FILE);
|
||||
Properties newProps = new OrderedProperties();
|
||||
newProps.putAll(oldProps);
|
||||
try {
|
||||
DataHelper.storeProps(newProps, newFile);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error storing I2PSnark config " + newFile, ioe);
|
||||
return dir;
|
||||
}
|
||||
oldFile.delete();
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Config migrated from " + oldFile + " to " + dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* The config for a torrent
|
||||
* @return non-null, possibly empty
|
||||
* @since 0.9.15
|
||||
*/
|
||||
private Properties getConfig(Snark snark) {
|
||||
return getConfig(snark.getInfoHash());
|
||||
}
|
||||
|
||||
/**
|
||||
* The config for a torrent
|
||||
* @param ih 20-byte infohash
|
||||
* @return non-null, possibly empty
|
||||
* @since 0.9.15
|
||||
*/
|
||||
private Properties getConfig(byte[] ih) {
|
||||
Properties rv = new OrderedProperties();
|
||||
File conf = configFile(_configDir, ih);
|
||||
synchronized(_configLock) { // one lock for all
|
||||
try {
|
||||
DataHelper.loadProps(rv, conf);
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* The config file for a torrent
|
||||
* @param confDir the config directory
|
||||
* @param ih 20-byte infohash
|
||||
* @since 0.9.15
|
||||
*/
|
||||
private static File configFile(File confDir, byte[] ih) {
|
||||
String hex = I2PSnarkUtil.toHex(ih);
|
||||
File subdir = new SecureDirectory(confDir, SUBDIR_PREFIX + B64.charAt((ih[0] >> 2) & 0x3f));
|
||||
return new File(subdir, hex + CONFIG_FILE_SUFFIX);
|
||||
}
|
||||
|
||||
/** null to set initial defaults */
|
||||
public void loadConfig(String filename) {
|
||||
synchronized(_configLock) {
|
||||
locked_loadConfig(filename);
|
||||
}
|
||||
}
|
||||
|
||||
/** null to set initial defaults */
|
||||
private void locked_loadConfig(String filename) {
|
||||
if (_config == null)
|
||||
_config = new OrderedProperties();
|
||||
if (filename != null) {
|
||||
File cfg = new File(filename);
|
||||
if (!cfg.isAbsolute())
|
||||
cfg = new File(_context.getConfigDir(), filename);
|
||||
_configFile = cfg;
|
||||
if (cfg.exists()) {
|
||||
_configDir = migrateConfig(cfg);
|
||||
_configFile = new File(_configDir, CONFIG_FILE);
|
||||
if (_configFile.exists()) {
|
||||
try {
|
||||
DataHelper.loadProps(_config, cfg);
|
||||
DataHelper.loadProps(_config, _configFile);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error loading I2PSnark config '" + filename + "'", ioe);
|
||||
_log.error("Error loading I2PSnark config " + _configFile, ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -347,7 +568,7 @@ public class SnarkManager implements CompleteListener {
|
||||
if (!_config.containsKey(PROP_I2CP_PORT))
|
||||
_config.setProperty(PROP_I2CP_PORT, "7654");
|
||||
if (!_config.containsKey(PROP_I2CP_OPTS))
|
||||
_config.setProperty(PROP_I2CP_OPTS, "inbound.length=2 inbound.lengthVariance=0 outbound.length=2 outbound.lengthVariance=0 inbound.quantity=3 outbound.quantity=3");
|
||||
_config.setProperty(PROP_I2CP_OPTS, "inbound.length=3 outbound.length=3 inbound.quantity=3 outbound.quantity=3");
|
||||
//if (!_config.containsKey(PROP_EEP_HOST))
|
||||
// _config.setProperty(PROP_EEP_HOST, "127.0.0.1");
|
||||
//if (!_config.containsKey(PROP_EEP_PORT))
|
||||
@ -371,6 +592,7 @@ public class SnarkManager implements CompleteListener {
|
||||
// _config.setProperty(PROP_USE_DHT, Boolean.toString(I2PSnarkUtil.DEFAULT_USE_DHT));
|
||||
updateConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current theme.
|
||||
* @return String -- the current theme
|
||||
@ -402,10 +624,10 @@ public class SnarkManager implements CompleteListener {
|
||||
|
||||
/**
|
||||
* Get all themes
|
||||
* @return String[] -- Array of all the themes found.
|
||||
* @return String[] -- Array of all the themes found, non-null, unsorted
|
||||
*/
|
||||
public String[] getThemes() {
|
||||
String[] themes = null;
|
||||
String[] themes;
|
||||
// "docs/themes/snark/"
|
||||
File dir = new File(_context.getBaseDir(), "docs/themes/snark");
|
||||
FileFilter fileFilter = new FileFilter() { public boolean accept(File file) { return file.isDirectory(); } };
|
||||
@ -416,6 +638,8 @@ public class SnarkManager implements CompleteListener {
|
||||
for(int i = 0; i < dirnames.length; i++) {
|
||||
themes[i] = dirnames[i].getName();
|
||||
}
|
||||
} else {
|
||||
themes = new String[0];
|
||||
}
|
||||
// return the map.
|
||||
return themes;
|
||||
@ -457,9 +681,7 @@ public class SnarkManager implements CompleteListener {
|
||||
_util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW));
|
||||
_util.setStartupDelay(getInt(PROP_STARTUP_DELAY, DEFAULT_STARTUP_DELAY));
|
||||
_util.setFilesPublic(areFilesPublic());
|
||||
String ot = _config.getProperty(PROP_OPENTRACKERS);
|
||||
if (ot != null)
|
||||
_util.setOpenTrackers(getOpenTrackers());
|
||||
_util.setOpenTrackers(getListConfig(PROP_OPENTRACKERS, DEFAULT_OPENTRACKERS));
|
||||
String useOT = _config.getProperty(PROP_USE_OPENTRACKERS);
|
||||
boolean bOT = useOT == null || Boolean.parseBoolean(useOT);
|
||||
_util.setUseOpenTrackers(bOT);
|
||||
@ -488,6 +710,18 @@ public class SnarkManager implements CompleteListener {
|
||||
String startDelay, String pageSize, String seedPct, String eepHost,
|
||||
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
|
||||
String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme) {
|
||||
synchronized(_configLock) {
|
||||
locked_updateConfig(dataDir, filesPublic, autoStart, refreshDelay,
|
||||
startDelay, pageSize, seedPct, eepHost,
|
||||
eepPort, i2cpHost, i2cpPort, i2cpOpts,
|
||||
upLimit, upBW, useOpenTrackers, useDHT, theme);
|
||||
}
|
||||
}
|
||||
|
||||
private void locked_updateConfig(String dataDir, boolean filesPublic, boolean autoStart, String refreshDelay,
|
||||
String startDelay, String pageSize, String seedPct, String eepHost,
|
||||
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
|
||||
String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme) {
|
||||
boolean changed = false;
|
||||
boolean interruptMonitor = false;
|
||||
//if (eepHost != null) {
|
||||
@ -575,7 +809,7 @@ public class SnarkManager implements CompleteListener {
|
||||
}
|
||||
|
||||
if (dataDir != null && !dataDir.equals(getDataDir().getAbsolutePath())) {
|
||||
dataDir = dataDir.trim();
|
||||
dataDir = DataHelper.stripHTML(dataDir.trim());
|
||||
File dd = new File(dataDir);
|
||||
if (!dd.isAbsolute()) {
|
||||
addMessage(_("Data directory must be an absolute path") + ": " + dataDir);
|
||||
@ -605,7 +839,7 @@ public class SnarkManager implements CompleteListener {
|
||||
}
|
||||
|
||||
Map<String, String> opts = new HashMap<String, String>();
|
||||
if (i2cpOpts == null) i2cpOpts = "";
|
||||
i2cpOpts = DataHelper.stripHTML(i2cpOpts);
|
||||
StringTokenizer tok = new StringTokenizer(i2cpOpts, " \t\n");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String pair = tok.nextToken();
|
||||
@ -747,7 +981,7 @@ public class SnarkManager implements CompleteListener {
|
||||
private List<String> getOpenTrackers() {
|
||||
if (!_util.shouldUseOpenTrackers())
|
||||
return Collections.emptyList();
|
||||
return getListConfig(PROP_OPENTRACKERS, I2PSnarkUtil.DEFAULT_OPENTRACKERS);
|
||||
return getListConfig(PROP_OPENTRACKERS, DEFAULT_OPENTRACKERS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -765,7 +999,7 @@ public class SnarkManager implements CompleteListener {
|
||||
public void saveOpenTrackers(List<String> ot) {
|
||||
setListConfig(PROP_OPENTRACKERS, ot);
|
||||
if (ot == null)
|
||||
ot = Collections.singletonList(I2PSnarkUtil.DEFAULT_OPENTRACKERS);
|
||||
ot = getListConfig(PROP_OPENTRACKERS, DEFAULT_OPENTRACKERS);
|
||||
_util.setOpenTrackers(ot);
|
||||
addMessage(_("Open Tracker list changed - torrent restart required to take effect."));
|
||||
saveConfig();
|
||||
@ -819,7 +1053,7 @@ public class SnarkManager implements CompleteListener {
|
||||
|
||||
public void saveConfig() {
|
||||
try {
|
||||
synchronized (_configFile) {
|
||||
synchronized (_configLock) {
|
||||
DataHelper.storeProps(_config, _configFile);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
@ -827,13 +1061,6 @@ public class SnarkManager implements CompleteListener {
|
||||
}
|
||||
}
|
||||
|
||||
public Properties getConfig() { return _config; }
|
||||
|
||||
/** @since Jetty 7 */
|
||||
public String getConfigFilename() {
|
||||
return _configFile.getAbsolutePath();
|
||||
}
|
||||
|
||||
/** hardcoded for sanity. perhaps this should be customizable, for people who increase their ulimit, etc. */
|
||||
public static final int MAX_FILES_PER_TORRENT = 512;
|
||||
|
||||
@ -891,15 +1118,25 @@ public class SnarkManager implements CompleteListener {
|
||||
|
||||
/**
|
||||
* Caller must verify this torrent is not already added.
|
||||
*
|
||||
* @param filename the absolute path to save the metainfo to, generally ending in ".torrent"
|
||||
* @param baseFile may be null, if so look in dataDir
|
||||
* @throws RuntimeException via Snark.fatal()
|
||||
*/
|
||||
private void addTorrent(String filename) { addTorrent(filename, false); }
|
||||
private void addTorrent(String filename, File baseFile, boolean dontAutoStart) {
|
||||
addTorrent(filename, baseFile, dontAutoStart, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Caller must verify this torrent is not already added.
|
||||
*
|
||||
* @param filename the absolute path to save the metainfo to, generally ending in ".torrent"
|
||||
* @param baseFile may be null, if so look in dataDir
|
||||
* @param dataDir must exist, or null to default to snark data directory
|
||||
* @throws RuntimeException via Snark.fatal()
|
||||
* @since 0.9.17
|
||||
*/
|
||||
private void addTorrent(String filename, boolean dontAutoStart) {
|
||||
private void addTorrent(String filename, File baseFile, boolean dontAutoStart, File dataDir) {
|
||||
if ((!dontAutoStart) && !_util.connected()) {
|
||||
addMessage(_("Connecting to I2P"));
|
||||
boolean ok = _util.connect();
|
||||
@ -916,7 +1153,8 @@ public class SnarkManager implements CompleteListener {
|
||||
addMessage(_("Error: Could not add the torrent {0}", filename) + ": " + ioe);
|
||||
return;
|
||||
}
|
||||
File dataDir = getDataDir();
|
||||
if (dataDir == null)
|
||||
dataDir = getDataDir();
|
||||
Snark torrent = null;
|
||||
synchronized (_snarks) {
|
||||
torrent = _snarks.get(filename);
|
||||
@ -980,9 +1218,13 @@ public class SnarkManager implements CompleteListener {
|
||||
} else {
|
||||
// TODO load saved closest DHT nodes and pass to the Snark ?
|
||||
// This may take a LONG time
|
||||
if (baseFile == null)
|
||||
baseFile = getSavedBaseFile(info.getInfoHash());
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("New Snark, torrent: " + filename + " base: " + baseFile);
|
||||
torrent = new Snark(_util, filename, null, -1, null, null, this,
|
||||
_peerCoordinatorSet, _connectionAcceptor,
|
||||
false, dataDir.getPath());
|
||||
false, dataDir.getPath(), baseFile);
|
||||
loadSavedFilePriorities(torrent);
|
||||
synchronized (_snarks) {
|
||||
_snarks.put(filename, torrent);
|
||||
@ -1028,7 +1270,25 @@ public class SnarkManager implements CompleteListener {
|
||||
*/
|
||||
public void addMagnet(String name, byte[] ih, String trackerURL, boolean updateStatus) {
|
||||
// updateStatus is true from UI, false from config file bulk add
|
||||
addMagnet(name, ih, trackerURL, updateStatus, updateStatus, this);
|
||||
addMagnet(name, ih, trackerURL, updateStatus, updateStatus, null, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a torrent with the info hash alone (magnet / maggot)
|
||||
*
|
||||
* @param name hex or b32 name from the magnet link
|
||||
* @param ih 20 byte info hash
|
||||
* @param trackerURL may be null
|
||||
* @param updateStatus should we add this magnet to the config file,
|
||||
* to save it across restarts, in case we don't get
|
||||
* the metadata before shutdown?
|
||||
* @param dataDir must exist, or null to default to snark data directory
|
||||
* @throws RuntimeException via Snark.fatal()
|
||||
* @since 0.9.17
|
||||
*/
|
||||
public void addMagnet(String name, byte[] ih, String trackerURL, boolean updateStatus, File dataDir) {
|
||||
// updateStatus is true from UI, false from config file bulk add
|
||||
addMagnet(name, ih, trackerURL, updateStatus, updateStatus, dataDir, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1041,16 +1301,18 @@ public class SnarkManager implements CompleteListener {
|
||||
* @param updateStatus should we add this magnet to the config file,
|
||||
* to save it across restarts, in case we don't get
|
||||
* the metadata before shutdown?
|
||||
* @param dataDir must exist, or null to default to snark data directory
|
||||
* @param listener to intercept callbacks, should pass through to this
|
||||
* @return the new Snark or null on failure
|
||||
* @throws RuntimeException via Snark.fatal()
|
||||
* @since 0.9.4
|
||||
*/
|
||||
public Snark addMagnet(String name, byte[] ih, String trackerURL, boolean updateStatus,
|
||||
boolean autoStart, CompleteListener listener) {
|
||||
boolean autoStart, File dataDir, CompleteListener listener) {
|
||||
String dirPath = dataDir != null ? dataDir.getAbsolutePath() : getDataDir().getPath();
|
||||
Snark torrent = new Snark(_util, name, ih, trackerURL, listener,
|
||||
_peerCoordinatorSet, _connectionAcceptor,
|
||||
false, getDataDir().getPath());
|
||||
false, dirPath);
|
||||
|
||||
synchronized (_snarks) {
|
||||
Snark snark = getTorrentByInfoHash(ih);
|
||||
@ -1125,32 +1387,39 @@ public class SnarkManager implements CompleteListener {
|
||||
* This verifies that a torrent with this infohash is not already added.
|
||||
* This may take a LONG time to create or check the storage.
|
||||
*
|
||||
* Called from servlet. This is only for the 'create torrent' form.
|
||||
*
|
||||
* @param metainfo the metainfo for the torrent
|
||||
* @param bitfield the current completion status of the torrent
|
||||
* @param filename the absolute path to save the metainfo to, generally ending in ".torrent", which is also the name of the torrent
|
||||
* Must be a filesystem-safe name.
|
||||
* @param baseFile may be null, if so look in rootDataDir
|
||||
* @throws RuntimeException via Snark.fatal()
|
||||
* @return success
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public void addTorrent(MetaInfo metainfo, BitField bitfield, String filename, boolean dontAutoStart) throws IOException {
|
||||
public boolean addTorrent(MetaInfo metainfo, BitField bitfield, String filename,
|
||||
File baseFile, boolean dontAutoStart) throws IOException {
|
||||
// prevent interference by DirMonitor
|
||||
synchronized (_snarks) {
|
||||
Snark snark = getTorrentByInfoHash(metainfo.getInfoHash());
|
||||
if (snark != null) {
|
||||
addMessage(_("Torrent with this info hash is already running: {0}", snark.getBaseName()));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// so addTorrent won't recheck
|
||||
saveTorrentStatus(metainfo, bitfield, null); // no file priorities
|
||||
saveTorrentStatus(metainfo, bitfield, null, baseFile, true, 0); // no file priorities
|
||||
try {
|
||||
locked_writeMetaInfo(metainfo, filename, areFilesPublic());
|
||||
// hold the lock for a long time
|
||||
addTorrent(filename, dontAutoStart);
|
||||
addTorrent(filename, baseFile, dontAutoStart);
|
||||
} catch (IOException ioe) {
|
||||
addMessage(_("Failed to copy torrent file to {0}", filename));
|
||||
_log.error("Failed to write torrent file", ioe);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1162,10 +1431,11 @@ public class SnarkManager implements CompleteListener {
|
||||
* @param fromfile where the file is now, presumably in a temp directory somewhere
|
||||
* @param filename the absolute path to save the metainfo to, generally ending in ".torrent", which is also the name of the torrent
|
||||
* Must be a filesystem-safe name.
|
||||
* @param dataDir must exist, or null to default to snark data directory
|
||||
* @throws RuntimeException via Snark.fatal()
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public void copyAndAddTorrent(File fromfile, String filename) throws IOException {
|
||||
public void copyAndAddTorrent(File fromfile, String filename, File dataDir) throws IOException {
|
||||
// prevent interference by DirMonitor
|
||||
synchronized (_snarks) {
|
||||
boolean success = FileUtil.copy(fromfile.getAbsolutePath(), filename, false);
|
||||
@ -1177,7 +1447,7 @@ public class SnarkManager implements CompleteListener {
|
||||
if (!areFilesPublic())
|
||||
SecureFileOutputStream.setPerms(new File(filename));
|
||||
// hold the lock for a long time
|
||||
addTorrent(filename);
|
||||
addTorrent(filename, null, false, dataDir);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1218,16 +1488,10 @@ public class SnarkManager implements CompleteListener {
|
||||
* A Snark.CompleteListener method.
|
||||
*/
|
||||
public long getSavedTorrentTime(Snark snark) {
|
||||
byte[] ih = snark.getInfoHash();
|
||||
String infohash = Base64.encode(ih);
|
||||
infohash = infohash.replace('=', '$');
|
||||
String time = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
|
||||
Properties config = getConfig(snark);
|
||||
String time = config.getProperty(PROP_META_STAMP);
|
||||
if (time == null)
|
||||
return 0;
|
||||
int comma = time.indexOf(',');
|
||||
if (comma <= 0)
|
||||
return 0;
|
||||
time = time.substring(0, comma);
|
||||
try { return Long.parseLong(time); } catch (NumberFormatException nfe) {}
|
||||
return 0;
|
||||
}
|
||||
@ -1241,16 +1505,10 @@ public class SnarkManager implements CompleteListener {
|
||||
MetaInfo metainfo = snark.getMetaInfo();
|
||||
if (metainfo == null)
|
||||
return null;
|
||||
byte[] ih = snark.getInfoHash();
|
||||
String infohash = Base64.encode(ih);
|
||||
infohash = infohash.replace('=', '$');
|
||||
String bf = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
|
||||
Properties config = getConfig(snark);
|
||||
String bf = config.getProperty(PROP_META_BITFIELD);
|
||||
if (bf == null)
|
||||
return null;
|
||||
int comma = bf.indexOf(',');
|
||||
if (comma <= 0)
|
||||
return null;
|
||||
bf = bf.substring(comma + 1).trim();
|
||||
int len = metainfo.getPieces();
|
||||
if (bf.equals(".")) {
|
||||
BitField bitfield = new BitField(len);
|
||||
@ -1277,10 +1535,8 @@ public class SnarkManager implements CompleteListener {
|
||||
return;
|
||||
if (metainfo.getFiles() == null)
|
||||
return;
|
||||
byte[] ih = snark.getInfoHash();
|
||||
String infohash = Base64.encode(ih);
|
||||
infohash = infohash.replace('=', '$');
|
||||
String pri = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX);
|
||||
Properties config = getConfig(snark);
|
||||
String pri = config.getProperty(PROP_META_PRIORITY);
|
||||
if (pri == null)
|
||||
return;
|
||||
int filecount = metainfo.getFiles().size();
|
||||
@ -1295,24 +1551,82 @@ public class SnarkManager implements CompleteListener {
|
||||
}
|
||||
storage.setFilePriorities(rv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base location for a torrent from the config file.
|
||||
* @return File or null, doesn't necessarily exist
|
||||
* @since 0.9.15
|
||||
*/
|
||||
private File getSavedBaseFile(byte[] ih) {
|
||||
Properties config = getConfig(ih);
|
||||
String base = config.getProperty(PROP_META_BASE);
|
||||
if (base == null)
|
||||
return null;
|
||||
return new File(base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get setting for a torrent from the config file.
|
||||
* @return setting, false if not found
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public boolean getSavedPreserveNamesSetting(Snark snark) {
|
||||
Properties config = getConfig(snark);
|
||||
return Boolean.parseBoolean(config.getProperty(PROP_META_PRESERVE_NAMES));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get setting for a torrent from the config file.
|
||||
* @return setting, 0 if not found
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public long getSavedUploaded(Snark snark) {
|
||||
Properties config = getConfig(snark);
|
||||
if (config != null) {
|
||||
try {
|
||||
return Long.parseLong(config.getProperty(PROP_META_UPLOADED));
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the completion status of a torrent and other data in the config file
|
||||
* for that torrent. Does nothing for magnets.
|
||||
*
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public void saveTorrentStatus(Snark snark) {
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
Storage storage = snark.getStorage();
|
||||
if (meta == null || storage == null)
|
||||
return;
|
||||
saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(),
|
||||
storage.getBase(), storage.getPreserveFileNames(),
|
||||
snark.getUploaded());
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the completion status of a torrent and the current time in the config file
|
||||
* in the form "i2psnark.zmeta.$base64infohash=$time,$base64bitfield".
|
||||
* The config file property key is appended with the Base64 of the infohash,
|
||||
* with the '=' changed to '$' since a key can't contain '='.
|
||||
* for that torrent.
|
||||
* The time is a standard long converted to string.
|
||||
* The status is either a bitfield converted to Base64 or "." for a completed
|
||||
* torrent to save space in the config file and in memory.
|
||||
*
|
||||
* @param bitfield non-null
|
||||
* @param priorities may be null
|
||||
* @param base may be null
|
||||
*/
|
||||
public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities) {
|
||||
private void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities,
|
||||
File base, boolean preserveNames, long uploaded) {
|
||||
synchronized (_configLock) {
|
||||
locked_saveTorrentStatus(metainfo, bitfield, priorities, base, preserveNames, uploaded);
|
||||
}
|
||||
}
|
||||
|
||||
private void locked_saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities,
|
||||
File base, boolean preserveNames, long uploaded) {
|
||||
byte[] ih = metainfo.getInfoHash();
|
||||
String infohash = Base64.encode(ih);
|
||||
infohash = infohash.replace('=', '$');
|
||||
String now = "" + System.currentTimeMillis();
|
||||
String bfs;
|
||||
if (bitfield.complete()) {
|
||||
bfs = ".";
|
||||
@ -1320,10 +1634,15 @@ public class SnarkManager implements CompleteListener {
|
||||
byte[] bf = bitfield.getFieldBytes();
|
||||
bfs = Base64.encode(bf);
|
||||
}
|
||||
_config.setProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX, now + "," + bfs);
|
||||
Properties config = getConfig(ih);
|
||||
config.setProperty(PROP_META_STAMP, Long.toString(System.currentTimeMillis()));
|
||||
config.setProperty(PROP_META_BITFIELD, bfs);
|
||||
config.setProperty(PROP_META_PRESERVE_NAMES, Boolean.toString(preserveNames));
|
||||
config.setProperty(PROP_META_UPLOADED, Long.toString(uploaded));
|
||||
if (base != null)
|
||||
config.setProperty(PROP_META_BASE, base.getAbsolutePath());
|
||||
|
||||
// now the file priorities
|
||||
String prop = PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX;
|
||||
if (priorities != null) {
|
||||
boolean nonzero = false;
|
||||
for (int i = 0; i < priorities.length; i++) {
|
||||
@ -1341,30 +1660,40 @@ public class SnarkManager implements CompleteListener {
|
||||
if (i != priorities.length - 1)
|
||||
buf.append(',');
|
||||
}
|
||||
_config.setProperty(prop, buf.toString());
|
||||
config.setProperty(PROP_META_PRIORITY, buf.toString());
|
||||
} else {
|
||||
_config.remove(prop);
|
||||
config.remove(PROP_META_PRIORITY);
|
||||
}
|
||||
} else {
|
||||
_config.remove(prop);
|
||||
config.remove(PROP_META_PRIORITY);
|
||||
}
|
||||
|
||||
// TODO save closest DHT nodes too
|
||||
|
||||
saveConfig();
|
||||
File conf = configFile(_configDir, ih);
|
||||
File subdir = conf.getParentFile();
|
||||
if (!subdir.exists())
|
||||
subdir.mkdirs();
|
||||
try {
|
||||
DataHelper.storeProps(config, conf);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Unable to save the config to " + conf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the status of a torrent from the config file.
|
||||
* This may help the config file from growing too big.
|
||||
* Remove the status of a torrent by removing the config file.
|
||||
*/
|
||||
public void removeTorrentStatus(MetaInfo metainfo) {
|
||||
byte[] ih = metainfo.getInfoHash();
|
||||
String infohash = Base64.encode(ih);
|
||||
infohash = infohash.replace('=', '$');
|
||||
_config.remove(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
|
||||
_config.remove(PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX);
|
||||
saveConfig();
|
||||
File conf = configFile(_configDir, ih);
|
||||
synchronized (_configLock) {
|
||||
conf.delete();
|
||||
File subdir = conf.getParentFile();
|
||||
String[] files = subdir.list();
|
||||
if (files != null && files.length == 0)
|
||||
subdir.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1549,11 +1878,12 @@ public class SnarkManager implements CompleteListener {
|
||||
if (meta == null || storage == null)
|
||||
return;
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
buf.append("<a href=\"").append(_contextPath).append('/').append(storage.getBaseName());
|
||||
String base = DataHelper.escapeHTML(storage.getBaseName());
|
||||
buf.append("<a href=\"").append(_contextPath).append('/').append(base);
|
||||
if (meta.getFiles() != null)
|
||||
buf.append('/');
|
||||
buf.append("\">").append(storage.getBaseName()).append("</a>");
|
||||
addMessage(_("Download finished: {0}", buf.toString())); // + " (" + _("size: {0}B", DataHelper.formatSize2(len)) + ')');
|
||||
buf.append("\">").append(base).append("</a>");
|
||||
addMessageNoEscape(_("Download finished: {0}", buf.toString())); // + " (" + _("size: {0}B", DataHelper.formatSize2(len)) + ')');
|
||||
updateStatus(snark);
|
||||
}
|
||||
|
||||
@ -1564,7 +1894,8 @@ public class SnarkManager implements CompleteListener {
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
Storage storage = snark.getStorage();
|
||||
if (meta != null && storage != null)
|
||||
saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities());
|
||||
saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(),
|
||||
storage.getBase(), storage.getPreserveFileNames(), snark.getUploaded());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1586,7 +1917,8 @@ public class SnarkManager implements CompleteListener {
|
||||
snark.stopTorrent();
|
||||
return null;
|
||||
}
|
||||
saveTorrentStatus(meta, storage.getBitField(), null); // no file priorities
|
||||
saveTorrentStatus(meta, storage.getBitField(), null,
|
||||
storage.getBase(), storage.getPreserveFileNames(), 0);
|
||||
// temp for addMessage() in case canonical throws
|
||||
String name = storage.getBaseName();
|
||||
try {
|
||||
@ -1687,7 +2019,7 @@ public class SnarkManager implements CompleteListener {
|
||||
try {
|
||||
// Snark.fatal() throws a RuntimeException
|
||||
// don't let one bad torrent kill the whole loop
|
||||
addTorrent(name, !shouldAutoStart());
|
||||
addTorrent(name, null, !shouldAutoStart());
|
||||
} catch (Exception e) {
|
||||
addMessage(_("Error: Could not add the torrent {0}", name) + ": " + e);
|
||||
_log.error("Unable to add the torrent " + name, e);
|
||||
@ -1785,6 +2117,8 @@ public class SnarkManager implements CompleteListener {
|
||||
_trackerMap.clear();
|
||||
for (int i = 0; i < DEFAULT_TRACKERS.length; i += 2) {
|
||||
String name = DEFAULT_TRACKERS[i];
|
||||
if (name.equals("TheBland") && !SigType.ECDSA_SHA256_P256.isAvailable())
|
||||
continue;
|
||||
String urls[] = DEFAULT_TRACKERS[i+1].split("=", 2);
|
||||
String url2 = urls.length > 1 ? urls[1] : null;
|
||||
_trackerMap.put(name, new Tracker(name, urls[0], url2));
|
||||
@ -1966,7 +2300,7 @@ public class SnarkManager implements CompleteListener {
|
||||
* ignore case, current locale
|
||||
* @since 0.9
|
||||
*/
|
||||
private static class IgnoreCaseComparator implements Comparator<Tracker> {
|
||||
private static class IgnoreCaseComparator implements Comparator<Tracker>, Serializable {
|
||||
public int compare(Tracker l, Tracker r) {
|
||||
return l.name.toLowerCase().compareTo(r.name.toLowerCase());
|
||||
}
|
||||
|
@ -32,7 +32,9 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@ -52,6 +54,7 @@ public class Storage
|
||||
{
|
||||
private final MetaInfo metainfo;
|
||||
private final List<TorrentFile> _torrentFiles;
|
||||
private final File _base;
|
||||
private final StorageListener listener;
|
||||
private final I2PSnarkUtil _util;
|
||||
private final Log _log;
|
||||
@ -63,6 +66,7 @@ public class Storage
|
||||
private final int piece_size;
|
||||
private final int pieces;
|
||||
private final long total_length;
|
||||
private final boolean _preserveFileNames;
|
||||
private boolean changed;
|
||||
private volatile boolean _isChecking;
|
||||
private final AtomicInteger _allocateCount = new AtomicInteger();
|
||||
@ -70,7 +74,7 @@ public class Storage
|
||||
/** The default piece size. */
|
||||
private static final int DEFAULT_PIECE_SIZE = 256*1024;
|
||||
/** bigger than this will be rejected */
|
||||
public static final int MAX_PIECE_SIZE = 4*1024*1024;
|
||||
public static final int MAX_PIECE_SIZE = 8*1024*1024;
|
||||
/** The maximum number of pieces in a torrent. */
|
||||
public static final int MAX_PIECES = 10*1024;
|
||||
public static final long MAX_TOTAL_SIZE = MAX_PIECE_SIZE * (long) MAX_PIECES;
|
||||
@ -83,15 +87,19 @@ public class Storage
|
||||
private static final ByteCache _cache = ByteCache.getInstance(16, BUFSIZE);
|
||||
|
||||
/**
|
||||
* Creates a new storage based on the supplied MetaInfo. This will
|
||||
* Creates a new storage based on the supplied MetaInfo.
|
||||
*
|
||||
* Does not check storage. Caller MUST call check(), which will
|
||||
* try to create and/or check all needed files in the MetaInfo.
|
||||
*
|
||||
* Does not check storage. Caller MUST call check()
|
||||
* @param baseFile the torrent data file or dir
|
||||
* @param preserveFileNames if true, do not remap names to a 'safe' charset
|
||||
*/
|
||||
public Storage(I2PSnarkUtil util, MetaInfo metainfo, StorageListener listener)
|
||||
public Storage(I2PSnarkUtil util, File baseFile, MetaInfo metainfo, StorageListener listener, boolean preserveFileNames)
|
||||
{
|
||||
_util = util;
|
||||
_log = util.getContext().logManager().getLog(Storage.class);
|
||||
_base = baseFile;
|
||||
this.metainfo = metainfo;
|
||||
this.listener = listener;
|
||||
needed = metainfo.getPieces();
|
||||
@ -102,6 +110,7 @@ public class Storage
|
||||
List<List<String>> files = metainfo.getFiles();
|
||||
int sz = files != null ? files.size() : 1;
|
||||
_torrentFiles = new ArrayList<TorrentFile>(sz);
|
||||
_preserveFileNames = preserveFileNames;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,8 +130,10 @@ public class Storage
|
||||
throws IOException
|
||||
{
|
||||
_util = util;
|
||||
_base = baseFile;
|
||||
_log = util.getContext().logManager().getLog(Storage.class);
|
||||
this.listener = listener;
|
||||
_preserveFileNames = true;
|
||||
// Create names, rafs and lengths arrays.
|
||||
_torrentFiles = getFiles(baseFile);
|
||||
|
||||
@ -305,24 +316,34 @@ public class Storage
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file canonical path (non-directory)
|
||||
* Get index to pass to remaining(), getPriority(), setPriority()
|
||||
*
|
||||
* @param file non-canonical path (non-directory)
|
||||
* @return internal index of file; -1 if unknown file
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public int indexOf(File file) {
|
||||
for (int i = 0; i < _torrentFiles.size(); i++) {
|
||||
File f = _torrentFiles.get(i).RAFfile;
|
||||
if (f.equals(file))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileIndex as obtained from indexOf
|
||||
* @return number of bytes remaining; -1 if unknown file
|
||||
* @since 0.7.14
|
||||
*/
|
||||
public long remaining(String file) {
|
||||
public long remaining(int fileIndex) {
|
||||
if (fileIndex < 0 || fileIndex >= _torrentFiles.size())
|
||||
return -1;
|
||||
long bytes = 0;
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
File f = tf.RAFfile;
|
||||
// use canonical in case snark dir or sub dirs are symlinked
|
||||
String canonical = null;
|
||||
if (f != null) {
|
||||
try {
|
||||
canonical = f.getCanonicalPath();
|
||||
} catch (IOException ioe) {
|
||||
f = null;
|
||||
}
|
||||
}
|
||||
if (f != null && canonical.equals(file)) {
|
||||
for (int i = 0; i < _torrentFiles.size(); i++) {
|
||||
TorrentFile tf = _torrentFiles.get(i);
|
||||
if (i == fileIndex) {
|
||||
File f = tf.RAFfile;
|
||||
if (complete())
|
||||
return 0;
|
||||
int psz = piece_size;
|
||||
@ -348,49 +369,30 @@ public class Storage
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file canonical path (non-directory)
|
||||
* @param fileIndex as obtained from indexOf
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public int getPriority(String file) {
|
||||
public int getPriority(int fileIndex) {
|
||||
if (complete() || metainfo.getFiles() == null)
|
||||
return 0;
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
File f = tf.RAFfile;
|
||||
// use canonical in case snark dir or sub dirs are symlinked
|
||||
if (f != null) {
|
||||
try {
|
||||
String canonical = f.getCanonicalPath();
|
||||
if (canonical.equals(file))
|
||||
return tf.priority;
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
if (fileIndex < 0 || fileIndex >= _torrentFiles.size())
|
||||
return 0;
|
||||
return _torrentFiles.get(fileIndex).priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must call Snark.updatePiecePriorities()
|
||||
* (which calls getPiecePriorities()) after calling this.
|
||||
* @param file canonical path (non-directory)
|
||||
* @param fileIndex as obtained from indexOf
|
||||
* @param pri default 0; <0 to disable
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public void setPriority(String file, int pri) {
|
||||
public void setPriority(int fileIndex, int pri) {
|
||||
if (complete() || metainfo.getFiles() == null)
|
||||
return;
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
File f = tf.RAFfile;
|
||||
// use canonical in case snark dir or sub dirs are symlinked
|
||||
if (f != null) {
|
||||
try {
|
||||
String canonical = f.getCanonicalPath();
|
||||
if (canonical.equals(file)) {
|
||||
tf.priority = pri;
|
||||
return;
|
||||
}
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
if (fileIndex < 0 || fileIndex >= _torrentFiles.size())
|
||||
return;
|
||||
_torrentFiles.get(fileIndex).priority = pri;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -483,16 +485,21 @@ public class Storage
|
||||
* @since 0.7.14
|
||||
*/
|
||||
public String getBaseName() {
|
||||
return filterName(metainfo.getName());
|
||||
return optFilterName(metainfo.getName());
|
||||
}
|
||||
|
||||
/** @since 0.9.15 */
|
||||
public boolean getPreserveFileNames() {
|
||||
return _preserveFileNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates (and/or checks) all files from the metainfo file list.
|
||||
* Only call this once, and only after the constructor with the metainfo.
|
||||
*/
|
||||
public void check(String rootDir) throws IOException
|
||||
public void check() throws IOException
|
||||
{
|
||||
check(rootDir, 0, null);
|
||||
check(0, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -500,14 +507,9 @@ public class Storage
|
||||
* Use a saved bitfield and timestamp from a config file.
|
||||
* Only call this once, and only after the constructor with the metainfo.
|
||||
*/
|
||||
public void check(String rootDir, long savedTime, BitField savedBitField) throws IOException
|
||||
public void check(long savedTime, BitField savedBitField) throws IOException
|
||||
{
|
||||
File base;
|
||||
boolean areFilesPublic = _util.getFilesPublic();
|
||||
if (areFilesPublic)
|
||||
base = new File(rootDir, filterName(metainfo.getName()));
|
||||
else
|
||||
base = new SecureFile(rootDir, filterName(metainfo.getName()));
|
||||
boolean useSavedBitField = savedTime > 0 && savedBitField != null;
|
||||
|
||||
if (!_torrentFiles.isEmpty())
|
||||
@ -517,16 +519,18 @@ public class Storage
|
||||
{
|
||||
// Create base as file.
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Creating/Checking file: " + base);
|
||||
if (!base.createNewFile() && !base.exists())
|
||||
throw new IOException("Could not create file " + base);
|
||||
_log.info("Creating/Checking file: " + _base);
|
||||
// createNewFile() can throw a "Permission denied" IOE even if the file exists???
|
||||
// so do it second
|
||||
if (!_base.exists() && !_base.createNewFile())
|
||||
throw new IOException("Could not create file " + _base);
|
||||
|
||||
_torrentFiles.add(new TorrentFile(base, base, metainfo.getTotalLength()));
|
||||
_torrentFiles.add(new TorrentFile(_base, _base, metainfo.getTotalLength()));
|
||||
if (useSavedBitField) {
|
||||
long lm = base.lastModified();
|
||||
long lm = _base.lastModified();
|
||||
if (lm <= 0 || lm > savedTime)
|
||||
useSavedBitField = false;
|
||||
else if (base.length() != metainfo.getTotalLength())
|
||||
else if (_base.length() != metainfo.getTotalLength())
|
||||
useSavedBitField = false;
|
||||
}
|
||||
}
|
||||
@ -534,9 +538,9 @@ public class Storage
|
||||
{
|
||||
// Create base as dir.
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Creating/Checking directory: " + base);
|
||||
if (!base.mkdir() && !base.isDirectory())
|
||||
throw new IOException("Could not create directory " + base);
|
||||
_log.info("Creating/Checking directory: " + _base);
|
||||
if (!_base.mkdir() && !_base.isDirectory())
|
||||
throw new IOException("Could not create directory " + _base);
|
||||
|
||||
List<Long> ls = metainfo.getLengths();
|
||||
int size = files.size();
|
||||
@ -544,7 +548,7 @@ public class Storage
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
List<String> path = files.get(i);
|
||||
File f = createFileFromNames(base, path, areFilesPublic);
|
||||
File f = createFileFromNames(_base, path, areFilesPublic);
|
||||
// dup file name check after filtering
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (f.equals(_torrentFiles.get(j).RAFfile)) {
|
||||
@ -560,12 +564,12 @@ public class Storage
|
||||
else
|
||||
lastPath = '_' + lastPath;
|
||||
path.set(last, lastPath);
|
||||
f = createFileFromNames(base, path, areFilesPublic);
|
||||
f = createFileFromNames(_base, path, areFilesPublic);
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
long len = ls.get(i).longValue();
|
||||
_torrentFiles.add(new TorrentFile(base, f, len));
|
||||
_torrentFiles.add(new TorrentFile(_base, f, len));
|
||||
total += len;
|
||||
if (useSavedBitField) {
|
||||
long lm = f.lastModified();
|
||||
@ -609,10 +613,9 @@ public class Storage
|
||||
* Doesn't really reopen the file descriptors for a restart.
|
||||
* Just does an existence check but no length check or data reverification
|
||||
*
|
||||
* @param rootDir ignored
|
||||
* @throws IOE on fail
|
||||
*/
|
||||
public void reopen(String rootDir) throws IOException
|
||||
public void reopen() throws IOException
|
||||
{
|
||||
if (_torrentFiles.isEmpty())
|
||||
throw new IOException("Storage not checked yet");
|
||||
@ -635,6 +638,19 @@ public class Storage
|
||||
0x2028, 0x2029
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter the name, but only if configured to do so.
|
||||
* We will do so on torrents received from others, but not
|
||||
* on those we created ourselves, so we do not lose track of files.
|
||||
*
|
||||
* @since 0.9.15
|
||||
*/
|
||||
private String optFilterName(String name) {
|
||||
if (_preserveFileNames)
|
||||
return name;
|
||||
return filterName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes 'suspicious' characters from the given file name.
|
||||
* http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx
|
||||
@ -688,14 +704,16 @@ public class Storage
|
||||
* Note that filtering each path element individually may lead to
|
||||
* things going in the wrong place if there are duplicates
|
||||
* in intermediate path elements after filtering.
|
||||
*
|
||||
* @param names path elements
|
||||
*/
|
||||
private static File createFileFromNames(File base, List<String> names, boolean areFilesPublic) throws IOException
|
||||
private File createFileFromNames(File base, List<String> names, boolean areFilesPublic) throws IOException
|
||||
{
|
||||
File f = null;
|
||||
Iterator<String> it = names.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
String name = filterName(it.next());
|
||||
String name = optFilterName(it.next());
|
||||
if (it.hasNext())
|
||||
{
|
||||
// Another dir in the hierarchy.
|
||||
@ -714,22 +732,56 @@ public class Storage
|
||||
f = new File(base, name);
|
||||
else
|
||||
f = new SecureFile(base, name);
|
||||
if (!f.createNewFile() && !f.exists())
|
||||
// createNewFile() can throw a "Permission denied" IOE even if the file exists???
|
||||
// so do it second
|
||||
if (!f.exists() && !f.createNewFile())
|
||||
throw new IOException("Could not create file " + f);
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
public static File getFileFromNames(File base, List<String> names)
|
||||
{
|
||||
Iterator<String> it = names.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
String name = filterName(it.next());
|
||||
base = new File(base, name);
|
||||
/**
|
||||
* The base file or directory.
|
||||
* @return the File
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public File getBase() {
|
||||
return _base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not include directories. Unsorted.
|
||||
* @return a new List
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public List<File> getFiles() {
|
||||
List<File> rv = new ArrayList<File>(_torrentFiles.size());
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
rv.add(tf.RAFfile);
|
||||
}
|
||||
return base;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes the base for a multi-file torrent.
|
||||
* Sorted bottom-up for easy deletion.
|
||||
* Slow. Use for deletion only.
|
||||
* @return a new Set or null for a single-file torrent
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public SortedSet<File> getDirectories() {
|
||||
if (!_base.isDirectory())
|
||||
return null;
|
||||
SortedSet<File> rv = new TreeSet<File>(Collections.reverseOrder());
|
||||
rv.add(_base);
|
||||
for (TorrentFile tf : _torrentFiles) {
|
||||
File f = tf.RAFfile;
|
||||
do {
|
||||
f = f.getParentFile();
|
||||
} while (f != null && rv.add(f));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,8 +71,9 @@ public class TrackerClient implements Runnable {
|
||||
private static final String COMPLETED_EVENT = "completed";
|
||||
private static final String STOPPED_EVENT = "stopped";
|
||||
private static final String NOT_REGISTERED = "torrent not registered"; //bytemonsoon
|
||||
/** this is our equivalent to router.utorrent.com for bootstrap */
|
||||
private static final String DEFAULT_BACKUP_TRACKER = "http://tracker.welterde.i2p/a";
|
||||
private static final String NOT_REGISTERED_2 = "torrent not found"; // diftracker
|
||||
private static final String NOT_REGISTERED_3 = "torrent unauthorised"; // vuze
|
||||
private static final String ERROR_GOT_HTML = "received html"; // fake return
|
||||
|
||||
private final static int SLEEP = 5; // 5 minutes.
|
||||
private final static int DELAY_MIN = 2000; // 2 secs.
|
||||
@ -82,8 +83,11 @@ public class TrackerClient implements Runnable {
|
||||
private final static int MAX_CONSEC_FAILS = 5; // slow down after this
|
||||
private final static int LONG_SLEEP = 30*60*1000; // sleep a while after lots of fails
|
||||
private final static long MIN_TRACKER_ANNOUNCE_INTERVAL = 15*60*1000;
|
||||
private final static long MIN_DHT_ANNOUNCE_INTERVAL = 10*60*1000;
|
||||
private final static long MIN_DHT_ANNOUNCE_INTERVAL = 39*60*1000;
|
||||
/** No guidance in BEP 5; standard practice is K (=8) */
|
||||
private static final int DHT_ANNOUNCE_PEERS = 4;
|
||||
public static final int PORT = 6881;
|
||||
private static final int MAX_TRACKERS = 12;
|
||||
|
||||
private final I2PSnarkUtil _util;
|
||||
private final MetaInfo meta;
|
||||
@ -106,6 +110,8 @@ public class TrackerClient implements Runnable {
|
||||
// these 2 used in loop()
|
||||
private volatile boolean runStarted;
|
||||
private volatile int consecutiveFails;
|
||||
// if we don't want anything else.
|
||||
// Not necessarily seeding, as we may have skipped some files.
|
||||
private boolean completed;
|
||||
private volatile boolean _fastUnannounce;
|
||||
private long lastDHTAnnounce;
|
||||
@ -288,6 +294,7 @@ public class TrackerClient implements Runnable {
|
||||
}
|
||||
|
||||
// announce list
|
||||
// We completely ignore the BEP 12 processing rules
|
||||
if (meta != null && !meta.isPrivate()) {
|
||||
List<List<String>> list = meta.getAnnounceList();
|
||||
if (list != null) {
|
||||
@ -300,6 +307,12 @@ public class TrackerClient implements Runnable {
|
||||
_log.debug("Additional announce (list): [" + url + "] for infoHash: " + infoHash);
|
||||
}
|
||||
}
|
||||
if (trackers.size() > 2) {
|
||||
// shuffle everything but the primary
|
||||
TCTracker pri = trackers.remove(0);
|
||||
Collections.shuffle(trackers, _util.getContext().random());
|
||||
trackers.add(0, pri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,7 +342,9 @@ public class TrackerClient implements Runnable {
|
||||
_log.debug("Backup announce: [" + url + "] for infoHash: " + infoHash);
|
||||
}
|
||||
if (backupTrackers.isEmpty()) {
|
||||
backupTrackers.add(new TCTracker(DEFAULT_BACKUP_TRACKER, false));
|
||||
backupTrackers.add(new TCTracker(SnarkManager.DEFAULT_BACKUP_TRACKER, false));
|
||||
} else if (trackers.size() > 1) {
|
||||
Collections.shuffle(backupTrackers, _util.getContext().random());
|
||||
}
|
||||
}
|
||||
this.completed = coordinator.getLeft() == 0;
|
||||
@ -345,7 +360,13 @@ public class TrackerClient implements Runnable {
|
||||
private boolean isNewValidTracker(Set<Hash> existing, String ann) {
|
||||
Hash h = getHostHash(ann);
|
||||
if (h == null) {
|
||||
_log.error("Bad announce URL: [" + ann + ']');
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Bad announce URL: [" + ann + ']');
|
||||
return false;
|
||||
}
|
||||
if (existing.size() >= MAX_TRACKERS) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Not using announce URL, we have enough: [" + ann + ']');
|
||||
return false;
|
||||
}
|
||||
boolean rv = existing.add(h);
|
||||
@ -375,7 +396,7 @@ public class TrackerClient implements Runnable {
|
||||
// Local DHT tracker announce
|
||||
DHT dht = _util.getDHT();
|
||||
if (dht != null && (meta == null || !meta.isPrivate()))
|
||||
dht.announce(snark.getInfoHash());
|
||||
dht.announce(snark.getInfoHash(), coordinator.completed());
|
||||
|
||||
int oldSeenPeers = snark.getTrackerSeenPeers();
|
||||
int maxSeenPeers = 0;
|
||||
@ -509,7 +530,7 @@ public class TrackerClient implements Runnable {
|
||||
coordinator.getPeerCount() <= 0 &&
|
||||
_util.getContext().clock().now() > _startedOn + 2*60*60*1000 &&
|
||||
snark.getTotalLength() > 0 &&
|
||||
uploaded >= 2 * snark.getTotalLength()) {
|
||||
uploaded >= snark.getTotalLength() * 5 / 4) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Auto stopping " + snark.getBaseName());
|
||||
snark.setAutoStoppable(false);
|
||||
@ -523,7 +544,8 @@ public class TrackerClient implements Runnable {
|
||||
DHT dht = _util.getDHT();
|
||||
if (dht != null) {
|
||||
for (Peer peer : peers) {
|
||||
dht.announce(snark.getInfoHash(), peer.getPeerID().getDestHash());
|
||||
dht.announce(snark.getInfoHash(), peer.getPeerID().getDestHash(),
|
||||
false); // TODO actual seed/leech status
|
||||
}
|
||||
}
|
||||
|
||||
@ -556,13 +578,21 @@ public class TrackerClient implements Runnable {
|
||||
// don't show secondary tracker problems to the user
|
||||
if (tr.isPrimary)
|
||||
snark.setTrackerProblems(tr.trackerProblems);
|
||||
if (tr.trackerProblems.toLowerCase(Locale.US).startsWith(NOT_REGISTERED)) {
|
||||
String tplc = tr.trackerProblems.toLowerCase(Locale.US);
|
||||
if (tplc.startsWith(NOT_REGISTERED) || tplc.startsWith(NOT_REGISTERED_2) ||
|
||||
tplc.startsWith(NOT_REGISTERED_3) || tplc.startsWith(ERROR_GOT_HTML)) {
|
||||
// Give a guy some time to register it if using opentrackers too
|
||||
//if (trckrs.size() == 1) {
|
||||
// stop = true;
|
||||
// snark.stopTorrent();
|
||||
//} else { // hopefully each on the opentrackers list is really open
|
||||
if (tr.registerFails++ > MAX_REGISTER_FAILS)
|
||||
if (tr.registerFails++ > MAX_REGISTER_FAILS ||
|
||||
!completed || // no use retrying if we aren't seeding
|
||||
tplc.startsWith(ERROR_GOT_HTML) || // fake msg from doRequest()
|
||||
(!tr.isPrimary && tr.registerFails > MAX_REGISTER_FAILS / 2))
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Not longer announcing to " + tr.announce + " : " +
|
||||
tr.trackerProblems + " after " + tr.registerFails + " failures");
|
||||
tr.stop = true;
|
||||
//
|
||||
}
|
||||
@ -636,7 +666,9 @@ public class TrackerClient implements Runnable {
|
||||
numwant = 1;
|
||||
else
|
||||
numwant = _util.getMaxConnections();
|
||||
Collection<Hash> hashes = dht.getPeersAndAnnounce(snark.getInfoHash(), numwant, 5*60*1000, 1, 3*60*1000);
|
||||
Collection<Hash> hashes = dht.getPeersAndAnnounce(snark.getInfoHash(), numwant,
|
||||
5*60*1000, DHT_ANNOUNCE_PEERS, 3*60*1000,
|
||||
coordinator.completed(), numwant <= 1);
|
||||
if (!hashes.isEmpty()) {
|
||||
runStarted = true;
|
||||
lastDHTAnnounce = _util.getContext().clock().now();
|
||||
@ -773,10 +805,15 @@ public class TrackerClient implements Runnable {
|
||||
tr.lastRequestTime = System.currentTimeMillis();
|
||||
// Don't wait for a response to stopped when shutting down
|
||||
boolean fast = _fastUnannounce && event.equals(STOPPED_EVENT);
|
||||
byte[] fetched = _util.get(s, true, fast ? -1 : 0, small ? 128 : 1024, small ? 1024 : 8*1024);
|
||||
if (fetched == null) {
|
||||
throw new IOException("Error fetching " + s);
|
||||
}
|
||||
byte[] fetched = _util.get(s, true, fast ? -1 : 0, small ? 128 : 1024, small ? 1024 : 32*1024);
|
||||
if (fetched == null)
|
||||
throw new IOException("Error fetching");
|
||||
if (fetched.length == 0)
|
||||
throw new IOException("No data");
|
||||
// The HTML check only works if we didn't exceed the maxium fetch size specified in get(),
|
||||
// otherwise we already threw an IOE.
|
||||
if (fetched[0] == '<')
|
||||
throw new IOException(ERROR_GOT_HTML);
|
||||
|
||||
InputStream in = new ByteArrayInputStream(fetched);
|
||||
|
||||
@ -836,8 +873,7 @@ public class TrackerClient implements Runnable {
|
||||
return false;
|
||||
}
|
||||
return url.getProtocol().equals("http") &&
|
||||
(url.getHost().endsWith(".i2p") || url.getHost().equals("i2p")) &&
|
||||
url.getPort() < 0;
|
||||
(url.getHost().endsWith(".i2p") || url.getHost().equals("i2p"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -852,7 +888,7 @@ public class TrackerClient implements Runnable {
|
||||
} catch (MalformedURLException mue) {
|
||||
return null;
|
||||
}
|
||||
if (url.getPort() >= 0 || !url.getProtocol().equals("http"))
|
||||
if (!url.getProtocol().equals("http"))
|
||||
return null;
|
||||
String host = url.getHost();
|
||||
if (host.endsWith(".i2p"))
|
||||
|
@ -62,7 +62,7 @@ class TrackerInfo
|
||||
private TrackerInfo(Map<String, BEValue> m, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
|
||||
throws IOException
|
||||
{
|
||||
BEValue reason = (BEValue)m.get("failure reason");
|
||||
BEValue reason = m.get("failure reason");
|
||||
if (reason != null)
|
||||
{
|
||||
failure_reason = reason.getString();
|
||||
@ -72,13 +72,13 @@ class TrackerInfo
|
||||
else
|
||||
{
|
||||
failure_reason = null;
|
||||
BEValue beInterval = (BEValue)m.get("interval");
|
||||
BEValue beInterval = m.get("interval");
|
||||
if (beInterval == null)
|
||||
throw new InvalidBEncodingException("No interval given");
|
||||
else
|
||||
interval = beInterval.getInt();
|
||||
|
||||
BEValue bePeers = (BEValue)m.get("peers");
|
||||
BEValue bePeers = m.get("peers");
|
||||
if (bePeers == null) {
|
||||
peers = Collections.emptySet();
|
||||
} else {
|
||||
@ -93,14 +93,14 @@ class TrackerInfo
|
||||
peers = p;
|
||||
}
|
||||
|
||||
BEValue bev = (BEValue)m.get("complete");
|
||||
BEValue bev = m.get("complete");
|
||||
if (bev != null) try {
|
||||
complete = bev.getInt();
|
||||
if (complete < 0)
|
||||
complete = 0;
|
||||
} catch (InvalidBEncodingException ibe) {}
|
||||
|
||||
bev = (BEValue)m.get("incomplete");
|
||||
bev = m.get("incomplete");
|
||||
if (bev != null) try {
|
||||
incomplete = bev.getInt();
|
||||
if (incomplete < 0)
|
||||
@ -196,6 +196,9 @@ class TrackerInfo
|
||||
return complete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not HTML escaped.
|
||||
*/
|
||||
public String getFailureReason()
|
||||
{
|
||||
return failure_reason;
|
||||
|
@ -6,6 +6,7 @@ import java.util.List;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.TrustedUpdate;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.update.*;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
@ -109,7 +110,7 @@ class UpdateRunner implements UpdateTask, CompleteListener {
|
||||
_umgr.notifyAttemptFailed(this, "No tracker, no DHT, no OT", null);
|
||||
continue;
|
||||
}
|
||||
_snark = _smgr.addMagnet(name, ih, trackerURL, true, true, this);
|
||||
_snark = _smgr.addMagnet(name, ih, trackerURL, true, true, null, this);
|
||||
if (_snark != null) {
|
||||
updateStatus("<b>" + _smgr.util().getString("Updating from {0}", linkify(updateURL)) + "</b>");
|
||||
new Timeout();
|
||||
@ -290,12 +291,21 @@ class UpdateRunner implements UpdateTask, CompleteListener {
|
||||
return _smgr.getSavedTorrentBitField(snark);
|
||||
}
|
||||
|
||||
public boolean getSavedPreserveNamesSetting(Snark snark) {
|
||||
return _smgr.getSavedPreserveNamesSetting(snark);
|
||||
}
|
||||
|
||||
public long getSavedUploaded(Snark snark) {
|
||||
return _smgr.getSavedUploaded(snark);
|
||||
}
|
||||
|
||||
//////// end CompleteListener methods
|
||||
|
||||
private static String linkify(String url) {
|
||||
String durl = url.length() <= 28 ? url :
|
||||
url.substring(0, 25) + "…";
|
||||
return "<a target=\"_blank\" href=\"" + url + "\"/>" + durl + "</a>";
|
||||
String durl = url.length() <= 28 ? DataHelper.escapeHTML(url) :
|
||||
DataHelper.escapeHTML(url.substring(0, 25)) + "…";
|
||||
// TODO urlEncode instead
|
||||
return "<a target=\"_blank\" href=\"" + DataHelper.escapeHTML(url) + "\"/>" + durl + "</a>";
|
||||
}
|
||||
|
||||
private void updateStatus(String s) {
|
||||
|
@ -21,6 +21,7 @@
|
||||
package org.klomp.snark.bencode;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
@ -232,10 +233,8 @@ public class BDecoder
|
||||
|
||||
if (c == '-')
|
||||
{
|
||||
c = read();
|
||||
if (c == '0')
|
||||
throw new InvalidBEncodingException("Negative zero not allowed");
|
||||
chars.append((char)c);
|
||||
c = read();
|
||||
}
|
||||
|
||||
if (c < '1' || c > '9')
|
||||
@ -376,4 +375,21 @@ public class BDecoder
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* prints out the decoded data
|
||||
* @since 0.9.14
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 1) {
|
||||
System.err.println("Usage: BDecoder file.torrent");
|
||||
System.exit(1);
|
||||
}
|
||||
try {
|
||||
BEValue bev = bdecode(new FileInputStream(args[0]));
|
||||
System.out.println(bev.toString());
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ public interface DHT {
|
||||
public void ping(Destination dest, int port);
|
||||
|
||||
/**
|
||||
* Get peers for a torrent, and announce to the closest node we find.
|
||||
* Get peers for a torrent, and announce to the closest annMax nodes we find.
|
||||
* Blocking!
|
||||
* Caller should run in a thread.
|
||||
*
|
||||
@ -45,9 +45,13 @@ public interface DHT {
|
||||
* @param maxWait the maximum time to wait (ms) must be > 0
|
||||
* @param annMax the number of peers to announce to
|
||||
* @param annMaxWait the maximum total time to wait for announces, may be 0 to return immediately without waiting for acks
|
||||
* @param isSeed true if seed, false if leech
|
||||
* @param noSeeds true if we do not want seeds in the result
|
||||
* @return possibly empty (never null)
|
||||
*/
|
||||
public Collection<Hash> getPeersAndAnnounce(byte[] ih, int max, long maxWait, int annMax, long annMaxWait);
|
||||
public Collection<Hash> getPeersAndAnnounce(byte[] ih, int max, long maxWait,
|
||||
int annMax, long annMaxWait,
|
||||
boolean isSeed, boolean noSeeds);
|
||||
|
||||
/**
|
||||
* Announce to ourselves.
|
||||
@ -55,16 +59,16 @@ public interface DHT {
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
*/
|
||||
public void announce(byte[] ih);
|
||||
public void announce(byte[] ih, boolean isSeed);
|
||||
|
||||
/**
|
||||
* Announce somebody else we know about.
|
||||
* Announce somebody else we know about to ourselves.
|
||||
* Non-blocking.
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param peerHash the peer's Hash
|
||||
*/
|
||||
public void announce(byte[] ih, byte[] peerHash);
|
||||
public void announce(byte[] ih, byte[] peerHash, boolean isSeed);
|
||||
|
||||
/**
|
||||
* Remove reference to ourselves in the local tracker.
|
||||
@ -84,9 +88,10 @@ public interface DHT {
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param maxWait the maximum total time to wait (ms) or 0 to do all in parallel and return immediately.
|
||||
* @param isSeed true if seed, false if leech
|
||||
* @return the number of successful announces, not counting ourselves.
|
||||
*/
|
||||
public int announce(byte[] ih, int max, long maxWait);
|
||||
public int announce(byte[] ih, int max, long maxWait, boolean isSeed);
|
||||
|
||||
/**
|
||||
* Stop everything.
|
||||
|
@ -34,7 +34,8 @@ class DHTTracker {
|
||||
|
||||
/** stagger with other cleaners */
|
||||
private static final long CLEAN_TIME = 199*1000;
|
||||
private static final long MAX_EXPIRE_TIME = 45*60*1000;
|
||||
/** no guidance in BEP 5; Vuze is 8h */
|
||||
private static final long MAX_EXPIRE_TIME = 3*60*60*1000L;
|
||||
private static final long MIN_EXPIRE_TIME = 15*60*1000;
|
||||
private static final long DELTA_EXPIRE_TIME = 3*60*1000;
|
||||
private static final int MAX_PEERS = 2000;
|
||||
@ -59,7 +60,7 @@ class DHTTracker {
|
||||
_isRunning = false;
|
||||
}
|
||||
|
||||
void announce(InfoHash ih, Hash hash) {
|
||||
void announce(InfoHash ih, Hash hash, boolean isSeed) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Announce " + hash + " for " + ih);
|
||||
Peers peers = _torrents.get(ih);
|
||||
@ -78,6 +79,9 @@ class DHTTracker {
|
||||
if (peer2 != null)
|
||||
peer = peer2;
|
||||
peer.setLastSeen(_context.clock().now());
|
||||
// don't let false trump true, as not all sources know the seed status
|
||||
if (isSeed)
|
||||
peer.setSeed(true);
|
||||
} else {
|
||||
// We could update setLastSeen if he is already
|
||||
// in there, but that would tend to keep
|
||||
@ -93,26 +97,42 @@ class DHTTracker {
|
||||
Peers peers = _torrents.get(ih);
|
||||
if (peers == null)
|
||||
return;
|
||||
Peer peer = new Peer(hash.getData());
|
||||
peers.remove(peer);
|
||||
peers.remove(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Caller's responsibility to remove himself from the list
|
||||
*
|
||||
* @param noSeeds true if we do not want seeds in the result
|
||||
* @return list or empty list (never null)
|
||||
*/
|
||||
List<Hash> getPeers(InfoHash ih, int max) {
|
||||
List<Hash> getPeers(InfoHash ih, int max, boolean noSeeds) {
|
||||
Peers peers = _torrents.get(ih);
|
||||
if (peers == null)
|
||||
if (peers == null || max <= 0)
|
||||
return Collections.emptyList();
|
||||
|
||||
int size = peers.size();
|
||||
List<Hash> rv = new ArrayList<Hash>(peers.values());
|
||||
if (max < size) {
|
||||
Collections.shuffle(rv, _context.random());
|
||||
List<Peer> rv = new ArrayList<Peer>(peers.values());
|
||||
int size = rv.size();
|
||||
if (max < size)
|
||||
Collections.shuffle(rv, _context.random());
|
||||
if (noSeeds) {
|
||||
int i = 0;
|
||||
for (Iterator<Peer> iter = rv.iterator(); iter.hasNext(); ) {
|
||||
if (iter.next().isSeed())
|
||||
iter.remove();
|
||||
else if (++i >= max)
|
||||
break;
|
||||
}
|
||||
if (max < rv.size())
|
||||
rv = rv.subList(0, max);
|
||||
} else {
|
||||
if (max < size)
|
||||
rv = rv.subList(0, max);
|
||||
}
|
||||
return rv;
|
||||
// a Peer is a Hash
|
||||
List rv1 = rv;
|
||||
List<Hash> rv2 = rv1;
|
||||
return rv2;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -10,6 +10,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@ -39,6 +40,7 @@ import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
import org.klomp.snark.SnarkManager;
|
||||
import org.klomp.snark.TrackerClient;
|
||||
import org.klomp.snark.bencode.BDecoder;
|
||||
import org.klomp.snark.bencode.BEncoder;
|
||||
@ -151,7 +153,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private static final long CLEAN_TIME = 63*1000;
|
||||
private static final long EXPLORE_TIME = 877*1000;
|
||||
private static final long BLACKLIST_CLEAN_TIME = 17*60*1000;
|
||||
private static final String DHT_FILE_SUFFIX = ".dht.dat";
|
||||
public static final String DHT_FILE_SUFFIX = ".dht.dat";
|
||||
|
||||
private static final int SEND_CRYPTO_TAGS = 8;
|
||||
private static final int LOW_CRYPTO_TAGS = 4;
|
||||
@ -184,8 +186,14 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
_myNID = new NID(_myID);
|
||||
}
|
||||
_myNodeInfo = new NodeInfo(_myNID, session.getMyDestination(), _qPort);
|
||||
_dhtFile = new File(ctx.getConfigDir(), baseName + DHT_FILE_SUFFIX);
|
||||
_backupDhtFile = baseName.equals("i2psnark") ? null : new File(ctx.getConfigDir(), "i2psnark" + DHT_FILE_SUFFIX);
|
||||
File conf = new File(ctx.getConfigDir(), baseName + ".config" + SnarkManager.CONFIG_DIR_SUFFIX);
|
||||
_dhtFile = new File(conf, "i2psnark" + DHT_FILE_SUFFIX);
|
||||
if (baseName.equals("i2psnark")) {
|
||||
_backupDhtFile = null;
|
||||
} else {
|
||||
File bconf = new File(ctx.getConfigDir(), "i2psnark.config" + SnarkManager.CONFIG_DIR_SUFFIX);
|
||||
_backupDhtFile = new File(bconf, "i2psnark" + DHT_FILE_SUFFIX);
|
||||
}
|
||||
_knownNodes = new DHTNodes(ctx, _myNID);
|
||||
|
||||
start();
|
||||
@ -305,7 +313,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peers for a torrent, and announce to the closest node we find.
|
||||
* Get peers for a torrent, and announce to the closest annMax nodes we find.
|
||||
* This is an iterative lookup in the DHT.
|
||||
* Blocking!
|
||||
* Caller should run in a thread.
|
||||
@ -315,12 +323,16 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* @param maxWait the maximum time to wait (ms) must be > 0
|
||||
* @param annMax the number of peers to announce to
|
||||
* @param annMaxWait the maximum total time to wait for announces, may be 0 to return immediately without waiting for acks
|
||||
* @param isSeed true if seed, false if leech
|
||||
* @param noSeeds true if we do not want seeds in the result
|
||||
* @return possibly empty (never null)
|
||||
*/
|
||||
public Collection<Hash> getPeersAndAnnounce(byte[] ih, int max, long maxWait, int annMax, long annMaxWait) {
|
||||
public Collection<Hash> getPeersAndAnnounce(byte[] ih, int max, long maxWait,
|
||||
int annMax, long annMaxWait,
|
||||
boolean isSeed, boolean noSeeds) {
|
||||
// check local tracker first
|
||||
InfoHash iHash = new InfoHash(ih);
|
||||
Collection<Hash> rv = _tracker.getPeers(iHash, max);
|
||||
Collection<Hash> rv = _tracker.getPeers(iHash, max, noSeeds);
|
||||
rv.remove(_myNodeInfo.getHash());
|
||||
if (rv.size() >= max)
|
||||
return rv;
|
||||
@ -356,7 +368,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Try " + i + ": " + nInfo);
|
||||
|
||||
ReplyWaiter waiter = sendGetPeers(nInfo, iHash);
|
||||
ReplyWaiter waiter = sendGetPeers(nInfo, iHash, noSeeds);
|
||||
if (waiter == null)
|
||||
continue;
|
||||
synchronized(waiter) {
|
||||
@ -415,7 +427,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
// now announce
|
||||
if (!heardFrom.isEmpty()) {
|
||||
announce(ih);
|
||||
announce(ih, isSeed);
|
||||
// announce to the closest we've heard from
|
||||
int annCnt = 0;
|
||||
long start = _context.clock().now();
|
||||
@ -424,7 +436,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Announcing to closest from get peers: " + annTo);
|
||||
long toWait = annMaxWait > 0 ? Math.min(annMaxWait, 60*1000) : 0;
|
||||
if (announce(ih, annTo, toWait))
|
||||
if (announce(ih, annTo, toWait, isSeed))
|
||||
annCnt++;
|
||||
if (annMaxWait > 0) {
|
||||
annMaxWait -= _context.clock().now() - start;
|
||||
@ -437,7 +449,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
// so this is essentially just a retry
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Announcing to closest in kbuckets after get peers failed");
|
||||
announce(ih, annMax, annMaxWait);
|
||||
announce(ih, annMax, annMaxWait, isSeed);
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
_log.info("Finished get Peers, returning " + rv.size());
|
||||
@ -454,21 +466,21 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
*/
|
||||
public void announce(byte[] ih) {
|
||||
public void announce(byte[] ih, boolean isSeed) {
|
||||
InfoHash iHash = new InfoHash(ih);
|
||||
_tracker.announce(iHash, _myNodeInfo.getHash());
|
||||
_tracker.announce(iHash, _myNodeInfo.getHash(), isSeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Announce somebody else we know about.
|
||||
* Announce somebody else we know about to ourselves.
|
||||
* Non-blocking.
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param peerHash the peer's Hash
|
||||
*/
|
||||
public void announce(byte[] ih, byte[] peerHash) {
|
||||
public void announce(byte[] ih, byte[] peerHash, boolean isSeed) {
|
||||
InfoHash iHash = new InfoHash(ih);
|
||||
_tracker.announce(iHash, new Hash(peerHash));
|
||||
_tracker.announce(iHash, new Hash(peerHash), isSeed);
|
||||
// Do NOT do this, corrupts the Hash cache and the Peer ID
|
||||
//_tracker.announce(iHash, Hash.create(peerHash));
|
||||
}
|
||||
@ -500,10 +512,11 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param max maximum number of peers to announce to
|
||||
* @param maxWait the maximum total time to wait (ms) or 0 to do all in parallel and return immediately.
|
||||
* @param isSeed true if seed, false if leech
|
||||
* @return the number of successful announces, not counting ourselves.
|
||||
*/
|
||||
public int announce(byte[] ih, int max, long maxWait) {
|
||||
announce(ih);
|
||||
public int announce(byte[] ih, int max, long maxWait, boolean isSeed) {
|
||||
announce(ih, isSeed);
|
||||
int rv = 0;
|
||||
long start = _context.clock().now();
|
||||
InfoHash iHash = new InfoHash(ih);
|
||||
@ -513,7 +526,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
for (NodeInfo nInfo : nodes) {
|
||||
if (!_isRunning)
|
||||
break;
|
||||
if (announce(ih, nInfo, Math.min(maxWait, 60*1000)))
|
||||
if (announce(ih, nInfo, Math.min(maxWait, 60*1000), isSeed))
|
||||
rv++;
|
||||
maxWait -= _context.clock().now() - start;
|
||||
if (maxWait < 1000)
|
||||
@ -531,9 +544,10 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param nInfo the peer to announce to
|
||||
* @param maxWait the maximum time to wait (ms) or 0 to return immediately.
|
||||
* @param isSeed true if seed, false if leech
|
||||
* @return success
|
||||
*/
|
||||
private boolean announce(byte[] ih, NodeInfo nInfo, long maxWait) {
|
||||
private boolean announce(byte[] ih, NodeInfo nInfo, long maxWait, boolean isSeed) {
|
||||
InfoHash iHash = new InfoHash(ih);
|
||||
// it isn't clear from BEP 5 if a token is bound to a single infohash?
|
||||
// for now, just bind to the NID
|
||||
@ -549,7 +563,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
return false;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("No token for announce to " + nInfo + ", sending get_peers first");
|
||||
ReplyWaiter waiter = sendGetPeers(nInfo, iHash);
|
||||
ReplyWaiter waiter = sendGetPeers(nInfo, iHash, false);
|
||||
if (waiter == null)
|
||||
return false;
|
||||
long start = _context.clock().now();
|
||||
@ -580,7 +594,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
|
||||
// send and wait on rcv msg lock unless maxWait <= 0
|
||||
ReplyWaiter waiter = sendAnnouncePeer(nInfo, iHash, token);
|
||||
ReplyWaiter waiter = sendAnnouncePeer(nInfo, iHash, token, isSeed);
|
||||
if (waiter == null)
|
||||
return false;
|
||||
if (maxWait <= 0)
|
||||
@ -722,15 +736,18 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* Blocking if we have to look up the dest for the nodeinfo
|
||||
*
|
||||
* @param nInfo who to send it to
|
||||
* @param noSeeds true if we do not want seeds in the result
|
||||
* @return null on error
|
||||
*/
|
||||
private ReplyWaiter sendGetPeers(NodeInfo nInfo, InfoHash ih) {
|
||||
private ReplyWaiter sendGetPeers(NodeInfo nInfo, InfoHash ih, boolean noSeeds) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending get peers of " + ih + " to: " + nInfo);
|
||||
_log.info("Sending get peers of " + ih + " to: " + nInfo + " noseeds? " + noSeeds);
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("q", "get_peers");
|
||||
Map<String, Object> args = new HashMap<String, Object>();
|
||||
args.put("info_hash", ih.getData());
|
||||
if (noSeeds)
|
||||
args.put("noseed", Integer.valueOf(1));
|
||||
map.put("a", args);
|
||||
ReplyWaiter rv = sendQuery(nInfo, map, true);
|
||||
// save the InfoHash so we can get it later
|
||||
@ -743,11 +760,12 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* Non-blocking, will fail if we don't have the dest for the nodeinfo
|
||||
*
|
||||
* @param nInfo who to send it to
|
||||
* @param isSeed true if seed, false if leech
|
||||
* @return null on error
|
||||
*/
|
||||
private ReplyWaiter sendAnnouncePeer(NodeInfo nInfo, InfoHash ih, Token token) {
|
||||
private ReplyWaiter sendAnnouncePeer(NodeInfo nInfo, InfoHash ih, Token token, boolean isSeed) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending announce of " + ih + " to: " + nInfo);
|
||||
_log.info("Sending announce of " + ih + " to: " + nInfo + " seed? " + isSeed);
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("q", "announce_peer");
|
||||
Map<String, Object> args = new HashMap<String, Object>();
|
||||
@ -755,6 +773,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
// port ignored
|
||||
args.put("port", Integer.valueOf(TrackerClient.PORT));
|
||||
args.put("token", token.getData());
|
||||
args.put("seed", Integer.valueOf(isSeed ? 1 : 0));
|
||||
map.put("a", args);
|
||||
// an announce need not be signed, we have a token
|
||||
ReplyWaiter rv = sendQuery(nInfo, map, false);
|
||||
@ -814,16 +833,20 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
// All errors use the response port.
|
||||
|
||||
/**
|
||||
* Unused
|
||||
*
|
||||
* @param nInfo who to send it to
|
||||
* @return success
|
||||
*/
|
||||
private boolean sendError(NodeInfo nInfo, MsgID msgID, int err, String msg) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending error " + msg + " to: " + nInfo);
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
Map<String, Object> resps = new HashMap<String, Object>();
|
||||
map.put("r", resps);
|
||||
return sendResponse(nInfo, msgID, map);
|
||||
Map<String, Object> map = new HashMap<String, Object>(4);
|
||||
List<Object> error = new ArrayList(2);
|
||||
error.add(Integer.valueOf(err));
|
||||
error.add(msg);
|
||||
map.put("e", error);
|
||||
return sendError(nInfo, msgID, map);
|
||||
}
|
||||
|
||||
// Low-level send methods
|
||||
@ -910,7 +933,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param toPort the query port, we will increment here
|
||||
* Unused
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
private boolean sendError(NodeInfo nInfo, MsgID msgID, Map<String, Object> map) {
|
||||
@ -1113,14 +1137,22 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
} else if (method.equals("get_peers")) {
|
||||
byte[] hash = args.get("info_hash").getBytes();
|
||||
InfoHash ih = new InfoHash(hash);
|
||||
receiveGetPeers(msgID, nInfo, ih);
|
||||
boolean noSeeds = false;
|
||||
BEValue nos = args.get("noseed");
|
||||
if (nos != null)
|
||||
noSeeds = nos.getInt() == 1;
|
||||
receiveGetPeers(msgID, nInfo, ih, noSeeds);
|
||||
} else if (method.equals("announce_peer")) {
|
||||
byte[] hash = args.get("info_hash").getBytes();
|
||||
InfoHash ih = new InfoHash(hash);
|
||||
// this is the "TCP" port, we don't care
|
||||
//int port = args.get("port").getInt();
|
||||
byte[] token = args.get("token").getBytes();
|
||||
receiveAnnouncePeer(msgID, ih, token);
|
||||
boolean isSeed = false;
|
||||
BEValue iss = args.get("seed");
|
||||
if (iss != null)
|
||||
isSeed = iss.getInt() == 1;
|
||||
receiveAnnouncePeer(msgID, ih, token, isSeed);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unknown query method rcvd: " + method);
|
||||
@ -1233,18 +1265,22 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
/**
|
||||
* Handle and respond to the query
|
||||
*/
|
||||
private void receiveGetPeers(MsgID msgID, NodeInfo nInfo, InfoHash ih) throws InvalidBEncodingException {
|
||||
private void receiveGetPeers(MsgID msgID, NodeInfo nInfo,
|
||||
InfoHash ih, boolean noSeeds) throws InvalidBEncodingException {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Rcvd get_peers from: " + nInfo + " for: " + ih);
|
||||
_log.info("Rcvd get_peers from: " + nInfo + " for: " + ih + " noseeds? " + noSeeds);
|
||||
// generate and save random token
|
||||
Token token = new Token(_context);
|
||||
_outgoingTokens.put(token, nInfo);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Stored new OB token: " + token + " for: " + nInfo);
|
||||
|
||||
List<Hash> peers = _tracker.getPeers(ih, MAX_WANT);
|
||||
List<Hash> peers = _tracker.getPeers(ih, MAX_WANT, noSeeds);
|
||||
// Check this before removing him, so we don't needlessly send nodes
|
||||
// if he's the only one on the torrent.
|
||||
boolean noPeers = peers.isEmpty();
|
||||
peers.remove(nInfo.getHash()); // him
|
||||
if (peers.isEmpty()) {
|
||||
if (noPeers) {
|
||||
// similar to find node, but with token
|
||||
// get closest from DHT
|
||||
List<NodeInfo> nodes = _knownNodes.findClosest(ih, K);
|
||||
@ -1256,9 +1292,14 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
sendNodes(nInfo, msgID, token, nodeArray);
|
||||
} else {
|
||||
List<byte[]> hashes = new ArrayList<byte[]>(peers.size());
|
||||
for (Hash peer : peers) {
|
||||
hashes.add(peer.getData());
|
||||
List<byte[]> hashes;
|
||||
if (peers.isEmpty()) {
|
||||
hashes = Collections.EMPTY_LIST;
|
||||
} else {
|
||||
hashes = new ArrayList<byte[]>(peers.size());
|
||||
for (Hash peer : peers) {
|
||||
hashes.add(peer.getData());
|
||||
}
|
||||
}
|
||||
sendPeers(nInfo, msgID, token, hashes);
|
||||
}
|
||||
@ -1269,7 +1310,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* We have no node info here, it came on response port, we have to get it from the token.
|
||||
* So we can't verify that it came from the same peer, as BEP 5 specifies.
|
||||
*/
|
||||
private void receiveAnnouncePeer(MsgID msgID, InfoHash ih, byte[] tok) throws InvalidBEncodingException {
|
||||
private void receiveAnnouncePeer(MsgID msgID, InfoHash ih,
|
||||
byte[] tok, boolean isSeed) throws InvalidBEncodingException {
|
||||
Token token = new Token(tok);
|
||||
NodeInfo nInfo = _outgoingTokens.get(token);
|
||||
if (nInfo == null) {
|
||||
@ -1280,9 +1322,9 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
return;
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Rcvd announce from: " + nInfo + " for: " + ih);
|
||||
_log.info("Rcvd announce from: " + nInfo + " for: " + ih + " seed? " + isSeed);
|
||||
|
||||
_tracker.announce(ih, nInfo.getHash());
|
||||
_tracker.announce(ih, nInfo.getHash(), isSeed);
|
||||
// the reply for an announce is the same as the reply for a ping
|
||||
sendPong(nInfo, msgID);
|
||||
}
|
||||
@ -1341,7 +1383,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
* @throws NPE, IllegalArgumentException, and others too
|
||||
*/
|
||||
private List<NodeInfo> receiveNodes(NodeInfo nInfo, byte[] ids) throws InvalidBEncodingException {
|
||||
int max = Math.min(K, ids.length / NodeInfo.LENGTH);
|
||||
// Azureus sends 20
|
||||
int max = Math.min(3 * K, ids.length / NodeInfo.LENGTH);
|
||||
List<NodeInfo> rv = new ArrayList<NodeInfo>(max);
|
||||
for (int off = 0; off < ids.length && rv.size() < max; off += NodeInfo.LENGTH) {
|
||||
NodeInfo nInf = new NodeInfo(ids, off);
|
||||
@ -1399,6 +1442,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
// Errors.....
|
||||
|
||||
/**
|
||||
* @param error 1st item is error code, 2nd is message string
|
||||
* @throws NPE, and others too
|
||||
*/
|
||||
private void receiveError(ReplyWaiter waiter, List<BEValue> error) throws InvalidBEncodingException {
|
||||
@ -1536,6 +1580,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
// TODO throttle
|
||||
try {
|
||||
byte[] payload = session.receiveMessage(msgId);
|
||||
if (payload == null)
|
||||
return;
|
||||
_rxPkts.incrementAndGet();
|
||||
_rxBytes.addAndGet(payload.length);
|
||||
if (toPort == _qPort) {
|
||||
|
@ -3,6 +3,7 @@ package org.klomp.snark.dht;
|
||||
* From zzzot, modded and relicensed to GPLv2
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
|
||||
import net.i2p.crypto.SHA1Hash;
|
||||
@ -15,7 +16,7 @@ import net.i2p.data.DataHelper;
|
||||
* @since 0.9.2
|
||||
* @author zzz
|
||||
*/
|
||||
class NodeInfoComparator implements Comparator<NodeInfo> {
|
||||
class NodeInfoComparator implements Comparator<NodeInfo>, Serializable {
|
||||
private final byte[] _base;
|
||||
|
||||
public NodeInfoComparator(SHA1Hash h) {
|
||||
|
@ -14,7 +14,9 @@ import net.i2p.data.Hash;
|
||||
*/
|
||||
class Peer extends Hash {
|
||||
|
||||
private long lastSeen;
|
||||
private volatile long lastSeen;
|
||||
// todo we could pack this into the upper bit of lastSeen
|
||||
private volatile boolean isSeed;
|
||||
|
||||
public Peer(byte[] data) {
|
||||
super(data);
|
||||
@ -27,4 +29,14 @@ class Peer extends Hash {
|
||||
public void setLastSeen(long now) {
|
||||
lastSeen = now;
|
||||
}
|
||||
|
||||
/** @since 0.9.14 */
|
||||
public boolean isSeed() {
|
||||
return isSeed;
|
||||
}
|
||||
|
||||
/** @since 0.9.14 */
|
||||
public void setSeed(boolean isSeed) {
|
||||
this.isSeed = isSeed;
|
||||
}
|
||||
}
|
||||
|
@ -84,16 +84,17 @@ import net.i2p.util.SystemVersion;
|
||||
*/
|
||||
class BasicServlet extends HttpServlet
|
||||
{
|
||||
protected final I2PAppContext _context;
|
||||
protected final Log _log;
|
||||
private static final long serialVersionUID = 1L;
|
||||
protected transient final I2PAppContext _context;
|
||||
protected transient final Log _log;
|
||||
protected File _resourceBase;
|
||||
private String _warBase;
|
||||
|
||||
private final MimeTypes _mimeTypes;
|
||||
private transient final MimeTypes _mimeTypes;
|
||||
|
||||
/** same as PeerState.PARTSIZE */
|
||||
private static final int BUFSIZE = 16*1024;
|
||||
private ByteCache _cache = ByteCache.getInstance(16, BUFSIZE);
|
||||
private transient ByteCache _cache = ByteCache.getInstance(16, BUFSIZE);
|
||||
|
||||
private static final int WAR_CACHE_CONTROL_SECS = 24*60*60;
|
||||
private static final int FILE_CACHE_CONTROL_SECS = 24*60*60;
|
||||
@ -123,8 +124,10 @@ class BasicServlet extends HttpServlet
|
||||
* Files are served from here
|
||||
*/
|
||||
protected void setResourceBase(File base) throws UnavailableException {
|
||||
if (!base.isDirectory())
|
||||
if (!base.isDirectory()) {
|
||||
_log.log(Log.CRIT, "Configured i2psnark directory " + base + " does not exist");
|
||||
throw new UnavailableException("Resource base does not exist: " + base);
|
||||
}
|
||||
_resourceBase = base;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Resource base is " + _resourceBase);
|
||||
@ -178,11 +181,10 @@ class BasicServlet extends HttpServlet
|
||||
HttpContent r = null;
|
||||
if (_warBase != null && pathInContext.startsWith(_warBase)) {
|
||||
r = new JarContent(pathInContext);
|
||||
} else if (!pathInContext.contains("..") &&
|
||||
!pathInContext.endsWith("/")) {
|
||||
File f = new File(_resourceBase, pathInContext);
|
||||
} else {
|
||||
File f = getResource(pathInContext);
|
||||
// exists && !directory
|
||||
if (f.isFile())
|
||||
if (f != null && f.isFile())
|
||||
r = new FileContent(f);
|
||||
}
|
||||
return r;
|
||||
@ -280,7 +282,12 @@ class BasicServlet extends HttpServlet
|
||||
{
|
||||
if (content.getLastModified()/1000 <= ifmsl/1000)
|
||||
{
|
||||
response.reset();
|
||||
try {
|
||||
response.reset();
|
||||
} catch (IllegalStateException ise) {
|
||||
// committed
|
||||
return true;
|
||||
}
|
||||
response.setStatus(304);
|
||||
response.flushBuffer();
|
||||
return false;
|
||||
@ -552,14 +559,17 @@ class BasicServlet extends HttpServlet
|
||||
/**
|
||||
* Simple version of URIUtil.encodePath()
|
||||
*/
|
||||
protected static String encodePath(String path) throws MalformedURLException {
|
||||
try {
|
||||
URI uri = new URI(null, null, path, null);
|
||||
return uri.toString();
|
||||
} catch (URISyntaxException use) {
|
||||
// for ease of use, since a USE is not an IOE but a MUE is...
|
||||
throw new MalformedURLException(use.getMessage());
|
||||
}
|
||||
protected static String encodePath(String path) /* throws MalformedURLException */ {
|
||||
// Does NOT handle a ':' correctly, throws MUE.
|
||||
// Can't convert to %3a before hand or the % gets escaped
|
||||
//try {
|
||||
// URI uri = new URI(null, null, path, null);
|
||||
// return uri.toString();
|
||||
//} catch (URISyntaxException use) {
|
||||
// // for ease of use, since a USE is not an IOE but a MUE is...
|
||||
// throw new MalformedURLException(use.getMessage());
|
||||
//}
|
||||
return URIUtil.encodePath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,6 +50,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
|
||||
private final String _url;
|
||||
private final byte[] _fakeHash;
|
||||
private final String _name;
|
||||
private final File _dataDir;
|
||||
private volatile long _remaining = -1;
|
||||
private volatile long _total = -1;
|
||||
private volatile long _transferred;
|
||||
@ -65,8 +66,10 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
|
||||
/**
|
||||
* Caller should call _mgr.addDownloader(this), which
|
||||
* will start things off.
|
||||
*
|
||||
* @param dataDir null to default to snark data directory
|
||||
*/
|
||||
public FetchAndAdd(I2PAppContext ctx, SnarkManager mgr, String url) {
|
||||
public FetchAndAdd(I2PAppContext ctx, SnarkManager mgr, String url, File dataDir) {
|
||||
// magnet constructor
|
||||
super(mgr.util(), "Torrent download",
|
||||
null, null, null, null, null, false, null);
|
||||
@ -75,6 +78,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
|
||||
_mgr = mgr;
|
||||
_url = url;
|
||||
_name = _("Download torrent file from {0}", url);
|
||||
_dataDir = dataDir;
|
||||
byte[] fake = null;
|
||||
try {
|
||||
fake = SHA1.getInstance().digest(url.getBytes("ISO-8859-1"));
|
||||
@ -86,7 +90,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
|
||||
* Set off by startTorrent()
|
||||
*/
|
||||
public void run() {
|
||||
_mgr.addMessage(_("Fetching {0}", urlify(_url)));
|
||||
_mgr.addMessageNoEscape(_("Fetching {0}", urlify(_url)));
|
||||
File file = get();
|
||||
if (!_isRunning) // stopped?
|
||||
return;
|
||||
@ -96,7 +100,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
|
||||
_mgr.deleteMagnet(this);
|
||||
add(file);
|
||||
} else {
|
||||
_mgr.addMessage(_("Torrent was not retrieved from {0}", urlify(_url)) +
|
||||
_mgr.addMessageNoEscape(_("Torrent was not retrieved from {0}", urlify(_url)) +
|
||||
((_failCause != null) ? (": " + DataHelper.stripHTML(_failCause)) : ""));
|
||||
}
|
||||
if (file != null)
|
||||
@ -150,7 +154,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
|
||||
* This Snark may then be deleted.
|
||||
*/
|
||||
private void add(File file) {
|
||||
_mgr.addMessage(_("Torrent fetched from {0}", urlify(_url)));
|
||||
_mgr.addMessageNoEscape(_("Torrent fetched from {0}", urlify(_url)));
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(file);
|
||||
@ -176,14 +180,17 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
|
||||
_mgr.addMessage(_("Torrent already in the queue: {0}", name));
|
||||
} else {
|
||||
// This may take a LONG time to create the storage.
|
||||
_mgr.copyAndAddTorrent(file, canonical);
|
||||
_mgr.copyAndAddTorrent(file, canonical, _dataDir);
|
||||
snark = _mgr.getTorrentByBaseName(originalName);
|
||||
snark.startTorrent();
|
||||
if (snark != null)
|
||||
snark.startTorrent();
|
||||
else
|
||||
throw new IOException("Unknown error - check logs");
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_mgr.addMessage(_("Torrent at {0} was not valid", urlify(_url)) + ": " + ioe.getMessage());
|
||||
_mgr.addMessageNoEscape(_("Torrent at {0} was not valid", urlify(_url)) + ": " + DataHelper.stripHTML(ioe.getMessage()));
|
||||
} catch (OutOfMemoryError oom) {
|
||||
_mgr.addMessage(_("ERROR - Out of memory, cannot create torrent from {0}", urlify(_url)) + ": " + oom.getMessage());
|
||||
_mgr.addMessageNoEscape(_("ERROR - Out of memory, cannot create torrent from {0}", urlify(_url)) + ": " + DataHelper.stripHTML(oom.getMessage()));
|
||||
} finally {
|
||||
try { if (in != null) in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
@ -5,13 +5,18 @@ import java.io.File;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.FileUtil;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
//import org.eclipse.jetty.server.Server;
|
||||
|
||||
/**
|
||||
* @deprecated does not work
|
||||
*/
|
||||
public class RunStandalone {
|
||||
/****
|
||||
static {
|
||||
System.setProperty("org.mortbay.http.Version.paranoid", "true");
|
||||
System.setProperty("org.mortbay.xml.XmlParser.NotValidating", "true");
|
||||
}
|
||||
****/
|
||||
|
||||
private RunStandalone(String args[]) {}
|
||||
|
||||
@ -21,6 +26,8 @@ public class RunStandalone {
|
||||
}
|
||||
|
||||
public void start() {
|
||||
throw new RuntimeException("unsupported");
|
||||
/****
|
||||
File workDir = new File(I2PAppContext.getGlobalContext().getTempDir(), "jetty-work");
|
||||
boolean workDirRemoved = FileUtil.rmdir(workDir, false);
|
||||
if (!workDirRemoved)
|
||||
@ -29,8 +36,6 @@ public class RunStandalone {
|
||||
if (!workDirCreated)
|
||||
System.err.println("ERROR: Unable to create Jetty temporary work directory");
|
||||
|
||||
throw new RuntimeException("unsupported");
|
||||
/****
|
||||
try {
|
||||
_server = new Server("jetty-i2psnark.xml");
|
||||
// just blow up NPE if we don't have a context
|
||||
|
530
apps/i2psnark/java/src/org/klomp/snark/web/Sorters.java
Normal file
@ -0,0 +1,530 @@
|
||||
package org.klomp.snark.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.text.Collator;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.klomp.snark.MetaInfo;
|
||||
import org.klomp.snark.Snark;
|
||||
import org.klomp.snark.Storage;
|
||||
|
||||
/**
|
||||
* Comparators for various columns
|
||||
*
|
||||
* @since 0.9.16 from TorrentNameComparator, moved from I2PSnarkservlet
|
||||
*/
|
||||
class Sorters {
|
||||
|
||||
/**
|
||||
* Negative is reverse
|
||||
*
|
||||
*<ul>
|
||||
*<li>0, 1: Name
|
||||
*<li>2: Status
|
||||
*<li>3: Peers
|
||||
*<li>4: ETA
|
||||
*<li>5: Size
|
||||
*<li>6: Downloaded
|
||||
*<li>7: Uploaded
|
||||
*<li>8: Down rate
|
||||
*<li>9: Up rate
|
||||
*<li>10: Remaining (needed)
|
||||
*<li>11: Upload ratio
|
||||
*<li>12: File type
|
||||
*</ul>
|
||||
*
|
||||
* @param servlet for file type callback only
|
||||
*/
|
||||
public static Comparator<Snark> getComparator(int type, I2PSnarkServlet servlet) {
|
||||
boolean rev = type < 0;
|
||||
Comparator<Snark> rv;
|
||||
switch (type) {
|
||||
|
||||
case -1:
|
||||
case 0:
|
||||
case 1:
|
||||
default:
|
||||
rv = new TorrentNameComparator();
|
||||
if (rev)
|
||||
rv = Collections.reverseOrder(rv);
|
||||
break;
|
||||
|
||||
case -2:
|
||||
case 2:
|
||||
rv = new StatusComparator(rev);
|
||||
break;
|
||||
|
||||
case -3:
|
||||
case 3:
|
||||
rv = new PeersComparator(rev);
|
||||
break;
|
||||
|
||||
case -4:
|
||||
case 4:
|
||||
rv = new ETAComparator(rev);
|
||||
break;
|
||||
|
||||
case -5:
|
||||
case 5:
|
||||
rv = new SizeComparator(rev);
|
||||
break;
|
||||
|
||||
case -6:
|
||||
case 6:
|
||||
rv = new DownloadedComparator(rev);
|
||||
break;
|
||||
|
||||
case -7:
|
||||
case 7:
|
||||
rv = new UploadedComparator(rev);
|
||||
break;
|
||||
|
||||
case -8:
|
||||
case 8:
|
||||
rv = new DownRateComparator(rev);
|
||||
break;
|
||||
|
||||
case -9:
|
||||
case 9:
|
||||
rv = new UpRateComparator(rev);
|
||||
break;
|
||||
|
||||
case -10:
|
||||
case 10:
|
||||
rv = new RemainingComparator(rev);
|
||||
break;
|
||||
|
||||
case -11:
|
||||
case 11:
|
||||
rv = new RatioComparator(rev);
|
||||
break;
|
||||
|
||||
case -12:
|
||||
case 12:
|
||||
rv = new FileTypeComparator(rev, servlet);
|
||||
break;
|
||||
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort alphabetically in current locale, ignore case, ignore leading "the "
|
||||
* (I guess this is worth it, a lot of torrents start with "The "
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private static class TorrentNameComparator implements Comparator<Snark>, Serializable {
|
||||
|
||||
public int compare(Snark l, Snark r) {
|
||||
return comp(l, r);
|
||||
}
|
||||
|
||||
public static int comp(Snark l, Snark r) {
|
||||
// put downloads and magnets first
|
||||
if (l.getStorage() == null && r.getStorage() != null)
|
||||
return -1;
|
||||
if (l.getStorage() != null && r.getStorage() == null)
|
||||
return 1;
|
||||
String ls = l.getBaseName();
|
||||
String llc = ls.toLowerCase(Locale.US);
|
||||
if (llc.startsWith("the ") || llc.startsWith("the.") || llc.startsWith("the_"))
|
||||
ls = ls.substring(4);
|
||||
String rs = r.getBaseName();
|
||||
String rlc = rs.toLowerCase(Locale.US);
|
||||
if (rlc.startsWith("the ") || rlc.startsWith("the.") || rlc.startsWith("the_"))
|
||||
rs = rs.substring(4);
|
||||
return Collator.getInstance().compare(ls, rs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward or reverse sort, but the fallback is always forward
|
||||
*/
|
||||
private static abstract class Sort implements Comparator<Snark>, Serializable {
|
||||
|
||||
private final boolean _rev;
|
||||
|
||||
public Sort(boolean rev) {
|
||||
_rev = rev;
|
||||
}
|
||||
|
||||
public int compare(Snark l, Snark r) {
|
||||
int rv = compareIt(l, r);
|
||||
if (rv != 0)
|
||||
return _rev ? 0 - rv : rv;
|
||||
return TorrentNameComparator.comp(l, r);
|
||||
}
|
||||
|
||||
protected abstract int compareIt(Snark l, Snark r);
|
||||
|
||||
protected static int compLong(long l, long r) {
|
||||
if (l < r)
|
||||
return -1;
|
||||
if (l > r)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class StatusComparator extends Sort {
|
||||
|
||||
private StatusComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
int rv = getStatus(l) - getStatus(r);
|
||||
if (rv != 0)
|
||||
return rv;
|
||||
// use reverse remaining as first tie break
|
||||
return compLong(r.getNeededLength(), l.getNeededLength());
|
||||
}
|
||||
|
||||
private static int getStatus(Snark snark) {
|
||||
long remaining = snark.getRemainingLength();
|
||||
if (snark.isStopped()) {
|
||||
if (remaining < 0)
|
||||
return 0;
|
||||
if (remaining > 0)
|
||||
return 5;
|
||||
return 10;
|
||||
}
|
||||
if (snark.isStarting())
|
||||
return 15;
|
||||
if (snark.isAllocating())
|
||||
return 20;
|
||||
if (remaining < 0)
|
||||
return 15; // magnet
|
||||
if (remaining == 0)
|
||||
return 100;
|
||||
if (snark.isChecking())
|
||||
return 95;
|
||||
if (snark.getNeededLength() <= 0)
|
||||
return 90;
|
||||
if (snark.getPeerCount() <= 0)
|
||||
return 40;
|
||||
if (snark.getDownloadRate() <= 0)
|
||||
return 50;
|
||||
return 60;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PeersComparator extends Sort {
|
||||
|
||||
public PeersComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return l.getPeerCount() - r.getPeerCount();
|
||||
}
|
||||
}
|
||||
|
||||
private static class RemainingComparator extends Sort {
|
||||
|
||||
public RemainingComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getNeededLength(), r.getNeededLength());
|
||||
}
|
||||
}
|
||||
|
||||
private static class ETAComparator extends Sort {
|
||||
|
||||
public ETAComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(eta(l), eta(r));
|
||||
}
|
||||
|
||||
private static long eta(Snark snark) {
|
||||
long needed = snark.getNeededLength();
|
||||
if (needed <= 0)
|
||||
return 0;
|
||||
long total = snark.getTotalLength();
|
||||
if (needed > total)
|
||||
needed = total;
|
||||
long downBps = snark.getDownloadRate();
|
||||
if (downBps > 0)
|
||||
return needed / downBps;
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SizeComparator extends Sort {
|
||||
|
||||
public SizeComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getTotalLength(), r.getTotalLength());
|
||||
}
|
||||
}
|
||||
|
||||
private static class DownloadedComparator extends Sort {
|
||||
|
||||
public DownloadedComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
long ld = l.getTotalLength() - l.getRemainingLength();
|
||||
long rd = r.getTotalLength() - r.getRemainingLength();
|
||||
return compLong(ld, rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static class UploadedComparator extends Sort {
|
||||
|
||||
public UploadedComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getUploaded(), r.getUploaded());
|
||||
}
|
||||
}
|
||||
|
||||
private static class DownRateComparator extends Sort {
|
||||
|
||||
public DownRateComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getDownloadRate(), r.getDownloadRate());
|
||||
}
|
||||
}
|
||||
|
||||
private static class UpRateComparator extends Sort {
|
||||
|
||||
public UpRateComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getUploadRate(), r.getUploadRate());
|
||||
}
|
||||
}
|
||||
|
||||
private static class RatioComparator extends Sort {
|
||||
|
||||
private static final long M = 128 * 1024 * 1024;
|
||||
|
||||
public RatioComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
long lt = l.getTotalLength();
|
||||
long ld = lt > 0 ? ((M * l.getUploaded()) / lt) : 0;
|
||||
long rt = r.getTotalLength();
|
||||
long rd = rt > 0 ? ((M * r.getUploaded()) / rt) : 0;
|
||||
return compLong(ld, rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FileTypeComparator extends Sort {
|
||||
|
||||
private final I2PSnarkServlet servlet;
|
||||
|
||||
public FileTypeComparator(boolean rev, I2PSnarkServlet servlet) {
|
||||
super(rev);
|
||||
this.servlet = servlet;
|
||||
}
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
String ls = toName(l);
|
||||
String rs = toName(r);
|
||||
return ls.compareTo(rs);
|
||||
}
|
||||
|
||||
private String toName(Snark snark) {
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
if (meta == null)
|
||||
return "0";
|
||||
if (meta.getFiles() != null)
|
||||
return "1";
|
||||
// arbitrary sort based on icon name
|
||||
return servlet.toIcon(meta.getName());
|
||||
}
|
||||
}
|
||||
|
||||
////////////// Comparators for details page below
|
||||
|
||||
/**
|
||||
* Class to precompute and efficiently sort data
|
||||
* on a torrent file entry.
|
||||
*/
|
||||
public static class FileAndIndex {
|
||||
public final File file;
|
||||
public final boolean isDirectory;
|
||||
public final long length;
|
||||
public final long remaining;
|
||||
public final int priority;
|
||||
public final int index;
|
||||
|
||||
/**
|
||||
* @param storage may be null
|
||||
*/
|
||||
public FileAndIndex(File file, Storage storage) {
|
||||
this.file = file;
|
||||
index = storage != null ? storage.indexOf(file) : -1;
|
||||
if (index >= 0) {
|
||||
isDirectory = false;
|
||||
remaining = storage.remaining(index);
|
||||
priority = storage.getPriority(index);
|
||||
} else {
|
||||
isDirectory = file.isDirectory();
|
||||
remaining = -1;
|
||||
priority = -999;
|
||||
}
|
||||
length = isDirectory ? 0 : file.length();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Negative is reverse
|
||||
*
|
||||
*<ul>
|
||||
*<li>0, 1: Name
|
||||
*<li>5: Size
|
||||
*<li>10: Remaining (needed)
|
||||
*<li>12: File type
|
||||
*<li>13: Priority
|
||||
*</ul>
|
||||
*
|
||||
* @param servlet for file type callback only
|
||||
*/
|
||||
public static Comparator<FileAndIndex> getFileComparator(int type, I2PSnarkServlet servlet) {
|
||||
boolean rev = type < 0;
|
||||
Comparator<FileAndIndex> rv;
|
||||
|
||||
switch (type) {
|
||||
|
||||
case -1:
|
||||
case 0:
|
||||
case 1:
|
||||
default:
|
||||
rv = new FileNameComparator();
|
||||
if (rev)
|
||||
rv = Collections.reverseOrder(rv);
|
||||
break;
|
||||
|
||||
case -5:
|
||||
case 5:
|
||||
rv = new FAISizeComparator(rev);
|
||||
break;
|
||||
|
||||
case -10:
|
||||
case 10:
|
||||
rv = new FAIRemainingComparator(rev);
|
||||
break;
|
||||
|
||||
case -12:
|
||||
case 12:
|
||||
rv = new FAITypeComparator(rev, servlet);
|
||||
break;
|
||||
|
||||
case -13:
|
||||
case 13:
|
||||
rv = new FAIPriorityComparator(rev);
|
||||
break;
|
||||
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort alphabetically in current locale, ignore case,
|
||||
* directories first
|
||||
* @since 0.9.6 moved from I2PSnarkServlet in 0.9.16
|
||||
*/
|
||||
private static class FileNameComparator implements Comparator<FileAndIndex>, Serializable {
|
||||
|
||||
public int compare(FileAndIndex l, FileAndIndex r) {
|
||||
return comp(l, r);
|
||||
}
|
||||
|
||||
public static int comp(FileAndIndex l, FileAndIndex r) {
|
||||
boolean ld = l.isDirectory;
|
||||
boolean rd = r.isDirectory;
|
||||
if (ld && !rd)
|
||||
return -1;
|
||||
if (rd && !ld)
|
||||
return 1;
|
||||
return Collator.getInstance().compare(l.file.getName(), r.file.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward or reverse sort, but the fallback is always forward
|
||||
*/
|
||||
private static abstract class FAISort implements Comparator<FileAndIndex>, Serializable {
|
||||
|
||||
private final boolean _rev;
|
||||
|
||||
public FAISort(boolean rev) {
|
||||
_rev = rev;
|
||||
}
|
||||
|
||||
public int compare(FileAndIndex l, FileAndIndex r) {
|
||||
int rv = compareIt(l, r);
|
||||
if (rv != 0)
|
||||
return _rev ? 0 - rv : rv;
|
||||
return FileNameComparator.comp(l, r);
|
||||
}
|
||||
|
||||
protected abstract int compareIt(FileAndIndex l, FileAndIndex r);
|
||||
|
||||
protected static int compLong(long l, long r) {
|
||||
if (l < r)
|
||||
return -1;
|
||||
if (l > r)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FAIRemainingComparator extends FAISort {
|
||||
|
||||
public FAIRemainingComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(FileAndIndex l, FileAndIndex r) {
|
||||
return compLong(l.remaining, r.remaining);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FAISizeComparator extends FAISort {
|
||||
|
||||
public FAISizeComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(FileAndIndex l, FileAndIndex r) {
|
||||
return compLong(l.length, r.length);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FAITypeComparator extends FAISort {
|
||||
|
||||
private final I2PSnarkServlet servlet;
|
||||
|
||||
public FAITypeComparator(boolean rev, I2PSnarkServlet servlet) {
|
||||
super(rev);
|
||||
this.servlet = servlet;
|
||||
}
|
||||
|
||||
public int compareIt(FileAndIndex l, FileAndIndex r) {
|
||||
String ls = toName(l);
|
||||
String rs = toName(r);
|
||||
return ls.compareTo(rs);
|
||||
}
|
||||
|
||||
private String toName(FileAndIndex fai) {
|
||||
if (fai.isDirectory)
|
||||
return "0";
|
||||
// arbitrary sort based on icon name
|
||||
return servlet.toIcon(fai.file.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static class FAIPriorityComparator extends FAISort {
|
||||
|
||||
public FAIPriorityComparator(boolean rev) { super(rev); }
|
||||
|
||||
/** highest first */
|
||||
public int compareIt(FileAndIndex l, FileAndIndex r) {
|
||||
return r.priority - l.priority;
|
||||
}
|
||||
}
|
||||
}
|
250
apps/i2psnark/java/src/org/klomp/snark/web/URIUtil.java
Normal file
@ -0,0 +1,250 @@
|
||||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.klomp.snark.web;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
|
||||
/** URI Holder.
|
||||
* This class assists with the decoding and encoding or HTTP URI's.
|
||||
* It differs from the java.net.URL class as it does not provide
|
||||
* communications ability, but it does assist with query string
|
||||
* formatting.
|
||||
* <P>UTF-8 encoding is used by default for % encoded characters. This
|
||||
* may be overridden with the org.eclipse.jetty.util.URI.charset system property.
|
||||
* see UrlEncoded
|
||||
*
|
||||
* I2P modded from Jetty 8.1.15
|
||||
* @since 0.9.15
|
||||
*/
|
||||
class URIUtil
|
||||
{
|
||||
|
||||
/** Encode a URI path.
|
||||
* This is the same encoding offered by URLEncoder, except that
|
||||
* the '/' character is not encoded.
|
||||
* @param path The path the encode
|
||||
* @return The encoded path
|
||||
*/
|
||||
public static String encodePath(String path)
|
||||
{
|
||||
if (path==null || path.length()==0)
|
||||
return path;
|
||||
|
||||
StringBuilder buf = encodePath(null,path);
|
||||
return buf==null?path:buf.toString();
|
||||
}
|
||||
|
||||
/** Encode a URI path.
|
||||
*
|
||||
* Somewhat oddly, this encodes all chars >= 0x80 if buf is null, (strict RFC 2396)
|
||||
* but only the control, space, and special chars if buf is non-null.
|
||||
*
|
||||
* @param path The path the encode
|
||||
* @param buf StringBuilder to encode path into (or null)
|
||||
* @return The StringBuilder or null if no substitutions required.
|
||||
*/
|
||||
public static StringBuilder encodePath(StringBuilder buf, String path)
|
||||
{
|
||||
byte[] bytes=null;
|
||||
if (buf==null)
|
||||
{
|
||||
loop:
|
||||
for (int i=0;i<path.length();i++)
|
||||
{
|
||||
char c=path.charAt(i);
|
||||
switch(c)
|
||||
{
|
||||
case '%':
|
||||
case '?':
|
||||
case ';':
|
||||
case '#':
|
||||
case '\'':
|
||||
case '"':
|
||||
case '<':
|
||||
case '>':
|
||||
case ' ':
|
||||
case ':':
|
||||
case '[':
|
||||
case ']':
|
||||
buf=new StringBuilder(path.length()*2);
|
||||
break loop;
|
||||
default:
|
||||
if (c >= 0x7f || c <= 0x1f)
|
||||
{
|
||||
bytes = DataHelper.getUTF8(path);
|
||||
buf=new StringBuilder(path.length()*2);
|
||||
break loop;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (buf==null)
|
||||
return null;
|
||||
}
|
||||
|
||||
//synchronized(buf)
|
||||
//{
|
||||
if (bytes!=null)
|
||||
{
|
||||
for (int i=0;i<bytes.length;i++)
|
||||
{
|
||||
byte c=bytes[i];
|
||||
switch(c)
|
||||
{
|
||||
case '%':
|
||||
buf.append("%25");
|
||||
continue;
|
||||
case '?':
|
||||
buf.append("%3F");
|
||||
continue;
|
||||
case ';':
|
||||
buf.append("%3B");
|
||||
continue;
|
||||
case '#':
|
||||
buf.append("%23");
|
||||
continue;
|
||||
case '"':
|
||||
buf.append("%22");
|
||||
continue;
|
||||
case '\'':
|
||||
buf.append("%27");
|
||||
continue;
|
||||
case '<':
|
||||
buf.append("%3C");
|
||||
continue;
|
||||
case '>':
|
||||
buf.append("%3E");
|
||||
continue;
|
||||
case ' ':
|
||||
buf.append("%20");
|
||||
continue;
|
||||
case 0x7f:
|
||||
buf.append("%7F");
|
||||
continue;
|
||||
case ':':
|
||||
buf.append("%3A");
|
||||
continue;
|
||||
case '[':
|
||||
buf.append("%5B");
|
||||
continue;
|
||||
case ']':
|
||||
buf.append("%5D");
|
||||
continue;
|
||||
default:
|
||||
if (c <= 0x1f) // includes negative
|
||||
toHex(c,buf);
|
||||
else
|
||||
buf.append((char)c);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i=0;i<path.length();i++)
|
||||
{
|
||||
char c=path.charAt(i);
|
||||
switch(c)
|
||||
{
|
||||
case '%':
|
||||
buf.append("%25");
|
||||
continue;
|
||||
case '?':
|
||||
buf.append("%3F");
|
||||
continue;
|
||||
case ';':
|
||||
buf.append("%3B");
|
||||
continue;
|
||||
case '#':
|
||||
buf.append("%23");
|
||||
continue;
|
||||
case '"':
|
||||
buf.append("%22");
|
||||
continue;
|
||||
case '\'':
|
||||
buf.append("%27");
|
||||
continue;
|
||||
case '<':
|
||||
buf.append("%3C");
|
||||
continue;
|
||||
case '>':
|
||||
buf.append("%3E");
|
||||
continue;
|
||||
case ' ':
|
||||
buf.append("%20");
|
||||
continue;
|
||||
case ':':
|
||||
buf.append("%3A");
|
||||
continue;
|
||||
case '[':
|
||||
buf.append("%5B");
|
||||
continue;
|
||||
case ']':
|
||||
buf.append("%5D");
|
||||
continue;
|
||||
default:
|
||||
if (c <= 0x1f || (c >= 0x7f && c <= 0x9f) || Character.isSpaceChar(c))
|
||||
toHex(c,buf);
|
||||
else
|
||||
buf.append(c);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modded from Jetty TypeUtil
|
||||
*/
|
||||
private static void toHex(byte b, StringBuilder buf)
|
||||
{
|
||||
buf.append('%');
|
||||
int d=0xf&((0xF0&b)>>4);
|
||||
buf.append((char)((d>9?('A'-10):'0')+d));
|
||||
d=0xf&b;
|
||||
buf.append((char)((d>9?('A'-10):'0')+d));
|
||||
}
|
||||
|
||||
/**
|
||||
* UTF-8
|
||||
*/
|
||||
private static void toHex(char c, StringBuilder buf)
|
||||
{
|
||||
if (c > 0x7f) {
|
||||
byte[] b = DataHelper.getUTF8(Character.toString(c));
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
toHex(b[i], buf);
|
||||
}
|
||||
} else {
|
||||
toHex((byte) c, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
1338
apps/i2psnark/locale/messages_pt_bR.po
Normal file
1344
apps/i2psnark/locale/messages_sk.po
Normal file
@ -24,6 +24,7 @@ su2 = application/zip
|
||||
su3 = application/zip
|
||||
sud = application/zip
|
||||
tbz = application/x-bzip2
|
||||
torrent = application/x-bittorrent
|
||||
txt = text/plain
|
||||
war = application/java-archive
|
||||
webm = video/webm
|
||||
|
Before Width: | Height: | Size: 464 B After Width: | Height: | Size: 464 B |
Before Width: | Height: | Size: 733 B After Width: | Height: | Size: 733 B |
Before Width: | Height: | Size: 587 B After Width: | Height: | Size: 587 B |
Before Width: | Height: | Size: 673 B After Width: | Height: | Size: 673 B |
Before Width: | Height: | Size: 882 B After Width: | Height: | Size: 882 B |
Before Width: | Height: | Size: 889 B After Width: | Height: | Size: 889 B |
Before Width: | Height: | Size: 766 B After Width: | Height: | Size: 766 B |
Before Width: | Height: | Size: 653 B After Width: | Height: | Size: 653 B |
Before Width: | Height: | Size: 537 B After Width: | Height: | Size: 537 B |
Before Width: | Height: | Size: 578 B After Width: | Height: | Size: 578 B |
BIN
apps/i2psnark/resources/icons/itoopie_xxsm.png
Normal file
After Width: | Height: | Size: 661 B |
Before Width: | Height: | Size: 591 B After Width: | Height: | Size: 591 B |
Before Width: | Height: | Size: 385 B After Width: | Height: | Size: 385 B |