Compare commits
3719 Commits
i2p-0.7
...
android-0.
Author | SHA1 | Date | |
---|---|---|---|
472fa6d49e | |||
60ece9d4d2 | |||
afd656c6b4 | |||
78e250a207 | |||
134db2ecd3 | |||
53caad9f2a | |||
742df967e2 | |||
5d0c5c30eb | |||
cb9924a0bf | |||
3ceed9a6b3 | |||
c3d95a608d | |||
1b004a628e | |||
b080bd387a | |||
7a429674a7 | |||
45a1511cab | |||
6c7be97ed5 | |||
3244509cab | |||
43a20d18c5 | |||
e2332543ec | |||
4d68da45b4 | |||
084ed85467 | |||
1d060fd419 | |||
a95ca82a3e | |||
c13101d535 | |||
7dc7697c14 | |||
91fb0a2248 | |||
2663cc7d57 | |||
f9fce317d3 | |||
f87a3eb03c | |||
36a0f2c678 | |||
f8fe3f082a | |||
6e130185de | |||
006fc1dc51 | |||
87836ddab6 | |||
ff7154b525 | |||
7a829236b9 | |||
d3edd31155 | |||
455726f05a | |||
5dc9d729f4 | |||
6d42b93de4 | |||
39b54c41ab | |||
f8920298ee | |||
cc3b37c4c0 | |||
0cdd5a5d88 | |||
b61bc8f5ef | |||
2a6a2ef7a7 | |||
245f69f2d3 | |||
4c43b6f5d5 | |||
28230c914d | |||
f7e1acdb68 | |||
995cd7f327 | |||
1e024c22da | |||
4e853a753f | |||
01547e9a20 | |||
09b2af14fd | |||
f5d06470e4 | |||
abe20c95d0 | |||
c9abfa80f0 | |||
f34ef46dbf | |||
0c30296888 | |||
3fd373ad8c | |||
41091dce25 | |||
0fbaeb0ea4 | |||
19791a5965 | |||
9d4c0aa839 | |||
0b12a952cb | |||
a95aa6e89b | |||
f13c772509 | |||
0f28c4d807 | |||
a649d434c7 | |||
23c5710d42 | |||
52a60b37e2 | |||
adb5c56f26 | |||
177ac2e3a1 | |||
1219725e6a | |||
db1932a908 | |||
9ff31a1872 | |||
0326783442 | |||
e1b07f45fa | |||
4d4eb2c48b | |||
6c48f042f9 | |||
9869d08295 | |||
0a5e6007da | |||
dbc6c22156 | |||
e62ce19a49 | |||
6c803935c7 | |||
a88edb8ea8 | |||
57f3c95d72 | |||
3021c0bc91 | |||
293e51c175 | |||
448d74e172 | |||
92d47564dd | |||
d51126cab4 | |||
752ac44930 | |||
8833020455 | |||
e489c8d937 | |||
9c4e661e53 | |||
a4215cf76e | |||
15afd309b3 | |||
a3aa64654f | |||
87cd67f498 | |||
b5b2692cee | |||
cadb6e8c1e | |||
1aa08d8341 | |||
628bae2497 | |||
4b5420f51a | |||
e86e69f437 | |||
29b9ca6359 | |||
b337f0f651 | |||
8b41459c5d | |||
d4769c7c5a | |||
a04a224b4a | |||
f1ea9dde51 | |||
95fd006143 | |||
eb04fb3335 | |||
f045191c82 | |||
097883c1ee | |||
d71ead3f53 | |||
77180cea17 | |||
0aa325ada8 | |||
ae8cadde95 | |||
14464772f9 | |||
5305b39ee4 | |||
8e2ec53e94 | |||
e80dcee65a | |||
a2ae1ccab2 | |||
05cdb11c55 | |||
3a7f4331f0 | |||
2ee5d5f02f | |||
cf5454d43e | |||
e94c332ed6 | |||
735da1408b | |||
0e6cecce7b | |||
0d97d92c64 | |||
3f62eded02 | |||
a9a3fbd5da | |||
6597847682 | |||
8fb166b61b | |||
7cc1e37cc2 | |||
84419904ad | |||
ac30407eb2 | |||
95189c8f53 | |||
7e84451ac3 | |||
15b9615a1c | |||
4dc9cfb457 | |||
1250617d6c | |||
ca5f35aea9 | |||
3dfa982b39 | |||
b2a5a94ce0 | |||
d834c8063c | |||
d28f4bd24b | |||
48f4c7286d | |||
d9f80e9de6 | |||
d1e42233a8 | |||
6110957921 | |||
d308d7da97 | |||
4d34078678 | |||
8d42ebc2f0 | |||
87cad7eaee | |||
b1f1c28c5c | |||
b612e925df | |||
af629d2442 | |||
e232a641a3 | |||
35495e4d5a | |||
5974160805 | |||
4e16ef35a2 | |||
885d549e84 | |||
173343e049 | |||
c3bcb8d020 | |||
d7de8ae630 | |||
d5f529819f | |||
d2093444a6 | |||
3f15c4324b | |||
219d7fd8c3 | |||
c5c6a9fa17 | |||
78779fe92f | |||
b37a64905b | |||
675ac79443 | |||
14ea9c2928 | |||
ec6084cd37 | |||
b65cbb0f78 | |||
fe15db51d8 | |||
761ad38bcc | |||
2a5ed938bb | |||
c5c4e3c7ce | |||
94af6550fa | |||
c767644c8f | |||
a3ee593d0b | |||
733d6db56e | |||
6d938a12f6 | |||
8a56531c90 | |||
43332bb6d0 | |||
5990dd5879 | |||
030a95cdd1 | |||
18d3536ffe | |||
3b80f53b8e | |||
5912c60692 | |||
f8dc8a298e | |||
a3dd538afb | |||
a90b8aa03a | |||
313ee79bae | |||
97f97448d5 | |||
3677cadcca | |||
455b5529b4 | |||
60204fef24 | |||
45064ec37e | |||
4ecacc7607 | |||
43ba27126c | |||
527c9ba5dd | |||
d4bf2523a6 | |||
acb4bac5e6 | |||
cbef38ac11 | |||
f9ffab62f4 | |||
656292e1b1 | |||
ed8a065da9 | |||
9834a36787 | |||
33b9a5c2ce | |||
1eb58a84df | |||
c9c35a3e5a | |||
e0dd52a4dc | |||
393d813d05 | |||
0000e4f28d | |||
e3a3a99317 | |||
6a9f73bd61 | |||
2c3be29016 | |||
8eb10872b8 | |||
d5fd682985 | |||
8f861f7ba2 | |||
efe53c831d | |||
0dba0a1d6f | |||
89c01696cc | |||
164f060a40 | |||
e347ee5880 | |||
a4ea70b33b | |||
4433a3db06 | |||
e019d3b8d7 | |||
1f497e171a | |||
cd8c954513 | |||
d08d5ffd64 | |||
8744eab46d | |||
5d7e936aed | |||
e786da2605 | |||
b6fab829cc | |||
150cb30339 | |||
724d8de9ae | |||
ddcd8cbb10 | |||
f5f4f14b7e | |||
6616ccd3b4 | |||
075f89f43c | |||
9a836ed072 | |||
552608744d | |||
2accbdcf05 | |||
ab17bd3150 | |||
d201a29d03 | |||
c4bbcc4617 | |||
82a0ac16f1 | |||
27b48034c5 | |||
641e71c141 | |||
4fd8da8041 | |||
68dccdfe2f | |||
592361b1a4 | |||
6e4df8830a | |||
7ab95d0144 | |||
4ea5622842 | |||
606300a042 | |||
96cf598691 | |||
6b923c7251 | |||
18e57d19db | |||
45f3020924 | |||
d8a3cb0d58 | |||
b5dc9b6f5a | |||
38a6e6b212 | |||
627519e69c | |||
2f2e0e539a | |||
e42b78e177 | |||
4b19801cdb | |||
3d76354cbb | |||
e2c98ac134 | |||
d4fe76afee | |||
3352c45517 | |||
0e719b8eb0 | |||
ff520b74dc | |||
48bf618ae5 | |||
d6c1202e4b | |||
20452c9387 | |||
64a753116b | |||
5ce439ffc4 | |||
00f2721640 | |||
454a310bbe | |||
18952f5109 | |||
7377086aae | |||
9f7cc9d887 | |||
ffa9ea9cd2 | |||
f9654661bb | |||
5ce06d02b4 | |||
c356792d02 | |||
90642a8ab5 | |||
6199e7b74c | |||
1e9ac05b63 | |||
ed338d9cfa | |||
a84e6f7854 | |||
fe21748b2d | |||
8b184f8c03 | |||
5ed22d5c16 | |||
525e0b4518 | |||
e8d94982e4 | |||
38db0b0ff3 | |||
e4a8b9621d | |||
66ea1c060a | |||
50dabfec8c | |||
a8722e033c | |||
1cec98d180 | |||
97bb2d81b7 | |||
9c4d107b5d | |||
eaccd029c8 | |||
b2c90b0633 | |||
1152e117b3 | |||
9f3cdeb29e | |||
699bfb3987 | |||
0695753084 | |||
ef0e9ca36c | |||
3e9e56ed28 | |||
b16cfb9df6 | |||
18c3430656 | |||
94298c3678 | |||
a3d22e829b | |||
63ecc10c5e | |||
937ef0ea4e | |||
428db02274 | |||
960f12d66c | |||
aea618061f | |||
3995d2b123 | |||
891f9b2e87 | |||
47efafa722 | |||
b19006690c | |||
6acef90087 | |||
5b7896d16e | |||
8a74eceac3 | |||
a5f576d912 | |||
e0c0f2a350 | |||
1ca63be423 | |||
eaee55e3a9 | |||
1fbaf24d59 | |||
1ada55f012 | |||
bcbd5bbda6 | |||
2a982a4c47 | |||
2061640cb6 | |||
a4ff83c0d4 | |||
e766202a2e | |||
0da9c606f2 | |||
398984009b | |||
3e00866bdf | |||
ac3c2bd872 | |||
281c2f5b51 | |||
2e9e2e20ef | |||
ed131f47de | |||
853e5ac607 | |||
1cffcdb670 | |||
a9b1603f9f | |||
aee08287eb | |||
131e8f6d68 | |||
c8544d016d | |||
8198e61acc | |||
b41ed69b0f | |||
5b2cf8b676 | |||
5d9116965b | |||
07e61d499a | |||
9f010d80fb | |||
660ba64ce1 | |||
29c731d660 | |||
df4714e628 | |||
a7d6048748 | |||
1a40178e8c | |||
49ac0278f0 | |||
4c74315411 | |||
f992a07df9 | |||
e62d9dfa48 | |||
6dd7431ccd | |||
7020bfd521 | |||
0fcf13ef73 | |||
9e37a5a4af | |||
9e4e9c197b | |||
172541a368 | |||
9f475c03cb | |||
fbfffa9987 | |||
8ea721696f | |||
3e5713e479 | |||
b7072d48b6 | |||
7feefc049c | |||
8ae398d786 | |||
a818e84dcf | |||
33780ef359 | |||
516216730b | |||
7a69024fb5 | |||
9fcb20a7bd | |||
991acd3917 | |||
f4905d2742 | |||
b7b7283ff9 | |||
22d50dd150 | |||
bc231b51b5 | |||
4d17643064 | |||
0b510cdc21 | |||
a188275608 | |||
0617ee99cd | |||
4fbde3a0bc | |||
9911d22ac3 | |||
b14c17c884 | |||
3fe232a535 | |||
52a0ab5482 | |||
b3dec96e1e | |||
b639482c4a | |||
d68e59fb04 | |||
d97f991230 | |||
cc780c23f8 | |||
a7fceb644f | |||
312534b635 | |||
8f9f102baf | |||
26cc6a26b8 | |||
bcf9d59c82 | |||
cf7e922dcd | |||
ba67bb9412 | |||
1271e03192 | |||
cdd986c56b | |||
ea86b6880f | |||
d47de894ab | |||
109bd978de | |||
25d2ce65e2 | |||
07b3ebefc1 | |||
e255de6f72 | |||
76bbc604ae | |||
103bde8a32 | |||
57140d2655 | |||
f61108bbae | |||
05a3738fdb | |||
ceb631719b | |||
078056f163 | |||
1adb3d19c7 | |||
b5cfe00ba5 | |||
46b2fd2749 | |||
f1d1a80f4e | |||
f3a30276f3 | |||
a9579cdd87 | |||
2d4c3453b7 | |||
1f58125eed | |||
fb36da7669 | |||
fa1820786a | |||
655d0ad77e | |||
0098816866 | |||
6e08fd614a | |||
0680725582 | |||
0cb10f9bb1 | |||
7751661f3d | |||
ca5484a984 | |||
f7beb5d68e | |||
d6999a3327 | |||
541ff01fd1 | |||
c85931cbc5 | |||
7e0d0e2b01 | |||
f289be908c | |||
42e9fd9431 | |||
5dc9214296 | |||
086bad4b47 | |||
22d66131f3 | |||
311bb7a4b6 | |||
8b10720b23 | |||
6f296a6f76 | |||
61590d39e0 | |||
c7a574fe01 | |||
1b2519cfb8 | |||
9c02aab4fb | |||
c89f752ccc | |||
40fd4ccd15 | |||
4c0a4a2296 | |||
ddddc686fe | |||
642376b0b6 | |||
3ba9f872ed | |||
bfb4560dc2 | |||
fa8f2290af | |||
c0422134f9 | |||
b11516f758 | |||
14986fbfa1 | |||
34d951aa6b | |||
f96342d33a | |||
5eb6bf1b1d | |||
295e1daa22 | |||
cf0d2197b8 | |||
79358f4271 | |||
12f3634f96 | |||
033dee0216 | |||
1324eaf056 | |||
2e5e3b9d40 | |||
ef26accde0 | |||
8461beba1c | |||
b93e3dfb55 | |||
5ef1dd87a5 | |||
07860d018c | |||
ce5ce12e3b | |||
02c4bbfc58 | |||
bf613448d7 | |||
5095e8a1d6 | |||
0352ca3ef6 | |||
12c5b9c21c | |||
be4be9716d | |||
ac5785c796 | |||
e6b24277b6 | |||
dca9f32d3f | |||
21a3657da5 | |||
3f141e72b8 | |||
e3a81f6091 | |||
3ecc38cc9c | |||
b23f278c3f | |||
e47afffd09 | |||
0f91899aa9 | |||
8e672ea548 | |||
cfc17d59b4 | |||
46e8add16a | |||
7f616f20ae | |||
133017d2db | |||
ee6a058602 | |||
6bbdb4fc03 | |||
5def26ab93 | |||
1657f151f6 | |||
a5b55728ba | |||
8b737b4adb | |||
f4e92572eb | |||
b048b016ad | |||
41fc9cf4ca | |||
7edbd3ad0a | |||
de815e271c | |||
ad24f1438f | |||
b6a0426477 | |||
f71dfe50f9 | |||
6b6aaab881 | |||
8bbcb0b337 | |||
a1032abc92 | |||
380c1e5053 | |||
21dbee03f1 | |||
f4ca3976d5 | |||
b02fe536bc | |||
f9b2c0bc63 | |||
6e607e6bfe | |||
0e854623c1 | |||
21c65475f9 | |||
97c36cc54e | |||
b30e1f5ba9 | |||
b2d4a799d5 | |||
9030b64dc8 | |||
cb707785cc | |||
f938cc7b0c | |||
28bd1802b4 | |||
35a0be6d1c | |||
7b29481124 | |||
cacd0f898d | |||
f8fffb5d20 | |||
9cd0e53d0f | |||
b0daab601a | |||
291b70036a | |||
bfad5889ad | |||
bb8736f2cd | |||
a3fb49adcb | |||
21f14ac22e | |||
30373bf6df | |||
e990d98dab | |||
5beb739913 | |||
6e84ee8361 | |||
dedb139fb0 | |||
d7f5f2c3d8 | |||
bb7d0287d9 | |||
f8e647796b | |||
fde783b156 | |||
f03064ac71 | |||
7bf8b46049 | |||
6789df50f3 | |||
f0eb74eeb0 | |||
da41096115 | |||
61f6ecd725 | |||
102d9183d8 | |||
c269546c08 | |||
79c68c5636 | |||
4967f8de3c | |||
99f3969b54 | |||
d131c4bfec | |||
e93a3ed908 | |||
bfb74f7181 | |||
9b77f5e81f | |||
36cbae2d7f | |||
632f34e114 | |||
bb31be231d | |||
258c260601 | |||
0c1bbb349d | |||
2dca305c3f | |||
8af15e01aa | |||
93be3cbdd4 | |||
ce103d58a8 | |||
b406c6c51e | |||
50e22b614f | |||
7c3af2cdd6 | |||
fbe3a488e4 | |||
fde7b21ca4 | |||
72692840a7 | |||
edb976bce5 | |||
3d30e142c1 | |||
aaf2fe9942 | |||
d4fce3b98e | |||
bd28397f83 | |||
0c7b9a137d | |||
f9b2c77161 | |||
6885f96741 | |||
ef50c122fc | |||
6c7e913d5e | |||
b208295cdb | |||
5027b6ce33 | |||
d59e3f9200 | |||
fd91927c11 | |||
a3d466eeaa | |||
bec1b4a8e1 | |||
84990d9d43 | |||
e1b40d4925 | |||
8a71ab4447 | |||
ecadbba98e | |||
d468a3a8e6 | |||
e227464748 | |||
6c61b39f60 | |||
ce634d4f44 | |||
3d1312d169 | |||
e75d0b7654 | |||
4d59570df2 | |||
8fa49968ed | |||
eb7e1d24cd | |||
c1d7182b08 | |||
81411601d5 | |||
b2433d0e50 | |||
2c6984ab99 | |||
c91d2bf278 | |||
4c1f3a3143 | |||
a6686cbed4 | |||
581cd72032 | |||
271f330cdd | |||
33701195b1 | |||
289c7d6c34 | |||
d15f682b17 | |||
854998832d | |||
38bfca1d65 | |||
1f702f1eb1 | |||
41d8177340 | |||
2e84134938 | |||
a75b2ccf15 | |||
0fef251efb | |||
5e96b86281 | |||
3c7aa6fcab | |||
6aaa6cd959 | |||
50cb4074d1 | |||
8781f89820 | |||
93010923f0 | |||
bf6b81f85a | |||
1d1472c1e4 | |||
93045c2a8d | |||
2d260a80fa | |||
f11a543233 | |||
f97402b141 | |||
4471567344 | |||
dd2c5067aa | |||
8ec3e58e27 | |||
1ad005ec6f | |||
8d15bb4a38 | |||
d8cff6c3e2 | |||
b43facebad | |||
ae9c160734 | |||
03bc4fc133 | |||
29028342b4 | |||
e68fbc0243 | |||
fc38d54bcb | |||
5eb511b0bf | |||
59a16cb689 | |||
2db136b420 | |||
c8866bebc4 | |||
99ebad3032 | |||
467de344b9 | |||
c2871e151b | |||
ad3342aefb | |||
54fdfd823b | |||
ba9c7015fe | |||
208db9a673 | |||
7f10a67804 | |||
3f3385fdde | |||
6c1f5dd5a9 | |||
86e663b2b4 | |||
33c5e14f90 | |||
7c0e82fa4d | |||
005fe48e12 | |||
dcacfb281a | |||
6c8cacfaa2 | |||
9bf90c54e1 | |||
f9d43a8a5f | |||
c38d72d178 | |||
b08efb6700 | |||
6a54aa7e4e | |||
e398c036a8 | |||
6b852afe15 | |||
121c817268 | |||
c979d65ee6 | |||
b502fd0e9d | |||
b8ab14b7d3 | |||
041e194471 | |||
ee6730fdaa | |||
db93421599 | |||
9257cbb0e7 | |||
daac11d2d1 | |||
85d5a8542e | |||
fa6e60da84 | |||
004d7874c4 | |||
051fdb2f2a | |||
ddc8953589 | |||
4dc686736b | |||
0ccbe818ce | |||
be92253330 | |||
97f93c64e6 | |||
e721ddd3a4 | |||
07b2e3e092 | |||
71dcbec10e | |||
37fc44867f | |||
a440358e60 | |||
89e0e6638d | |||
232ba5612a | |||
fa15710455 | |||
fbac928313 | |||
656670919d | |||
bf99b04e0a | |||
cf1cf9e738 | |||
4191eb98b0 | |||
1eec4484fd | |||
dc0c97c0ae | |||
a71372c679 | |||
cb2ce5ea29 | |||
b3f8025393 | |||
809bb59c9d | |||
f046eb79bd | |||
6981db4fa3 | |||
e924052fd8 | |||
9ee8e045b4 | |||
b80c0546a2 | |||
d2adbfdf24 | |||
61456ba57e | |||
d05b02de13 | |||
a4d270fa63 | |||
a946af721e | |||
568f242998 | |||
9338196c30 | |||
587dd3041a | |||
90fc3b0e57 | |||
4c84930245 | |||
08de93e7ef | |||
96593c7287 | |||
471ddaa209 | |||
17946fbfab | |||
22108f2c58 | |||
2a7bb5b550 | |||
da2f4cb915 | |||
653abbcc7e | |||
84d86834b9 | |||
8da1e90226 | |||
0d52399b15 | |||
96de505b5b | |||
7b9f98721d | |||
7538766c88 | |||
7a7889b59a | |||
08d24b059a | |||
ae00ec5dd6 | |||
f9384d627c | |||
bbb731af6c | |||
2ea05f01eb | |||
99405b7dff | |||
5a1027c5ed | |||
0ed88c5686 | |||
32b817f9b1 | |||
04d04d0169 | |||
d4a717e6b6 | |||
394dd3cd57 | |||
f7df15fd5d | |||
29dfa77bce | |||
02fbd7f9d6 | |||
c453dd0e34 | |||
f679ef250b | |||
8b8a3a4a60 | |||
8b664fe2a1 | |||
50a6a81706 | |||
9944dc0696 | |||
65387def16 | |||
19f1f7c380 | |||
c36aaa62ba | |||
dc120847e1 | |||
1aea5b3dec | |||
9ea41b6e67 | |||
4a85f30bf4 | |||
c1c4e50b5b | |||
461e3b65f1 | |||
c92a8851b2 | |||
b55bfd034e | |||
65c6186479 | |||
c4445143f9 | |||
9e2c063465 | |||
4dd78ed31b | |||
330f1f341e | |||
b4e0fe121c | |||
79bd5f1c11 | |||
1ad1883d56 | |||
4c1050b93a | |||
4b85c56903 | |||
dfcb81c32f | |||
7289c89171 | |||
c6a2e99a0d | |||
1757a2cc08 | |||
1811989089 | |||
1ae6c28592 | |||
6670209cb3 | |||
5371481007 | |||
c29d0917e9 | |||
e4bb053a61 | |||
f15ad23482 | |||
0c5d88d230 | |||
f68c095222 | |||
2a85263259 | |||
b963e2a855 | |||
a51ae64e41 | |||
fd4e4fbc64 | |||
9a63be8f69 | |||
212c87ffd8 | |||
16509e5921 | |||
4c172760cc | |||
7330e5fef8 | |||
7710b22cfd | |||
6cfb2baad3 | |||
158063658c | |||
0e1d978774 | |||
3870924fbd | |||
087c7b86de | |||
0129051063 | |||
a087c82db9 | |||
a692ab42fa | |||
05a290cdd5 | |||
3867e6144a | |||
f8a2befbc0 | |||
9bbcdadd0e | |||
ea29c961ed | |||
4005bd6804 | |||
e826ce723a | |||
226cb7fdb9 | |||
6822bf7978 | |||
e2c3ea3ffc | |||
2e51577f39 | |||
976544bd1d | |||
9b0c42ca6f | |||
2a6c763c31 | |||
3305f1790c | |||
8d59fd1cbe | |||
532c9d3fc5 | |||
1a3b0d3812 | |||
b92ba5edbb | |||
aeb1d40658 | |||
bb39d8c6fd | |||
bb74bc33d1 | |||
af90121b47 | |||
f015ce1f0b | |||
378490886f | |||
785d184676 | |||
ca5ed9d1b7 | |||
8fdc4bc9b0 | |||
7d2bbaad94 | |||
0eebfbacd7 | |||
4d4bfa00b3 | |||
ffcff54c9f | |||
ed8197f6d2 | |||
e0f77731c5 | |||
3b51f420c4 | |||
cd1ed63908 | |||
a64d0a54b1 | |||
1b74b49e0f | |||
95cf306526 | |||
a18197dbdd | |||
dcb9d38114 | |||
544563672c | |||
0de6c932f5 | |||
ba8c8aa90d | |||
f9cc72e892 | |||
751134f5df | |||
4025a57529 | |||
bad27e648a | |||
5a4af0eeb8 | |||
5ee08c4b9a | |||
8ab1892560 | |||
6ec1772c82 | |||
08a3165c44 | |||
5d6edad9d4 | |||
01f9be4622 | |||
5615b13027 | |||
cc4158a7e1 | |||
80ee0a3b54 | |||
f21b079e25 | |||
8a4745a465 | |||
7bd978e569 | |||
563e1f97e8 | |||
fc589db9b2 | |||
3309b53401 | |||
a61183303f | |||
cd58dfdf99 | |||
45f3a35fac | |||
9919a41436 | |||
01b67acfa0 | |||
42ea77df7f | |||
2ae91a9801 | |||
71043c41f1 | |||
ab4a5d15a1 | |||
f225c1cca0 | |||
6ee162002a | |||
fefcb6c2cb | |||
cdcbc80248 | |||
965b2611b3 | |||
bebd6b2022 | |||
443abce647 | |||
923c3d8b4c | |||
925f9d0a28 | |||
49b11bb49e | |||
5d494ba89a | |||
7e229ccea0 | |||
cd0d062fca | |||
b3d1a76146 | |||
824d5a0d36 | |||
34973371ac | |||
7ee7cbf660 | |||
66f3484508 | |||
c54b40288b | |||
ff0c168d65 | |||
12fae66948 | |||
82e344055b | |||
63c6613261 | |||
15a8d39ef5 | |||
85629aca86 | |||
5069bebe99 | |||
b81ff32434 | |||
5d2f5c7a1f | |||
b862d14ecb | |||
b97ad6c5f0 | |||
97f0c13c15 | |||
973407498b | |||
4e3effa7e5 | |||
a825132961 | |||
690aea255b | |||
f02a0d96dd | |||
eefa732815 | |||
299c1bd67b | |||
1f22ae6a0c | |||
f15b329874 | |||
bb637eb757 | |||
94f8b81bd3 | |||
8e40b35210 | |||
867286b47b | |||
8451610737 | |||
ad00c16f85 | |||
ebe7f3b127 | |||
7602999274 | |||
4899a6d306 | |||
1d89e136bd | |||
e247e959f3 | |||
a3644ccaa9 | |||
5e7bad2af0 | |||
de112fd63c | |||
afe57512ab | |||
91f1ece753 | |||
6725ce096c | |||
a568ad2bbc | |||
8e72445273 | |||
c3f43bc63b | |||
f1fe29e4ba | |||
086004879d | |||
8ded0392bf | |||
58cacd88e4 | |||
3c4994c0e1 | |||
0802b9a8dc | |||
ed5c61725a | |||
4622080e07 | |||
8d6a12552c | |||
360d96d085 | |||
f8ed3c561e | |||
da8661526f | |||
0d20d95ccd | |||
5781880707 | |||
59e5ec7426 | |||
7944065e3f | |||
d3498138ac | |||
2302545a03 | |||
5531b4be51 | |||
3b4007f8fe | |||
5a567705ca | |||
0a5818e1c3 | |||
6c6609c937 | |||
7e3c347ac3 | |||
c09664c57a | |||
73f5eed45c | |||
112f3736fb | |||
c55778ea1a | |||
7c8e5c6d66 | |||
d699eaaec9 | |||
60e57ec29e | |||
03f58d1886 | |||
8a1fab0c2f | |||
65c623a9cc | |||
4e4165e30d | |||
ef340e6b3e | |||
cab63efacf | |||
6bab0e7251 | |||
0bd75f0e56 | |||
df3fc6e05d | |||
fe575a38aa | |||
cb7e6cfb38 | |||
aca0ae2103 | |||
ed9b0bbdd5 | |||
97bb51c67b | |||
4c9558c586 | |||
a85f931c1c | |||
2deee2b1b7 | |||
8e709eec2e | |||
c7c7731f91 | |||
77f910ee36 | |||
d000d2047d | |||
9d41e86866 | |||
c15c97f69c | |||
5ed8be2466 | |||
6826c1eb69 | |||
46b8befda5 | |||
2082feeaa9 | |||
f9c2624b24 | |||
8bcfdc3c6e | |||
e772107c58 | |||
c44c222557 | |||
8efefeeb5b | |||
ceb9bfcc07 | |||
1a41334e34 | |||
311f295f22 | |||
194abc7916 | |||
30145ff298 | |||
f8bf32e05c | |||
35feab3dad | |||
7f3650650d | |||
57edfbbc15 | |||
7e1a5d5cb5 | |||
59af763dcd | |||
2880d61c1b | |||
af8751a3a4 | |||
f194f78953 | |||
a5354f64a5 | |||
6d41aadc24 | |||
9a993c00e4 | |||
114c398056 | |||
384f1bd174 | |||
653a68b8a5 | |||
7c63866c51 | |||
d4b0bfda7e | |||
c3597cfd99 | |||
5f91be1b47 | |||
50caf108cc | |||
dea69fee76 | |||
0be2ca8beb | |||
eec96ff670 | |||
8c4298167b | |||
2ca8fc6e62 | |||
89b3e3bcb4 | |||
b55b552e06 | |||
3098d6ef8e | |||
036e36f611 | |||
11de315834 | |||
638e04beb8 | |||
10f674a782 | |||
8d54dbac9a | |||
198280eea5 | |||
6644bb0f53 | |||
99ebcb5e17 | |||
624aff645d | |||
b0ac5d3bdd | |||
3887b0c4e4 | |||
1abf447f85 | |||
587b7861cd | |||
4833b68fc0 | |||
a292770415 | |||
b77bcd4692 | |||
ec9238c99c | |||
a9f1f1bc1f | |||
ceb45dd17b | |||
b0564a8aeb | |||
b7d89a9796 | |||
49a03bdde9 | |||
173e03a050 | |||
651e258de0 | |||
4e7b013ce6 | |||
60b60f20c9 | |||
f196ec8854 | |||
507dc44b1d | |||
36305db762 | |||
ea3f886597 | |||
6983380668 | |||
36d555523d | |||
59343b5899 | |||
6df27b273d | |||
1a3076a162 | |||
e46d5c6658 | |||
0fdb10940e | |||
29b8788fc2 | |||
6996c60a41 | |||
7d5cfecb64 | |||
6fe4477ccb | |||
e349e13efe | |||
020da9d0be | |||
022e77d58d | |||
7280110c7b | |||
03ff26acc7 | |||
26356ce35f | |||
1ddf19c7f9 | |||
f9e05f5e56 | |||
001e5a0816 | |||
7e00d830a9 | |||
c1f992f21c | |||
510b4e295b | |||
eeb7669c2e | |||
2486ebbbd1 | |||
a1698b8eab | |||
274272111f | |||
a723bce18a | |||
5a0fcc7791 | |||
b4d2eda02c | |||
6360fba8f8 | |||
e7426f727e | |||
51ba6c16fc | |||
6749931450 | |||
43e09b00b6 | |||
bd11011d01 | |||
a31e3a2a1f | |||
6ae3d8ed01 | |||
e6c4d23402 | |||
f054663b17 | |||
78b990880c | |||
6a11c472ed | |||
c8d9dee46e | |||
e4417c3582 | |||
55d1bf353d | |||
4a9f7b740c | |||
3d0394e63b | |||
a00845ce4a | |||
57963c9c10 | |||
c13d2c2dfc | |||
26fda3944b | |||
bbad4dd5fa | |||
b411de7bf8 | |||
9664ac2a8b | |||
0bab0ae217 | |||
b5be73a15f | |||
1531fde198 | |||
c94fa6ef17 | |||
3872cad2fb | |||
9c9d91c5d3 | |||
44d5dd65ba | |||
e577379519 | |||
5b0a9fd287 | |||
aa7e1cf72f | |||
bf9ce6e82e | |||
2403d82a7b | |||
225cd17cf5 | |||
6efec491c6 | |||
589f4ba29d | |||
11f0259b36 | |||
86de251691 | |||
0b2bc726df | |||
b42967848e | |||
04af255045 | |||
3d759d76cf | |||
7c583fb1e6 | |||
9aaf95ca98 | |||
6dfd9bca69 | |||
eadf472dd0 | |||
04ea1fb9ca | |||
947010ad01 | |||
3a05abe556 | |||
a1c69082e2 | |||
3d5a42658f | |||
8f104223df | |||
c713ff6ac0 | |||
2b11267b45 | |||
9fbeca08e1 | |||
b23abfb8fc | |||
e88a1d2a4d | |||
62be1bf1ce | |||
ab29e1e560 | |||
176f54023a | |||
c36d2409a7 | |||
333e015a53 | |||
d0ac53fa5e | |||
d2a1a6d113 | |||
d236a3c72c | |||
5474646fb2 | |||
8b75b3c773 | |||
36a7fa1b64 | |||
46dcba12ed | |||
6d3b09a7a2 | |||
e9aca5dacb | |||
467b082e82 | |||
6b8f420ad0 | |||
42753be69b | |||
1054080cf2 | |||
a6946803e4 | |||
3f3c44d432 | |||
f23b1880f5 | |||
1330930867 | |||
5c3e5cf1e6 | |||
d60da1bf63 | |||
26d423ff6b | |||
5ab813179d | |||
92254f4295 | |||
e59797e660 | |||
c7f6e72807 | |||
8976746867 | |||
f4ceb163bd | |||
e3f2673919 | |||
4a1235a03f | |||
33dde2b44e | |||
3f63633b45 | |||
fabbda659b | |||
2b87eb86ef | |||
e9e1890b14 | |||
61f6ac56e0 | |||
5c73a60551 | |||
cfc69c22b5 | |||
a20ed8aa18 | |||
b4fce55aee | |||
19fb2877d3 | |||
9906fc4bdf | |||
ae6a6a1d9c | |||
94620d6acb | |||
1442fd68f3 | |||
90e87046aa | |||
f088302b05 | |||
24839d9b04 | |||
ef028005bf | |||
240642803a | |||
efd11d1950 | |||
2f49575adb | |||
e7dc90907d | |||
56fbb54580 | |||
cf236deec0 | |||
594765dd4e | |||
17526f435c | |||
b649d8424a | |||
faf3d08164 | |||
e4281cfbab | |||
1b36b3efe1 | |||
336f576499 | |||
3e0da23b4d | |||
acf09bb3d0 | |||
5ba101063a | |||
8f8fb0e5cb | |||
c1e56cd05c | |||
2126b5156e | |||
3d6a5bd9e7 | |||
2c8421d8ad | |||
d226d6047f | |||
9a6a66d70f | |||
3c51725916 | |||
6eee69835d | |||
19c6760ea7 | |||
e3bb912d08 | |||
28ee1d1f1f | |||
5fa17238e0 | |||
2f044f1345 | |||
17afef63f2 | |||
8d62632945 | |||
35a72e8a97 | |||
9f3bcc20f9 | |||
a2a406fb7c | |||
293eea9e38 | |||
6de6fb1b56 | |||
ac74befd83 | |||
620128a767 | |||
e04252f2e7 | |||
578d656f9d | |||
c97e72d050 | |||
ad54822383 | |||
243bd412e1 | |||
45b8d8b6b5 | |||
f7ed341263 | |||
9147fddb8e | |||
db23534e36 | |||
4efeecdaba | |||
59b53eb6f5 | |||
2980794645 | |||
f12556714a | |||
0f288ed720 | |||
e06ce250ab | |||
e243f90b35 | |||
601abdce6d | |||
b5f652ef04 | |||
671f48e77e | |||
174c222662 | |||
d31113255e | |||
a86fef2a21 | |||
2138599567 | |||
8d2ea460c8 | |||
054eae8718 | |||
c23116bca8 | |||
47ce7b24fa | |||
4a94d48ef7 | |||
d2afaa4586 | |||
140d893364 | |||
693945a471 | |||
f3fc28ff74 | |||
3480f8e827 | |||
de85a8d3f9 | |||
77221de703 | |||
26d5390e85 | |||
1ddea5c134 | |||
48adefca22 | |||
609a17f438 | |||
e37f831ce6 | |||
b9413d540a | |||
a52fb65c64 | |||
9ba86e86aa | |||
612d06bd53 | |||
a59e52bff5 | |||
5b5459f6e8 | |||
86bbb8578d | |||
0e19f45862 | |||
8f3d4e8e6b | |||
bcf1999347 | |||
ab80fafa67 | |||
0d3f85a2c4 | |||
6a9ea5a131 | |||
48419588c3 | |||
8aaa2ebb3c | |||
0ea55cbcb8 | |||
9158ce9ae2 | |||
fdf2b5f7d2 | |||
ce332a407e | |||
8e98f58f6d | |||
fdb19bb671 | |||
b61f564d3b | |||
b805bc7a56 | |||
3c45b038c6 | |||
8c5fd29233 | |||
ff828e6417 | |||
28b4239d08 | |||
5b951b5b4b | |||
df4f40f6f4 | |||
595b562461 | |||
daa4ff6308 | |||
2d0e8b6ec8 | |||
f7c85a4746 | |||
bcf27dbe12 | |||
8eef3808a4 | |||
49a946d0f1 | |||
64999e7f01 | |||
be7609baed | |||
d57988fbbc | |||
a034a8c4f5 | |||
cb99f4191f | |||
56cf29c626 | |||
f6ad9be8e4 | |||
f56992e8e8 | |||
6aa4baa2b4 | |||
f0f6aeaea1 | |||
31f3159991 | |||
bf0af85714 | |||
c4424b4235 | |||
17f0627262 | |||
eac4613cec | |||
c16ea7b05b | |||
4690ce4533 | |||
14bb8bf37d | |||
537ef93eb1 | |||
2160608a21 | |||
ccecd72dc0 | |||
25b9ce1076 | |||
0e7385a77a | |||
c79f0caa67 | |||
bbfb8583c7 | |||
b54598e9ba | |||
08372be34f | |||
df55494c58 | |||
b902656dd4 | |||
a0a3622f16 | |||
78a588af0e | |||
558d0284e9 | |||
e1e6db2b3c | |||
f66fbfd0fd | |||
d7128b4db2 | |||
15382478fa | |||
b21f7f7a89 | |||
b9567f1e54 | |||
3c256bbd30 | |||
a82c50fa59 | |||
505d2cd469 | |||
8b3c072c3c | |||
66e0a6d79d | |||
bf21c28ecd | |||
5642529cc4 | |||
06400a56ae | |||
d49c4f4658 | |||
7f1ace4dbe | |||
d37944e081 | |||
358846ab04 | |||
c3a2982154 | |||
1197a5c8c9 | |||
9e250bc07d | |||
ec51ea6513 | |||
02c3abfc4c | |||
ab57b55e64 | |||
b885046c64 | |||
949f933f04 | |||
8e996cd09a | |||
bab97bfbe4 | |||
35db17fa50 | |||
c66b787006 | |||
00aa884a72 | |||
fc7b1ea150 | |||
0a5ffe6651 | |||
e7272fce53 | |||
b9c36e436d | |||
a8a608c5c5 | |||
ef92123e00 | |||
58da5d7942 | |||
838da762f8 | |||
9c96c07f3e | |||
9053a86eb0 | |||
d5b079faa8 | |||
8228365d4b | |||
a8b602bc54 | |||
0b59af6551 | |||
90490cb65d | |||
af519732c4 | |||
868f5b1c38 | |||
55db8bf3f6 | |||
18a90516b3 | |||
5f3834d398 | |||
4dfac0846b | |||
ffee32535e | |||
1b59135b4a | |||
8b5c0a2db1 | |||
dec1a9d77d | |||
b39d1c7322 | |||
19696e1ec1 | |||
d6a6836d90 | |||
53efb4e046 | |||
fa4379aef1 | |||
591e531ab6 | |||
c41a0c49b3 | |||
7c37590800 | |||
e4e0697ea8 | |||
ee831106b7 | |||
502f247d08 | |||
ad96c8498d | |||
705598d66a | |||
3e52d6959b | |||
76bc6f5aee | |||
6c19e7e399 | |||
b5ae626425 | |||
d710da5c11 | |||
883fb2cb4a | |||
2a34ea8356 | |||
9e8af7367e | |||
106af9967a | |||
c8cad6ab79 | |||
d8139cb19e | |||
7a469b048e | |||
b23d6c9dbe | |||
d23fdd6b4c | |||
49325d491d | |||
635b53c329 | |||
72d2137e9b | |||
c06198491e | |||
9b69dad06b | |||
1a8406e0f7 | |||
509befc912 | |||
e73439d876 | |||
729aedee5f | |||
765d4b8563 | |||
6b0c931200 | |||
dd39f3f244 | |||
af4a285e5b | |||
166f378f2f | |||
9d037911d0 | |||
73baec8539 | |||
118872ab69 | |||
f0ac96cab1 | |||
4545a98968 | |||
0ba6299655 | |||
e32e316146 | |||
e940f51599 | |||
50d9080e26 | |||
6836b548af | |||
457e1d293a | |||
11807df8b3 | |||
96ff36c159 | |||
da782c07a4 | |||
b32399ac60 | |||
f1e36f7fd0 | |||
b434a475a3 | |||
1cad02c461 | |||
b56563deee | |||
81d885c5a4 | |||
ad3039390d | |||
365e0f093d | |||
138be42aa5 | |||
8a385ffc32 | |||
995c736a71 | |||
a9801766e5 | |||
6544e135b2 | |||
a71b379ff8 | |||
2f880f7b5b | |||
f698ef93e8 | |||
bf0275ddcb | |||
e68a3fb856 | |||
b9a6dfbcda | |||
28f790bbe7 | |||
1becd42695 | |||
d6f80a7b77 | |||
bdbbe30c2c | |||
b0ae907a86 | |||
4078c70caa | |||
fb6560db40 | |||
10aed35b08 | |||
cbf0239c23 | |||
a7c50fcfd9 | |||
a598d9019c | |||
a91d9bc68f | |||
355ca7b2f7 | |||
e963c3d3a2 | |||
3b4371ad4b | |||
2121b04f31 | |||
2420373389 | |||
235f6e0383 | |||
3f7d432f91 | |||
894e649be9 | |||
388767258a | |||
f3307d6508 | |||
c29a275969 | |||
c890f61d0b | |||
1e0e24826e | |||
d6ea9cb0a4 | |||
dc6fc0185c | |||
1d627371ce | |||
d3b05f44d5 | |||
581b915748 | |||
e293b25bb7 | |||
23005a82b1 | |||
d47dcddb9b | |||
cd621f2b4b | |||
7cbf74d3f2 | |||
7967653dd1 | |||
ad060c5d5d | |||
9af33974eb | |||
fdbfa00d96 | |||
e844cf25c2 | |||
4df05f69b1 | |||
c52693d2ac | |||
8d2a75bc01 | |||
5fe654e7e8 | |||
cd741439d9 | |||
bdff919d3f | |||
f4b49f7425 | |||
db7e4a273b | |||
42f6b9e24b | |||
ad3ae84083 | |||
a4c9397db0 | |||
5380879aba | |||
eda1f8d640 | |||
88e98f0f67 | |||
2faa60ee59 | |||
b614d14037 | |||
d9bf826baf | |||
2152c5f6c9 | |||
0d23e37124 | |||
fddf32a6ca | |||
a07339e1ff | |||
c5a6c5d412 | |||
7e17ac989b | |||
7b5e331038 | |||
ae101f6cad | |||
98f559c9c0 | |||
d368bb8ae0 | |||
33932eb373 | |||
7d6e237183 | |||
056fb5ea88 | |||
92d013752a | |||
a9daf8fc8f | |||
c3aa84f961 | |||
b4524c67d5 | |||
11b69ee121 | |||
be3330d84f | |||
9439477799 | |||
826efdf767 | |||
7ef35e0284 | |||
0324bc4eec | |||
f157471ac1 | |||
416e7825a8 | |||
6b12d26388 | |||
0adac224fb | |||
6935d7361a | |||
05409bae6e | |||
283e915514 | |||
676d84a081 | |||
9f6e6cd54d | |||
dc51d694db | |||
0f63158f50 | |||
903d27ec0d | |||
5d9ed45cbd | |||
7c0ef0ab80 | |||
bda00e18fe | |||
49fb6c59d1 | |||
224aa5fd9c | |||
25e21ffb1e | |||
0165c6068a | |||
585339e0d4 | |||
83ae568d38 | |||
b323408cee | |||
71707bf0c0 | |||
7db5340159 | |||
dee2f2431c | |||
0b0fa04210 | |||
18374fe426 | |||
ab432e14ee | |||
5d9a7b9452 | |||
e9af7406c6 | |||
20e2e20212 | |||
7897df5544 | |||
0e9f0a741e | |||
31ff9b2747 | |||
dcd915457b | |||
454a5c5286 | |||
168a4ca6f9 | |||
6e48ecb9ce | |||
959e57e755 | |||
0e53445e91 | |||
3ee85fed30 | |||
010a1fde3f | |||
c2349662e7 | |||
43c7cc0893 | |||
d64a2b0306 | |||
1bc563832e | |||
1f48c6c03d | |||
50aca88438 | |||
be5bd43194 | |||
8894aa7d38 | |||
092d29fe56 | |||
8593931171 | |||
77e0cb94d3 | |||
2b2c3cf118 | |||
be308a0444 | |||
9b39f02ce5 | |||
7109061ee0 | |||
f71dd25b3c | |||
012fbe3a45 | |||
853f941d88 | |||
c03abb50d3 | |||
94bc3c3503 | |||
252473d7cf | |||
6eb8cbfacc | |||
ddc86b54c7 | |||
3678aa157e | |||
4d7a77d318 | |||
8d13bcbac0 | |||
2f54ec61bd | |||
af541662f3 | |||
3e2c530281 | |||
ff5b7950f1 | |||
148ce25af7 | |||
56ef384595 | |||
ea24f3ba6d | |||
ba4f6608e4 | |||
07aa07981d | |||
0afabbd609 | |||
2ea3f9b9bb | |||
35a8c703a7 | |||
d0855ee892 | |||
e95b41511a | |||
30a5c4907b | |||
f170baab3f | |||
643687472a | |||
c76058efc3 | |||
502cf72653 | |||
9baa6e7bc8 | |||
7efb0fa7ed | |||
571ad83e03 | |||
983e7683fd | |||
b9af4a8cf0 | |||
f239d4f350 | |||
4d77f62e38 | |||
ac3e6e27dc | |||
4f9c442d55 | |||
adab0cc3d3 | |||
b1f1725506 | |||
4bb902a8b9 | |||
ed399a07d8 | |||
4db38b9ba5 | |||
22934545eb | |||
7fe6b35359 | |||
cfd2ad9a1c | |||
74a30aeee4 | |||
1bff62e3c7 | |||
31032cd794 | |||
8ccad29353 | |||
7ff873bbc9 | |||
a55a464694 | |||
c14760c294 | |||
e6bf1af982 | |||
3998ce311f | |||
e6c45ae5f8 | |||
e8abe14395 | |||
466128c179 | |||
8c7a39f00a | |||
1400c4d4d0 | |||
24dd78394b | |||
9afff4f80a | |||
1aba324481 | |||
3daa6b964d | |||
8cda5104e3 | |||
8db45ffaa1 | |||
b41e714a1b | |||
6cd645b34b | |||
772c1d4fb8 | |||
5a782cca4d | |||
647b8f7fa1 | |||
798bdf32c1 | |||
fbc20da606 | |||
0820b2c13f | |||
5f2361fe7c | |||
6e6142a91f | |||
500f6cf896 | |||
a23ea5e5f1 | |||
86a7d68f08 | |||
373fce2988 | |||
8ac5d5d5fc | |||
3841e92d53 | |||
e5f53ed5e9 | |||
5ef9d46d0b | |||
5389ee056a | |||
e2b7f93d11 | |||
09d1eb17d4 | |||
895c9a33a9 | |||
ab91d35331 | |||
2d601099f3 | |||
48ccf85e97 | |||
6cf7bc7985 | |||
3d9b6061ce | |||
042cde2952 | |||
3b2aa946af | |||
a687180d98 | |||
b1fd835f56 | |||
53847dc3ad | |||
ec0c678cc9 | |||
b83184e895 | |||
f0f1a6f529 | |||
333f80680e | |||
3489512a54 | |||
6100c799b7 | |||
4a96e88118 | |||
ed4c09b456 | |||
939dcee537 | |||
7424fdd623 | |||
4456048e79 | |||
4c31c70298 | |||
c10a4f51ba | |||
53dd0c7655 | |||
6f449aa4f6 | |||
171e3abe34 | |||
2bffeea7eb | |||
90288202e5 | |||
a4d24c61ba | |||
3075593767 | |||
8ab134ffe5 | |||
4800e73a4a | |||
3bd97646a9 | |||
059e4176a1 | |||
57b627fb71 | |||
5281862932 | |||
0fe2313754 | |||
f62dfb0abf | |||
7507282886 | |||
9db5dd36b9 | |||
9ce54d803f | |||
7e7d36f0d6 | |||
82323cd806 | |||
511182f148 | |||
e9b1db7ac7 | |||
9795334f12 | |||
321d88e795 | |||
99d2e2d0d0 | |||
3fb1fbe1b3 | |||
d4f3304397 | |||
4865373b4f | |||
5378b0ad56 | |||
7de357df98 | |||
27808012d0 | |||
dc22949b47 | |||
3d7ad215d9 | |||
67994d7e99 | |||
deab6b40e0 | |||
0205fa6385 | |||
69b3343f45 | |||
9c5b8419a5 | |||
fedf6d7537 | |||
21306dbf5d | |||
9b69f2266a | |||
161f86b6bb | |||
7f24dc5f03 | |||
6423c92b84 | |||
f7ea958961 | |||
8262048edc | |||
73d956462f | |||
db0bc1a618 | |||
91bcf947df | |||
c035ef6eb7 | |||
4f31691c8a | |||
2244142bd8 | |||
4323036992 | |||
8eeabe4409 | |||
6add722a25 | |||
87abc1d6b4 | |||
6ddac9a478 | |||
38169b6d70 | |||
8cc561775b | |||
0634154b28 | |||
c08f79f71e | |||
7532276a00 | |||
ee29074a30 | |||
870ace55e2 | |||
05ac2594b6 | |||
8353b623da | |||
c19af4dbcf | |||
ad7447f8ae | |||
4f827a5b1d | |||
be75455b84 | |||
96d3f67436 | |||
906bce637a | |||
3c0d0dfeee | |||
2ca5802e4d | |||
f4b06e586e | |||
525806d776 | |||
01ef6baa53 | |||
ed04747517 | |||
e13d336f2f | |||
5accdd24fc | |||
5c61c28772 | |||
3a767d84df | |||
d04ce7a2b7 | |||
a1524241cb | |||
b312fdeac1 | |||
30c8cf7b96 | |||
50bda941ad | |||
fc6306575d | |||
c43ca7de87 | |||
826951536b | |||
5f52edf831 | |||
29bc53d618 | |||
378c855902 | |||
546a588aa5 | |||
6e517c4a19 | |||
30d3f52f30 | |||
5dee6cb3d5 | |||
2a96c9a145 | |||
6435514e0d | |||
cd7a41924d | |||
b9452546c5 | |||
4fa89d5e86 | |||
63ece7e1aa | |||
4808055054 | |||
115016e75e | |||
ee09bfac66 | |||
530a3fcd10 | |||
0010229363 | |||
d241afcbd8 | |||
615257831c | |||
b9b737f4ce | |||
36a032d249 | |||
726079e0bb | |||
66421858e7 | |||
df7b3dd861 | |||
22ea79a4ff | |||
2025fe7c20 | |||
a11c529557 | |||
edaa2fba16 | |||
110f01a55b | |||
ada39a970e | |||
8c2641703c | |||
9fcb07250d | |||
47f39d0766 | |||
bcba5af8a9 | |||
aec1b3aeef | |||
a979ed770d | |||
5485568764 | |||
6f3597cc83 | |||
1202d09966 | |||
266eb8307c | |||
8843cc2948 | |||
2c4acce0f3 | |||
87beb2ea1a | |||
9c5b7760ba | |||
6964786552 | |||
d755756ee5 | |||
fbc970e4a7 | |||
364b905790 | |||
34a1085604 | |||
c460ac8ade | |||
49a09f61a2 | |||
08b4563f49 | |||
026f62f183 | |||
2307ac5a22 | |||
2b186421a7 | |||
1dc471e07e | |||
db1fb7ccf7 | |||
e5071a3b7c | |||
e6bfe0c10b | |||
919a97d4c8 | |||
61216b638d | |||
e065d2b01e | |||
746bad3c30 | |||
5bbd61b75c | |||
27eb7e46d0 | |||
c20bef3731 | |||
d5aaff7f06 | |||
fc60768a66 | |||
8ef1dac95b | |||
2024fb1b65 | |||
617ca79b8f | |||
5081755d0b | |||
7bfb5b1bf4 | |||
8d73529fa4 | |||
a19d04d3ba | |||
a9c7748a52 | |||
41e4e952b7 | |||
c0b0b5e4c5 | |||
e424479e7e | |||
a8804f3093 | |||
6479a24bb7 | |||
8b372ad306 | |||
86791a2f1b | |||
7cf0aad388 | |||
c5ea51beec | |||
7cc8e51d73 | |||
75ba58d68c | |||
cd35b219db | |||
4a863f8ce7 | |||
24264548a6 | |||
f9e4b1a56b | |||
13b54b864e | |||
05d45fe945 | |||
2781f6035a | |||
dc3378d084 | |||
9132e94143 | |||
b61e2aa73c | |||
7fdbae3b0f | |||
4dc6fc3b5d | |||
618275b1f9 | |||
7a1111d845 | |||
3af356840e | |||
911a278926 | |||
014063700f | |||
f7c0db0454 | |||
a534d25d82 | |||
bcf3e4a2d3 | |||
0cdfbd9803 | |||
a3e5654d86 | |||
2f9364db2b | |||
5d7c9ebf82 | |||
48da98d0e4 | |||
55e994ac3c | |||
6d46a21f9f | |||
fdc83484fd | |||
6786817fff | |||
b77cd0db15 | |||
20bef76878 | |||
7a30490482 | |||
3bc2e469cc | |||
d770d3c6da | |||
339a001592 | |||
ace57a96a9 | |||
2c26b8d422 | |||
e1eafa2394 | |||
39cb51c9eb | |||
fa5016ab04 | |||
b134ef1a74 | |||
234dff888d | |||
a08c15a3ee | |||
cfa894e7b6 | |||
d6c8e64575 | |||
dc91580e30 | |||
7ec1dd7a98 | |||
82f3f7506c | |||
e26df1c26b | |||
aea77cf225 | |||
a1e3ef9c5c | |||
7aece71342 | |||
bdbde54f04 | |||
157e035710 | |||
97d9a3a4e5 | |||
cb7f111ade | |||
35f670706a | |||
3fac888fe5 | |||
d843646b4f | |||
c2b73d9fb5 | |||
9da95b8165 | |||
5bcd8efe14 | |||
027a1d748d | |||
6d6e012c19 | |||
a8db6b007f | |||
f3576e54c6 | |||
0325f6c4d2 | |||
8225ce063a | |||
c2c379c994 | |||
7344c2af47 | |||
f484ea8c64 | |||
ac790492eb | |||
9ac5fb4890 | |||
2baee7413c | |||
16bec08f09 | |||
afb3c76922 | |||
2f526b35e8 | |||
2dc32aa310 | |||
10e669165a | |||
b6cb90d731 | |||
949a8901fb | |||
d608f450af | |||
e0a1341901 | |||
2cfb03f17d | |||
4dd0f51da4 | |||
d65a3e54a2 | |||
c212eacf19 | |||
46f341d782 | |||
ab4ff5548d | |||
d4713e1e6c | |||
8a3a1466c9 | |||
a5af9dc973 | |||
049a083e42 | |||
9683a110d6 | |||
c44698f61a | |||
106bccda0e | |||
b1aafa5aaf | |||
e2e43cd534 | |||
43b4fe8300 | |||
7c3e4fd947 | |||
9916ef4d3d | |||
ad4da54bc4 | |||
2415c5a38b | |||
ecbc0a2a2d | |||
10d37a9be5 | |||
590d2e4639 | |||
806a07acc5 | |||
8258cdd6cf | |||
2fcee6e87a | |||
04efbc8819 | |||
1fc288917a | |||
27587e83c8 | |||
a0d6741ff5 | |||
63562ddd48 | |||
aac96b15b0 | |||
0f502b4229 | |||
a916f970b1 | |||
7f2d0acc3b | |||
8b6751f419 | |||
70e9cf5838 | |||
24020302fd | |||
d7e2f39d25 | |||
89d0d7b266 | |||
e3c222b5c1 | |||
a199015bc9 | |||
23617f7b30 | |||
f5f02236df | |||
ad76bc378c | |||
570d8d15af | |||
e254c5f31a | |||
2a92be5946 | |||
caab860351 | |||
32861b7ce9 | |||
a08802c4b6 | |||
6b51be6fae | |||
5b5c975884 | |||
605dfec5e7 | |||
71aa0cfba7 | |||
55e45c4274 | |||
8c880b2518 | |||
c43b16cfbb | |||
394903a8f0 | |||
e31c0636ab | |||
e9fe80f8e5 | |||
7671550a9f | |||
83d24fa90d | |||
3e2956da3f | |||
cf3fd01012 | |||
319071c73b | |||
ab8d9bb79b | |||
25eaf8cad7 | |||
c8f97d9c73 | |||
d3f1fe1c30 | |||
5fb01a01af | |||
617d1cd648 | |||
f672193fcf | |||
d3c490e9d7 | |||
2e8fd23f2b | |||
3eef403b04 | |||
f3b78fc82f | |||
80654b2732 | |||
05597ae914 | |||
0f1eb464e8 | |||
8745ffd42f | |||
db99e98658 | |||
9f1a663f63 | |||
db0b3da446 | |||
5d22d41201 | |||
b397de1d54 | |||
4bda79b263 | |||
697a9dbd06 | |||
accaabcfde | |||
5026cbdc8d | |||
16a14d4ebd | |||
c151352910 | |||
52e2aaa20d | |||
9df87ba167 | |||
b80f70fc54 | |||
939cdb019b | |||
fde36fe238 | |||
116be93160 | |||
40e820cabb | |||
d79387bd92 | |||
05f2a62cbb | |||
78a965dc90 | |||
5b603d6627 | |||
e93d2046d3 | |||
995871db8a | |||
501535f196 | |||
91e854e99c | |||
9b05d8e774 | |||
e70793c3bc | |||
abb2603bea | |||
c91218be27 | |||
9eab44128a | |||
f98101afa6 | |||
4fae7a8cb6 | |||
26cf1922db | |||
c087b0695f | |||
16930d2004 | |||
33939e7cfb | |||
e759ef5865 | |||
2be1b1ece4 | |||
1820a29aed | |||
ee9f85d53c | |||
afbb1dbe86 | |||
9244bd6b0f | |||
6bb4403207 | |||
24ebd503d4 | |||
285a5eed35 | |||
26aebe6a0f | |||
ca9f174171 | |||
ffbced22b3 | |||
45ca459ceb | |||
5a539f0619 | |||
c6cef72cb7 | |||
8081d053cc | |||
efdc8e5df0 | |||
b4911a2b2f | |||
7b70210c9a | |||
e3353df8bb | |||
25285fc059 | |||
7720f71e44 | |||
1657ac5357 | |||
299214aa1d | |||
5fd4488e08 | |||
87fcaf2651 | |||
f6b9cf6f21 | |||
eae18e61b7 | |||
96735f2543 | |||
54459d3b5c | |||
82444f9e7b | |||
3d8365a473 | |||
e2dc9715d2 | |||
d4f1230b37 | |||
7701693d37 | |||
b6704fce4e | |||
39a68d4a2b | |||
94633899d7 | |||
c45bc1554f | |||
789c8edc45 | |||
e0b44f43e3 | |||
b45069e377 | |||
c3a156ce4b | |||
ee5cc099ed | |||
f189587153 | |||
a1fb5ef6ed | |||
8c2550c39a | |||
abd96a920f | |||
1d3f0fe96c | |||
49a6cdbda6 | |||
2700028da7 | |||
51a1564566 | |||
bd068058c3 | |||
4591f77928 | |||
4f70a7d0fe | |||
6d67848096 | |||
c145ed103a | |||
f265db4037 | |||
62308f26bc | |||
2b4b47eff6 | |||
04ae0e2610 | |||
cada9fae44 | |||
949aea951e | |||
cfc49ab261 | |||
0e853a3119 | |||
a0cad7e8e9 | |||
880f1866dc | |||
05d22344b5 | |||
7212f855d8 | |||
54171e4be2 | |||
a820c01ba5 | |||
9d1ae891bb | |||
2df7247e83 | |||
a109ebef28 | |||
c0135b592d | |||
b7a0aeea34 | |||
66375e25c6 | |||
85482a67f5 | |||
d7e90969d0 | |||
9012baf51e | |||
3c8355790f | |||
e9f1da85e4 | |||
58adccfd4a | |||
040f3e016e | |||
505d5f5cae | |||
2a99e2a295 | |||
49fae646e2 | |||
f7780b6745 | |||
7a59d15e9c | |||
9b141bd9dd | |||
d0d062b6f4 | |||
375118fe02 | |||
7b59ceb4ae | |||
3aebe45a7d | |||
7c236c0fa0 | |||
7a7e650ca0 | |||
7a32f8efd6 | |||
2f8b55ceda | |||
b77be20cc1 | |||
101135a99a | |||
746c1bd628 | |||
b0502b1873 | |||
6801fc667a | |||
794db19b6d | |||
d4637818be | |||
33b7dca782 | |||
d7015cf2e6 | |||
5689fa8512 | |||
25e51a945c | |||
bfd1306a56 | |||
839986db22 | |||
ed443fe0d6 | |||
390981e10c | |||
56b3e6a993 | |||
f86f2701ff | |||
63d3685652 | |||
0a93466999 | |||
81664bc776 | |||
6d60a6f833 | |||
4186894a39 | |||
cfeafacd07 | |||
9c2edf5c6a | |||
72396f0f96 | |||
45388b0d48 | |||
a8ae36c403 | |||
164b39d8df | |||
ccc95087a1 | |||
b97f4f8bd7 | |||
76c1f47b20 | |||
ce74e49236 | |||
977d39aeb1 | |||
a821ea2752 | |||
b97197c0fa | |||
474691927a | |||
81dcbedd17 | |||
4f5cfdee57 | |||
2c7725a8e4 | |||
0ba6482da5 | |||
4c1fd67925 | |||
25683cc0de | |||
087fd5a909 | |||
fdfbab850a | |||
f1c50b7fc3 | |||
959bf4a7f4 | |||
5dda915467 | |||
66d8fd6c2a | |||
06efe306c3 | |||
15d1b005f5 | |||
7efab75c3c | |||
a9e4248c93 | |||
5338dc5540 | |||
09d3dc8e90 | |||
3bf95e566c | |||
958a5a3c4e | |||
7b48f6387f | |||
fcd2cdb136 | |||
6b59356bc5 | |||
1c7f098d9b | |||
103c15f000 | |||
0fd55f8b07 | |||
511c1e7b9d | |||
7df3bde2ce | |||
25e6b6e6c4 | |||
cb81f2c9ba | |||
6c25f0fd16 | |||
934f3d1814 | |||
e883fd6b1b | |||
e3b300e978 | |||
03a9f69739 | |||
be2dca8ee7 | |||
5c595ef289 | |||
d9534e5f23 | |||
00fa3806d8 | |||
6c5ef9acdc | |||
2db5914ba0 | |||
35a0dafb83 | |||
5f12688a90 | |||
f8d9af871a | |||
08a2b4bbf0 | |||
27a5793fd0 | |||
b1151f82b5 | |||
b6332f8313 | |||
e036cd4332 | |||
174fedc2e6 | |||
4803e60db4 | |||
abb62b93e3 | |||
95bb322cd7 | |||
670b4033cb | |||
715ae13997 | |||
77b88ab59d | |||
64235bd745 | |||
0a1960461a | |||
11249657ac | |||
188ac4f730 | |||
865116b3f4 | |||
9f28c06e9e | |||
3cd6520758 | |||
da1a50bfeb | |||
9ec79f50fa | |||
b15392ea85 | |||
b8339e72b0 | |||
6eaec7fd44 | |||
9e8f2ce771 | |||
e8e6f6f531 | |||
f16ba8ee06 | |||
76f11859b2 | |||
5be21a19db | |||
043359dd40 | |||
1b95b00b44 | |||
eaaf6af31d | |||
d8d50aaf41 | |||
5fcddd581e | |||
f1a9613a92 | |||
a2ce10759c | |||
5065aec773 | |||
25fc64933a | |||
bbdd54efc8 | |||
92597baab9 | |||
62786dcc09 | |||
c012e5bf17 | |||
5ed29b6c27 | |||
2d5decd943 | |||
9d167dc83a | |||
afe8394658 | |||
72db6d1a08 | |||
9d0e300924 | |||
4f548b7b27 | |||
cbd50372e4 | |||
3caee8bc71 | |||
6c64faf0ba | |||
65290f1ed7 | |||
498af5d203 | |||
883b53de0a | |||
cefc1f130d | |||
b2a137c5bc | |||
62f056f884 | |||
3944ea53d2 | |||
d372ea753f | |||
2528cd205d | |||
afd54fc212 | |||
8e7a9fc513 | |||
acb75f4212 | |||
c40b56c19d | |||
98027a06ec | |||
d92dfec1bf | |||
72a588bfbf | |||
213bc4bb71 | |||
1a01aa0ae4 | |||
0b0e3fffe4 | |||
1fc32c5e6f | |||
ba9f05ca06 | |||
629d12ade1 | |||
08929752a6 | |||
aaa5b4ca86 | |||
8d7e84494b | |||
99df95697a | |||
a877b21839 | |||
3f267693d2 | |||
30b7dbf1f7 | |||
f32d162b62 | |||
126fa320e5 | |||
ecae0b055d | |||
9bf22fb0d3 | |||
2a1d358141 | |||
579b450029 | |||
72eafe0920 | |||
f226392c9d | |||
2cd5c209f5 | |||
f0d444b32e | |||
4baff9fbab | |||
8e656427d8 | |||
aaa7302e80 | |||
33407fd5be | |||
513b93f789 | |||
08e54c515e | |||
55682810b1 | |||
ec893d09d0 | |||
f9b745a671 | |||
43b71a263c | |||
5c4672d1e3 | |||
7ca8e0c3a1 | |||
b530316850 | |||
fa92beae5a | |||
f58f703ecb | |||
6b83fc6b3b | |||
cdb390f7ce | |||
7b1caac9ac | |||
cec75fa60e | |||
eb23306b12 | |||
43f6813609 | |||
ea36b7b153 | |||
448ff4d398 | |||
e16227211f | |||
e07e329c26 | |||
a3d79aaa4e | |||
3fa9ae0b82 | |||
7feed50af4 | |||
0c0eb5765d | |||
e87ff25ef6 | |||
21e09cb0a5 | |||
04d54607ae | |||
ad8bfbdaab | |||
aca815c4aa | |||
24683c19a4 | |||
6b8dffc401 | |||
c4bb84058a | |||
c3dc76e35f | |||
993fa5c210 | |||
1cec793217 | |||
6029e1a291 | |||
7cc75c0cb9 | |||
e5248b09ab | |||
c39f047703 | |||
4562254862 | |||
8116d88aaf | |||
14362630d7 | |||
9af796a10a | |||
eeb884e8ce | |||
02baf905c6 | |||
df7029d2c2 | |||
c22b7568b1 | |||
73537d27d0 | |||
449d7ab589 | |||
0ddef91e16 | |||
b205af5a0d | |||
1e32170df2 | |||
f446a5f1fc | |||
fb21fb25ee | |||
deae0e8856 | |||
1cd54dc12e | |||
8ebc8bd209 | |||
9d11866f86 | |||
2c456c291a | |||
c5b3c2f430 | |||
e46e747ac0 | |||
e1a88c9426 | |||
23d3c33a12 | |||
ec8130f443 | |||
4b7aeb8418 | |||
18a0f01f8b | |||
df2e639692 | |||
708b3a662c | |||
4fee7844f8 | |||
891416e6b9 | |||
978cd2c484 | |||
c88c905926 | |||
bda4eb830e | |||
f5c1acc749 | |||
c4e6148b9f | |||
43029de2f3 | |||
7262c014c0 | |||
9f7bd99051 | |||
9e4f04cc18 | |||
454b2c8941 | |||
4c812c7bff | |||
f95b5324e0 | |||
64ee1b313b | |||
1ca651e803 | |||
f3a88398f2 | |||
412d641eb6 | |||
d7d058e772 | |||
278b917494 | |||
4b6989ef7b | |||
c10ea84ade | |||
0642fa8093 | |||
2db7c2bdd8 | |||
8682e7deb5 | |||
ea0747171f | |||
13349777ad | |||
ab0a5a06af | |||
6371f66677 | |||
105524d9c0 | |||
95e0492b32 | |||
234c084c2a | |||
94faf74aa4 | |||
e78dd1fdc3 | |||
38045d876d | |||
8433724452 | |||
e9f9e0dabb | |||
6bdf750c19 | |||
647a09b5b9 | |||
6144bfb437 | |||
88dbd7710a | |||
26a71232f0 | |||
f58a1768b9 | |||
125e6581e0 | |||
54105086ad | |||
3674ac2922 | |||
18c023e6f7 | |||
312ba2599f | |||
746dc6f884 | |||
edd4d4c114 | |||
56e0c3e047 | |||
1e83d1b304 | |||
74aa84b183 | |||
168c213288 | |||
d268a3852c | |||
01e87438c3 | |||
060f4e6632 | |||
3df5540a97 | |||
30dec5b9f5 | |||
43b437fc58 | |||
8e889cd292 | |||
eceac6def0 | |||
1e3d6776aa | |||
784ca67ddf | |||
0f6b49cc31 | |||
8cede2a2b2 | |||
38ed04bbd8 | |||
c393e70ca9 | |||
1c25c0f408 | |||
b7ebce48ee | |||
d6814a0489 | |||
6f24c74f8c | |||
d8389dcc46 | |||
9f939553ee | |||
529988f394 | |||
9f46aa1e18 | |||
418c1b6f96 | |||
8056fb9502 | |||
ab2e21147f | |||
6c00bb20b6 | |||
573ac357d8 | |||
5b139f9246 | |||
76d75b712e | |||
8bc2fd7e42 | |||
4533a86712 | |||
a6239e2ce3 | |||
1f8e61f480 | |||
c40f845279 | |||
aa74962263 | |||
42cbd6c12b | |||
ee51f69a5a | |||
0fb6e9cf6c | |||
e02845076d | |||
2dc3798116 | |||
fa252f5e8f | |||
580b9b450f | |||
f0730cd1c8 | |||
b045fb3a45 | |||
6c64111d7e | |||
37a2ccca95 | |||
b4615edfcc | |||
6019cf8148 | |||
e3d945201b | |||
05b17e5a00 | |||
13b3edfb07 | |||
f61372b2fd | |||
f85d8f7060 | |||
a0b4b7db86 | |||
827a92ef2f | |||
625d76b914 | |||
a1de894b64 | |||
413ab6d7e4 | |||
a3b1c79006 | |||
5a7d3ba4c8 | |||
8a1db31184 | |||
5190b2db1f | |||
820c573476 | |||
ab40454bce | |||
b65865b854 | |||
a5772e62c3 | |||
9976bea03f | |||
7997aeaca5 | |||
ba95084d27 | |||
e952e91b54 | |||
da21c0ddb7 | |||
0133711c3b | |||
6eae2cd460 | |||
9314eebc4f | |||
428cbdce2a | |||
7594c4383b | |||
e2f6911e9f | |||
9cdae03069 | |||
06946f026e | |||
d43dac5c04 | |||
b4d83b18fa | |||
f9424dbd6d | |||
e5212937af | |||
aeb6635e71 | |||
e94b478317 | |||
88216de42c | |||
1fcf707bb2 | |||
5101486fe7 | |||
7ae4d0e981 | |||
c644c128c0 | |||
2603f6fe45 | |||
957a296ca9 | |||
e4f44fa1e9 | |||
2b469567bb | |||
a2d90eebea | |||
850a8da0a9 | |||
ac6d711a99 | |||
5aa254a178 | |||
ad396adf39 | |||
05cce164f7 | |||
935b69bc71 | |||
61f800999a | |||
9833743eb9 | |||
ac65bc7302 | |||
b36f207bb2 | |||
e87a7c7bb4 | |||
7e5128bb85 | |||
0a178ec35e | |||
a5cd0bdd3f | |||
9cbf9d0422 | |||
097a05aab9 | |||
0e5b2598fb | |||
4f492e33e6 | |||
1828b2bd17 | |||
0c74e640df | |||
ffd2721627 | |||
f6ce4cb29f | |||
4863ef3360 | |||
9f7807ee10 | |||
b2285b0beb | |||
8e4f4f82a8 | |||
db2158d4c1 | |||
1c461bbeda | |||
0757f4f309 | |||
a2f287cdfe | |||
1ef448d518 | |||
f44b1a35bc | |||
26f02a4771 | |||
dc3c730937 | |||
77d45e7a3a | |||
45a59f009b | |||
dc6d6ead69 | |||
27693826a2 | |||
8a647b42d5 | |||
eba9f3c03b | |||
eacf46b367 | |||
83cacaad05 | |||
cf10451d14 | |||
44069645df | |||
4497463778 | |||
10b84418c3 | |||
fadda4ceec | |||
011a32f741 | |||
10bb3c100e | |||
2738d5851d | |||
219095404a | |||
001c361338 | |||
e21a172e95 | |||
4651b7007b | |||
5b4be5ba1b | |||
9c2c90c0b1 | |||
bdd0c3f961 | |||
b7013361c2 | |||
f73b3e522b | |||
fa6d17a1b8 | |||
c3e646ca22 | |||
fdc9e11fb4 | |||
a1ec838282 | |||
7da46517ae | |||
d8e2939307 | |||
8cad72c654 | |||
2cfc2bb60d | |||
1811e3b9cd | |||
960cd18d0a | |||
405b85c4b4 | |||
92e323df51 | |||
3f2c34903d | |||
3c260aa333 | |||
da41f3a93b | |||
aeb711acde | |||
a46ee9a5b2 | |||
088f9558ec | |||
376d61c155 | |||
285a2b92e5 | |||
8939c573ea | |||
3839d9873a | |||
116762ebce | |||
7dd8f00e95 | |||
29a3db4b5c | |||
5d95907996 | |||
2f940d01b6 | |||
517e170b88 | |||
b27fbb9b8e | |||
9b97d32ea8 | |||
d4f3952a90 | |||
f7840652ed | |||
08634c1ff6 | |||
dec4205890 | |||
b49ab59e3b | |||
7e1171ec9f | |||
3eae787957 | |||
9ecba4fc5e | |||
4fb9eef198 | |||
49ecfd8224 | |||
09a2854b8b | |||
82976e609f | |||
b2d6b60300 | |||
90737493e6 | |||
fe3abc79d6 | |||
9931112387 | |||
1cd646afe2 | |||
0d262d28b7 | |||
f33e950780 | |||
7094489536 | |||
2dd650df01 | |||
7e8037979f | |||
3a1c042cd2 | |||
ca81c35b3d | |||
6a2dfff34d | |||
5eccc01de6 | |||
1850e893e9 | |||
2341793546 | |||
1f1d089fda | |||
5372a50bcc | |||
83588d9b98 | |||
58e960ceb5 | |||
de07705671 | |||
49ff3cfbf3 | |||
e392469835 | |||
7745bd89a9 | |||
01bed932c7 | |||
157190757b | |||
e0f1047d72 | |||
15f0cda41f | |||
9a95122c7c | |||
d868f7c02a | |||
5ca2f306b8 | |||
c714c1a0c9 | |||
e6e6c00497 | |||
62a3da2fa6 | |||
f1f97e8ec5 | |||
6c361679c6 | |||
ae89d2f2ab | |||
8053fb5eae | |||
ebf5eb6e20 | |||
2e2bff3f0a | |||
a3f290e4d8 | |||
e0dd1f13e3 | |||
787def6a1c | |||
1f5d7d7b5b | |||
4bc5215833 | |||
7736545f5b | |||
1ecf4377c6 | |||
593d4dc508 | |||
93d366fea1 | |||
7973f2e8b9 | |||
bb14fa0b4e | |||
95aba0c537 | |||
df4143f036 | |||
ed0575e937 | |||
456ed0aab4 | |||
47c8389419 | |||
51fd4d70da | |||
79e32231fb | |||
80f9f857e5 | |||
5a4c2de425 | |||
7e547743c7 | |||
3f3d43df41 | |||
0cc72a49c8 | |||
35c9e99914 | |||
2e4bd1e440 | |||
22c0b8e524 | |||
faeb58f7e2 | |||
cdc184c5e5 | |||
2f9a4f0fa5 | |||
37437da34d | |||
6d3a5856b4 | |||
4d4538a346 | |||
327102a254 | |||
3602eb14f5 | |||
cf82b51a1f | |||
412d3bc2f8 | |||
4735508a0a | |||
9225d01b29 | |||
7782970d51 | |||
6f053287d5 | |||
7a88f59f08 | |||
d56aae8913 | |||
a309a14396 | |||
b80cbbdd4a | |||
5bc2dab1d2 | |||
8bbe7fabb3 | |||
c537d160a6 | |||
475187fcbc | |||
a379e36e24 | |||
a9054a3cab | |||
ea7a9c259f | |||
2cba48d4d7 | |||
42b79c5a20 | |||
be9523f1e4 | |||
6a8dd0f053 | |||
1b63aa411b | |||
33a7f3351b | |||
f7af5e1329 | |||
3a9a029d70 | |||
0aaacc86e2 | |||
1b8b7b741c | |||
66831c619b | |||
30628fb5f9 | |||
064ff8a7d2 | |||
9a18955de3 | |||
35da3f3334 | |||
335d45f03e | |||
c466cd77ad | |||
d998e2e9bb | |||
0a20315280 | |||
01753f5aea | |||
bcd22cfbf3 | |||
7e039d0339 | |||
ab08ac70aa | |||
5decf3cd7a | |||
5c6d757e35 | |||
4b75be804f | |||
6515e6ee17 | |||
55e8583663 | |||
3fbff71861 | |||
e5eea47b66 | |||
b10b0e8f57 | |||
631a367b1c | |||
8ea279fbe2 | |||
089572befd | |||
0f96b9569a | |||
d1114666de | |||
f676abc0d4 | |||
3492b7219b | |||
a12ae6e399 | |||
9b2e18a65b | |||
c52ccf7eef | |||
1282434684 | |||
4e844187f7 | |||
ccd67d658d | |||
62383819cb | |||
3febcf6043 | |||
a431137f45 | |||
5a6e14b9df | |||
536bc3112f | |||
81b2e6b789 | |||
4cf376ec1d | |||
d3a0c91398 | |||
43140d3efd | |||
3dd3bf829d | |||
bdcad06ece | |||
69fdfb0635 | |||
55a8002b9c | |||
e36f9b2273 | |||
70ae99f31a | |||
15565ca09c | |||
f188e02a5d | |||
4d005349a7 | |||
c65a97882e | |||
cf880548d9 | |||
b5876e7f04 | |||
2436ea1131 | |||
5395b6829a | |||
c3af134a5b | |||
cce72a5f1b | |||
32c143f8d7 | |||
895cb1f2e5 | |||
7986f5646e | |||
59a5776f9d | |||
fc8c0ccfe1 | |||
0930ead814 | |||
b5a17637cb | |||
460c8a319f | |||
7aa051ef4f | |||
c7c132c0ac | |||
d84b1125eb | |||
8d4a1899f2 | |||
3a0cdf1388 | |||
d8d76fd327 | |||
abf7296de1 | |||
316c20ee44 | |||
13e8c95667 | |||
a14ad423a6 | |||
e1a5d5e19a | |||
6e29eddaa7 | |||
65ae26a961 | |||
1afd946a94 | |||
d6820634ac | |||
95dd744633 | |||
219af36090 | |||
24e83398ba | |||
4172ed21a9 | |||
1cba7b8ec1 | |||
0bef85277e | |||
2f4c428316 | |||
4de0b73cd8 | |||
7ffb3f46b5 | |||
ad6cd05295 | |||
6e7ad3ecdb | |||
a6243d14c0 | |||
b0a477c5ca | |||
1d655c7abc | |||
abf2bead33 | |||
f1103bec7b | |||
fb1a6534dc | |||
22b1d5fe75 | |||
d2c939bc09 | |||
73f8cb4819 | |||
256bb771e1 | |||
cc533b0431 | |||
3c76fda8d9 | |||
c5555350ae | |||
22744084dc | |||
e4b212ec90 | |||
4be0af5de5 | |||
d771745981 | |||
7628842b0a | |||
6bcdb7fd92 | |||
27561fb632 | |||
f6ec3f66f8 | |||
2f0b9a8f94 | |||
3fb1a4ebc5 | |||
9abb0a1581 | |||
e60e29b70f | |||
d84352896f | |||
ee419454f4 | |||
68445fe195 | |||
12b2d4c00b | |||
fbb4d3a636 | |||
ca415376c7 | |||
718f73ebb1 | |||
fb47eef218 | |||
87dd473148 | |||
9bbbccee1a | |||
0088750b16 | |||
0aae2deb58 | |||
0d62b37c13 | |||
64ece1080c | |||
b4256e484a | |||
b0ea204be5 | |||
118d7fce09 | |||
5e498e0bd6 | |||
c312fa869b | |||
fe394b0b46 | |||
6039f3931d | |||
71d72b426f | |||
7d5042c507 | |||
e2b0e14771 | |||
0e9bfba84d | |||
99f53413a6 | |||
f46600d7fe | |||
0607a87514 | |||
1658690b97 | |||
eae0f3273b | |||
508beb2fc7 | |||
0de2f492d1 | |||
72a7393844 | |||
99f8468f63 | |||
a9e8fc2f1f | |||
e225244887 | |||
cbde15b00f | |||
8e38047d43 | |||
dc1d0195eb | |||
7d7f264bc0 | |||
b3c30b4fd2 | |||
6c87005eeb | |||
8d2a516044 | |||
dc0cc49dbf | |||
497bc5a414 | |||
2a71baef90 | |||
49a74e8610 | |||
6340fbb3d4 | |||
748203f4e7 | |||
a91cc94228 | |||
029ac71e58 | |||
e830b46173 | |||
a4d053f555 | |||
b298796060 | |||
895490df3d | |||
a09c06849b | |||
37c494bf0a | |||
d6fccc07be | |||
819efc132b | |||
8ffdae4182 | |||
efd6dcc8df | |||
70a17ecfaf | |||
15dbf67983 | |||
a43df30051 | |||
1bff5a7b9e | |||
ec49d9becf | |||
bc49f51dbb | |||
b869860a83 | |||
2772a0e5e4 | |||
46970f5a4a | |||
ccf9cd3f71 | |||
b9c464f8cb | |||
48510c1157 | |||
3035fcf080 | |||
930f402eb9 | |||
4e93191312 | |||
9bc4f6bacb | |||
9b42129fea | |||
0650137e32 | |||
a13e528972 | |||
82f8dd5635 | |||
7f93cb22ce | |||
e6738053b2 | |||
b74ab51438 | |||
d7a591ce3a | |||
cfe33a8bbd | |||
ca7593cdf2 | |||
b185d11daf | |||
5106c37ac4 | |||
5d40ad1749 | |||
24edce3daf | |||
cfd1ab7d78 | |||
bfaa648edd | |||
fd19210bce | |||
9a3b103324 | |||
0ee5c905e4 | |||
5f22a226cc | |||
56b3144c56 | |||
dad4bef70c | |||
13a04dba71 | |||
ab66dfcb65 | |||
ab349a5303 | |||
53a68ff5a3 | |||
29f13868b7 | |||
8ce71d724c | |||
e3de3dcc1d | |||
fa6a6fa4ef | |||
e78e4c93c0 | |||
0173f5877b | |||
32e829da74 | |||
68e81fa8c8 | |||
9f84d13542 | |||
48f591eea8 | |||
cb6dce4e03 | |||
e5e7f01e84 | |||
f90a320d95 | |||
96c94a294e | |||
ab81887138 | |||
d13e3d95d3 | |||
64f1fe0b51 | |||
0a77f88229 | |||
12d34a50ff | |||
92daa920d2 | |||
091d03b9d8 | |||
f7ee11cb44 | |||
6226b8ae6d | |||
a6a61421d9 | |||
5c20574044 | |||
aca006253a | |||
7a752cc7a2 | |||
de7dc2c1f7 | |||
35b5bf187e | |||
efbc4c5184 | |||
711c2b7dfd | |||
a4adb4709b | |||
7121b5fa31 | |||
c944648b99 | |||
45cdd556c7 | |||
429bd0a4e3 | |||
135f8c1be2 | |||
7b7a590e21 | |||
95728ef29f | |||
021b44724e | |||
ba0efe64c7 | |||
1a247d8d3a | |||
21b03e8e8a | |||
03790e3e4d | |||
9af239aa65 | |||
02fc700ac0 | |||
1fb5238642 | |||
461af7d0a2 | |||
cf3efc11ba | |||
b6e24b5094 | |||
0b590763f3 | |||
3cc4ee7199 | |||
4828cd2f16 | |||
45da7e8704 | |||
e00b88ca32 | |||
e497680ca8 | |||
dbb9eefe70 | |||
7388fdf820 | |||
0c46d561ec | |||
4248ab936c | |||
e5139113b1 | |||
0f1a4ad4cd | |||
00d99fb1a9 | |||
ee8dc4af2d | |||
d7b4be1e74 | |||
7ed509b76a | |||
c616f66e83 | |||
e8bfac27aa | |||
df3b3bfc8f | |||
ac358502ce | |||
2b905d2ed5 | |||
49c55ae12a | |||
d280d7a389 | |||
19ccdcd951 | |||
cac9b2e1f1 | |||
e0e42a0f87 | |||
1d845dab03 | |||
71b9bb67a6 | |||
06be251032 | |||
454a6ab177 | |||
1e1c3cdff0 | |||
52de5e569c | |||
2f97942286 | |||
4b1eabf1fc | |||
6c7f1cbf8a | |||
ffc582093b | |||
cc833f3ca4 | |||
00d0aa7830 | |||
54f649a4b4 | |||
1c2cd4dcb2 | |||
43d6851199 | |||
9527684cf7 | |||
1965815d7d | |||
cc2be105a6 | |||
e4c599b756 | |||
a8a21ddb73 | |||
942c31621b | |||
ca14002bd1 | |||
5eec098e2b | |||
37043195ba | |||
ab8e2cf34d | |||
882e08fc4f | |||
2e5caac8bf | |||
714fe82d2f | |||
1724e5b499 | |||
a5fa6acf5d | |||
59105a9ad6 | |||
abc23e9a49 | |||
72071566e7 | |||
055cd99dde | |||
d2c52e5c94 | |||
c7541f819a | |||
7972c0c862 | |||
3fee5a3781 | |||
db45e74fcc | |||
206e45b9e8 | |||
a9345953f3 | |||
eb324d7652 | |||
85adfc40fb | |||
f1bb8910cb | |||
8ca794dc57 | |||
fe3cd65c62 | |||
9e1181900b | |||
256c5356fb | |||
0bc6fd246f | |||
77ce768cb4 | |||
b23256dc4e | |||
5906fb7139 | |||
0606050231 | |||
56a700e82d | |||
47cd9beefa | |||
b93aada213 | |||
0ce6872693 | |||
1c9d84771e | |||
6699366597 | |||
5d0d7aca58 | |||
29d0c19b0e | |||
917e1023e4 | |||
1fd5a20373 | |||
abc83362e7 | |||
9792336b33 | |||
4a6d0e4ba2 | |||
bc38ca4f91 | |||
306b3017e4 | |||
ce92b6cb66 | |||
8e2df567d8 | |||
4622f369c3 | |||
1eb4473e9d | |||
22c2829714 | |||
a87f8e8687 | |||
c89fb9ad73 | |||
978de73351 | |||
a1ec01ec2d | |||
7aa9949332 | |||
71f3cd648f | |||
7f379027ca | |||
279f3e4934 | |||
bdd75793bc | |||
58660bed3c | |||
2ca0ae7529 | |||
3ee09df6ce | |||
9b866b8e06 | |||
4d4954c5b8 | |||
17751ffd57 | |||
e8773f6a98 | |||
112ddc7156 | |||
e5ec72b09b | |||
24daf00616 | |||
718375419e | |||
a16bcf8e51 | |||
5c28125350 | |||
937de87dbf | |||
cadbe2c2c0 | |||
7c646f8693 | |||
1479ef9a6a | |||
daf078d4d7 | |||
c0fd1dbcbc | |||
821dcddda0 | |||
d9cb4e2620 | |||
290af4c187 | |||
11ad98e7bd | |||
ce50a9ca44 | |||
b7d581b412 | |||
a08dd4aefa | |||
be52b5930c | |||
0382120363 | |||
7151590abc | |||
51faed2c4d | |||
c1a8c8a3fd | |||
4975d30714 | |||
45d9f9d07d | |||
9b0e553ece | |||
dcaa0594f1 | |||
1202a303b7 | |||
ed8fd0bde2 | |||
834bfde45c | |||
fd4e57aafc | |||
4c4b3b776c | |||
eee160503b | |||
bef55b25ea | |||
623f972e8b | |||
7e651d53a0 | |||
5d212d3cea | |||
3e31de1602 | |||
245b7168ab | |||
4a9b567ebd | |||
2f6ae99452 | |||
1538ba0cc3 | |||
06a3417124 | |||
524a25eb2c | |||
8ff2724213 | |||
a9a2d2debb | |||
0070abc04c | |||
936a338e0e | |||
6c349d0ec4 | |||
7e1e3c3c32 | |||
ce23d76c72 | |||
cb488ebf7b | |||
7931451a49 | |||
59449cf513 | |||
39e5ff7eaa | |||
b430b02021 | |||
0bc67f5a89 | |||
56dc96de1e | |||
3ccbf37b41 | |||
7725b9e8a1 | |||
f511c9bc90 | |||
56b768fe19 | |||
f8901e94a9 | |||
ec5cc7e4d3 | |||
2413bcd99a | |||
1838ad4ae7 | |||
26c4f983d7 | |||
22609bbfdb | |||
0a8cbcbfb6 | |||
7f33051fe1 | |||
e65c2e279b | |||
b43338bd63 | |||
65ae9138ef | |||
665b691711 | |||
87953c4b93 | |||
be480d577c | |||
32566a43bd | |||
fd598dea5b | |||
8fef5d9a06 | |||
2f1d6e3f90 | |||
53f62bd8dd | |||
e6c87c54be | |||
ee3edc9661 | |||
32dddac75b | |||
906482823c | |||
91c389777a | |||
042a08b90e | |||
7f4c23a038 | |||
e7b0691116 | |||
235058eab1 | |||
85963a5c72 | |||
c639525c1d | |||
de18ebbb73 | |||
47e32c82fc | |||
71b708b0f2 | |||
ab9fee260f | |||
e86e42818a | |||
067e76c1b0 | |||
13b3bb4490 | |||
e82898c988 | |||
f4fdbceb3b | |||
c75ff538a3 | |||
7e71ead3e9 | |||
29489ad198 | |||
eac45256aa | |||
7feb97e415 | |||
695752629d | |||
c3c20ceca4 | |||
aa04820fd1 | |||
3208b7289e | |||
b4336fdec7 | |||
e0652a4607 | |||
4ab46b1de8 | |||
d7122cdad7 | |||
9c3eac58f1 | |||
79954cec68 | |||
d927f8bcb0 | |||
4426cc359c | |||
b1465b7dff | |||
438ea1b269 | |||
f10bfda115 | |||
b7e2d3a10b | |||
846c5fcfc3 | |||
31cd726b7b | |||
3ba43a77f4 | |||
cb5390b2fb | |||
fae7262d31 | |||
9b6abc1e19 | |||
631cd37f8b | |||
b59a38ffad | |||
9a6a993a81 | |||
9234a94da9 | |||
aa2f9e34c0 | |||
87eafa30d0 | |||
287eff50b7 | |||
8514e7962d | |||
9bed2bde3c | |||
9e003a9f93 | |||
86f02691a8 | |||
3ad3988937 | |||
757855a5c9 | |||
ccab73ff66 | |||
9df04724c4 | |||
94c2ba7fc5 | |||
a267fcc9cc | |||
66a292a9c6 | |||
7cd0112211 | |||
9195e754cf | |||
965b183d9e | |||
7372a18cec | |||
c5ba5f3a5b | |||
90251b8545 | |||
a8c97053c7 | |||
117d3a5614 | |||
70a2e48a72 | |||
a6f106ed6d | |||
47dd1b6168 | |||
bc7a963f5c | |||
47fc3b0d0b | |||
3d7b9560cb | |||
9bd3bea90a | |||
9541abc0a4 | |||
0be28c1701 | |||
8bb28ea825 | |||
85382863d2 | |||
ef737415c5 | |||
72e4dabd30 | |||
6bcd547ca0 | |||
283f7a3f37 | |||
d9a2e024ee | |||
fe4e76a7f8 | |||
9c4a7c02fd | |||
a516d6474e | |||
eba6ca5430 | |||
ee7e70b98a | |||
0b7fb21263 | |||
95a38779de | |||
e7913061e9 | |||
7d91bdba1e | |||
29fe221fe6 | |||
30b73ffe6d | |||
fceec5c129 | |||
e42f7ab8fc | |||
54b80d6724 | |||
e82f173f85 | |||
b45dfb85f0 | |||
3db244f5d2 | |||
65a41908ec | |||
7e3bda9d4d | |||
c6b2492e73 | |||
d41afc0c43 | |||
40637e0f28 | |||
0e4c846942 | |||
6dc5d0f8d7 | |||
7688df6fe5 | |||
32b6c77156 | |||
a8a0e2a91c | |||
4c396e5b95 | |||
d3181e53cc | |||
4139b36eac | |||
becc6dc0fc | |||
79a963fcab | |||
b21e011203 | |||
5a6b65d20c | |||
5b44bcb44f | |||
c3bafcab05 | |||
9ae4fce0db | |||
4929a7e635 | |||
b61861f840 | |||
35b5eb74f6 | |||
fc10031ff0 | |||
accf3dbf3f | |||
c2fedf8538 | |||
3b19afe7ed | |||
64ada538f4 | |||
a0c4e79c8a | |||
34249e3dca | |||
f5bd10207b | |||
9cc1511863 | |||
c1d7562331 | |||
542e0f2ed3 | |||
ab33b52f23 | |||
a223c7ac75 | |||
c32c9a2391 | |||
798e1422c6 | |||
e05b46002b | |||
fa6aa44a86 | |||
4f0013e8da | |||
b2e5e14bc6 | |||
e15b469833 | |||
c9b6d72c5a | |||
a279f8d530 | |||
89f0f09b86 | |||
157b85b11b | |||
7b15cc8113 | |||
8d4c332987 | |||
4ffa2450c2 | |||
d670d98e60 | |||
33b2762003 | |||
507b6d45d5 | |||
f331dad72d | |||
5b41827a7b | |||
178b5996a5 | |||
8054078b9e | |||
452adfa860 | |||
833ebd0714 | |||
b430abf172 | |||
939311d2de | |||
0c738e2c6f | |||
48e544c014 | |||
08b186aa24 | |||
2f46efe78d | |||
3ddd5f2a51 | |||
29a58cb030 | |||
0bcbe6ae05 | |||
3944688829 | |||
e126233fbc | |||
ae83b4202d | |||
7cdfe45acf | |||
eae4362181 | |||
f2e9e2cc23 | |||
311ec4eb54 | |||
f4254659ea | |||
fc9e3ee77d | |||
b542f17194 | |||
ddf5cf80b2 | |||
e6cecab0b6 | |||
c3f9e20247 | |||
d0376f82a5 | |||
834fdfe9b3 | |||
8eedcb039a | |||
fc9c479ed5 | |||
12978ba4fb | |||
548efed8d8 | |||
24b012a843 | |||
d493addf95 | |||
d8e502722e | |||
0835c05e89 | |||
429280e416 | |||
879404f7e0 | |||
56c6f41131 | |||
305fc7314f | |||
384d655b1a | |||
2a2d3c0fb5 | |||
977d6eec88 | |||
0f1f33eaa4 | |||
8f690a8f67 | |||
f9f9aa4498 | |||
18a475eff3 | |||
ed259ac94c | |||
bb7884ca3b | |||
0cfbe9c28b | |||
85e5013dbd | |||
bf50695c93 | |||
0764e19441 | |||
2f1d93756d | |||
91de396821 | |||
f9efabba16 | |||
54255cab4a | |||
c03b519849 | |||
98617723a2 | |||
8c8a2deefc | |||
2c84cddda3 | |||
37667247c3 | |||
f5614c8a41 | |||
9e7dd238a4 | |||
495558a949 | |||
bc831d3c35 | |||
d54695e542 | |||
12625a46c2 | |||
ab84a5ce8e | |||
fcbfd7554f | |||
4d27f18710 | |||
1bc4cb382e | |||
6b825fbe25 | |||
884663d077 | |||
bd489cf439 | |||
a4b5c63702 | |||
b1bedb565f | |||
3dd5950bd1 | |||
2cf5221620 | |||
de6edc6a99 | |||
fe9b891b37 | |||
7804475698 | |||
8de5609817 | |||
bb0531053d | |||
e5b1450e83 | |||
0c7cb9d781 | |||
25d5883a0e | |||
c7d815b5b8 | |||
8c4800309e | |||
1aa7fbbba0 | |||
d2fc397295 | |||
a8e43ab552 | |||
3a12182838 | |||
49c7fc30c0 | |||
f6bc9e8707 | |||
53cb80636a | |||
bc086a78eb | |||
e0dccb5970 | |||
a4d16af95d | |||
0b89171abd | |||
f81a24a0cc | |||
58fc3a501d | |||
7b373743aa | |||
e692e18d44 | |||
0c98d1843a | |||
4a9543be78 | |||
fe0d0d6737 | |||
0343e8ffcd | |||
6a6cd14398 | |||
29df534161 | |||
5414d41de4 | |||
2695461bd4 | |||
186f2bc22f | |||
41718b47c1 | |||
bb51bf49b0 | |||
6c365bef85 | |||
e9063a22d5 | |||
47edc3c853 | |||
09d700e1d6 | |||
e5f19c98a8 | |||
0da964e47f | |||
98fda81b79 | |||
d0a969ca33 | |||
91b3889cbc | |||
33f4fac48f | |||
f70adf8da6 | |||
66eae60c48 | |||
5022575429 | |||
b8f22bf3bf | |||
5a8b3eb8f3 | |||
cf02abd19c | |||
ca3b6eb00d | |||
ae2f48f55d | |||
d26ac84126 | |||
82045b3fde | |||
5eda7c30fc | |||
14ce5a2432 | |||
f2bfa2e15c | |||
ee0aada892 | |||
7179a64fee | |||
f3ddf3fa93 | |||
91b8f7c2ae | |||
54f1c0ec66 | |||
1d690f46ae | |||
ca783caff1 | |||
c4fa0d894f | |||
03f16565fe | |||
5785f500ef | |||
8f5257d5dc | |||
c455fa6309 | |||
59b624a4a4 | |||
bfa02f3b82 | |||
60ab94689c | |||
7f33eb4959 | |||
467095f85e | |||
1fc890c6f0 | |||
3733b78ccf | |||
6648e182ae | |||
56473c6b65 | |||
d222c7a998 | |||
84bd8274ad | |||
0d2812db50 | |||
6484005569 | |||
559653f0ab | |||
7a684c160b | |||
7e21afe6a6 | |||
720aa704c4 | |||
532077a4c1 | |||
8bce2fd7a2 | |||
3603cc23ee | |||
f4c3607c4d | |||
06aeff9a30 | |||
78075cb3aa | |||
fbe7e42f46 | |||
312e6071d7 | |||
f3143d8b3d | |||
fd32d77976 | |||
39e8e93bfa | |||
e151ef74e1 | |||
609e70692d | |||
c5ac0981b5 | |||
129fc5b838 | |||
775ab9a7bf | |||
374360c7b4 | |||
cc3165bf72 | |||
6b0a2464dd | |||
7b12f700dd | |||
806e2f88c8 | |||
8591dfe71c | |||
7756e20b86 | |||
39a1958bf4 | |||
f9d8a2d79b | |||
cdab99bd25 | |||
f344c9e0be | |||
7acaa964af | |||
08deabb262 | |||
6504e1f91d | |||
b125276be9 | |||
dc9607024e | |||
06e1305df2 | |||
28a14782a6 | |||
e7bccb2f47 | |||
bdf7dda3b4 | |||
a7d4b3d6ba | |||
a82de3d1cf | |||
a6dc27adaf | |||
69f051da41 | |||
5946c35a88 | |||
3d8cb3b90d | |||
3b9fec1857 | |||
ececf5407d | |||
d236b9b44a | |||
7ec29b0c5a | |||
8d7340500f | |||
1ee2b5e899 | |||
6f948df089 | |||
b6b1491368 | |||
f70be29651 | |||
c48700216c | |||
45a2159290 | |||
ac7ea4ac4c | |||
2a96dde20b | |||
78d5080d78 | |||
395baf0274 | |||
951f082884 | |||
a5ab6f576d | |||
f7f93fda0c | |||
7365ca849f | |||
d75e1deae7 | |||
4aa9c7fdcf | |||
69e6393442 | |||
9d9d4093bc | |||
37f9d3afe1 | |||
82180592f9 | |||
d88cfae80d | |||
6235b49300 | |||
4682bb4147 | |||
6ed17c1a5f | |||
ae0bcc492d | |||
d8298c63ab | |||
9a089b7da0 | |||
e5d76a5a77 | |||
f7170aa00a | |||
c02711ccad | |||
9885779cab | |||
e105ca92f2 | |||
10e2c3832d | |||
c620420a6f | |||
6be54942ec | |||
ab92206b77 | |||
0e2a4227ef | |||
8d891b99d1 |
24
.mtn-ignore
Normal file
24
.mtn-ignore
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Just to try and prevent some noob disasters.
|
||||||
|
# Use mtn add --no-respect-ignore foo.jar to ignore this ignore list
|
||||||
|
_jsp\.java$
|
||||||
|
\.bz2$
|
||||||
|
\.class$
|
||||||
|
\.diff$
|
||||||
|
\.exe$
|
||||||
|
\.fba$
|
||||||
|
\.gz$
|
||||||
|
\.jar$
|
||||||
|
\.out$
|
||||||
|
\.patch$
|
||||||
|
\.sig$
|
||||||
|
\.sud$
|
||||||
|
\.su2$
|
||||||
|
\.tar$
|
||||||
|
\.war$
|
||||||
|
\.zip$
|
||||||
|
^\.
|
||||||
|
^build/
|
||||||
|
^pkg-temp/
|
||||||
|
~$
|
||||||
|
/build/
|
||||||
|
/classes/
|
80
AndroidManifest.xml.in
Normal file
80
AndroidManifest.xml.in
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="net.i2p.android.router"
|
||||||
|
android.versionCode="0"
|
||||||
|
android.versionName="0.0.0-0_b0-API8"
|
||||||
|
android:installLocation="auto"
|
||||||
|
>
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
|
<uses-sdk android:minSdkVersion="8" />
|
||||||
|
|
||||||
|
<application android:label="@string/app_name"
|
||||||
|
android:icon="@drawable/ic_launcher_itoopie" >
|
||||||
|
<service android:name=".service.RouterService"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:icon="@drawable/ic_launcher_itoopie" />
|
||||||
|
<provider android:name=".provider.CacheProvider"
|
||||||
|
android:authorities="net.i2p.android.router" />
|
||||||
|
<activity android:name=".activity.MainActivity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:icon="@drawable/ic_launcher_itoopie"
|
||||||
|
android.theme="@android:style/Theme.NoTitleBar"
|
||||||
|
android:launchMode="singleTop" >
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity android:name=".activity.NewsActivity"
|
||||||
|
android:label="I2P News"
|
||||||
|
android:configChanges="orientation|keyboardHidden"
|
||||||
|
android.theme="@android:style/Theme.NoTitleBar" >
|
||||||
|
</activity>
|
||||||
|
<activity android:name=".activity.TextResourceActivity"
|
||||||
|
android:label="I2P Information"
|
||||||
|
android.theme="@android:style/Theme.NoTitleBar" >
|
||||||
|
</activity>
|
||||||
|
<activity android:name=".activity.LicenseActivity"
|
||||||
|
android:label="I2P License Information"
|
||||||
|
android.theme="@android:style/Theme.NoTitleBar" >
|
||||||
|
</activity>
|
||||||
|
<activity android:name=".activity.WebActivity"
|
||||||
|
android:label="I2P Web Browser"
|
||||||
|
android:configChanges="orientation|keyboardHidden"
|
||||||
|
android.theme="@android:style/Theme.NoTitleBar" >
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:host="*.i2p" android:scheme="http" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity android:name=".activity.SettingsActivity"
|
||||||
|
android:label="I2P Settings"
|
||||||
|
android.theme="@android:style/Theme.NoTitleBar"
|
||||||
|
android:launchMode="singleTop" >
|
||||||
|
</activity>
|
||||||
|
<activity android:name=".activity.AddressbookSettingsActivity"
|
||||||
|
android:label="I2P Addressbook Settings"
|
||||||
|
android.theme="@android:style/Theme.NoTitleBar"
|
||||||
|
android:launchMode="singleTop" >
|
||||||
|
</activity>
|
||||||
|
<activity android:name=".activity.AddressbookActivity"
|
||||||
|
android:label="I2P Address Book"
|
||||||
|
android.theme="@android:style/Theme.NoTitleBar"
|
||||||
|
android:launchMode="singleTop" >
|
||||||
|
</activity>
|
||||||
|
<activity android:name=".activity.LogActivity"
|
||||||
|
android:label="I2P Logs"
|
||||||
|
android.theme="@android:style/Theme.NoTitleBar" >
|
||||||
|
</activity>
|
||||||
|
<activity android:name=".activity.PeersActivity"
|
||||||
|
android:label="I2P Peers and Transport Status"
|
||||||
|
android:configChanges="orientation|keyboardHidden"
|
||||||
|
android.theme="@android:style/Theme.NoTitleBar"
|
||||||
|
android:launchMode="singleTop" >
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
50
LICENSE.txt
Normal file
50
LICENSE.txt
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
This product includes both public domain code and licensed code as described below.
|
||||||
|
For all code, unless otherwise stated in the appropriate license, the following applies:
|
||||||
|
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
|
||||||
|
===================================
|
||||||
|
|
||||||
|
License for the Android App:
|
||||||
|
|
||||||
|
Copyright 2011 The I2P Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use these files except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
===================================
|
||||||
|
|
||||||
|
|
||||||
|
See the file licenses/LICENSE-Apache2.0.txt
|
101
Makefile.gcj
101
Makefile.gcj
@ -1,101 +0,0 @@
|
|||||||
# Makefile for building native I2P binaries and libraries with GCJ
|
|
||||||
#
|
|
||||||
# WARNING: Do not use this yet, as it may explode (etc).
|
|
||||||
#
|
|
||||||
GCJ=gcj #/usr/local/gcc-4.0.2/bin/gcj
|
|
||||||
EXTRA_LD_PATH= #/usr/local/gcc-4.0.2/lib
|
|
||||||
ANT=ant #/opt/apache-ant-1.6.5/bin/ant
|
|
||||||
ANT_TARGET=buildclean
|
|
||||||
NATIVE_DIR=native
|
|
||||||
|
|
||||||
##
|
|
||||||
# Define what jar files get into libi2p.so. The current setup is
|
|
||||||
# *incredibly* lazy, throwing everything in the .so, rather than
|
|
||||||
# give each .jar file its own .so.
|
|
||||||
# i2p.jar: base SDK
|
|
||||||
# mstreaming.jar: streaming API
|
|
||||||
# streaming.jar: full streaming lib implementation
|
|
||||||
# i2ptunnel.jar: I2PTunnel proxy
|
|
||||||
# sam.jar: SAM bridge and API
|
|
||||||
# i2psnark.jar: bittorrent client
|
|
||||||
# router.jar: full I2P router
|
|
||||||
# jbigi.jar: collection of native optimized GMP routines for crypto
|
|
||||||
JAR_BASE=i2p.jar mstreaming.jar streaming.jar
|
|
||||||
JAR_CLIENTS=i2ptunnel.jar sam.jar
|
|
||||||
JAR_ROUTER=router.jar
|
|
||||||
JAR_JBIGI=jbigi.jar
|
|
||||||
JAR_XML=xml-apis.jar resolver.jar xercesImpl.jar
|
|
||||||
JAR_CONSOLE=\
|
|
||||||
i2psnark.jar \
|
|
||||||
javax.servlet.jar \
|
|
||||||
commons-el.jar \
|
|
||||||
commons-logging.jar \
|
|
||||||
jasper-runtime.jar \
|
|
||||||
ant-apache-bcel.jar \
|
|
||||||
ant.jar \
|
|
||||||
jasper-compiler.jar \
|
|
||||||
org.mortbay.jetty.jar \
|
|
||||||
routerconsole.jar
|
|
||||||
JAR_SUCKER=jdom.jar rome-0.7.jar sucker.jar
|
|
||||||
LIBI2P_JARS=${JAR_BASE} ${JAR_CLIENTS} ${JAR_ROUTER} ${JAR_JBIGI}
|
|
||||||
# unfortunately, its not quite ready for most end users, as the
|
|
||||||
# ${JAR_CONSOLE} fails to compile with:
|
|
||||||
# org/apache/commons/logging/impl/LogKitLogger.java: In class 'org.apache.commons.logging.impl.LogKitLogger':
|
|
||||||
# .../LogKitLogger.java: In constructor '(java.lang.String)':
|
|
||||||
# .../LogKitLogger.java:91: error: cannot find file for class org.apache.log.Hierarchy
|
|
||||||
# .../LogKitLogger.java:91: error: cannot find file for class org.apache.log.Hierarchy
|
|
||||||
# .../LogKitLogger.java:104: error: cannot find file for class org.apache.log.Hierarchy
|
|
||||||
# .../LogKitLogger.java:104: confused by earlier errors, bailing out
|
|
||||||
|
|
||||||
#${JAR_CONSOLE}\
|
|
||||||
#${JAR_XML} \
|
|
||||||
#${JAR_SUCKER}
|
|
||||||
#${JAR_CONSOLE}
|
|
||||||
|
|
||||||
SYSTEM_PROPS=-DloggerFilenameOverride=logs/log-router-@.txt \
|
|
||||||
-Dorg.mortbay.http.Version.paranoid=true \
|
|
||||||
-Dorg.mortbay.util.FileResource.checkAliases=false \
|
|
||||||
-Dorg.mortbay.xml.XmlParser.NotValidating=true
|
|
||||||
#SYSTEM_PROPS=-Di2p.weakPRNG=true
|
|
||||||
OPTIMIZE=-O2
|
|
||||||
#OPTIMIZE=-O3
|
|
||||||
|
|
||||||
LD_LIBRARY_PATH=${EXTRA_LD_PATH}:.
|
|
||||||
|
|
||||||
all: jars native
|
|
||||||
@echo "* Build complete"
|
|
||||||
|
|
||||||
jars:
|
|
||||||
@${ANT} ${ANT_TARGET}
|
|
||||||
|
|
||||||
clean: native_clean
|
|
||||||
|
|
||||||
native: native_clean native_shared
|
|
||||||
@echo "* Native code build in ${NATIVE}"
|
|
||||||
|
|
||||||
native_clean:
|
|
||||||
@rm -rf ${NATIVE_DIR}
|
|
||||||
@mkdir ${NATIVE_DIR}
|
|
||||||
|
|
||||||
native_shared: libi2p.so
|
|
||||||
@cd build ; ${GCJ} ${OPTIMIZE} -fjni -L../${NATIVE_DIR} -li2p ${SYSTEM_PROPS} -o ../${NATIVE_DIR}/i2p_dsa --main=net.i2p.crypto.DSAEngine
|
|
||||||
@echo "* i2p_dsa is a simple test app with the DSA engine and Fortuna PRNG to make sure crypto is working"
|
|
||||||
@cd build ; ${GCJ} ${OPTIMIZE} -fjni -L../${NATIVE_DIR} -li2p ${SYSTEM_PROPS} -o ../${NATIVE_DIR}/prng --main=gnu.crypto.prng.FortunaStandalone
|
|
||||||
@cd build ; ${GCJ} ${OPTIMIZE} -fjni -L../${NATIVE_DIR} -li2p ${SYSTEM_PROPS} -o ../${NATIVE_DIR}/i2ptunnel --main=net.i2p.i2ptunnel.I2PTunnel
|
|
||||||
@echo "* i2ptunnel is mihi's I2PTunnel CLI"
|
|
||||||
@echo " run it as ./i2ptunnel -cli to avoid awt complaints"
|
|
||||||
@cd build ; ${GCJ} ${OPTIMIZE} -fjni -L../${NATIVE_DIR} -li2p ${SYSTEM_PROPS} -o ../${NATIVE_DIR}/i2ptunnelctl --main=net.i2p.i2ptunnel.TunnelControllerGroup
|
|
||||||
@echo "* i2ptunnelctl is a controller for I2PTunnel, reading i2ptunnel.config"
|
|
||||||
@echo " and launching the appropriate proxies"
|
|
||||||
#@cd build ; ${GCJ} ${OPTIMIZE} -fjni -L../${NATIVE_DIR} -li2p ${SYSTEM_PROPS} -o ../${NATIVE_DIR}/i2psnark --main=org.klomp.snark.Snark
|
|
||||||
#@echo "* i2psnark is an anonymous bittorrent client"
|
|
||||||
@cd build ; ${GCJ} ${OPTIMIZE} -fjni -L../${NATIVE_DIR} -li2p ${SYSTEM_PROPS} -o ../${NATIVE_DIR}/i2prouter --main=net.i2p.router.Router
|
|
||||||
@echo "* i2prouter is the main I2P router"
|
|
||||||
@echo " it can be used, and while the router console won't load,"
|
|
||||||
@echo " i2ptunnel will, so it will start all the proxies defined in i2ptunnel.config"
|
|
||||||
|
|
||||||
libi2p.so:
|
|
||||||
@echo "* Building libi2p.so"
|
|
||||||
@(cd build ; time ${GCJ} ${OPTIMIZE} -fPIC -fjni -shared -o ../${NATIVE_DIR}/libi2p.so ${LIBI2P_JARS} ; cd .. )
|
|
||||||
@ls -l ${NATIVE_DIR}/libi2p.so
|
|
||||||
@echo "* libi2p.so built"
|
|
102
README.txt
Normal file
102
README.txt
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
These instructions are for a recent Android SDK (Rev 20 or better) on Linux.
|
||||||
|
Windows building is not currently supported.
|
||||||
|
|
||||||
|
These instructions were last updated for SDK Tools Version 20 with
|
||||||
|
SDK Platform-tools Version 12 from updates.
|
||||||
|
|
||||||
|
The i2p source must be installed in ../i2p.i2p,
|
||||||
|
or else add i2psrc=/path/to/source in the local.properties file.
|
||||||
|
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
|
||||||
|
- Java SDK (preferably Oracle/Sun or OpenJDK) 1.6.0 or higher
|
||||||
|
- Apache Ant 1.8.0 or higher
|
||||||
|
- I2P source in ../i2p.i2p
|
||||||
|
- Android SDK (tested with Rev 20 and platform-tools version 12)
|
||||||
|
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Instructions:
|
||||||
|
|
||||||
|
# Download the SDK from http://developer.android.com/sdk/index.html
|
||||||
|
# Unzip the android SDK in ../
|
||||||
|
# So then the android tools will be in ../android-sdk-linux/tools/
|
||||||
|
#
|
||||||
|
# Run the GUI updater, which you must do to get an SDK Platform:
|
||||||
|
../android-sdk-linux/tools/android &
|
||||||
|
|
||||||
|
# now go to the available packages tab, check the box and click refresh,
|
||||||
|
# and download an SDK Platform
|
||||||
|
# Since I2P is configured to run on 2.2 or higher
|
||||||
|
# (API 8) download at least that one. Otherwise you must change the
|
||||||
|
# target in project.properties from android-8 to andriod-x
|
||||||
|
# where x is the API version.
|
||||||
|
|
||||||
|
# To run the debugger (ddms) you also need to download the
|
||||||
|
# "Android SDK Platform-Tools" package from the GUI updater.
|
||||||
|
|
||||||
|
# create a file local.properties with the following line (without the leading # of course),
|
||||||
|
# do NOT use a relative path
|
||||||
|
# sdk.dir=/path/to/your/android-sdk-linux
|
||||||
|
# Copy this file to the routerjars/ directory, it is needed in both places.
|
||||||
|
|
||||||
|
# DO NOT create a new project or anything. It's all set up right here for you.
|
||||||
|
|
||||||
|
# Create the android 2.2 (API 8) virtual device
|
||||||
|
# (don't make a custom hardware profile)
|
||||||
|
../android-sdk-linux/tools/android create avd --name i2p --target 8
|
||||||
|
|
||||||
|
# then run the emulator:
|
||||||
|
# This may take a LONG time the first time (half an hour or more)...
|
||||||
|
# Run the debugger to ensure it is making progress
|
||||||
|
# -no-boot-anim for faster boot
|
||||||
|
# -dns-server 8.8.8.8 if the router can't reseed
|
||||||
|
# ../android-sdk-linux/tools/emulator -avd i2p -no-boot-anim -dns-server 8.8.8.8 &
|
||||||
|
../android-sdk-linux/tools/emulator -avd i2p &
|
||||||
|
|
||||||
|
# or to talk to a real device in debug mode:
|
||||||
|
# You have to do this if you get a permission error -
|
||||||
|
# Stop ddms, unplug the device, do the following,
|
||||||
|
# then plug in the device, then start ddms
|
||||||
|
adb kill-server
|
||||||
|
sudo adb start-server
|
||||||
|
adb devices
|
||||||
|
|
||||||
|
# then wait a couple minutes until the emulator or device is up
|
||||||
|
# compile and install for a release
|
||||||
|
ant release
|
||||||
|
ant installr
|
||||||
|
|
||||||
|
# or compile and install for a debug version
|
||||||
|
ant debug
|
||||||
|
ant installd
|
||||||
|
|
||||||
|
# then run the debugger
|
||||||
|
../android-sdk-linux/tools/ddms &
|
||||||
|
|
||||||
|
# to rebuild and reinstall to emulator or device:
|
||||||
|
ant clean
|
||||||
|
# then do which ever from the above compile and install choices.
|
||||||
|
|
||||||
|
|
||||||
|
# to uninstall
|
||||||
|
ant uninstall
|
||||||
|
# or use your device's menu.
|
||||||
|
|
||||||
|
# Other ant tagets are available, just type
|
||||||
|
ant
|
||||||
|
|
||||||
|
# Anyway, with I2P installed, click on the I2P icon on your device and enjoy!
|
||||||
|
|
||||||
|
#other helpful commands
|
||||||
|
../android-sdk-linux/platform-tools/adb shell
|
||||||
|
../android-sdk-linux/platform-tools/adb pull /some/file/on/emulator some-local-dir/
|
||||||
|
|
||||||
|
# copy the Dev Tools app from the emulator to your device
|
||||||
|
adb -e pull /system/app/Development.apk ./Development.apk
|
||||||
|
adb -d install Development.apk
|
||||||
|
|
||||||
|
# reinstall an existing apk onto the emulator
|
||||||
|
adb -e install -r bin/I2PAndroid-debug.apk
|
5
ant.properties
Normal file
5
ant.properties
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
application-package=net.i2p.router
|
||||||
|
key.store=${user.home}/.android/${application-package}.keystore
|
||||||
|
key.alias=${application-package}
|
||||||
|
android.library.reference.1=./routerjars
|
||||||
|
key.store.password=android
|
@ -1,69 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- You may freely edit this file. See commented blocks below for -->
|
|
||||||
<!-- some examples of how to customize the build. -->
|
|
||||||
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
|
||||||
<project name="echoclient" default="default" basedir=".">
|
|
||||||
<description>Builds, tests, and runs the project echoclient.</description>
|
|
||||||
<import file="nbproject/build-impl.xml"/>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
There exist several targets which are by default empty and which can be
|
|
||||||
used for execution of your tasks. These targets are usually executed
|
|
||||||
before and after some main targets. They are:
|
|
||||||
|
|
||||||
-pre-init: called before initialization of project properties
|
|
||||||
-post-init: called after initialization of project properties
|
|
||||||
-pre-compile: called before javac compilation
|
|
||||||
-post-compile: called after javac compilation
|
|
||||||
-pre-compile-single: called before javac compilation of single file
|
|
||||||
-post-compile-single: called after javac compilation of single file
|
|
||||||
-pre-compile-test: called before javac compilation of JUnit tests
|
|
||||||
-post-compile-test: called after javac compilation of JUnit tests
|
|
||||||
-pre-compile-test-single: called before javac compilation of single JUnit test
|
|
||||||
-post-compile-test-single: called after javac compilation of single JUunit test
|
|
||||||
-pre-jar: called before JAR building
|
|
||||||
-post-jar: called after JAR building
|
|
||||||
-post-clean: called after cleaning build products
|
|
||||||
|
|
||||||
(Targets beginning with '-' are not intended to be called on their own.)
|
|
||||||
|
|
||||||
Example of inserting an obfuscator after compilation could look like this:
|
|
||||||
|
|
||||||
<target name="-post-compile">
|
|
||||||
<obfuscate>
|
|
||||||
<fileset dir="${build.classes.dir}"/>
|
|
||||||
</obfuscate>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
For list of available properties check the imported
|
|
||||||
nbproject/build-impl.xml file.
|
|
||||||
|
|
||||||
|
|
||||||
Another way to customize the build is by overriding existing main targets.
|
|
||||||
The targets of interest are:
|
|
||||||
|
|
||||||
-init-macrodef-javac: defines macro for javac compilation
|
|
||||||
-init-macrodef-junit: defines macro for junit execution
|
|
||||||
-init-macrodef-debug: defines macro for class debugging
|
|
||||||
-init-macrodef-java: defines macro for class execution
|
|
||||||
-do-jar-with-manifest: JAR building (if you are using a manifest)
|
|
||||||
-do-jar-without-manifest: JAR building (if you are not using a manifest)
|
|
||||||
run: execution of project
|
|
||||||
-javadoc-build: Javadoc generation
|
|
||||||
test-report: JUnit report generation
|
|
||||||
|
|
||||||
An example of overriding the target for project execution could look like this:
|
|
||||||
|
|
||||||
<target name="run" depends="echoclient-impl.jar">
|
|
||||||
<exec dir="bin" executable="launcher.exe">
|
|
||||||
<arg file="${dist.jar}"/>
|
|
||||||
</exec>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
Notice that the overridden target depends on the jar target and not only on
|
|
||||||
the compile target as the regular run target does. Again, for a list of available
|
|
||||||
properties which you can use, check the target you are overriding in the
|
|
||||||
nbproject/build-impl.xml file.
|
|
||||||
|
|
||||||
-->
|
|
||||||
</project>
|
|
@ -1,5 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
(
|
|
||||||
cd dist
|
|
||||||
java -jar echoclient.jar main 37338 testclient $1
|
|
||||||
)
|
|
@ -1,3 +0,0 @@
|
|||||||
Manifest-Version: 1.0
|
|
||||||
X-COMMENT: Main-Class will be added automatically by build
|
|
||||||
|
|
@ -1,629 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
*** GENERATED FROM project.xml - DO NOT EDIT ***
|
|
||||||
*** EDIT ../build.xml INSTEAD ***
|
|
||||||
|
|
||||||
For the purpose of easier reading the script
|
|
||||||
is divided into following sections:
|
|
||||||
|
|
||||||
- initialization
|
|
||||||
- compilation
|
|
||||||
- jar
|
|
||||||
- execution
|
|
||||||
- debugging
|
|
||||||
- javadoc
|
|
||||||
- junit compilation
|
|
||||||
- junit execution
|
|
||||||
- junit debugging
|
|
||||||
- applet
|
|
||||||
- cleanup
|
|
||||||
|
|
||||||
-->
|
|
||||||
<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="echoclient-impl">
|
|
||||||
<target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
|
|
||||||
<!--
|
|
||||||
======================
|
|
||||||
INITIALIZATION SECTION
|
|
||||||
======================
|
|
||||||
-->
|
|
||||||
<target name="-pre-init">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init" name="-init-private">
|
|
||||||
<property file="nbproject/private/config.properties"/>
|
|
||||||
<property file="nbproject/private/configs/${config}.properties"/>
|
|
||||||
<property file="nbproject/private/private.properties"/>
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private" name="-init-user">
|
|
||||||
<property file="${user.properties.file}"/>
|
|
||||||
<!-- The two properties below are usually overridden -->
|
|
||||||
<!-- by the active platform. Just a fallback. -->
|
|
||||||
<property name="default.javac.source" value="1.4"/>
|
|
||||||
<property name="default.javac.target" value="1.4"/>
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private,-init-user" name="-init-project">
|
|
||||||
<property file="nbproject/configs/${config}.properties"/>
|
|
||||||
<property file="nbproject/project.properties"/>
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
|
|
||||||
<available file="${manifest.file}" property="manifest.available"/>
|
|
||||||
<condition property="manifest.available+main.class">
|
|
||||||
<and>
|
|
||||||
<isset property="manifest.available"/>
|
|
||||||
<isset property="main.class"/>
|
|
||||||
<not>
|
|
||||||
<equals arg1="${main.class}" arg2="" trim="true"/>
|
|
||||||
</not>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<condition property="manifest.available+main.class+mkdist.available">
|
|
||||||
<and>
|
|
||||||
<istrue value="${manifest.available+main.class}"/>
|
|
||||||
<isset property="libs.CopyLibs.classpath"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<condition property="have.tests">
|
|
||||||
<or>
|
|
||||||
<available file="${test.src.dir}"/>
|
|
||||||
</or>
|
|
||||||
</condition>
|
|
||||||
<condition property="have.sources">
|
|
||||||
<or>
|
|
||||||
<available file="${src.dir}"/>
|
|
||||||
</or>
|
|
||||||
</condition>
|
|
||||||
<condition property="netbeans.home+have.tests">
|
|
||||||
<and>
|
|
||||||
<isset property="netbeans.home"/>
|
|
||||||
<isset property="have.tests"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<condition property="no.javadoc.preview">
|
|
||||||
<and>
|
|
||||||
<isset property="javadoc.preview"/>
|
|
||||||
<isfalse value="${javadoc.preview}"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<property name="run.jvmargs" value=""/>
|
|
||||||
<property name="javac.compilerargs" value=""/>
|
|
||||||
<property name="work.dir" value="${basedir}"/>
|
|
||||||
<condition property="no.deps">
|
|
||||||
<and>
|
|
||||||
<istrue value="${no.dependencies}"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<property name="javac.debug" value="true"/>
|
|
||||||
<property name="javadoc.preview" value="true"/>
|
|
||||||
<property name="application.args" value=""/>
|
|
||||||
<property name="source.encoding" value="${file.encoding}"/>
|
|
||||||
<condition property="javadoc.encoding.used" value="${javadoc.encoding}">
|
|
||||||
<and>
|
|
||||||
<isset property="javadoc.encoding"/>
|
|
||||||
<not>
|
|
||||||
<equals arg1="${javadoc.encoding}" arg2=""/>
|
|
||||||
</not>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<property name="javadoc.encoding.used" value="${source.encoding}"/>
|
|
||||||
<property name="includes" value="**"/>
|
|
||||||
<property name="excludes" value=""/>
|
|
||||||
<property name="do.depend" value="false"/>
|
|
||||||
<condition property="do.depend.true">
|
|
||||||
<istrue value="${do.depend}"/>
|
|
||||||
</condition>
|
|
||||||
<condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
|
|
||||||
<and>
|
|
||||||
<isset property="jaxws.endorsed.dir"/>
|
|
||||||
<available file="nbproject/jaxws-build.xml"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
</target>
|
|
||||||
<target name="-post-init">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
|
|
||||||
<fail unless="src.dir">Must set src.dir</fail>
|
|
||||||
<fail unless="test.src.dir">Must set test.src.dir</fail>
|
|
||||||
<fail unless="build.dir">Must set build.dir</fail>
|
|
||||||
<fail unless="dist.dir">Must set dist.dir</fail>
|
|
||||||
<fail unless="build.classes.dir">Must set build.classes.dir</fail>
|
|
||||||
<fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
|
|
||||||
<fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
|
|
||||||
<fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
|
|
||||||
<fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
|
|
||||||
<fail unless="dist.jar">Must set dist.jar</fail>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-property">
|
|
||||||
<macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<attribute name="name"/>
|
|
||||||
<attribute name="value"/>
|
|
||||||
<sequential>
|
|
||||||
<property name="@{name}" value="${@{value}}"/>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-javac">
|
|
||||||
<macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${src.dir}" name="srcdir"/>
|
|
||||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
|
||||||
<attribute default="${javac.classpath}" name="classpath"/>
|
|
||||||
<attribute default="${includes}" name="includes"/>
|
|
||||||
<attribute default="${excludes}" name="excludes"/>
|
|
||||||
<attribute default="${javac.debug}" name="debug"/>
|
|
||||||
<attribute default="" name="sourcepath"/>
|
|
||||||
<element name="customize" optional="true"/>
|
|
||||||
<sequential>
|
|
||||||
<javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
|
|
||||||
<classpath>
|
|
||||||
<path path="@{classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
|
|
||||||
<customize/>
|
|
||||||
</javac>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
<macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${src.dir}" name="srcdir"/>
|
|
||||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
|
||||||
<attribute default="${javac.classpath}" name="classpath"/>
|
|
||||||
<sequential>
|
|
||||||
<depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
|
|
||||||
<classpath>
|
|
||||||
<path path="@{classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
</depend>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
<macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
|
||||||
<sequential>
|
|
||||||
<fail unless="javac.includes">Must set javac.includes</fail>
|
|
||||||
<pathconvert pathsep="," property="javac.includes.binary">
|
|
||||||
<path>
|
|
||||||
<filelist dir="@{destdir}" files="${javac.includes}"/>
|
|
||||||
</path>
|
|
||||||
<globmapper from="*.java" to="*.class"/>
|
|
||||||
</pathconvert>
|
|
||||||
<delete>
|
|
||||||
<files includes="${javac.includes.binary}"/>
|
|
||||||
</delete>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-junit">
|
|
||||||
<macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${includes}" name="includes"/>
|
|
||||||
<attribute default="${excludes}" name="excludes"/>
|
|
||||||
<attribute default="**" name="testincludes"/>
|
|
||||||
<sequential>
|
|
||||||
<junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
|
|
||||||
<batchtest todir="${build.test.results.dir}">
|
|
||||||
<fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
|
|
||||||
<filename name="@{testincludes}"/>
|
|
||||||
</fileset>
|
|
||||||
</batchtest>
|
|
||||||
<classpath>
|
|
||||||
<path path="${run.test.classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<syspropertyset>
|
|
||||||
<propertyref prefix="test-sys-prop."/>
|
|
||||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
|
||||||
</syspropertyset>
|
|
||||||
<formatter type="brief" usefile="false"/>
|
|
||||||
<formatter type="xml"/>
|
|
||||||
<jvmarg line="${run.jvmargs}"/>
|
|
||||||
</junit>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-nbjpda">
|
|
||||||
<macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<attribute default="${main.class}" name="name"/>
|
|
||||||
<attribute default="${debug.classpath}" name="classpath"/>
|
|
||||||
<attribute default="" name="stopclassname"/>
|
|
||||||
<sequential>
|
|
||||||
<nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="dt_socket">
|
|
||||||
<classpath>
|
|
||||||
<path path="@{classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
</nbjpdastart>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
<macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<attribute default="${build.classes.dir}" name="dir"/>
|
|
||||||
<sequential>
|
|
||||||
<nbjpdareload>
|
|
||||||
<fileset dir="@{dir}" includes="${fix.classes}">
|
|
||||||
<include name="${fix.includes}*.class"/>
|
|
||||||
</fileset>
|
|
||||||
</nbjpdareload>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-debug-args">
|
|
||||||
<property name="version-output" value="java version "${ant.java.version}"/>
|
|
||||||
<condition property="have-jdk-older-than-1.4">
|
|
||||||
<or>
|
|
||||||
<contains string="${version-output}" substring="java version "1.0"/>
|
|
||||||
<contains string="${version-output}" substring="java version "1.1"/>
|
|
||||||
<contains string="${version-output}" substring="java version "1.2"/>
|
|
||||||
<contains string="${version-output}" substring="java version "1.3"/>
|
|
||||||
</or>
|
|
||||||
</condition>
|
|
||||||
<condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
|
|
||||||
<istrue value="${have-jdk-older-than-1.4}"/>
|
|
||||||
</condition>
|
|
||||||
</target>
|
|
||||||
<target depends="-init-debug-args" name="-init-macrodef-debug">
|
|
||||||
<macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${main.class}" name="classname"/>
|
|
||||||
<attribute default="${debug.classpath}" name="classpath"/>
|
|
||||||
<element name="customize" optional="true"/>
|
|
||||||
<sequential>
|
|
||||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
|
||||||
<jvmarg line="${debug-args-line}"/>
|
|
||||||
<jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
|
|
||||||
<jvmarg line="${run.jvmargs}"/>
|
|
||||||
<classpath>
|
|
||||||
<path path="@{classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<syspropertyset>
|
|
||||||
<propertyref prefix="run-sys-prop."/>
|
|
||||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
|
||||||
</syspropertyset>
|
|
||||||
<customize/>
|
|
||||||
</java>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-java">
|
|
||||||
<macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<attribute default="${main.class}" name="classname"/>
|
|
||||||
<element name="customize" optional="true"/>
|
|
||||||
<sequential>
|
|
||||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
|
||||||
<jvmarg line="${run.jvmargs}"/>
|
|
||||||
<classpath>
|
|
||||||
<path path="${run.classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<syspropertyset>
|
|
||||||
<propertyref prefix="run-sys-prop."/>
|
|
||||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
|
||||||
</syspropertyset>
|
|
||||||
<customize/>
|
|
||||||
</java>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-presetdef-jar">
|
|
||||||
<presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<jar compress="${jar.compress}" jarfile="${dist.jar}">
|
|
||||||
<j2seproject1:fileset dir="${build.classes.dir}"/>
|
|
||||||
</jar>
|
|
||||||
</presetdef>
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
|
|
||||||
<!--
|
|
||||||
===================
|
|
||||||
COMPILATION SECTION
|
|
||||||
===================
|
|
||||||
-->
|
|
||||||
<target depends="init" name="deps-jar" unless="no.deps"/>
|
|
||||||
<target depends="init,deps-jar" name="-pre-pre-compile">
|
|
||||||
<mkdir dir="${build.classes.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-pre-compile">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target if="do.depend.true" name="-compile-depend">
|
|
||||||
<j2seproject3:depend/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
|
|
||||||
<j2seproject3:javac/>
|
|
||||||
<copy todir="${build.classes.dir}">
|
|
||||||
<fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
|
||||||
</copy>
|
|
||||||
</target>
|
|
||||||
<target name="-post-compile">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
|
|
||||||
<target name="-pre-compile-single">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
|
|
||||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
|
||||||
<j2seproject3:force-recompile/>
|
|
||||||
<j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-post-compile-single">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
|
|
||||||
<!--
|
|
||||||
====================
|
|
||||||
JAR BUILDING SECTION
|
|
||||||
====================
|
|
||||||
-->
|
|
||||||
<target depends="init" name="-pre-pre-jar">
|
|
||||||
<dirname file="${dist.jar}" property="dist.jar.dir"/>
|
|
||||||
<mkdir dir="${dist.jar.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-pre-jar">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
|
|
||||||
<j2seproject1:jar/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
|
|
||||||
<j2seproject1:jar manifest="${manifest.file}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
|
|
||||||
<j2seproject1:jar manifest="${manifest.file}">
|
|
||||||
<j2seproject1:manifest>
|
|
||||||
<j2seproject1:attribute name="Main-Class" value="${main.class}"/>
|
|
||||||
</j2seproject1:manifest>
|
|
||||||
</j2seproject1:jar>
|
|
||||||
<echo>To run this application from the command line without Ant, try:</echo>
|
|
||||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
|
||||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
|
||||||
<pathconvert property="run.classpath.with.dist.jar">
|
|
||||||
<path path="${run.classpath}"/>
|
|
||||||
<map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
|
|
||||||
</pathconvert>
|
|
||||||
<echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
|
|
||||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
|
||||||
<pathconvert property="run.classpath.without.build.classes.dir">
|
|
||||||
<path path="${run.classpath}"/>
|
|
||||||
<map from="${build.classes.dir.resolved}" to=""/>
|
|
||||||
</pathconvert>
|
|
||||||
<pathconvert pathsep=" " property="jar.classpath">
|
|
||||||
<path path="${run.classpath.without.build.classes.dir}"/>
|
|
||||||
<chainedmapper>
|
|
||||||
<flattenmapper/>
|
|
||||||
<globmapper from="*" to="lib/*"/>
|
|
||||||
</chainedmapper>
|
|
||||||
</pathconvert>
|
|
||||||
<taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
|
|
||||||
<copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
|
|
||||||
<fileset dir="${build.classes.dir}"/>
|
|
||||||
<manifest>
|
|
||||||
<attribute name="Main-Class" value="${main.class}"/>
|
|
||||||
<attribute name="Class-Path" value="${jar.classpath}"/>
|
|
||||||
</manifest>
|
|
||||||
</copylibs>
|
|
||||||
<echo>To run this application from the command line without Ant, try:</echo>
|
|
||||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
|
||||||
<echo>java -jar "${dist.jar.resolved}"</echo>
|
|
||||||
</target>
|
|
||||||
<target name="-post-jar">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
|
|
||||||
<!--
|
|
||||||
=================
|
|
||||||
EXECUTION SECTION
|
|
||||||
=================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile" description="Run a main class." name="run">
|
|
||||||
<j2seproject1:java>
|
|
||||||
<customize>
|
|
||||||
<arg line="${application.args}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject1:java>
|
|
||||||
</target>
|
|
||||||
<target name="-do-not-recompile">
|
|
||||||
<property name="javac.includes.binary" value=""/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-do-not-recompile,compile-single" name="run-single">
|
|
||||||
<fail unless="run.class">Must select one file in the IDE or set run.class</fail>
|
|
||||||
<j2seproject1:java classname="${run.class}"/>
|
|
||||||
</target>
|
|
||||||
<!--
|
|
||||||
=================
|
|
||||||
DEBUGGING SECTION
|
|
||||||
=================
|
|
||||||
-->
|
|
||||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger">
|
|
||||||
<j2seproject1:nbjpdastart name="${debug.class}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile" name="-debug-start-debuggee">
|
|
||||||
<j2seproject3:debug>
|
|
||||||
<customize>
|
|
||||||
<arg line="${application.args}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject3:debug>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
|
|
||||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
|
|
||||||
<j2seproject1:nbjpdastart stopclassname="${main.class}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
|
|
||||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
|
|
||||||
<fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
|
|
||||||
<j2seproject3:debug classname="${debug.class}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
|
|
||||||
<target depends="init" name="-pre-debug-fix">
|
|
||||||
<fail unless="fix.includes">Must set fix.includes</fail>
|
|
||||||
<property name="javac.includes" value="${fix.includes}.java"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
|
|
||||||
<j2seproject1:nbjpdareload/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
|
|
||||||
<!--
|
|
||||||
===============
|
|
||||||
JAVADOC SECTION
|
|
||||||
===============
|
|
||||||
-->
|
|
||||||
<target depends="init" name="-javadoc-build">
|
|
||||||
<mkdir dir="${dist.javadoc.dir}"/>
|
|
||||||
<javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
|
|
||||||
<classpath>
|
|
||||||
<path path="${javac.classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
|
|
||||||
<filename name="**/*.java"/>
|
|
||||||
</fileset>
|
|
||||||
</javadoc>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
|
|
||||||
<nbbrowse file="${dist.javadoc.dir}/index.html"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
|
|
||||||
<!--
|
|
||||||
=========================
|
|
||||||
JUNIT COMPILATION SECTION
|
|
||||||
=========================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
|
|
||||||
<mkdir dir="${build.test.classes.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-pre-compile-test">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target if="do.depend.true" name="-compile-test-depend">
|
|
||||||
<j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
|
|
||||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
|
||||||
<copy todir="${build.test.classes.dir}">
|
|
||||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
|
||||||
</copy>
|
|
||||||
</target>
|
|
||||||
<target name="-post-compile-test">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
|
|
||||||
<target name="-pre-compile-test-single">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
|
|
||||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
|
||||||
<j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
|
|
||||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
|
|
||||||
<copy todir="${build.test.classes.dir}">
|
|
||||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
|
||||||
</copy>
|
|
||||||
</target>
|
|
||||||
<target name="-post-compile-test-single">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
|
|
||||||
<!--
|
|
||||||
=======================
|
|
||||||
JUNIT EXECUTION SECTION
|
|
||||||
=======================
|
|
||||||
-->
|
|
||||||
<target depends="init" if="have.tests" name="-pre-test-run">
|
|
||||||
<mkdir dir="${build.test.results.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
|
|
||||||
<j2seproject3:junit testincludes="**/*Test.java"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
|
|
||||||
<fail if="tests.failed">Some tests failed; see details above.</fail>
|
|
||||||
</target>
|
|
||||||
<target depends="init" if="have.tests" name="test-report"/>
|
|
||||||
<target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
|
|
||||||
<target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
|
|
||||||
<target depends="init" if="have.tests" name="-pre-test-run-single">
|
|
||||||
<mkdir dir="${build.test.results.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
|
|
||||||
<fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
|
|
||||||
<j2seproject3:junit excludes="" includes="${test.includes}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
|
|
||||||
<fail if="tests.failed">Some tests failed; see details above.</fail>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
|
|
||||||
<!--
|
|
||||||
=======================
|
|
||||||
JUNIT DEBUGGING SECTION
|
|
||||||
=======================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
|
|
||||||
<fail unless="test.class">Must select one file in the IDE or set test.class</fail>
|
|
||||||
<property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
|
|
||||||
<delete file="${test.report.file}"/>
|
|
||||||
<mkdir dir="${build.test.results.dir}"/>
|
|
||||||
<j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
|
|
||||||
<customize>
|
|
||||||
<syspropertyset>
|
|
||||||
<propertyref prefix="test-sys-prop."/>
|
|
||||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
|
||||||
</syspropertyset>
|
|
||||||
<arg value="${test.class}"/>
|
|
||||||
<arg value="showoutput=true"/>
|
|
||||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
|
|
||||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject3:debug>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
|
|
||||||
<j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
|
|
||||||
<target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
|
|
||||||
<j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
|
|
||||||
<!--
|
|
||||||
=========================
|
|
||||||
APPLET EXECUTION SECTION
|
|
||||||
=========================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile-single" name="run-applet">
|
|
||||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
|
||||||
<j2seproject1:java classname="sun.applet.AppletViewer">
|
|
||||||
<customize>
|
|
||||||
<arg value="${applet.url}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject1:java>
|
|
||||||
</target>
|
|
||||||
<!--
|
|
||||||
=========================
|
|
||||||
APPLET DEBUGGING SECTION
|
|
||||||
=========================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
|
|
||||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
|
||||||
<j2seproject3:debug classname="sun.applet.AppletViewer">
|
|
||||||
<customize>
|
|
||||||
<arg value="${applet.url}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject3:debug>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
|
|
||||||
<!--
|
|
||||||
===============
|
|
||||||
CLEANUP SECTION
|
|
||||||
===============
|
|
||||||
-->
|
|
||||||
<target depends="init" name="deps-clean" unless="no.deps"/>
|
|
||||||
<target depends="init" name="-do-clean">
|
|
||||||
<delete dir="${build.dir}"/>
|
|
||||||
<delete dir="${dist.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-post-clean">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
|
|
||||||
</project>
|
|
@ -1,8 +0,0 @@
|
|||||||
build.xml.data.CRC32=8ce3cee9
|
|
||||||
build.xml.script.CRC32=d1de2df3
|
|
||||||
build.xml.stylesheet.CRC32=be360661
|
|
||||||
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
|
|
||||||
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
|
|
||||||
nbproject/build-impl.xml.data.CRC32=8ce3cee9
|
|
||||||
nbproject/build-impl.xml.script.CRC32=22d1fbbb
|
|
||||||
nbproject/build-impl.xml.stylesheet.CRC32=487672f9
|
|
@ -1,2 +0,0 @@
|
|||||||
jaxws.endorsed.dir=/usr/local/netbeans-6.1/java2/modules/ext/jaxws21/api
|
|
||||||
user.properties.file=/root/.netbeans/6.1/build.properties
|
|
@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
|
||||||
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
|
|
||||||
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/1">
|
|
||||||
<file>file:/root/NetBeansProjects/BOB/Demos/echo/echoclient/src/net/i2p/BOB/Demos/echo/echoclient/Main.java</file>
|
|
||||||
</open-files>
|
|
||||||
</project-private>
|
|
@ -1,60 +0,0 @@
|
|||||||
build.classes.dir=${build.dir}/classes
|
|
||||||
build.classes.excludes=**/*.java,**/*.form
|
|
||||||
# This directory is removed when the project is cleaned:
|
|
||||||
build.dir=build
|
|
||||||
build.generated.dir=${build.dir}/generated
|
|
||||||
# Only compile against the classpath explicitly listed here:
|
|
||||||
build.sysclasspath=ignore
|
|
||||||
build.test.classes.dir=${build.dir}/test/classes
|
|
||||||
build.test.results.dir=${build.dir}/test/results
|
|
||||||
debug.classpath=\
|
|
||||||
${run.classpath}
|
|
||||||
debug.test.classpath=\
|
|
||||||
${run.test.classpath}
|
|
||||||
# This directory is removed when the project is cleaned:
|
|
||||||
dist.dir=dist
|
|
||||||
dist.jar=${dist.dir}/echoclient.jar
|
|
||||||
dist.javadoc.dir=${dist.dir}/javadoc
|
|
||||||
excludes=
|
|
||||||
file.reference.BOB.jar=../../../dist/BOB.jar
|
|
||||||
includes=**
|
|
||||||
jar.compress=false
|
|
||||||
javac.classpath=
|
|
||||||
# Space-separated list of extra javac options
|
|
||||||
javac.compilerargs=
|
|
||||||
javac.deprecation=false
|
|
||||||
javac.source=1.5
|
|
||||||
javac.target=1.5
|
|
||||||
javac.test.classpath=\
|
|
||||||
${javac.classpath}:\
|
|
||||||
${build.classes.dir}:\
|
|
||||||
${libs.junit.classpath}:\
|
|
||||||
${libs.junit_4.classpath}
|
|
||||||
javadoc.additionalparam=
|
|
||||||
javadoc.author=false
|
|
||||||
javadoc.encoding=${source.encoding}
|
|
||||||
javadoc.noindex=false
|
|
||||||
javadoc.nonavbar=false
|
|
||||||
javadoc.notree=false
|
|
||||||
javadoc.private=false
|
|
||||||
javadoc.splitindex=true
|
|
||||||
javadoc.use=true
|
|
||||||
javadoc.version=false
|
|
||||||
javadoc.windowtitle=
|
|
||||||
main.class=net.i2p.BOB.Demos.echo.echoclient.Main
|
|
||||||
manifest.file=manifest.mf
|
|
||||||
meta.inf.dir=${src.dir}/META-INF
|
|
||||||
platform.active=default_platform
|
|
||||||
run.classpath=\
|
|
||||||
${javac.classpath}:\
|
|
||||||
${build.classes.dir}
|
|
||||||
# Space-separated list of JVM arguments used when running the project
|
|
||||||
# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
|
|
||||||
# or test-sys-prop.name=value to set system properties for unit tests):
|
|
||||||
run.jvmargs=
|
|
||||||
run.test.classpath=\
|
|
||||||
${javac.test.classpath}:\
|
|
||||||
${build.test.classes.dir}
|
|
||||||
source.encoding=UTF-8
|
|
||||||
src.dir=src
|
|
||||||
test.src.dir=test
|
|
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://www.netbeans.org/ns/project/1">
|
|
||||||
<type>org.netbeans.modules.java.j2seproject</type>
|
|
||||||
<configuration>
|
|
||||||
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<name>echoclient</name>
|
|
||||||
<minimum-ant-version>1.6.5</minimum-ant-version>
|
|
||||||
<source-roots>
|
|
||||||
<root id="src.dir"/>
|
|
||||||
</source-roots>
|
|
||||||
<test-roots>
|
|
||||||
<root id="test.src.dir"/>
|
|
||||||
</test-roots>
|
|
||||||
</data>
|
|
||||||
</configuration>
|
|
||||||
</project>
|
|
@ -1,201 +0,0 @@
|
|||||||
/**
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* Version 2, December 2004
|
|
||||||
*
|
|
||||||
* Copyright (C) sponge
|
|
||||||
* Planet Earth
|
|
||||||
* Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
* copies of this license document, and changing it is allowed as long
|
|
||||||
* as the name is changed.
|
|
||||||
*
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
*
|
|
||||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
*
|
|
||||||
* See...
|
|
||||||
*
|
|
||||||
* http://sam.zoy.org/wtfpl/
|
|
||||||
* and
|
|
||||||
* http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
*
|
|
||||||
* ...for any additional details and liscense questions.
|
|
||||||
*/
|
|
||||||
package net.i2p.BOB.Demos.echo.echoclient;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author sponge
|
|
||||||
*/
|
|
||||||
public class Main {
|
|
||||||
|
|
||||||
public static String Lread(InputStream in) throws IOException {
|
|
||||||
String S;
|
|
||||||
int b;
|
|
||||||
char c;
|
|
||||||
|
|
||||||
S = new String();
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
b = in.read();
|
|
||||||
if(b == 13) {
|
|
||||||
//skip CR
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(b < 20 || b > 126) {
|
|
||||||
// exit on anything not legal
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
c = (char)(b & 0x7f); // We only really give a fuck about ASCII
|
|
||||||
S = new String(S + c);
|
|
||||||
}
|
|
||||||
return S;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for "ERROR" and if so, throw RuntimeException
|
|
||||||
* @param line
|
|
||||||
* @throws java.lang.RuntimeException
|
|
||||||
*/
|
|
||||||
static void checkline(String line) throws RuntimeException {
|
|
||||||
System.out.println(line); // print status
|
|
||||||
if(line.startsWith("ERROR")) {
|
|
||||||
throw new RuntimeException(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wrtxt(OutputStream CMDout, String s) throws IOException {
|
|
||||||
CMDout.write(s.getBytes());
|
|
||||||
CMDout.write('\n');
|
|
||||||
CMDout.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setupconn(String[] args) throws UnknownHostException, IOException, RuntimeException {
|
|
||||||
String line;
|
|
||||||
Socket CMDsock = new Socket("localhost", 0xB0B);
|
|
||||||
InputStream CMDin = CMDsock.getInputStream();
|
|
||||||
OutputStream CMDout = CMDsock.getOutputStream();
|
|
||||||
// setup the tunnel.
|
|
||||||
line = Lread(CMDin);
|
|
||||||
System.out.println(line); // print the banner
|
|
||||||
line = Lread(CMDin);
|
|
||||||
System.out.println(line); // print initial status, should always be "OK"
|
|
||||||
try {
|
|
||||||
wrtxt(CMDout, "status " + args[2]);
|
|
||||||
line = Lread(CMDin); // get the status of this nickname, if it's an error, create it
|
|
||||||
checkline(line);
|
|
||||||
} catch(RuntimeException rte) {
|
|
||||||
wrtxt(CMDout, "setnick " + args[2]);
|
|
||||||
line = Lread(CMDin); // create a new nickname
|
|
||||||
checkline(line);
|
|
||||||
wrtxt(CMDout, "newkeys");
|
|
||||||
line = Lread(CMDin); // set up new keys
|
|
||||||
checkline(line);
|
|
||||||
wrtxt(CMDout, "inport " + args[1]);
|
|
||||||
line = Lread(CMDin); // set the port we connect in on
|
|
||||||
checkline(line);
|
|
||||||
}
|
|
||||||
wrtxt(CMDout, "getnick " + args[2]);
|
|
||||||
line = Lread(CMDin); // Set to our nick
|
|
||||||
try {
|
|
||||||
checkline(line);
|
|
||||||
} catch(RuntimeException rte) {
|
|
||||||
System.out.println("Continuing on existing tunnel..");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wrtxt(CMDout, "start");
|
|
||||||
line = Lread(CMDin); // an error here is OK
|
|
||||||
System.out.println(line); // print status
|
|
||||||
CMDsock.close(); // we no longer need this particular socket
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void deleteconn(String[] args) throws UnknownHostException, IOException, RuntimeException {
|
|
||||||
String line;
|
|
||||||
// Wait for things to flush
|
|
||||||
try {
|
|
||||||
Thread.sleep(10000);
|
|
||||||
} catch(InterruptedException ex) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
Socket CMDsock = new Socket("localhost", 0xB0B);
|
|
||||||
InputStream CMDin = CMDsock.getInputStream();
|
|
||||||
OutputStream CMDout = CMDsock.getOutputStream();
|
|
||||||
// delete the tunnel.
|
|
||||||
line = Lread(CMDin);
|
|
||||||
System.out.println(line); // print the banner
|
|
||||||
line = Lread(CMDin);
|
|
||||||
System.out.println(line); // print initial status, should always be "OK"
|
|
||||||
wrtxt(CMDout, "getnick " + args[2]); // Set to our nick
|
|
||||||
line = Lread(CMDin);
|
|
||||||
checkline(line);
|
|
||||||
wrtxt(CMDout, "stop");
|
|
||||||
line = Lread(CMDin);
|
|
||||||
checkline(line);
|
|
||||||
try {
|
|
||||||
Thread.sleep(2000); //sleep for 2000 ms (Two seconds)
|
|
||||||
} catch(Exception e) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
|
|
||||||
wrtxt(CMDout, "clear");
|
|
||||||
line = Lread(CMDin);
|
|
||||||
while(line.startsWith("ERROR")) {
|
|
||||||
wrtxt(CMDout, "clear");
|
|
||||||
line = Lread(CMDin);
|
|
||||||
}
|
|
||||||
System.out.println(line); // print status
|
|
||||||
CMDsock.close(); // we no longer need this particular socket
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void chatter(String[] args) throws UnknownHostException, IOException, RuntimeException {
|
|
||||||
String line;
|
|
||||||
Socket sock = new Socket("localhost", Integer.parseInt(args[1]));
|
|
||||||
InputStream in = sock.getInputStream();
|
|
||||||
OutputStreamWriter out = new OutputStreamWriter(sock.getOutputStream());
|
|
||||||
out.write(args[3] + "\n"); // send out the i2p address to connect to
|
|
||||||
out.flush();
|
|
||||||
System.out.println("Connecting to " + args[3]);
|
|
||||||
line = Lread(in); // get server greeting
|
|
||||||
System.out.println("Got " + line); // show user
|
|
||||||
out.write("Test complete.\n"); // send something back
|
|
||||||
out.flush(); // make sure it's sent.
|
|
||||||
sock.close(); // done.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param args tunnelport tunnelnickname I2Pdestkey
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
// I'm lazy, and want to exit on any failures.
|
|
||||||
try {
|
|
||||||
setupconn(args); // talk to BOB, set up an outbound port
|
|
||||||
chatter(args); // talk over the connection
|
|
||||||
|
|
||||||
} catch(UnknownHostException ex) {
|
|
||||||
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
} catch(IOException ex) {
|
|
||||||
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
deleteconn(args);
|
|
||||||
} catch(UnknownHostException ex) {
|
|
||||||
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
} catch(IOException ex) {
|
|
||||||
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
} catch(RuntimeException ex) {
|
|
||||||
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- You may freely edit this file. See commented blocks below for -->
|
|
||||||
<!-- some examples of how to customize the build. -->
|
|
||||||
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
|
||||||
<project name="echoserver" default="default" basedir=".">
|
|
||||||
<description>Builds, tests, and runs the project echoserver.</description>
|
|
||||||
<import file="nbproject/build-impl.xml"/>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
There exist several targets which are by default empty and which can be
|
|
||||||
used for execution of your tasks. These targets are usually executed
|
|
||||||
before and after some main targets. They are:
|
|
||||||
|
|
||||||
-pre-init: called before initialization of project properties
|
|
||||||
-post-init: called after initialization of project properties
|
|
||||||
-pre-compile: called before javac compilation
|
|
||||||
-post-compile: called after javac compilation
|
|
||||||
-pre-compile-single: called before javac compilation of single file
|
|
||||||
-post-compile-single: called after javac compilation of single file
|
|
||||||
-pre-compile-test: called before javac compilation of JUnit tests
|
|
||||||
-post-compile-test: called after javac compilation of JUnit tests
|
|
||||||
-pre-compile-test-single: called before javac compilation of single JUnit test
|
|
||||||
-post-compile-test-single: called after javac compilation of single JUunit test
|
|
||||||
-pre-jar: called before JAR building
|
|
||||||
-post-jar: called after JAR building
|
|
||||||
-post-clean: called after cleaning build products
|
|
||||||
|
|
||||||
(Targets beginning with '-' are not intended to be called on their own.)
|
|
||||||
|
|
||||||
Example of inserting an obfuscator after compilation could look like this:
|
|
||||||
|
|
||||||
<target name="-post-compile">
|
|
||||||
<obfuscate>
|
|
||||||
<fileset dir="${build.classes.dir}"/>
|
|
||||||
</obfuscate>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
For list of available properties check the imported
|
|
||||||
nbproject/build-impl.xml file.
|
|
||||||
|
|
||||||
|
|
||||||
Another way to customize the build is by overriding existing main targets.
|
|
||||||
The targets of interest are:
|
|
||||||
|
|
||||||
-init-macrodef-javac: defines macro for javac compilation
|
|
||||||
-init-macrodef-junit: defines macro for junit execution
|
|
||||||
-init-macrodef-debug: defines macro for class debugging
|
|
||||||
-init-macrodef-java: defines macro for class execution
|
|
||||||
-do-jar-with-manifest: JAR building (if you are using a manifest)
|
|
||||||
-do-jar-without-manifest: JAR building (if you are not using a manifest)
|
|
||||||
run: execution of project
|
|
||||||
-javadoc-build: Javadoc generation
|
|
||||||
test-report: JUnit report generation
|
|
||||||
|
|
||||||
An example of overriding the target for project execution could look like this:
|
|
||||||
|
|
||||||
<target name="run" depends="echoserver-impl.jar">
|
|
||||||
<exec dir="bin" executable="launcher.exe">
|
|
||||||
<arg file="${dist.jar}"/>
|
|
||||||
</exec>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
Notice that the overridden target depends on the jar target and not only on
|
|
||||||
the compile target as the regular run target does. Again, for a list of available
|
|
||||||
properties which you can use, check the target you are overriding in the
|
|
||||||
nbproject/build-impl.xml file.
|
|
||||||
|
|
||||||
-->
|
|
||||||
</project>
|
|
@ -1,3 +0,0 @@
|
|||||||
Manifest-Version: 1.0
|
|
||||||
X-COMMENT: Main-Class will be added automatically by build
|
|
||||||
|
|
@ -1,629 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
*** GENERATED FROM project.xml - DO NOT EDIT ***
|
|
||||||
*** EDIT ../build.xml INSTEAD ***
|
|
||||||
|
|
||||||
For the purpose of easier reading the script
|
|
||||||
is divided into following sections:
|
|
||||||
|
|
||||||
- initialization
|
|
||||||
- compilation
|
|
||||||
- jar
|
|
||||||
- execution
|
|
||||||
- debugging
|
|
||||||
- javadoc
|
|
||||||
- junit compilation
|
|
||||||
- junit execution
|
|
||||||
- junit debugging
|
|
||||||
- applet
|
|
||||||
- cleanup
|
|
||||||
|
|
||||||
-->
|
|
||||||
<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="echoserver-impl">
|
|
||||||
<target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
|
|
||||||
<!--
|
|
||||||
======================
|
|
||||||
INITIALIZATION SECTION
|
|
||||||
======================
|
|
||||||
-->
|
|
||||||
<target name="-pre-init">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init" name="-init-private">
|
|
||||||
<property file="nbproject/private/config.properties"/>
|
|
||||||
<property file="nbproject/private/configs/${config}.properties"/>
|
|
||||||
<property file="nbproject/private/private.properties"/>
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private" name="-init-user">
|
|
||||||
<property file="${user.properties.file}"/>
|
|
||||||
<!-- The two properties below are usually overridden -->
|
|
||||||
<!-- by the active platform. Just a fallback. -->
|
|
||||||
<property name="default.javac.source" value="1.4"/>
|
|
||||||
<property name="default.javac.target" value="1.4"/>
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private,-init-user" name="-init-project">
|
|
||||||
<property file="nbproject/configs/${config}.properties"/>
|
|
||||||
<property file="nbproject/project.properties"/>
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
|
|
||||||
<available file="${manifest.file}" property="manifest.available"/>
|
|
||||||
<condition property="manifest.available+main.class">
|
|
||||||
<and>
|
|
||||||
<isset property="manifest.available"/>
|
|
||||||
<isset property="main.class"/>
|
|
||||||
<not>
|
|
||||||
<equals arg1="${main.class}" arg2="" trim="true"/>
|
|
||||||
</not>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<condition property="manifest.available+main.class+mkdist.available">
|
|
||||||
<and>
|
|
||||||
<istrue value="${manifest.available+main.class}"/>
|
|
||||||
<isset property="libs.CopyLibs.classpath"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<condition property="have.tests">
|
|
||||||
<or>
|
|
||||||
<available file="${test.src.dir}"/>
|
|
||||||
</or>
|
|
||||||
</condition>
|
|
||||||
<condition property="have.sources">
|
|
||||||
<or>
|
|
||||||
<available file="${src.dir}"/>
|
|
||||||
</or>
|
|
||||||
</condition>
|
|
||||||
<condition property="netbeans.home+have.tests">
|
|
||||||
<and>
|
|
||||||
<isset property="netbeans.home"/>
|
|
||||||
<isset property="have.tests"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<condition property="no.javadoc.preview">
|
|
||||||
<and>
|
|
||||||
<isset property="javadoc.preview"/>
|
|
||||||
<isfalse value="${javadoc.preview}"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<property name="run.jvmargs" value=""/>
|
|
||||||
<property name="javac.compilerargs" value=""/>
|
|
||||||
<property name="work.dir" value="${basedir}"/>
|
|
||||||
<condition property="no.deps">
|
|
||||||
<and>
|
|
||||||
<istrue value="${no.dependencies}"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<property name="javac.debug" value="true"/>
|
|
||||||
<property name="javadoc.preview" value="true"/>
|
|
||||||
<property name="application.args" value=""/>
|
|
||||||
<property name="source.encoding" value="${file.encoding}"/>
|
|
||||||
<condition property="javadoc.encoding.used" value="${javadoc.encoding}">
|
|
||||||
<and>
|
|
||||||
<isset property="javadoc.encoding"/>
|
|
||||||
<not>
|
|
||||||
<equals arg1="${javadoc.encoding}" arg2=""/>
|
|
||||||
</not>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<property name="javadoc.encoding.used" value="${source.encoding}"/>
|
|
||||||
<property name="includes" value="**"/>
|
|
||||||
<property name="excludes" value=""/>
|
|
||||||
<property name="do.depend" value="false"/>
|
|
||||||
<condition property="do.depend.true">
|
|
||||||
<istrue value="${do.depend}"/>
|
|
||||||
</condition>
|
|
||||||
<condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
|
|
||||||
<and>
|
|
||||||
<isset property="jaxws.endorsed.dir"/>
|
|
||||||
<available file="nbproject/jaxws-build.xml"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
</target>
|
|
||||||
<target name="-post-init">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
|
|
||||||
<fail unless="src.dir">Must set src.dir</fail>
|
|
||||||
<fail unless="test.src.dir">Must set test.src.dir</fail>
|
|
||||||
<fail unless="build.dir">Must set build.dir</fail>
|
|
||||||
<fail unless="dist.dir">Must set dist.dir</fail>
|
|
||||||
<fail unless="build.classes.dir">Must set build.classes.dir</fail>
|
|
||||||
<fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
|
|
||||||
<fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
|
|
||||||
<fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
|
|
||||||
<fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
|
|
||||||
<fail unless="dist.jar">Must set dist.jar</fail>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-property">
|
|
||||||
<macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<attribute name="name"/>
|
|
||||||
<attribute name="value"/>
|
|
||||||
<sequential>
|
|
||||||
<property name="@{name}" value="${@{value}}"/>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-javac">
|
|
||||||
<macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${src.dir}" name="srcdir"/>
|
|
||||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
|
||||||
<attribute default="${javac.classpath}" name="classpath"/>
|
|
||||||
<attribute default="${includes}" name="includes"/>
|
|
||||||
<attribute default="${excludes}" name="excludes"/>
|
|
||||||
<attribute default="${javac.debug}" name="debug"/>
|
|
||||||
<attribute default="" name="sourcepath"/>
|
|
||||||
<element name="customize" optional="true"/>
|
|
||||||
<sequential>
|
|
||||||
<javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
|
|
||||||
<classpath>
|
|
||||||
<path path="@{classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
|
|
||||||
<customize/>
|
|
||||||
</javac>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
<macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${src.dir}" name="srcdir"/>
|
|
||||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
|
||||||
<attribute default="${javac.classpath}" name="classpath"/>
|
|
||||||
<sequential>
|
|
||||||
<depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
|
|
||||||
<classpath>
|
|
||||||
<path path="@{classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
</depend>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
<macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
|
||||||
<sequential>
|
|
||||||
<fail unless="javac.includes">Must set javac.includes</fail>
|
|
||||||
<pathconvert pathsep="," property="javac.includes.binary">
|
|
||||||
<path>
|
|
||||||
<filelist dir="@{destdir}" files="${javac.includes}"/>
|
|
||||||
</path>
|
|
||||||
<globmapper from="*.java" to="*.class"/>
|
|
||||||
</pathconvert>
|
|
||||||
<delete>
|
|
||||||
<files includes="${javac.includes.binary}"/>
|
|
||||||
</delete>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-junit">
|
|
||||||
<macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${includes}" name="includes"/>
|
|
||||||
<attribute default="${excludes}" name="excludes"/>
|
|
||||||
<attribute default="**" name="testincludes"/>
|
|
||||||
<sequential>
|
|
||||||
<junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
|
|
||||||
<batchtest todir="${build.test.results.dir}">
|
|
||||||
<fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
|
|
||||||
<filename name="@{testincludes}"/>
|
|
||||||
</fileset>
|
|
||||||
</batchtest>
|
|
||||||
<classpath>
|
|
||||||
<path path="${run.test.classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<syspropertyset>
|
|
||||||
<propertyref prefix="test-sys-prop."/>
|
|
||||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
|
||||||
</syspropertyset>
|
|
||||||
<formatter type="brief" usefile="false"/>
|
|
||||||
<formatter type="xml"/>
|
|
||||||
<jvmarg line="${run.jvmargs}"/>
|
|
||||||
</junit>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-nbjpda">
|
|
||||||
<macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<attribute default="${main.class}" name="name"/>
|
|
||||||
<attribute default="${debug.classpath}" name="classpath"/>
|
|
||||||
<attribute default="" name="stopclassname"/>
|
|
||||||
<sequential>
|
|
||||||
<nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="dt_socket">
|
|
||||||
<classpath>
|
|
||||||
<path path="@{classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
</nbjpdastart>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
<macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<attribute default="${build.classes.dir}" name="dir"/>
|
|
||||||
<sequential>
|
|
||||||
<nbjpdareload>
|
|
||||||
<fileset dir="@{dir}" includes="${fix.classes}">
|
|
||||||
<include name="${fix.includes}*.class"/>
|
|
||||||
</fileset>
|
|
||||||
</nbjpdareload>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-debug-args">
|
|
||||||
<property name="version-output" value="java version "${ant.java.version}"/>
|
|
||||||
<condition property="have-jdk-older-than-1.4">
|
|
||||||
<or>
|
|
||||||
<contains string="${version-output}" substring="java version "1.0"/>
|
|
||||||
<contains string="${version-output}" substring="java version "1.1"/>
|
|
||||||
<contains string="${version-output}" substring="java version "1.2"/>
|
|
||||||
<contains string="${version-output}" substring="java version "1.3"/>
|
|
||||||
</or>
|
|
||||||
</condition>
|
|
||||||
<condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
|
|
||||||
<istrue value="${have-jdk-older-than-1.4}"/>
|
|
||||||
</condition>
|
|
||||||
</target>
|
|
||||||
<target depends="-init-debug-args" name="-init-macrodef-debug">
|
|
||||||
<macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${main.class}" name="classname"/>
|
|
||||||
<attribute default="${debug.classpath}" name="classpath"/>
|
|
||||||
<element name="customize" optional="true"/>
|
|
||||||
<sequential>
|
|
||||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
|
||||||
<jvmarg line="${debug-args-line}"/>
|
|
||||||
<jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
|
|
||||||
<jvmarg line="${run.jvmargs}"/>
|
|
||||||
<classpath>
|
|
||||||
<path path="@{classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<syspropertyset>
|
|
||||||
<propertyref prefix="run-sys-prop."/>
|
|
||||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
|
||||||
</syspropertyset>
|
|
||||||
<customize/>
|
|
||||||
</java>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-java">
|
|
||||||
<macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<attribute default="${main.class}" name="classname"/>
|
|
||||||
<element name="customize" optional="true"/>
|
|
||||||
<sequential>
|
|
||||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
|
||||||
<jvmarg line="${run.jvmargs}"/>
|
|
||||||
<classpath>
|
|
||||||
<path path="${run.classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<syspropertyset>
|
|
||||||
<propertyref prefix="run-sys-prop."/>
|
|
||||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
|
||||||
</syspropertyset>
|
|
||||||
<customize/>
|
|
||||||
</java>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-presetdef-jar">
|
|
||||||
<presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<jar compress="${jar.compress}" jarfile="${dist.jar}">
|
|
||||||
<j2seproject1:fileset dir="${build.classes.dir}"/>
|
|
||||||
</jar>
|
|
||||||
</presetdef>
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
|
|
||||||
<!--
|
|
||||||
===================
|
|
||||||
COMPILATION SECTION
|
|
||||||
===================
|
|
||||||
-->
|
|
||||||
<target depends="init" name="deps-jar" unless="no.deps"/>
|
|
||||||
<target depends="init,deps-jar" name="-pre-pre-compile">
|
|
||||||
<mkdir dir="${build.classes.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-pre-compile">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target if="do.depend.true" name="-compile-depend">
|
|
||||||
<j2seproject3:depend/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
|
|
||||||
<j2seproject3:javac/>
|
|
||||||
<copy todir="${build.classes.dir}">
|
|
||||||
<fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
|
||||||
</copy>
|
|
||||||
</target>
|
|
||||||
<target name="-post-compile">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
|
|
||||||
<target name="-pre-compile-single">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
|
|
||||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
|
||||||
<j2seproject3:force-recompile/>
|
|
||||||
<j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-post-compile-single">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
|
|
||||||
<!--
|
|
||||||
====================
|
|
||||||
JAR BUILDING SECTION
|
|
||||||
====================
|
|
||||||
-->
|
|
||||||
<target depends="init" name="-pre-pre-jar">
|
|
||||||
<dirname file="${dist.jar}" property="dist.jar.dir"/>
|
|
||||||
<mkdir dir="${dist.jar.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-pre-jar">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
|
|
||||||
<j2seproject1:jar/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
|
|
||||||
<j2seproject1:jar manifest="${manifest.file}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
|
|
||||||
<j2seproject1:jar manifest="${manifest.file}">
|
|
||||||
<j2seproject1:manifest>
|
|
||||||
<j2seproject1:attribute name="Main-Class" value="${main.class}"/>
|
|
||||||
</j2seproject1:manifest>
|
|
||||||
</j2seproject1:jar>
|
|
||||||
<echo>To run this application from the command line without Ant, try:</echo>
|
|
||||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
|
||||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
|
||||||
<pathconvert property="run.classpath.with.dist.jar">
|
|
||||||
<path path="${run.classpath}"/>
|
|
||||||
<map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
|
|
||||||
</pathconvert>
|
|
||||||
<echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
|
|
||||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
|
||||||
<pathconvert property="run.classpath.without.build.classes.dir">
|
|
||||||
<path path="${run.classpath}"/>
|
|
||||||
<map from="${build.classes.dir.resolved}" to=""/>
|
|
||||||
</pathconvert>
|
|
||||||
<pathconvert pathsep=" " property="jar.classpath">
|
|
||||||
<path path="${run.classpath.without.build.classes.dir}"/>
|
|
||||||
<chainedmapper>
|
|
||||||
<flattenmapper/>
|
|
||||||
<globmapper from="*" to="lib/*"/>
|
|
||||||
</chainedmapper>
|
|
||||||
</pathconvert>
|
|
||||||
<taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
|
|
||||||
<copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
|
|
||||||
<fileset dir="${build.classes.dir}"/>
|
|
||||||
<manifest>
|
|
||||||
<attribute name="Main-Class" value="${main.class}"/>
|
|
||||||
<attribute name="Class-Path" value="${jar.classpath}"/>
|
|
||||||
</manifest>
|
|
||||||
</copylibs>
|
|
||||||
<echo>To run this application from the command line without Ant, try:</echo>
|
|
||||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
|
||||||
<echo>java -jar "${dist.jar.resolved}"</echo>
|
|
||||||
</target>
|
|
||||||
<target name="-post-jar">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
|
|
||||||
<!--
|
|
||||||
=================
|
|
||||||
EXECUTION SECTION
|
|
||||||
=================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile" description="Run a main class." name="run">
|
|
||||||
<j2seproject1:java>
|
|
||||||
<customize>
|
|
||||||
<arg line="${application.args}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject1:java>
|
|
||||||
</target>
|
|
||||||
<target name="-do-not-recompile">
|
|
||||||
<property name="javac.includes.binary" value=""/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-do-not-recompile,compile-single" name="run-single">
|
|
||||||
<fail unless="run.class">Must select one file in the IDE or set run.class</fail>
|
|
||||||
<j2seproject1:java classname="${run.class}"/>
|
|
||||||
</target>
|
|
||||||
<!--
|
|
||||||
=================
|
|
||||||
DEBUGGING SECTION
|
|
||||||
=================
|
|
||||||
-->
|
|
||||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger">
|
|
||||||
<j2seproject1:nbjpdastart name="${debug.class}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile" name="-debug-start-debuggee">
|
|
||||||
<j2seproject3:debug>
|
|
||||||
<customize>
|
|
||||||
<arg line="${application.args}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject3:debug>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
|
|
||||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
|
|
||||||
<j2seproject1:nbjpdastart stopclassname="${main.class}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
|
|
||||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
|
|
||||||
<fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
|
|
||||||
<j2seproject3:debug classname="${debug.class}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
|
|
||||||
<target depends="init" name="-pre-debug-fix">
|
|
||||||
<fail unless="fix.includes">Must set fix.includes</fail>
|
|
||||||
<property name="javac.includes" value="${fix.includes}.java"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
|
|
||||||
<j2seproject1:nbjpdareload/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
|
|
||||||
<!--
|
|
||||||
===============
|
|
||||||
JAVADOC SECTION
|
|
||||||
===============
|
|
||||||
-->
|
|
||||||
<target depends="init" name="-javadoc-build">
|
|
||||||
<mkdir dir="${dist.javadoc.dir}"/>
|
|
||||||
<javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
|
|
||||||
<classpath>
|
|
||||||
<path path="${javac.classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
|
|
||||||
<filename name="**/*.java"/>
|
|
||||||
</fileset>
|
|
||||||
</javadoc>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
|
|
||||||
<nbbrowse file="${dist.javadoc.dir}/index.html"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
|
|
||||||
<!--
|
|
||||||
=========================
|
|
||||||
JUNIT COMPILATION SECTION
|
|
||||||
=========================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
|
|
||||||
<mkdir dir="${build.test.classes.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-pre-compile-test">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target if="do.depend.true" name="-compile-test-depend">
|
|
||||||
<j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
|
|
||||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
|
||||||
<copy todir="${build.test.classes.dir}">
|
|
||||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
|
||||||
</copy>
|
|
||||||
</target>
|
|
||||||
<target name="-post-compile-test">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
|
|
||||||
<target name="-pre-compile-test-single">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
|
|
||||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
|
||||||
<j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
|
|
||||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
|
|
||||||
<copy todir="${build.test.classes.dir}">
|
|
||||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
|
||||||
</copy>
|
|
||||||
</target>
|
|
||||||
<target name="-post-compile-test-single">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
|
|
||||||
<!--
|
|
||||||
=======================
|
|
||||||
JUNIT EXECUTION SECTION
|
|
||||||
=======================
|
|
||||||
-->
|
|
||||||
<target depends="init" if="have.tests" name="-pre-test-run">
|
|
||||||
<mkdir dir="${build.test.results.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
|
|
||||||
<j2seproject3:junit testincludes="**/*Test.java"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
|
|
||||||
<fail if="tests.failed">Some tests failed; see details above.</fail>
|
|
||||||
</target>
|
|
||||||
<target depends="init" if="have.tests" name="test-report"/>
|
|
||||||
<target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
|
|
||||||
<target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
|
|
||||||
<target depends="init" if="have.tests" name="-pre-test-run-single">
|
|
||||||
<mkdir dir="${build.test.results.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
|
|
||||||
<fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
|
|
||||||
<j2seproject3:junit excludes="" includes="${test.includes}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
|
|
||||||
<fail if="tests.failed">Some tests failed; see details above.</fail>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
|
|
||||||
<!--
|
|
||||||
=======================
|
|
||||||
JUNIT DEBUGGING SECTION
|
|
||||||
=======================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
|
|
||||||
<fail unless="test.class">Must select one file in the IDE or set test.class</fail>
|
|
||||||
<property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
|
|
||||||
<delete file="${test.report.file}"/>
|
|
||||||
<mkdir dir="${build.test.results.dir}"/>
|
|
||||||
<j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
|
|
||||||
<customize>
|
|
||||||
<syspropertyset>
|
|
||||||
<propertyref prefix="test-sys-prop."/>
|
|
||||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
|
||||||
</syspropertyset>
|
|
||||||
<arg value="${test.class}"/>
|
|
||||||
<arg value="showoutput=true"/>
|
|
||||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
|
|
||||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject3:debug>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
|
|
||||||
<j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
|
|
||||||
<target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
|
|
||||||
<j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
|
|
||||||
<!--
|
|
||||||
=========================
|
|
||||||
APPLET EXECUTION SECTION
|
|
||||||
=========================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile-single" name="run-applet">
|
|
||||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
|
||||||
<j2seproject1:java classname="sun.applet.AppletViewer">
|
|
||||||
<customize>
|
|
||||||
<arg value="${applet.url}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject1:java>
|
|
||||||
</target>
|
|
||||||
<!--
|
|
||||||
=========================
|
|
||||||
APPLET DEBUGGING SECTION
|
|
||||||
=========================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
|
|
||||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
|
||||||
<j2seproject3:debug classname="sun.applet.AppletViewer">
|
|
||||||
<customize>
|
|
||||||
<arg value="${applet.url}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject3:debug>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
|
|
||||||
<!--
|
|
||||||
===============
|
|
||||||
CLEANUP SECTION
|
|
||||||
===============
|
|
||||||
-->
|
|
||||||
<target depends="init" name="deps-clean" unless="no.deps"/>
|
|
||||||
<target depends="init" name="-do-clean">
|
|
||||||
<delete dir="${build.dir}"/>
|
|
||||||
<delete dir="${dist.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-post-clean">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
|
|
||||||
</project>
|
|
@ -1,8 +0,0 @@
|
|||||||
build.xml.data.CRC32=4ce39738
|
|
||||||
build.xml.script.CRC32=c1deb82c
|
|
||||||
build.xml.stylesheet.CRC32=be360661
|
|
||||||
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
|
|
||||||
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
|
|
||||||
nbproject/build-impl.xml.data.CRC32=4ce39738
|
|
||||||
nbproject/build-impl.xml.script.CRC32=555cdd2d
|
|
||||||
nbproject/build-impl.xml.stylesheet.CRC32=487672f9
|
|
@ -1,2 +0,0 @@
|
|||||||
jaxws.endorsed.dir=/usr/local/netbeans-6.1/java2/modules/ext/jaxws21/api
|
|
||||||
user.properties.file=/root/.netbeans/6.1/build.properties
|
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
|
||||||
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/1">
|
|
||||||
<file>file:/root/NetBeansProjects/BOB/Demos/echo/echoserver/src/net/i2p/BOB/Demos/echo/echoserver/Main.java</file>
|
|
||||||
</open-files>
|
|
||||||
</project-private>
|
|
@ -1,60 +0,0 @@
|
|||||||
build.classes.dir=${build.dir}/classes
|
|
||||||
build.classes.excludes=**/*.java,**/*.form
|
|
||||||
# This directory is removed when the project is cleaned:
|
|
||||||
build.dir=build
|
|
||||||
build.generated.dir=${build.dir}/generated
|
|
||||||
# Only compile against the classpath explicitly listed here:
|
|
||||||
build.sysclasspath=ignore
|
|
||||||
build.test.classes.dir=${build.dir}/test/classes
|
|
||||||
build.test.results.dir=${build.dir}/test/results
|
|
||||||
debug.classpath=\
|
|
||||||
${run.classpath}
|
|
||||||
debug.test.classpath=\
|
|
||||||
${run.test.classpath}
|
|
||||||
# This directory is removed when the project is cleaned:
|
|
||||||
dist.dir=dist
|
|
||||||
dist.jar=${dist.dir}/echoserver.jar
|
|
||||||
dist.javadoc.dir=${dist.dir}/javadoc
|
|
||||||
excludes=
|
|
||||||
file.reference.BOB.jar=../../../dist/BOB.jar
|
|
||||||
includes=**
|
|
||||||
jar.compress=false
|
|
||||||
javac.classpath=
|
|
||||||
# Space-separated list of extra javac options
|
|
||||||
javac.compilerargs=
|
|
||||||
javac.deprecation=false
|
|
||||||
javac.source=1.5
|
|
||||||
javac.target=1.5
|
|
||||||
javac.test.classpath=\
|
|
||||||
${javac.classpath}:\
|
|
||||||
${build.classes.dir}:\
|
|
||||||
${libs.junit.classpath}:\
|
|
||||||
${libs.junit_4.classpath}
|
|
||||||
javadoc.additionalparam=
|
|
||||||
javadoc.author=false
|
|
||||||
javadoc.encoding=${source.encoding}
|
|
||||||
javadoc.noindex=false
|
|
||||||
javadoc.nonavbar=false
|
|
||||||
javadoc.notree=false
|
|
||||||
javadoc.private=false
|
|
||||||
javadoc.splitindex=true
|
|
||||||
javadoc.use=true
|
|
||||||
javadoc.version=false
|
|
||||||
javadoc.windowtitle=
|
|
||||||
main.class=net.i2p.BOB.Demos.echo.echoserver.Main
|
|
||||||
manifest.file=manifest.mf
|
|
||||||
meta.inf.dir=${src.dir}/META-INF
|
|
||||||
platform.active=default_platform
|
|
||||||
run.classpath=\
|
|
||||||
${javac.classpath}:\
|
|
||||||
${build.classes.dir}
|
|
||||||
# Space-separated list of JVM arguments used when running the project
|
|
||||||
# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
|
|
||||||
# or test-sys-prop.name=value to set system properties for unit tests):
|
|
||||||
run.jvmargs=
|
|
||||||
run.test.classpath=\
|
|
||||||
${javac.test.classpath}:\
|
|
||||||
${build.test.classes.dir}
|
|
||||||
source.encoding=UTF-8
|
|
||||||
src.dir=src
|
|
||||||
test.src.dir=test
|
|
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://www.netbeans.org/ns/project/1">
|
|
||||||
<type>org.netbeans.modules.java.j2seproject</type>
|
|
||||||
<configuration>
|
|
||||||
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<name>echoserver</name>
|
|
||||||
<minimum-ant-version>1.6.5</minimum-ant-version>
|
|
||||||
<source-roots>
|
|
||||||
<root id="src.dir"/>
|
|
||||||
</source-roots>
|
|
||||||
<test-roots>
|
|
||||||
<root id="test.src.dir"/>
|
|
||||||
</test-roots>
|
|
||||||
</data>
|
|
||||||
</configuration>
|
|
||||||
</project>
|
|
@ -1,4 +0,0 @@
|
|||||||
(
|
|
||||||
cd dist
|
|
||||||
java -jar echoserver.jar main 37337 testserver
|
|
||||||
)
|
|
@ -1,197 +0,0 @@
|
|||||||
/**
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* Version 2, December 2004
|
|
||||||
*
|
|
||||||
* Copyright (C) sponge
|
|
||||||
* Planet Earth
|
|
||||||
* Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
* copies of this license document, and changing it is allowed as long
|
|
||||||
* as the name is changed.
|
|
||||||
*
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
*
|
|
||||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
*
|
|
||||||
* See...
|
|
||||||
*
|
|
||||||
* http://sam.zoy.org/wtfpl/
|
|
||||||
* and
|
|
||||||
* http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
*
|
|
||||||
* ...for any additional details and liscense questions.
|
|
||||||
*/
|
|
||||||
package net.i2p.BOB.Demos.echo.echoserver;
|
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author sponge
|
|
||||||
*/
|
|
||||||
public class Main {
|
|
||||||
public static String Lread(InputStream in) throws IOException {
|
|
||||||
String S;
|
|
||||||
int b;
|
|
||||||
char c;
|
|
||||||
|
|
||||||
S = new String();
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
b = in.read();
|
|
||||||
if(b < 20) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
c = (char)(b & 0x7f); // We only really give a fuck about ASCII
|
|
||||||
S = new String(S + c);
|
|
||||||
}
|
|
||||||
return S;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for "ERROR" and if so, throw RuntimeException
|
|
||||||
* @param line
|
|
||||||
* @throws java.lang.RuntimeException
|
|
||||||
*/
|
|
||||||
static void checkline(String line) throws RuntimeException {
|
|
||||||
System.out.println(line); // print status
|
|
||||||
if(line.startsWith("ERROR")) {
|
|
||||||
throw new RuntimeException(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wrtxt(OutputStream CMDout, String s) throws IOException {
|
|
||||||
CMDout.write(s.getBytes());
|
|
||||||
CMDout.write('\n');
|
|
||||||
CMDout.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setupconn(String[] args) throws UnknownHostException, IOException, RuntimeException {
|
|
||||||
String line;
|
|
||||||
Socket CMDsock = new Socket("localhost", 0xB0B);
|
|
||||||
InputStream CMDin = CMDsock.getInputStream();
|
|
||||||
OutputStream CMDout = CMDsock.getOutputStream();
|
|
||||||
// setup the tunnel.
|
|
||||||
line = Lread(CMDin);
|
|
||||||
System.out.println(line); // print the banner
|
|
||||||
line = Lread(CMDin);
|
|
||||||
System.out.println(line); // print initial status, should always be "OK"
|
|
||||||
try {
|
|
||||||
wrtxt(CMDout, "status " + args[2]);
|
|
||||||
line =Lread(CMDin); // get the status of this nickname, if it's an error, create it
|
|
||||||
checkline(line);
|
|
||||||
} catch(RuntimeException rte) {
|
|
||||||
wrtxt(CMDout, "setnick " + args[2]);
|
|
||||||
line =Lread(CMDin); // create a new nickname
|
|
||||||
checkline(line);
|
|
||||||
wrtxt(CMDout, "newkeys ");
|
|
||||||
line =Lread(CMDin); // set up new keys
|
|
||||||
checkline(line);
|
|
||||||
wrtxt(CMDout, "outport " + args[1]);
|
|
||||||
line = Lread(CMDin); // set the port we connect out on
|
|
||||||
checkline(line);
|
|
||||||
}
|
|
||||||
wrtxt(CMDout, "getnick " + args[2]);
|
|
||||||
line = Lread(CMDin); // Set to our nick
|
|
||||||
checkline(line);
|
|
||||||
wrtxt(CMDout, "start ");
|
|
||||||
line = Lread(CMDin); // an error here is OK
|
|
||||||
System.out.println(line); // print status
|
|
||||||
CMDsock.close(); // we no longer need this particular socket
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void deleteconn(String[] args) throws UnknownHostException, IOException, RuntimeException {
|
|
||||||
String line;
|
|
||||||
Socket CMDsock = new Socket("localhost", 0xB0B);
|
|
||||||
InputStream CMDin = CMDsock.getInputStream();
|
|
||||||
OutputStream CMDout = CMDsock.getOutputStream();
|
|
||||||
// delete the tunnel.
|
|
||||||
line = Lread(CMDin);
|
|
||||||
System.out.println(line); // print the banner
|
|
||||||
line = Lread(CMDin);
|
|
||||||
System.out.println(line); // print initial status, should always be "OK"
|
|
||||||
wrtxt(CMDout, "getnick " + args[2]); // Set to our nick
|
|
||||||
line = Lread(CMDin);
|
|
||||||
checkline(line);
|
|
||||||
wrtxt(CMDout, "stop");
|
|
||||||
line = Lread(CMDin);
|
|
||||||
checkline(line);
|
|
||||||
try {
|
|
||||||
Thread.sleep(2000); //sleep for 2000 ms (Two seconds)
|
|
||||||
} catch(Exception e) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
|
|
||||||
wrtxt(CMDout, "clear");
|
|
||||||
line = Lread(CMDin);
|
|
||||||
while(line.startsWith("ERROR")) {
|
|
||||||
wrtxt(CMDout, "clear");
|
|
||||||
line = Lread(CMDin);
|
|
||||||
}
|
|
||||||
System.out.println(line); // print status
|
|
||||||
CMDsock.close(); // we no longer need this particular socket
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void chatter(Socket sock) throws UnknownHostException, IOException, RuntimeException {
|
|
||||||
String line;
|
|
||||||
InputStream in = sock.getInputStream();
|
|
||||||
OutputStreamWriter out = new OutputStreamWriter(new BufferedOutputStream(sock.getOutputStream()));
|
|
||||||
|
|
||||||
line = Lread(in); // get remote I2P address
|
|
||||||
System.out.println("Connect from: " + line); // show user
|
|
||||||
|
|
||||||
out.write("Hello, You are connecting from " + line + "\n"); // send greeting
|
|
||||||
out.flush(); // make sure it's sent.
|
|
||||||
line = Lread(in); // get test text from client
|
|
||||||
System.out.println("Got "+line); // show user
|
|
||||||
sock.close(); // done.
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void serverlistener(String[] args) throws UnknownHostException, IOException, RuntimeException {
|
|
||||||
ServerSocket listener = new ServerSocket(Integer.parseInt(args[1]), 10, InetAddress.getByName("localhost"));
|
|
||||||
Socket server;
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
server = listener.accept();
|
|
||||||
chatter(server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param args tunnelport tunnelnickname
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
// I'm lazy, and want to exit on any failures.
|
|
||||||
try {
|
|
||||||
setupconn(args); // talk to BOB, set up an inbound port
|
|
||||||
serverlistener(args); // talk over the connection
|
|
||||||
|
|
||||||
} catch(UnknownHostException ex) {
|
|
||||||
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
} catch(IOException ex) {
|
|
||||||
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
deleteconn(args);
|
|
||||||
} catch(UnknownHostException ex) {
|
|
||||||
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
} catch(IOException ex) {
|
|
||||||
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
} catch(RuntimeException ex) {
|
|
||||||
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
#bob.config
|
|
||||||
#Tue Dec 30 00:00:00 UTC 2008
|
|
||||||
# Please leave this file here for testing.
|
|
||||||
# Thank you,
|
|
||||||
# Sponge
|
|
||||||
i2cp.tcp.port=7654
|
|
||||||
BOB.host=localhost
|
|
||||||
inbound.lengthVariance=0
|
|
||||||
i2cp.messageReliability=BestEffort
|
|
||||||
BOB.port=45067
|
|
||||||
outbound.length=1
|
|
||||||
inbound.length=1
|
|
||||||
outbound.lengthVariance=0
|
|
||||||
i2cp.tcp.host=localhost
|
|
@ -1,74 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- You may freely edit this file. See commented blocks below for -->
|
|
||||||
<!-- some examples of how to customize the build. -->
|
|
||||||
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
|
||||||
<!-- By default, only the Clean and Build commands use this build script. -->
|
|
||||||
<!-- Commands such as Run, Debug, and Test only use this build script if -->
|
|
||||||
<!-- the Compile on Save feature is turned off for the project. -->
|
|
||||||
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
|
|
||||||
<!-- in the project's Project Properties dialog box.-->
|
|
||||||
<project name="BOB" default="default" basedir=".">
|
|
||||||
<description>Builds, tests, and runs the project BOB.</description>
|
|
||||||
<import file="nbproject/build-impl.xml"/>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
There exist several targets which are by default empty and which can be
|
|
||||||
used for execution of your tasks. These targets are usually executed
|
|
||||||
before and after some main targets. They are:
|
|
||||||
|
|
||||||
-pre-init: called before initialization of project properties
|
|
||||||
-post-init: called after initialization of project properties
|
|
||||||
-pre-compile: called before javac compilation
|
|
||||||
-post-compile: called after javac compilation
|
|
||||||
-pre-compile-single: called before javac compilation of single file
|
|
||||||
-post-compile-single: called after javac compilation of single file
|
|
||||||
-pre-compile-test: called before javac compilation of JUnit tests
|
|
||||||
-post-compile-test: called after javac compilation of JUnit tests
|
|
||||||
-pre-compile-test-single: called before javac compilation of single JUnit test
|
|
||||||
-post-compile-test-single: called after javac compilation of single JUunit test
|
|
||||||
-pre-jar: called before JAR building
|
|
||||||
-post-jar: called after JAR building
|
|
||||||
-post-clean: called after cleaning build products
|
|
||||||
|
|
||||||
(Targets beginning with '-' are not intended to be called on their own.)
|
|
||||||
|
|
||||||
Example of inserting an obfuscator after compilation could look like this:
|
|
||||||
|
|
||||||
<target name="-post-compile">
|
|
||||||
<obfuscate>
|
|
||||||
<fileset dir="${build.classes.dir}"/>
|
|
||||||
</obfuscate>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
For list of available properties check the imported
|
|
||||||
nbproject/build-impl.xml file.
|
|
||||||
|
|
||||||
|
|
||||||
Another way to customize the build is by overriding existing main targets.
|
|
||||||
The targets of interest are:
|
|
||||||
|
|
||||||
-init-macrodef-javac: defines macro for javac compilation
|
|
||||||
-init-macrodef-junit: defines macro for junit execution
|
|
||||||
-init-macrodef-debug: defines macro for class debugging
|
|
||||||
-init-macrodef-java: defines macro for class execution
|
|
||||||
-do-jar-with-manifest: JAR building (if you are using a manifest)
|
|
||||||
-do-jar-without-manifest: JAR building (if you are not using a manifest)
|
|
||||||
run: execution of project
|
|
||||||
-javadoc-build: Javadoc generation
|
|
||||||
test-report: JUnit report generation
|
|
||||||
|
|
||||||
An example of overriding the target for project execution could look like this:
|
|
||||||
|
|
||||||
<target name="run" depends="BOB-impl.jar">
|
|
||||||
<exec dir="bin" executable="launcher.exe">
|
|
||||||
<arg file="${dist.jar}"/>
|
|
||||||
</exec>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
Notice that the overridden target depends on the jar target and not only on
|
|
||||||
the compile target as the regular run target does. Again, for a list of available
|
|
||||||
properties which you can use, check the target you are overriding in the
|
|
||||||
nbproject/build-impl.xml file.
|
|
||||||
|
|
||||||
-->
|
|
||||||
</project>
|
|
@ -1,3 +0,0 @@
|
|||||||
Manifest-Version: 1.0
|
|
||||||
X-COMMENT: Main-Class will be added automatically by build
|
|
||||||
|
|
@ -1,642 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
*** GENERATED FROM project.xml - DO NOT EDIT ***
|
|
||||||
*** EDIT ../build.xml INSTEAD ***
|
|
||||||
|
|
||||||
For the purpose of easier reading the script
|
|
||||||
is divided into following sections:
|
|
||||||
|
|
||||||
- initialization
|
|
||||||
- compilation
|
|
||||||
- jar
|
|
||||||
- execution
|
|
||||||
- debugging
|
|
||||||
- javadoc
|
|
||||||
- junit compilation
|
|
||||||
- junit execution
|
|
||||||
- junit debugging
|
|
||||||
- applet
|
|
||||||
- cleanup
|
|
||||||
|
|
||||||
-->
|
|
||||||
<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="BOB-impl">
|
|
||||||
<target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
|
|
||||||
<!--
|
|
||||||
======================
|
|
||||||
INITIALIZATION SECTION
|
|
||||||
======================
|
|
||||||
-->
|
|
||||||
<target name="-pre-init">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init" name="-init-private">
|
|
||||||
<property file="nbproject/private/config.properties"/>
|
|
||||||
<property file="nbproject/private/configs/${config}.properties"/>
|
|
||||||
<property file="nbproject/private/private.properties"/>
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private" name="-init-user">
|
|
||||||
<property file="${user.properties.file}"/>
|
|
||||||
<!-- The two properties below are usually overridden -->
|
|
||||||
<!-- by the active platform. Just a fallback. -->
|
|
||||||
<property name="default.javac.source" value="1.4"/>
|
|
||||||
<property name="default.javac.target" value="1.4"/>
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private,-init-user" name="-init-project">
|
|
||||||
<property file="nbproject/configs/${config}.properties"/>
|
|
||||||
<property file="nbproject/project.properties"/>
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
|
|
||||||
<available file="${manifest.file}" property="manifest.available"/>
|
|
||||||
<condition property="manifest.available+main.class">
|
|
||||||
<and>
|
|
||||||
<isset property="manifest.available"/>
|
|
||||||
<isset property="main.class"/>
|
|
||||||
<not>
|
|
||||||
<equals arg1="${main.class}" arg2="" trim="true"/>
|
|
||||||
</not>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<condition property="manifest.available+main.class+mkdist.available">
|
|
||||||
<and>
|
|
||||||
<istrue value="${manifest.available+main.class}"/>
|
|
||||||
<isset property="libs.CopyLibs.classpath"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<condition property="have.tests">
|
|
||||||
<or>
|
|
||||||
<available file="${test.src.dir}"/>
|
|
||||||
</or>
|
|
||||||
</condition>
|
|
||||||
<condition property="have.sources">
|
|
||||||
<or>
|
|
||||||
<available file="${src.dir}"/>
|
|
||||||
</or>
|
|
||||||
</condition>
|
|
||||||
<condition property="netbeans.home+have.tests">
|
|
||||||
<and>
|
|
||||||
<isset property="netbeans.home"/>
|
|
||||||
<isset property="have.tests"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<condition property="no.javadoc.preview">
|
|
||||||
<and>
|
|
||||||
<isset property="javadoc.preview"/>
|
|
||||||
<isfalse value="${javadoc.preview}"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<property name="run.jvmargs" value=""/>
|
|
||||||
<property name="javac.compilerargs" value=""/>
|
|
||||||
<property name="work.dir" value="${basedir}"/>
|
|
||||||
<condition property="no.deps">
|
|
||||||
<and>
|
|
||||||
<istrue value="${no.dependencies}"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<property name="javac.debug" value="true"/>
|
|
||||||
<property name="javadoc.preview" value="true"/>
|
|
||||||
<property name="application.args" value=""/>
|
|
||||||
<property name="source.encoding" value="${file.encoding}"/>
|
|
||||||
<condition property="javadoc.encoding.used" value="${javadoc.encoding}">
|
|
||||||
<and>
|
|
||||||
<isset property="javadoc.encoding"/>
|
|
||||||
<not>
|
|
||||||
<equals arg1="${javadoc.encoding}" arg2=""/>
|
|
||||||
</not>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<property name="javadoc.encoding.used" value="${source.encoding}"/>
|
|
||||||
<property name="includes" value="**"/>
|
|
||||||
<property name="excludes" value=""/>
|
|
||||||
<property name="do.depend" value="false"/>
|
|
||||||
<condition property="do.depend.true">
|
|
||||||
<istrue value="${do.depend}"/>
|
|
||||||
</condition>
|
|
||||||
<condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
|
|
||||||
<and>
|
|
||||||
<isset property="jaxws.endorsed.dir"/>
|
|
||||||
<available file="nbproject/jaxws-build.xml"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
</target>
|
|
||||||
<target name="-post-init">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
|
|
||||||
<fail unless="src.dir">Must set src.dir</fail>
|
|
||||||
<fail unless="test.src.dir">Must set test.src.dir</fail>
|
|
||||||
<fail unless="build.dir">Must set build.dir</fail>
|
|
||||||
<fail unless="dist.dir">Must set dist.dir</fail>
|
|
||||||
<fail unless="build.classes.dir">Must set build.classes.dir</fail>
|
|
||||||
<fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
|
|
||||||
<fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
|
|
||||||
<fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
|
|
||||||
<fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
|
|
||||||
<fail unless="dist.jar">Must set dist.jar</fail>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-property">
|
|
||||||
<macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<attribute name="name"/>
|
|
||||||
<attribute name="value"/>
|
|
||||||
<sequential>
|
|
||||||
<property name="@{name}" value="${@{value}}"/>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-javac">
|
|
||||||
<macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${src.dir}" name="srcdir"/>
|
|
||||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
|
||||||
<attribute default="${javac.classpath}" name="classpath"/>
|
|
||||||
<attribute default="${includes}" name="includes"/>
|
|
||||||
<attribute default="${excludes}" name="excludes"/>
|
|
||||||
<attribute default="${javac.debug}" name="debug"/>
|
|
||||||
<attribute default="" name="sourcepath"/>
|
|
||||||
<element name="customize" optional="true"/>
|
|
||||||
<sequential>
|
|
||||||
<javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
|
|
||||||
<classpath>
|
|
||||||
<path path="@{classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
|
|
||||||
<customize/>
|
|
||||||
</javac>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
<macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${src.dir}" name="srcdir"/>
|
|
||||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
|
||||||
<attribute default="${javac.classpath}" name="classpath"/>
|
|
||||||
<sequential>
|
|
||||||
<depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
|
|
||||||
<classpath>
|
|
||||||
<path path="@{classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
</depend>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
<macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
|
||||||
<sequential>
|
|
||||||
<fail unless="javac.includes">Must set javac.includes</fail>
|
|
||||||
<pathconvert pathsep="," property="javac.includes.binary">
|
|
||||||
<path>
|
|
||||||
<filelist dir="@{destdir}" files="${javac.includes}"/>
|
|
||||||
</path>
|
|
||||||
<globmapper from="*.java" to="*.class"/>
|
|
||||||
</pathconvert>
|
|
||||||
<delete>
|
|
||||||
<files includes="${javac.includes.binary}"/>
|
|
||||||
</delete>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-junit">
|
|
||||||
<macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${includes}" name="includes"/>
|
|
||||||
<attribute default="${excludes}" name="excludes"/>
|
|
||||||
<attribute default="**" name="testincludes"/>
|
|
||||||
<sequential>
|
|
||||||
<junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
|
|
||||||
<batchtest todir="${build.test.results.dir}">
|
|
||||||
<fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
|
|
||||||
<filename name="@{testincludes}"/>
|
|
||||||
</fileset>
|
|
||||||
</batchtest>
|
|
||||||
<classpath>
|
|
||||||
<path path="${run.test.classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<syspropertyset>
|
|
||||||
<propertyref prefix="test-sys-prop."/>
|
|
||||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
|
||||||
</syspropertyset>
|
|
||||||
<formatter type="brief" usefile="false"/>
|
|
||||||
<formatter type="xml"/>
|
|
||||||
<jvmarg line="${run.jvmargs}"/>
|
|
||||||
</junit>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target depends="-init-debug-args" name="-init-macrodef-nbjpda">
|
|
||||||
<macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<attribute default="${main.class}" name="name"/>
|
|
||||||
<attribute default="${debug.classpath}" name="classpath"/>
|
|
||||||
<attribute default="" name="stopclassname"/>
|
|
||||||
<sequential>
|
|
||||||
<nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
|
|
||||||
<classpath>
|
|
||||||
<path path="@{classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
</nbjpdastart>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
<macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<attribute default="${build.classes.dir}" name="dir"/>
|
|
||||||
<sequential>
|
|
||||||
<nbjpdareload>
|
|
||||||
<fileset dir="@{dir}" includes="${fix.classes}">
|
|
||||||
<include name="${fix.includes}*.class"/>
|
|
||||||
</fileset>
|
|
||||||
</nbjpdareload>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-debug-args">
|
|
||||||
<property name="version-output" value="java version "${ant.java.version}"/>
|
|
||||||
<condition property="have-jdk-older-than-1.4">
|
|
||||||
<or>
|
|
||||||
<contains string="${version-output}" substring="java version "1.0"/>
|
|
||||||
<contains string="${version-output}" substring="java version "1.1"/>
|
|
||||||
<contains string="${version-output}" substring="java version "1.2"/>
|
|
||||||
<contains string="${version-output}" substring="java version "1.3"/>
|
|
||||||
</or>
|
|
||||||
</condition>
|
|
||||||
<condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
|
|
||||||
<istrue value="${have-jdk-older-than-1.4}"/>
|
|
||||||
</condition>
|
|
||||||
<condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
|
|
||||||
<os family="windows"/>
|
|
||||||
</condition>
|
|
||||||
<condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
|
|
||||||
<isset property="debug.transport"/>
|
|
||||||
</condition>
|
|
||||||
</target>
|
|
||||||
<target depends="-init-debug-args" name="-init-macrodef-debug">
|
|
||||||
<macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<attribute default="${main.class}" name="classname"/>
|
|
||||||
<attribute default="${debug.classpath}" name="classpath"/>
|
|
||||||
<element name="customize" optional="true"/>
|
|
||||||
<sequential>
|
|
||||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
|
||||||
<jvmarg line="${debug-args-line}"/>
|
|
||||||
<jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
|
|
||||||
<jvmarg line="${run.jvmargs}"/>
|
|
||||||
<classpath>
|
|
||||||
<path path="@{classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<syspropertyset>
|
|
||||||
<propertyref prefix="run-sys-prop."/>
|
|
||||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
|
||||||
</syspropertyset>
|
|
||||||
<customize/>
|
|
||||||
</java>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-macrodef-java">
|
|
||||||
<macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<attribute default="${main.class}" name="classname"/>
|
|
||||||
<element name="customize" optional="true"/>
|
|
||||||
<sequential>
|
|
||||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
|
||||||
<jvmarg line="${run.jvmargs}"/>
|
|
||||||
<classpath>
|
|
||||||
<path path="${run.classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<syspropertyset>
|
|
||||||
<propertyref prefix="run-sys-prop."/>
|
|
||||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
|
||||||
</syspropertyset>
|
|
||||||
<customize/>
|
|
||||||
</java>
|
|
||||||
</sequential>
|
|
||||||
</macrodef>
|
|
||||||
</target>
|
|
||||||
<target name="-init-presetdef-jar">
|
|
||||||
<presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
|
|
||||||
<jar compress="${jar.compress}" jarfile="${dist.jar}">
|
|
||||||
<j2seproject1:fileset dir="${build.classes.dir}"/>
|
|
||||||
</jar>
|
|
||||||
</presetdef>
|
|
||||||
</target>
|
|
||||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
|
|
||||||
<!--
|
|
||||||
===================
|
|
||||||
COMPILATION SECTION
|
|
||||||
===================
|
|
||||||
-->
|
|
||||||
<target depends="init" name="deps-jar" unless="no.deps"/>
|
|
||||||
<target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
|
|
||||||
<target depends="init" name="-check-automatic-build">
|
|
||||||
<available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
|
|
||||||
<antcall target="clean"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-jar" name="-pre-pre-compile">
|
|
||||||
<mkdir dir="${build.classes.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-pre-compile">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target if="do.depend.true" name="-compile-depend">
|
|
||||||
<j2seproject3:depend/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
|
|
||||||
<j2seproject3:javac/>
|
|
||||||
<copy todir="${build.classes.dir}">
|
|
||||||
<fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
|
||||||
</copy>
|
|
||||||
</target>
|
|
||||||
<target name="-post-compile">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
|
|
||||||
<target name="-pre-compile-single">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
|
|
||||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
|
||||||
<j2seproject3:force-recompile/>
|
|
||||||
<j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-post-compile-single">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
|
|
||||||
<!--
|
|
||||||
====================
|
|
||||||
JAR BUILDING SECTION
|
|
||||||
====================
|
|
||||||
-->
|
|
||||||
<target depends="init" name="-pre-pre-jar">
|
|
||||||
<dirname file="${dist.jar}" property="dist.jar.dir"/>
|
|
||||||
<mkdir dir="${dist.jar.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-pre-jar">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
|
|
||||||
<j2seproject1:jar/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
|
|
||||||
<j2seproject1:jar manifest="${manifest.file}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
|
|
||||||
<j2seproject1:jar manifest="${manifest.file}">
|
|
||||||
<j2seproject1:manifest>
|
|
||||||
<j2seproject1:attribute name="Main-Class" value="${main.class}"/>
|
|
||||||
</j2seproject1:manifest>
|
|
||||||
</j2seproject1:jar>
|
|
||||||
<echo>To run this application from the command line without Ant, try:</echo>
|
|
||||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
|
||||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
|
||||||
<pathconvert property="run.classpath.with.dist.jar">
|
|
||||||
<path path="${run.classpath}"/>
|
|
||||||
<map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
|
|
||||||
</pathconvert>
|
|
||||||
<echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
|
|
||||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
|
||||||
<pathconvert property="run.classpath.without.build.classes.dir">
|
|
||||||
<path path="${run.classpath}"/>
|
|
||||||
<map from="${build.classes.dir.resolved}" to=""/>
|
|
||||||
</pathconvert>
|
|
||||||
<pathconvert pathsep=" " property="jar.classpath">
|
|
||||||
<path path="${run.classpath.without.build.classes.dir}"/>
|
|
||||||
<chainedmapper>
|
|
||||||
<flattenmapper/>
|
|
||||||
<globmapper from="*" to="lib/*"/>
|
|
||||||
</chainedmapper>
|
|
||||||
</pathconvert>
|
|
||||||
<taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
|
|
||||||
<copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
|
|
||||||
<fileset dir="${build.classes.dir}"/>
|
|
||||||
<manifest>
|
|
||||||
<attribute name="Main-Class" value="${main.class}"/>
|
|
||||||
<attribute name="Class-Path" value="${jar.classpath}"/>
|
|
||||||
</manifest>
|
|
||||||
</copylibs>
|
|
||||||
<echo>To run this application from the command line without Ant, try:</echo>
|
|
||||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
|
||||||
<echo>java -jar "${dist.jar.resolved}"</echo>
|
|
||||||
</target>
|
|
||||||
<target name="-post-jar">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
|
|
||||||
<!--
|
|
||||||
=================
|
|
||||||
EXECUTION SECTION
|
|
||||||
=================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile" description="Run a main class." name="run">
|
|
||||||
<j2seproject1:java>
|
|
||||||
<customize>
|
|
||||||
<arg line="${application.args}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject1:java>
|
|
||||||
</target>
|
|
||||||
<target name="-do-not-recompile">
|
|
||||||
<property name="javac.includes.binary" value=""/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-do-not-recompile,compile-single" name="run-single">
|
|
||||||
<fail unless="run.class">Must select one file in the IDE or set run.class</fail>
|
|
||||||
<j2seproject1:java classname="${run.class}"/>
|
|
||||||
</target>
|
|
||||||
<!--
|
|
||||||
=================
|
|
||||||
DEBUGGING SECTION
|
|
||||||
=================
|
|
||||||
-->
|
|
||||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger">
|
|
||||||
<j2seproject1:nbjpdastart name="${debug.class}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile" name="-debug-start-debuggee">
|
|
||||||
<j2seproject3:debug>
|
|
||||||
<customize>
|
|
||||||
<arg line="${application.args}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject3:debug>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
|
|
||||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
|
|
||||||
<j2seproject1:nbjpdastart stopclassname="${main.class}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
|
|
||||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
|
|
||||||
<fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
|
|
||||||
<j2seproject3:debug classname="${debug.class}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
|
|
||||||
<target depends="init" name="-pre-debug-fix">
|
|
||||||
<fail unless="fix.includes">Must set fix.includes</fail>
|
|
||||||
<property name="javac.includes" value="${fix.includes}.java"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
|
|
||||||
<j2seproject1:nbjpdareload/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
|
|
||||||
<!--
|
|
||||||
===============
|
|
||||||
JAVADOC SECTION
|
|
||||||
===============
|
|
||||||
-->
|
|
||||||
<target depends="init" name="-javadoc-build">
|
|
||||||
<mkdir dir="${dist.javadoc.dir}"/>
|
|
||||||
<javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
|
|
||||||
<classpath>
|
|
||||||
<path path="${javac.classpath}"/>
|
|
||||||
</classpath>
|
|
||||||
<fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
|
|
||||||
<filename name="**/*.java"/>
|
|
||||||
</fileset>
|
|
||||||
</javadoc>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
|
|
||||||
<nbbrowse file="${dist.javadoc.dir}/index.html"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
|
|
||||||
<!--
|
|
||||||
=========================
|
|
||||||
JUNIT COMPILATION SECTION
|
|
||||||
=========================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
|
|
||||||
<mkdir dir="${build.test.classes.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-pre-compile-test">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target if="do.depend.true" name="-compile-test-depend">
|
|
||||||
<j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
|
|
||||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
|
||||||
<copy todir="${build.test.classes.dir}">
|
|
||||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
|
||||||
</copy>
|
|
||||||
</target>
|
|
||||||
<target name="-post-compile-test">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
|
|
||||||
<target name="-pre-compile-test-single">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
|
|
||||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
|
||||||
<j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
|
|
||||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
|
|
||||||
<copy todir="${build.test.classes.dir}">
|
|
||||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
|
||||||
</copy>
|
|
||||||
</target>
|
|
||||||
<target name="-post-compile-test-single">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
|
|
||||||
<!--
|
|
||||||
=======================
|
|
||||||
JUNIT EXECUTION SECTION
|
|
||||||
=======================
|
|
||||||
-->
|
|
||||||
<target depends="init" if="have.tests" name="-pre-test-run">
|
|
||||||
<mkdir dir="${build.test.results.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
|
|
||||||
<j2seproject3:junit testincludes="**/*Test.java"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
|
|
||||||
<fail if="tests.failed">Some tests failed; see details above.</fail>
|
|
||||||
</target>
|
|
||||||
<target depends="init" if="have.tests" name="test-report"/>
|
|
||||||
<target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
|
|
||||||
<target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
|
|
||||||
<target depends="init" if="have.tests" name="-pre-test-run-single">
|
|
||||||
<mkdir dir="${build.test.results.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
|
|
||||||
<fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
|
|
||||||
<j2seproject3:junit excludes="" includes="${test.includes}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
|
|
||||||
<fail if="tests.failed">Some tests failed; see details above.</fail>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
|
|
||||||
<!--
|
|
||||||
=======================
|
|
||||||
JUNIT DEBUGGING SECTION
|
|
||||||
=======================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
|
|
||||||
<fail unless="test.class">Must select one file in the IDE or set test.class</fail>
|
|
||||||
<property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
|
|
||||||
<delete file="${test.report.file}"/>
|
|
||||||
<mkdir dir="${build.test.results.dir}"/>
|
|
||||||
<j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
|
|
||||||
<customize>
|
|
||||||
<syspropertyset>
|
|
||||||
<propertyref prefix="test-sys-prop."/>
|
|
||||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
|
||||||
</syspropertyset>
|
|
||||||
<arg value="${test.class}"/>
|
|
||||||
<arg value="showoutput=true"/>
|
|
||||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
|
|
||||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject3:debug>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
|
|
||||||
<j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
|
|
||||||
<target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
|
|
||||||
<j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
|
|
||||||
<!--
|
|
||||||
=========================
|
|
||||||
APPLET EXECUTION SECTION
|
|
||||||
=========================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile-single" name="run-applet">
|
|
||||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
|
||||||
<j2seproject1:java classname="sun.applet.AppletViewer">
|
|
||||||
<customize>
|
|
||||||
<arg value="${applet.url}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject1:java>
|
|
||||||
</target>
|
|
||||||
<!--
|
|
||||||
=========================
|
|
||||||
APPLET DEBUGGING SECTION
|
|
||||||
=========================
|
|
||||||
-->
|
|
||||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
|
|
||||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
|
||||||
<j2seproject3:debug classname="sun.applet.AppletViewer">
|
|
||||||
<customize>
|
|
||||||
<arg value="${applet.url}"/>
|
|
||||||
</customize>
|
|
||||||
</j2seproject3:debug>
|
|
||||||
</target>
|
|
||||||
<target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
|
|
||||||
<!--
|
|
||||||
===============
|
|
||||||
CLEANUP SECTION
|
|
||||||
===============
|
|
||||||
-->
|
|
||||||
<target depends="init" name="deps-clean" unless="no.deps"/>
|
|
||||||
<target depends="init" name="-do-clean">
|
|
||||||
<delete dir="${build.dir}"/>
|
|
||||||
<delete dir="${dist.dir}"/>
|
|
||||||
</target>
|
|
||||||
<target name="-post-clean">
|
|
||||||
<!-- Empty placeholder for easier customization. -->
|
|
||||||
<!-- You can override this target in the ../build.xml file. -->
|
|
||||||
</target>
|
|
||||||
<target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
|
|
||||||
</project>
|
|
@ -1,8 +0,0 @@
|
|||||||
build.xml.data.CRC32=209349b6
|
|
||||||
build.xml.script.CRC32=403e69e6
|
|
||||||
build.xml.stylesheet.CRC32=958a1d3e
|
|
||||||
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
|
|
||||||
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
|
|
||||||
nbproject/build-impl.xml.data.CRC32=209349b6
|
|
||||||
nbproject/build-impl.xml.script.CRC32=75fac64c
|
|
||||||
nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5
|
|
@ -1,7 +0,0 @@
|
|||||||
compile.on.save=false
|
|
||||||
do.depend=false
|
|
||||||
do.jar=true
|
|
||||||
javac.debug=true
|
|
||||||
javadoc.preview=true
|
|
||||||
jaxws.endorsed.dir=/usr/local/netbeans-6.5/java2/modules/ext/jaxws21/api:/usr/local/netbeans-6.5/ide10/modules/ext/jaxb/api
|
|
||||||
user.properties.file=/root/.netbeans/6.5/build.properties
|
|
@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
|
||||||
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
|
|
||||||
</project-private>
|
|
@ -1,107 +0,0 @@
|
|||||||
application.title=BOB
|
|
||||||
application.vendor=root
|
|
||||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs=false
|
|
||||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width=8
|
|
||||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab=8
|
|
||||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=8
|
|
||||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
|
|
||||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project
|
|
||||||
build.classes.dir=${build.dir}/classes
|
|
||||||
build.classes.excludes=**/*.java,**/*.form
|
|
||||||
# This directory is removed when the project is cleaned:
|
|
||||||
build.dir=build
|
|
||||||
build.generated.dir=${build.dir}/generated
|
|
||||||
# Only compile against the classpath explicitly listed here:
|
|
||||||
build.sysclasspath=ignore
|
|
||||||
build.test.classes.dir=${build.dir}/test/classes
|
|
||||||
build.test.results.dir=${build.dir}/test/results
|
|
||||||
debug.classpath=\
|
|
||||||
${run.classpath}
|
|
||||||
debug.test.classpath=\
|
|
||||||
${run.test.classpath}
|
|
||||||
# This directory is removed when the project is cleaned:
|
|
||||||
dist.dir=dist
|
|
||||||
dist.jar=${dist.dir}/BOB.jar
|
|
||||||
dist.javadoc.dir=${dist.dir}/javadoc
|
|
||||||
excludes=
|
|
||||||
file.reference.core.jar=../i2p.i2p/core/dist/core.jar
|
|
||||||
file.reference.i2p.jar=../../bob/i2p/i2p.i2p/build/i2p.jar
|
|
||||||
file.reference.i2p.jar-1=../../core/java/build/i2p.jar
|
|
||||||
file.reference.i2p.jar-2=../i2p.i2p/core/java/build/i2p.jar
|
|
||||||
file.reference.i2ptunnel.jar=../i2ptunnel/java/build/i2ptunnel.jar
|
|
||||||
file.reference.java-src=../i2p.i2p/core/java/src/
|
|
||||||
file.reference.jbigi.jar=../../bob/i2p/i2p.i2p/build/jbigi.jar
|
|
||||||
file.reference.mstreaming.jar=../../bob/i2p/i2p.i2p/build/mstreaming.jar
|
|
||||||
file.reference.mstreaming.jar-1=../ministreaming/java/build/mstreaming.jar
|
|
||||||
file.reference.NetBeansProjects-i2p.i2p=../i2p.i2p/
|
|
||||||
file.reference.streaming.jar=../../bob/i2p/i2p.i2p/build/streaming.jar
|
|
||||||
file.reference.streaming.jar-1=../streaming/java/build/streaming.jar
|
|
||||||
file.reference.wrapper-freebsd=../../installer/lib/wrapper/freebsd/
|
|
||||||
file.reference.wrapper-linux=../../installer/lib/wrapper/linux/
|
|
||||||
file.reference.wrapper-linux64=../../installer/lib/wrapper/linux64/
|
|
||||||
file.reference.wrapper-macosx=../../installer/lib/wrapper/macosx/
|
|
||||||
file.reference.wrapper-solaris=../../installer/lib/wrapper/solaris/
|
|
||||||
file.reference.wrapper-win32=../../installer/lib/wrapper/win32/
|
|
||||||
file.reference.wrapper.jar=../../installer/lib/wrapper/linux/wrapper.jar
|
|
||||||
file.reference.wrapper.jar-1=../../installer/lib/wrapper/freebsd/wrapper.jar
|
|
||||||
file.reference.wrapper.jar-2=../../installer/lib/wrapper/linux64/wrapper.jar
|
|
||||||
file.reference.wrapper.jar-3=../../installer/lib/wrapper/macosx/wrapper.jar
|
|
||||||
file.reference.wrapper.jar-4=../../installer/lib/wrapper/solaris/wrapper.jar
|
|
||||||
file.reference.wrapper.jar-5=../../installer/lib/wrapper/win32/wrapper.jar
|
|
||||||
includes=**
|
|
||||||
jar.compress=false
|
|
||||||
javac.classpath=\
|
|
||||||
${file.reference.i2p.jar-1}:\
|
|
||||||
${file.reference.i2ptunnel.jar}:\
|
|
||||||
${file.reference.mstreaming.jar-1}:\
|
|
||||||
${file.reference.streaming.jar-1}:\
|
|
||||||
${file.reference.wrapper.jar-1}:\
|
|
||||||
${file.reference.wrapper.jar}:\
|
|
||||||
${file.reference.wrapper.jar-2}:\
|
|
||||||
${file.reference.wrapper.jar-3}:\
|
|
||||||
${file.reference.wrapper.jar-4}:\
|
|
||||||
${file.reference.wrapper.jar-5}
|
|
||||||
# Space-separated list of extra javac options
|
|
||||||
javac.compilerargs=
|
|
||||||
javac.deprecation=false
|
|
||||||
javac.source=1.5
|
|
||||||
javac.target=1.5
|
|
||||||
javac.test.classpath=\
|
|
||||||
${javac.classpath}:\
|
|
||||||
${build.classes.dir}:\
|
|
||||||
${libs.junit.classpath}:\
|
|
||||||
${libs.junit_4.classpath}
|
|
||||||
javadoc.additionalparam=
|
|
||||||
javadoc.author=false
|
|
||||||
javadoc.encoding=${source.encoding}
|
|
||||||
javadoc.noindex=false
|
|
||||||
javadoc.nonavbar=false
|
|
||||||
javadoc.notree=false
|
|
||||||
javadoc.private=false
|
|
||||||
javadoc.splitindex=true
|
|
||||||
javadoc.use=true
|
|
||||||
javadoc.version=false
|
|
||||||
javadoc.windowtitle=
|
|
||||||
jnlp.codebase.type=local
|
|
||||||
jnlp.codebase.url=file:/root/NetBeansProjects/i2p.i2p/apps/BOB/dist/
|
|
||||||
jnlp.descriptor=application
|
|
||||||
jnlp.enabled=false
|
|
||||||
jnlp.offline-allowed=false
|
|
||||||
jnlp.signed=false
|
|
||||||
main.class=net.i2p.BOB.Main
|
|
||||||
manifest.file=manifest.mf
|
|
||||||
meta.inf.dir=${src.dir}/META-INF
|
|
||||||
platform.active=default_platform
|
|
||||||
run.classpath=\
|
|
||||||
${javac.classpath}:\
|
|
||||||
${build.classes.dir}
|
|
||||||
# Space-separated list of JVM arguments used when running the project
|
|
||||||
# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
|
|
||||||
# or test-sys-prop.name=value to set system properties for unit tests):
|
|
||||||
run.jvmargs=
|
|
||||||
run.test.classpath=\
|
|
||||||
${javac.test.classpath}:\
|
|
||||||
${build.test.classes.dir}
|
|
||||||
source.encoding=UTF-8
|
|
||||||
src.dir=src
|
|
||||||
test.src.dir=test
|
|
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://www.netbeans.org/ns/project/1">
|
|
||||||
<type>org.netbeans.modules.java.j2seproject</type>
|
|
||||||
<configuration>
|
|
||||||
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
|
|
||||||
<name>BOB</name>
|
|
||||||
<minimum-ant-version>1.6.5</minimum-ant-version>
|
|
||||||
<source-roots>
|
|
||||||
<root id="src.dir"/>
|
|
||||||
</source-roots>
|
|
||||||
<test-roots>
|
|
||||||
<root id="test.src.dir"/>
|
|
||||||
</test-roots>
|
|
||||||
</data>
|
|
||||||
</configuration>
|
|
||||||
</project>
|
|
@ -1,238 +0,0 @@
|
|||||||
/**
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* Version 2, December 2004
|
|
||||||
*
|
|
||||||
* Copyright (C) sponge
|
|
||||||
* Planet Earth
|
|
||||||
* Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
* copies of this license document, and changing it is allowed as long
|
|
||||||
* as the name is changed.
|
|
||||||
*
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
*
|
|
||||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
*
|
|
||||||
* See...
|
|
||||||
*
|
|
||||||
* http://sam.zoy.org/wtfpl/
|
|
||||||
* and
|
|
||||||
* http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
*
|
|
||||||
* ...for any additional details and liscense questions.
|
|
||||||
*/
|
|
||||||
package net.i2p.BOB;
|
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.util.Properties;
|
|
||||||
import net.i2p.client.I2PClient;
|
|
||||||
import net.i2p.client.streaming.RetransmissionTimer;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
import net.i2p.util.SimpleTimer;
|
|
||||||
/**
|
|
||||||
* <span style="font-size:8px;font-family:courier;color:#EEEEEE;background-color:#000000">
|
|
||||||
* ################################################################################<br>
|
|
||||||
* ############################.#..........#..#..........##########################<br>
|
|
||||||
* #######################......................................###################<br>
|
|
||||||
* ####################...........................#.......#........################<br>
|
|
||||||
* #################..................##...................#.........##############<br>
|
|
||||||
* ###############................###...####.....#..###.....#.........#############<br>
|
|
||||||
* #############...........###..#..###...#####...###.##........#.......############<br>
|
|
||||||
* ###########................#......##...#####...##..##.......#..#........########<br>
|
|
||||||
* ##########.........................#....##.##..#...##.....................######<br>
|
|
||||||
* #########...................................#....#.........................#####<br>
|
|
||||||
* ########.........................................#...............#..........####<br>
|
|
||||||
* ########.........................................#..........#######..........###<br>
|
|
||||||
* #######.................................................############..........##<br>
|
|
||||||
* #######..........................................####################.........##<br>
|
|
||||||
* #######............####################......########################.........##<br>
|
|
||||||
* ######.............###############################################.##.........##<br>
|
|
||||||
* ######............################################################..##........##<br>
|
|
||||||
* ######............################################################..##........##<br>
|
|
||||||
* ######.............##############################################..##.........##<br>
|
|
||||||
* ######............##############################################...##..........#<br>
|
|
||||||
* ######............#..###########################################...##..........#<br>
|
|
||||||
* ######.............#############################################....#..........#<br>
|
|
||||||
* #######...........###############################################..##.........##<br>
|
|
||||||
* #######...........#####.#.#.#.########################.....#.####...##........##<br>
|
|
||||||
* ######............#..............##################.................##.........#<br>
|
|
||||||
* ######................####.........###############........#####......##........#<br>
|
|
||||||
* ######..............####..#.........############.......##.#.######...##.......##<br>
|
|
||||||
* ######.................#.####.........########...........##....###...##.......##<br>
|
|
||||||
* #######....#....###...................#######...............#...###..##.......##<br>
|
|
||||||
* #######.........###..###.....###.......######.##.#####.........####..##.......##<br>
|
|
||||||
* #######.....#...##############.........############......###########.###......##<br>
|
|
||||||
* #######....##...##########.......##...##############......#.############.....###<br>
|
|
||||||
* ########....#..########......######...##################################....####<br>
|
|
||||||
* ########....##.####################...##################################....####<br>
|
|
||||||
* ########..#.##..###################..##################################..#..####<br>
|
|
||||||
* ##########..###..#################...##################################...#.####<br>
|
|
||||||
* #########....##...##############....########..#####.################.##..#.#####<br>
|
|
||||||
* ############.##....##########.......#########.###.......###########..#.#########<br>
|
|
||||||
* ###############.....#######...#.......########.....##.....######.....###########<br>
|
|
||||||
* ###############......###....##..........##.......######....#.........#.#########<br>
|
|
||||||
* ##############............##..................##########..............##########<br>
|
|
||||||
* ##############..............................##########..#.............##########<br>
|
|
||||||
* ###############.......##..................#####..............####....###########<br>
|
|
||||||
* ###############.......#####.......#.............####.....#######.....###########<br>
|
|
||||||
* ################...#...####......##################.....########....############<br>
|
|
||||||
* ################...##..#####.........####.##.....#....##########....############<br>
|
|
||||||
* ##################..##..####...........#####.#....############.....#############<br>
|
|
||||||
* ##################......#####.................################....##############<br>
|
|
||||||
* ###################.....####..........##########..###########....###############<br>
|
|
||||||
* ####################..#..#..........................########.....###############<br>
|
|
||||||
* #####################.##.......###.................########....#################<br>
|
|
||||||
* ######################.........#.......#.##.###############....#################<br>
|
|
||||||
* #############.#######...............#####################....###################<br>
|
|
||||||
* ###..#.....##...####..........#.....####################....####################<br>
|
|
||||||
* ####......##........................##################....######################<br>
|
|
||||||
* #.##...###..............###.........###############......#######################<br>
|
|
||||||
* #...###..##............######...........................########################<br>
|
|
||||||
* ##.......###..........##########....#...#...........############################<br>
|
|
||||||
* ##.........##.......############################################################<br>
|
|
||||||
* ###........##.....##############################################################<br>
|
|
||||||
* ####.............###############################################################<br>
|
|
||||||
* ######.........#################################################################<br>
|
|
||||||
* #########....###################################################################<br>
|
|
||||||
* ################################################################################<br>
|
|
||||||
* </span>
|
|
||||||
* BOB, main command socket listener, launches the command parser engine.
|
|
||||||
*
|
|
||||||
* @author sponge
|
|
||||||
*/
|
|
||||||
public class BOB {
|
|
||||||
|
|
||||||
private final static Log _log = new Log(BOB.class);
|
|
||||||
public final static String PROP_CONFIG_LOCATION = "BOB.config";
|
|
||||||
public final static String PROP_BOB_PORT = "BOB.port";
|
|
||||||
public final static String PROP_BOB_HOST = "BOB.host";
|
|
||||||
private static int maxConnections = 0;
|
|
||||||
private static NamedDB database;
|
|
||||||
private static Properties props = new Properties();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log a warning
|
|
||||||
*
|
|
||||||
* @param arg
|
|
||||||
*/
|
|
||||||
public static void info(String arg) {
|
|
||||||
System.out.println("INFO:" + arg);
|
|
||||||
_log.info(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log a warning
|
|
||||||
*
|
|
||||||
* @param arg
|
|
||||||
*/
|
|
||||||
public static void warn(String arg) {
|
|
||||||
System.out.println("WARNING:" + arg);
|
|
||||||
_log.warn(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log an error
|
|
||||||
*
|
|
||||||
* @param arg
|
|
||||||
*/
|
|
||||||
public static void error(String arg) {
|
|
||||||
System.out.println("ERROR: " + arg);
|
|
||||||
_log.error(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listen for incoming connections and handle them
|
|
||||||
*
|
|
||||||
* @param args
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
database = new NamedDB();
|
|
||||||
int i = 0;
|
|
||||||
boolean save = false;
|
|
||||||
// Set up all defaults to be passed forward to other threads.
|
|
||||||
// Re-reading the config file in each thread is pretty damn stupid.
|
|
||||||
// I2PClient client = I2PClientFactory.createClient();
|
|
||||||
String configLocation = System.getProperty(PROP_CONFIG_LOCATION, "bob.config");
|
|
||||||
|
|
||||||
// This is here just to ensure there is no interference with our threadgroups.
|
|
||||||
SimpleTimer Y = RetransmissionTimer.getInstance();
|
|
||||||
i = Y.hashCode();
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
FileInputStream fi = new FileInputStream(configLocation);
|
|
||||||
props.load(fi);
|
|
||||||
fi.close();
|
|
||||||
} catch(FileNotFoundException fnfe) {
|
|
||||||
warn("Unable to load up the BOB config file " + configLocation + ", Using defaults.");
|
|
||||||
warn(fnfe.toString());
|
|
||||||
save = true;
|
|
||||||
} catch(IOException ioe) {
|
|
||||||
warn("IOException on BOB config file " + configLocation + ", using defaults.");
|
|
||||||
warn(ioe.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Global router and client API configurations that are missing are set to defaults here.
|
|
||||||
if(!props.containsKey(I2PClient.PROP_TCP_HOST)) {
|
|
||||||
props.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
|
|
||||||
}
|
|
||||||
if(!props.containsKey(I2PClient.PROP_TCP_PORT)) {
|
|
||||||
props.setProperty(I2PClient.PROP_TCP_PORT, "7654");
|
|
||||||
}
|
|
||||||
if(!props.containsKey(I2PClient.PROP_RELIABILITY)) {
|
|
||||||
props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
|
|
||||||
}
|
|
||||||
if(!props.containsKey(PROP_BOB_PORT)) {
|
|
||||||
props.setProperty(PROP_BOB_PORT, "2827"); // 0xB0B
|
|
||||||
}
|
|
||||||
if(!props.containsKey("inbound.length")) {
|
|
||||||
props.setProperty("inbound.length", "1");
|
|
||||||
}
|
|
||||||
if(!props.containsKey("outbound.length")) {
|
|
||||||
props.setProperty("outbound.length", "1");
|
|
||||||
}
|
|
||||||
if(!props.containsKey("inbound.lengthVariance")) {
|
|
||||||
props.setProperty("inbound.lengthVariance", "0");
|
|
||||||
}
|
|
||||||
if(!props.containsKey("outbound.lengthVariance")) {
|
|
||||||
props.setProperty("outbound.lengthVariance", "0");
|
|
||||||
}
|
|
||||||
if(!props.containsKey(PROP_BOB_HOST)) {
|
|
||||||
props.setProperty(PROP_BOB_HOST, "localhost");
|
|
||||||
}
|
|
||||||
if(save) {
|
|
||||||
try {
|
|
||||||
warn("Writing new defaults file " + configLocation);
|
|
||||||
FileOutputStream fo = new FileOutputStream(configLocation);
|
|
||||||
props.store(fo, configLocation);
|
|
||||||
fo.close();
|
|
||||||
} catch(IOException ioe) {
|
|
||||||
error("IOException on BOB config file " + configLocation + ", " + ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
info("BOB is now running.");
|
|
||||||
ServerSocket listener = new ServerSocket(Integer.parseInt(props.getProperty(PROP_BOB_PORT)), 10, InetAddress.getByName(props.getProperty(PROP_BOB_HOST)));
|
|
||||||
Socket server;
|
|
||||||
|
|
||||||
while((i++ < maxConnections) || (maxConnections == 0)) {
|
|
||||||
//DoCMDS connection;
|
|
||||||
|
|
||||||
server = listener.accept();
|
|
||||||
DoCMDS conn_c = new DoCMDS(server, props, database, _log);
|
|
||||||
Thread t = new Thread(conn_c);
|
|
||||||
t.start();
|
|
||||||
}
|
|
||||||
} catch(IOException ioe) {
|
|
||||||
error("IOException on socket listen: " + ioe);
|
|
||||||
ioe.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
Version 2, December 2004
|
|
||||||
|
|
||||||
Copyright (C) sponge
|
|
||||||
Planet Earth
|
|
||||||
Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
copies of this license document, and changing it is allowed as long
|
|
||||||
as the name is changed.
|
|
||||||
|
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
|
|
||||||
See...
|
|
||||||
|
|
||||||
http://sam.zoy.org/wtfpl/
|
|
||||||
and
|
|
||||||
http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
|
|
||||||
...for any additional details and license questions.
|
|
File diff suppressed because it is too large
Load Diff
@ -1,137 +0,0 @@
|
|||||||
/**
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* Version 2, December 2004
|
|
||||||
*
|
|
||||||
* Copyright (C) sponge
|
|
||||||
* Planet Earth
|
|
||||||
* Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
* copies of this license document, and changing it is allowed as long
|
|
||||||
* as the name is changed.
|
|
||||||
*
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
*
|
|
||||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
*
|
|
||||||
* See...
|
|
||||||
*
|
|
||||||
* http://sam.zoy.org/wtfpl/
|
|
||||||
* and
|
|
||||||
* http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
*
|
|
||||||
* ...for any additional details and liscense questions.
|
|
||||||
*/
|
|
||||||
package net.i2p.BOB;
|
|
||||||
|
|
||||||
import java.net.ConnectException;
|
|
||||||
import java.net.SocketTimeoutException;
|
|
||||||
import net.i2p.I2PException;
|
|
||||||
import net.i2p.client.I2PSession;
|
|
||||||
import net.i2p.client.I2PSessionException;
|
|
||||||
import net.i2p.client.streaming.I2PServerSocket;
|
|
||||||
import net.i2p.client.streaming.I2PSocket;
|
|
||||||
import net.i2p.client.streaming.I2PSocketManager;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listen on I2P and connect to TCP
|
|
||||||
*
|
|
||||||
* @author sponge
|
|
||||||
*/
|
|
||||||
public class I2Plistener implements Runnable {
|
|
||||||
|
|
||||||
private NamedDB info, database;
|
|
||||||
private Log _log;
|
|
||||||
private int tgwatch;
|
|
||||||
public I2PSocketManager socketManager;
|
|
||||||
public I2PServerSocket serverSocket;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
* @param S
|
|
||||||
* @param info
|
|
||||||
* @param database
|
|
||||||
* @param _log
|
|
||||||
*/
|
|
||||||
I2Plistener(I2PSocketManager S, NamedDB info, NamedDB database, Log _log) {
|
|
||||||
this.database = database;
|
|
||||||
this.info = info;
|
|
||||||
this._log = _log;
|
|
||||||
this.socketManager = S;
|
|
||||||
serverSocket = this.socketManager.getServerSocket();
|
|
||||||
tgwatch = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simply listen on I2P port, and thread connections
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void run() {
|
|
||||||
boolean g = false;
|
|
||||||
I2PSocket sessSocket = null;
|
|
||||||
|
|
||||||
serverSocket.setSoTimeout(100);
|
|
||||||
database.getReadLock();
|
|
||||||
info.getReadLock();
|
|
||||||
if(info.exists("INPORT")) {
|
|
||||||
tgwatch = 2;
|
|
||||||
}
|
|
||||||
info.releaseReadLock();
|
|
||||||
database.releaseReadLock();
|
|
||||||
boolean spin = true;
|
|
||||||
while(spin) {
|
|
||||||
|
|
||||||
database.getReadLock();
|
|
||||||
info.getReadLock();
|
|
||||||
spin = info.get("RUNNING").equals(Boolean.TRUE);
|
|
||||||
info.releaseReadLock();
|
|
||||||
database.releaseReadLock();
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
sessSocket = serverSocket.accept();
|
|
||||||
g = true;
|
|
||||||
} catch(ConnectException ce) {
|
|
||||||
g = false;
|
|
||||||
} catch(SocketTimeoutException ste) {
|
|
||||||
g = false;
|
|
||||||
}
|
|
||||||
if(g) {
|
|
||||||
g = false;
|
|
||||||
// toss the connection to a new thread.
|
|
||||||
I2PtoTCP conn_c = new I2PtoTCP(sessSocket, info, database);
|
|
||||||
Thread t = new Thread(conn_c, "BOBI2PtoTCP");
|
|
||||||
t.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch(I2PException e) {
|
|
||||||
// System.out.println("Exception " + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// System.out.println("I2Plistener: Close");
|
|
||||||
try {
|
|
||||||
serverSocket.close();
|
|
||||||
} catch(I2PException e) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
// need to kill off the socket manager too.
|
|
||||||
I2PSession session = socketManager.getSession();
|
|
||||||
if(session != null) {
|
|
||||||
// System.out.println("I2Plistener: destroySession");
|
|
||||||
try {
|
|
||||||
session.destroySession();
|
|
||||||
} catch(I2PSessionException ex) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// System.out.println("I2Plistener: Waiting for children");
|
|
||||||
while(Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish
|
|
||||||
try {
|
|
||||||
Thread.sleep(100); //sleep for 100 ms (One tenth second)
|
|
||||||
} catch(Exception e) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// System.out.println("I2Plistener: Done.");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
/**
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* Version 2, December 2004
|
|
||||||
*
|
|
||||||
* Copyright (C) sponge
|
|
||||||
* Planet Earth
|
|
||||||
* Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
* copies of this license document, and changing it is allowed as long
|
|
||||||
* as the name is changed.
|
|
||||||
*
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
*
|
|
||||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
*
|
|
||||||
* See...
|
|
||||||
*
|
|
||||||
* http://sam.zoy.org/wtfpl/
|
|
||||||
* and
|
|
||||||
* http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
*
|
|
||||||
* ...for any additional details and liscense questions.
|
|
||||||
*/
|
|
||||||
package net.i2p.BOB;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.Socket;
|
|
||||||
import net.i2p.client.streaming.I2PSocket;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process I2P->TCP
|
|
||||||
*
|
|
||||||
* @author sponge
|
|
||||||
*/
|
|
||||||
public class I2PtoTCP implements Runnable {
|
|
||||||
|
|
||||||
private I2PSocket I2P;
|
|
||||||
private NamedDB info, database;
|
|
||||||
private Socket sock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param I2Psock
|
|
||||||
* @param info
|
|
||||||
* @param database
|
|
||||||
*/
|
|
||||||
I2PtoTCP(I2PSocket I2Psock, NamedDB info, NamedDB database) {
|
|
||||||
this.I2P = I2Psock;
|
|
||||||
this.info = info;
|
|
||||||
this.database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void rlock() throws Exception {
|
|
||||||
database.getReadLock();
|
|
||||||
info.getReadLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runlock() throws Exception {
|
|
||||||
database.releaseReadLock();
|
|
||||||
info.releaseReadLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* I2P stream to TCP stream thread starter
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void run() {
|
|
||||||
String host;
|
|
||||||
int port;
|
|
||||||
boolean tell;
|
|
||||||
die: {
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
rlock();
|
|
||||||
} catch(Exception e) {
|
|
||||||
break die;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
host = info.get("OUTHOST").toString();
|
|
||||||
port = Integer.parseInt(info.get("OUTPORT").toString());
|
|
||||||
tell = info.get("QUIET").equals(Boolean.FALSE);
|
|
||||||
} catch(Exception e) {
|
|
||||||
runlock();
|
|
||||||
break die;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
runlock();
|
|
||||||
} catch(Exception e) {
|
|
||||||
break die;
|
|
||||||
}
|
|
||||||
sock = new Socket(host, port);
|
|
||||||
// make readers/writers
|
|
||||||
InputStream in = sock.getInputStream();
|
|
||||||
OutputStream out = sock.getOutputStream();
|
|
||||||
InputStream Iin = I2P.getInputStream();
|
|
||||||
OutputStream Iout = I2P.getOutputStream();
|
|
||||||
I2P.setReadTimeout(0); // temp bugfix, this *SHOULD* be the default
|
|
||||||
|
|
||||||
if(tell) {
|
|
||||||
// tell who is connecting
|
|
||||||
out.write(I2P.getPeerDestination().toBase64().getBytes());
|
|
||||||
out.write(10); // nl
|
|
||||||
out.flush(); // not really needed, but...
|
|
||||||
}
|
|
||||||
// setup to cross the streams
|
|
||||||
TCPio conn_c = new TCPio(in, Iout, info, database); // app -> I2P
|
|
||||||
TCPio conn_a = new TCPio(Iin, out, info, database); // I2P -> app
|
|
||||||
Thread t = new Thread(conn_c, "TCPioA");
|
|
||||||
Thread q = new Thread(conn_a, "TCPioB");
|
|
||||||
// Fire!
|
|
||||||
t.start();
|
|
||||||
q.start();
|
|
||||||
while(t.isAlive() && q.isAlive()) { // AND is used here to kill off the other thread
|
|
||||||
try {
|
|
||||||
Thread.sleep(10); //sleep for 10 ms
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// System.out.println("I2PtoTCP: Going away...");
|
|
||||||
} catch(Exception e) {
|
|
||||||
// System.out.println("I2PtoTCP: Owch! damn!");
|
|
||||||
break die;
|
|
||||||
}
|
|
||||||
} // die
|
|
||||||
try {
|
|
||||||
// System.out.println("I2PtoTCP: Close I2P");
|
|
||||||
I2P.close();
|
|
||||||
} catch(Exception e) {
|
|
||||||
tell = false;
|
|
||||||
}
|
|
||||||
//System.out.println("I2PtoTCP: Closed I2P");
|
|
||||||
try {
|
|
||||||
// System.out.println("I2PtoTCP: Close sock");
|
|
||||||
sock.close();
|
|
||||||
} catch(Exception e) {
|
|
||||||
tell = false;
|
|
||||||
}
|
|
||||||
// System.out.println("I2PtoTCP: Done");
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
/**
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* Version 2, December 2004
|
|
||||||
*
|
|
||||||
* Copyright (C) sponge
|
|
||||||
* Planet Earth
|
|
||||||
* Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
* copies of this license document, and changing it is allowed as long
|
|
||||||
* as the name is changed.
|
|
||||||
*
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
*
|
|
||||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
*
|
|
||||||
* See...
|
|
||||||
*
|
|
||||||
* http://sam.zoy.org/wtfpl/
|
|
||||||
* and
|
|
||||||
* http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
*
|
|
||||||
* ...for any additional details and liscense questions.
|
|
||||||
*/
|
|
||||||
package net.i2p.BOB;
|
|
||||||
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets of "friendly" utilities to make life easier.
|
|
||||||
* Any "Lifted" code will apear here, and credits given.
|
|
||||||
* It's better to "Lift" a small chunk of "free" code than add in piles of
|
|
||||||
* code we don't need, and don't want.
|
|
||||||
*
|
|
||||||
* @author sponge
|
|
||||||
*/
|
|
||||||
public class Lifted {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy a set of properties from one Property to another.
|
|
||||||
* Lifted from Apache Derby code svn repository.
|
|
||||||
* Liscenced as follows:
|
|
||||||
* http://svn.apache.org/repos/asf/db/derby/code/trunk/LICENSE
|
|
||||||
*
|
|
||||||
* @param src_prop Source set of properties to copy from.
|
|
||||||
* @param dest_prop Dest Properties to copy into.
|
|
||||||
*
|
|
||||||
**/
|
|
||||||
public static void copyProperties(Properties src_prop, Properties dest_prop) {
|
|
||||||
for (Enumeration propertyNames = src_prop.propertyNames();
|
|
||||||
propertyNames.hasMoreElements();) {
|
|
||||||
Object key = propertyNames.nextElement();
|
|
||||||
dest_prop.put(key, src_prop.get(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,276 +0,0 @@
|
|||||||
/**
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* Version 2, December 2004
|
|
||||||
*
|
|
||||||
* Copyright (C) sponge
|
|
||||||
* Planet Earth
|
|
||||||
* Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
* copies of this license document, and changing it is allowed as long
|
|
||||||
* as the name is changed.
|
|
||||||
*
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
*
|
|
||||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
*
|
|
||||||
* See...
|
|
||||||
*
|
|
||||||
* http://sam.zoy.org/wtfpl/
|
|
||||||
* and
|
|
||||||
* http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
*
|
|
||||||
* ...for any additional details and liscense questions.
|
|
||||||
*/
|
|
||||||
package net.i2p.BOB;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.util.Properties;
|
|
||||||
import net.i2p.I2PException;
|
|
||||||
import net.i2p.client.streaming.I2PSocketManager;
|
|
||||||
import net.i2p.client.streaming.I2PSocketManagerFactory;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
import org.tanukisoftware.wrapper.WrapperManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Multiplex listeners for TCP and I2P
|
|
||||||
*
|
|
||||||
* @author sponge
|
|
||||||
*/
|
|
||||||
public class MUXlisten implements Runnable {
|
|
||||||
|
|
||||||
private NamedDB database, info;
|
|
||||||
private Log _log;
|
|
||||||
private I2PSocketManager socketManager;
|
|
||||||
private ByteArrayInputStream prikey;
|
|
||||||
private ThreadGroup tg;
|
|
||||||
private String N;
|
|
||||||
private ServerSocket listener;
|
|
||||||
private int backlog = 50; // should this be more? less?
|
|
||||||
boolean go_out;
|
|
||||||
boolean come_in;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor Will fail if INPORT is occupied.
|
|
||||||
*
|
|
||||||
* @param info
|
|
||||||
* @param database
|
|
||||||
* @param _log
|
|
||||||
* @throws net.i2p.I2PException
|
|
||||||
* @throws java.io.IOException
|
|
||||||
*/
|
|
||||||
MUXlisten(NamedDB database, NamedDB info, Log _log) throws I2PException, IOException, RuntimeException {
|
|
||||||
int port = 0;
|
|
||||||
InetAddress host = null;
|
|
||||||
this.tg = null;
|
|
||||||
this.database = database;
|
|
||||||
this.info = info;
|
|
||||||
this._log = _log;
|
|
||||||
|
|
||||||
this.database.getReadLock();
|
|
||||||
this.info.getReadLock();
|
|
||||||
N = this.info.get("NICKNAME").toString();
|
|
||||||
prikey = new ByteArrayInputStream((byte[])info.get("KEYS"));
|
|
||||||
// Make a new copy so that anything else won't muck with our database.
|
|
||||||
Properties R = (Properties)info.get("PROPERTIES");
|
|
||||||
Properties Q = new Properties();
|
|
||||||
Lifted.copyProperties(R, Q);
|
|
||||||
this.database.releaseReadLock();
|
|
||||||
this.info.releaseReadLock();
|
|
||||||
|
|
||||||
this.database.getReadLock();
|
|
||||||
this.info.getReadLock();
|
|
||||||
this.go_out = info.exists("OUTPORT");
|
|
||||||
this.come_in = info.exists("INPORT");
|
|
||||||
if(this.come_in) {
|
|
||||||
port = Integer.parseInt(info.get("INPORT").toString());
|
|
||||||
host = InetAddress.getByName(info.get("INHOST").toString());
|
|
||||||
}
|
|
||||||
this.database.releaseReadLock();
|
|
||||||
this.info.releaseReadLock();
|
|
||||||
|
|
||||||
socketManager = I2PSocketManagerFactory.createManager(prikey, Q);
|
|
||||||
if(this.come_in) {
|
|
||||||
this.listener = new ServerSocket(port, backlog, host);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything is OK as far as we can tell.
|
|
||||||
this.database.getWriteLock();
|
|
||||||
this.info.getWriteLock();
|
|
||||||
this.info.add("STARTING", Boolean.TRUE);
|
|
||||||
this.info.releaseWriteLock();
|
|
||||||
this.database.releaseWriteLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void rlock() throws Exception {
|
|
||||||
database.getReadLock();
|
|
||||||
info.getReadLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runlock() throws Exception {
|
|
||||||
database.releaseReadLock();
|
|
||||||
info.releaseReadLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void wlock() throws Exception {
|
|
||||||
database.getWriteLock();
|
|
||||||
info.getWriteLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void wunlock() throws Exception {
|
|
||||||
info.releaseWriteLock();
|
|
||||||
database.releaseWriteLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MUX sockets, fire off a thread to connect, get destination info, and do I/O
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void run() {
|
|
||||||
|
|
||||||
try {
|
|
||||||
wlock();
|
|
||||||
try {
|
|
||||||
info.add("RUNNING", Boolean.TRUE);
|
|
||||||
info.add("STARTING", Boolean.FALSE);
|
|
||||||
} catch(Exception e) {
|
|
||||||
wunlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
wunlock();
|
|
||||||
} catch(Exception e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
quit: {
|
|
||||||
try {
|
|
||||||
tg = new ThreadGroup(N);
|
|
||||||
die: {
|
|
||||||
// toss the connections to a new threads.
|
|
||||||
// will wrap with TCP and UDP when UDP works
|
|
||||||
|
|
||||||
if(go_out) {
|
|
||||||
// I2P -> TCP
|
|
||||||
I2Plistener conn = new I2Plistener(socketManager, info, database, _log);
|
|
||||||
Thread t = new Thread(tg, conn, "BOBI2Plistener " + N);
|
|
||||||
t.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(come_in) {
|
|
||||||
// TCP -> I2P
|
|
||||||
TCPlistener conn = new TCPlistener(listener, socketManager, info, database, _log);
|
|
||||||
Thread q = new Thread(tg, conn, "BOBTCPlistener" + N);
|
|
||||||
q.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean spin = true;
|
|
||||||
while(spin) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(1000); //sleep for 1000 ms (One second)
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
rlock();
|
|
||||||
try {
|
|
||||||
spin = info.get("STOPPING").equals(Boolean.FALSE);
|
|
||||||
} catch(Exception e) {
|
|
||||||
runlock();
|
|
||||||
break die;
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
break die;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
runlock();
|
|
||||||
} catch(Exception e) {
|
|
||||||
break die;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
wlock();
|
|
||||||
try {
|
|
||||||
info.add("RUNNING", Boolean.FALSE);
|
|
||||||
} catch(Exception e) {
|
|
||||||
wunlock();
|
|
||||||
break die;
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
break die;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
wunlock();
|
|
||||||
} catch(Exception e) {
|
|
||||||
break die;
|
|
||||||
}
|
|
||||||
} // die
|
|
||||||
|
|
||||||
// wait for child threads and thread groups to die
|
|
||||||
// System.out.println("MUXlisten: waiting for children");
|
|
||||||
while(tg.activeCount() + tg.activeGroupCount() != 0) {
|
|
||||||
tg.interrupt(); // unwedge any blocking threads.
|
|
||||||
try {
|
|
||||||
Thread.sleep(100); //sleep for 100 ms (One tenth second)
|
|
||||||
} catch(InterruptedException ex) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tg.destroy();
|
|
||||||
// Zap reference to the ThreadGroup so the JVM can GC it.
|
|
||||||
tg = null;
|
|
||||||
} catch(Exception e) {
|
|
||||||
// System.out.println("MUXlisten: Caught an exception" + e);
|
|
||||||
break quit;
|
|
||||||
}
|
|
||||||
} // quit
|
|
||||||
// This is here to catch when something fucks up REALLY bad.
|
|
||||||
if(tg != null) {
|
|
||||||
System.out.println("BOB: MUXlisten: Something fucked up REALLY bad!");
|
|
||||||
System.out.println("BOB: MUXlisten: Please email the following dump to sponge@mail.i2p");
|
|
||||||
WrapperManager.requestThreadDump();
|
|
||||||
System.out.println("BOB: MUXlisten: Something fucked up REALLY bad!");
|
|
||||||
System.out.println("BOB: MUXlisten: Please email the above dump to sponge@mail.i2p");
|
|
||||||
}
|
|
||||||
// zero out everything, just incase.
|
|
||||||
try {
|
|
||||||
socketManager.destroySocketManager();
|
|
||||||
} catch(Exception e) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
wlock();
|
|
||||||
try {
|
|
||||||
info.add("STARTING", Boolean.FALSE);
|
|
||||||
info.add("STOPPING", Boolean.FALSE);
|
|
||||||
info.add("RUNNING", Boolean.FALSE);
|
|
||||||
} catch(Exception e) {
|
|
||||||
wunlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wunlock();
|
|
||||||
} catch(Exception e) {
|
|
||||||
}
|
|
||||||
// This is here to catch when something fucks up REALLY bad.
|
|
||||||
if(tg != null) {
|
|
||||||
while(tg.activeCount() + tg.activeGroupCount() != 0) {
|
|
||||||
tg.interrupt(); // unwedge any blocking threads.
|
|
||||||
try {
|
|
||||||
Thread.sleep(100); //sleep for 100 ms (One tenth second)
|
|
||||||
} catch(InterruptedException ex) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tg.destroy();
|
|
||||||
// Zap reference to the ThreadGroup so the JVM can GC it.
|
|
||||||
tg = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
/**
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* Version 2, December 2004
|
|
||||||
*
|
|
||||||
* Copyright (C) sponge
|
|
||||||
* Planet Earth
|
|
||||||
* Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
* copies of this license document, and changing it is allowed as long
|
|
||||||
* as the name is changed.
|
|
||||||
*
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
*
|
|
||||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
*
|
|
||||||
* See...
|
|
||||||
*
|
|
||||||
* http://sam.zoy.org/wtfpl/
|
|
||||||
* and
|
|
||||||
* http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
*
|
|
||||||
* ...for any additional details and liscense questions.
|
|
||||||
*/
|
|
||||||
package net.i2p.BOB;
|
|
||||||
|
|
||||||
import net.i2p.client.streaming.RetransmissionTimer;
|
|
||||||
import net.i2p.util.SimpleTimer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start from command line
|
|
||||||
*
|
|
||||||
* @author sponge
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class Main {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param args the command line arguments, these are not used yet
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
// THINK THINK THINK THINK THINK THINK
|
|
||||||
SimpleTimer Y = RetransmissionTimer.getInstance();
|
|
||||||
BOB.main(args);
|
|
||||||
Y.removeSimpleTimer();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,195 +0,0 @@
|
|||||||
/**
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* Version 2, December 2004
|
|
||||||
*
|
|
||||||
* Copyright (C) sponge
|
|
||||||
* Planet Earth
|
|
||||||
* Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
* copies of this license document, and changing it is allowed as long
|
|
||||||
* as the name is changed.
|
|
||||||
*
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
*
|
|
||||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
*
|
|
||||||
* See...
|
|
||||||
*
|
|
||||||
* http://sam.zoy.org/wtfpl/
|
|
||||||
* and
|
|
||||||
* http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
*
|
|
||||||
* ...for any additional details and liscense questions.
|
|
||||||
*/
|
|
||||||
package net.i2p.BOB;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal database to relate nicknames to options to values
|
|
||||||
*
|
|
||||||
* @author sponge
|
|
||||||
*/
|
|
||||||
public class NamedDB {
|
|
||||||
|
|
||||||
private volatile Object[][] data;
|
|
||||||
private volatile int index, writersWaiting, readers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* make initial NULL object
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public NamedDB() {
|
|
||||||
this.data = new Object[1][2];
|
|
||||||
this.index = this.writersWaiting = this.readers = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized public void getReadLock() {
|
|
||||||
while((writersWaiting != 0)) {
|
|
||||||
try {
|
|
||||||
wait();
|
|
||||||
} catch(InterruptedException ie) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readers++;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized public void releaseReadLock() {
|
|
||||||
readers--;
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized public void getWriteLock() {
|
|
||||||
writersWaiting++;
|
|
||||||
while(readers != 0 && writersWaiting != 1 ) {
|
|
||||||
try {
|
|
||||||
wait();
|
|
||||||
} catch(InterruptedException ie) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized public void releaseWriteLock() {
|
|
||||||
writersWaiting--;
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find objects in the array, returns it's index or throws exception
|
|
||||||
* @param key
|
|
||||||
* @return an objects index
|
|
||||||
* @throws ArrayIndexOutOfBoundsException when key does not exist
|
|
||||||
*/
|
|
||||||
public int idx(Object key) throws ArrayIndexOutOfBoundsException {
|
|
||||||
for(int i = 0; i < index; i++) {
|
|
||||||
if(key.equals(data[i][0])) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new ArrayIndexOutOfBoundsException("Can't locate key for index");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an object from array if it exists
|
|
||||||
*
|
|
||||||
* @param key
|
|
||||||
*/
|
|
||||||
public void kill(Object key) {
|
|
||||||
|
|
||||||
int i, j, k, l;
|
|
||||||
Object[][] olddata;
|
|
||||||
int didsomething = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
k = idx(key);
|
|
||||||
} catch(ArrayIndexOutOfBoundsException b) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
olddata = new Object[index + 2][2];
|
|
||||||
// copy to olddata, skipping 'k'
|
|
||||||
for(i = 0 , l = 0; l < index; i++, l++) {
|
|
||||||
if(i == k) {
|
|
||||||
l++;
|
|
||||||
didsomething++;
|
|
||||||
}
|
|
||||||
for(j = 0; j < 2; j++) {
|
|
||||||
olddata[i][j] = data[l][j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
index -= didsomething;
|
|
||||||
data = olddata;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add object to the array, deletes the old one if it exists
|
|
||||||
*
|
|
||||||
* @param key
|
|
||||||
* @param val
|
|
||||||
*/
|
|
||||||
public void add(Object key, Object val) {
|
|
||||||
Object[][] olddata;
|
|
||||||
int i, j;
|
|
||||||
i = 0;
|
|
||||||
kill(key);
|
|
||||||
|
|
||||||
olddata = new Object[index + 2][2];
|
|
||||||
// copy to olddata
|
|
||||||
for(i = 0; i < index; i++) {
|
|
||||||
for(j = 0; j < 2; j++) {
|
|
||||||
olddata[i][j] = data[i][j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data = olddata;
|
|
||||||
data[index++] = new Object[] {key, val};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the object, and return it, throws RuntimeException
|
|
||||||
*
|
|
||||||
* @param key
|
|
||||||
* @return Object
|
|
||||||
* @throws java.lang.RuntimeException
|
|
||||||
*/
|
|
||||||
public Object get(Object key) throws RuntimeException {
|
|
||||||
for(int i = 0; i < index; i++) {
|
|
||||||
if(key.equals(data[i][0])) {
|
|
||||||
return data[i][1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new RuntimeException("Key not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns true if an object exists, else returns false
|
|
||||||
*
|
|
||||||
* @param key
|
|
||||||
* @return true if an object exists, else returns false
|
|
||||||
*/
|
|
||||||
public boolean exists(Object key) {
|
|
||||||
for(int i = 0; i < index; i++) {
|
|
||||||
if(key.equals(data[i][0])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param i index
|
|
||||||
* @return an indexed Object
|
|
||||||
* @throws java.lang.RuntimeException
|
|
||||||
*/
|
|
||||||
public Object getnext(int i) throws RuntimeException {
|
|
||||||
if(i < index && i > -1) {
|
|
||||||
return data[i][1];
|
|
||||||
}
|
|
||||||
throw new RuntimeException("No more data");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the count of how many objects
|
|
||||||
*/
|
|
||||||
public int getcount() {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
/**
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* Version 2, December 2004
|
|
||||||
*
|
|
||||||
* Copyright (C) sponge
|
|
||||||
* Planet Earth
|
|
||||||
* Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
* copies of this license document, and changing it is allowed as long
|
|
||||||
* as the name is changed.
|
|
||||||
*
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
*
|
|
||||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
*
|
|
||||||
* See...
|
|
||||||
*
|
|
||||||
* http://sam.zoy.org/wtfpl/
|
|
||||||
* and
|
|
||||||
* http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
*
|
|
||||||
* ...for any additional details and liscense questions.
|
|
||||||
*/
|
|
||||||
package net.i2p.BOB;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shove data from one stream to the other.
|
|
||||||
*
|
|
||||||
* @author sponge
|
|
||||||
*/
|
|
||||||
public class TCPio implements Runnable {
|
|
||||||
|
|
||||||
private InputStream Ain;
|
|
||||||
private OutputStream Aout;
|
|
||||||
private NamedDB info, database;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param Ain
|
|
||||||
* @param Aout
|
|
||||||
* @param info
|
|
||||||
* @param database
|
|
||||||
*/
|
|
||||||
TCPio(InputStream Ain, OutputStream Aout, NamedDB info, NamedDB database) {
|
|
||||||
this.Ain = Ain;
|
|
||||||
this.Aout = Aout;
|
|
||||||
this.info = info;
|
|
||||||
this.database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy from source to destination...
|
|
||||||
* and yes, we are totally OK to block here on writes,
|
|
||||||
* The OS has buffers, and I intend to use them.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void run() {
|
|
||||||
int b;
|
|
||||||
byte a[] = new byte[1];
|
|
||||||
boolean spin = true;
|
|
||||||
try {
|
|
||||||
while(spin) {
|
|
||||||
database.getReadLock();
|
|
||||||
info.getReadLock();
|
|
||||||
spin = info.get("RUNNING").equals(Boolean.TRUE);
|
|
||||||
info.releaseReadLock();
|
|
||||||
database.releaseReadLock();
|
|
||||||
b = Ain.read(a, 0, 1);
|
|
||||||
// System.out.println(info.get("NICKNAME").toString() + " " + b);
|
|
||||||
if(b > 0) {
|
|
||||||
Aout.write(a, 0, b);
|
|
||||||
} else if(b == 0) {
|
|
||||||
Thread.yield(); // this should act like a mini sleep.
|
|
||||||
if(Ain.available() == 0) {
|
|
||||||
try {
|
|
||||||
// Thread.yield();
|
|
||||||
Thread.sleep(10);
|
|
||||||
} catch(InterruptedException ex) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* according to the specs:
|
|
||||||
*
|
|
||||||
* The total number of bytes read into the buffer,
|
|
||||||
* or -1 if there is no more data because the end of
|
|
||||||
* the stream has been reached.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
// System.out.println("TCPio: End Of Stream");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// System.out.println("TCPio: RUNNING = false");
|
|
||||||
} catch(Exception e) {
|
|
||||||
// Eject!!! Eject!!!
|
|
||||||
// System.out.println("TCPio: Caught an exception " + e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// System.out.println("TCPio: Leaving.");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,147 +0,0 @@
|
|||||||
/**
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* Version 2, December 2004
|
|
||||||
*
|
|
||||||
* Copyright (C) sponge
|
|
||||||
* Planet Earth
|
|
||||||
* Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
* copies of this license document, and changing it is allowed as long
|
|
||||||
* as the name is changed.
|
|
||||||
*
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
*
|
|
||||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
*
|
|
||||||
* See...
|
|
||||||
*
|
|
||||||
* http://sam.zoy.org/wtfpl/
|
|
||||||
* and
|
|
||||||
* http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
*
|
|
||||||
* ...for any additional details and liscense questions.
|
|
||||||
*/
|
|
||||||
package net.i2p.BOB;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.SocketTimeoutException;
|
|
||||||
import net.i2p.client.I2PSession;
|
|
||||||
import net.i2p.client.I2PSessionException;
|
|
||||||
import net.i2p.client.streaming.I2PServerSocket;
|
|
||||||
import net.i2p.client.streaming.I2PSocketManager;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listen on TCP port and connect to I2P
|
|
||||||
*
|
|
||||||
* @author sponge
|
|
||||||
*/
|
|
||||||
public class TCPlistener implements Runnable {
|
|
||||||
|
|
||||||
private NamedDB info, database;
|
|
||||||
private Log _log;
|
|
||||||
private int tgwatch;
|
|
||||||
public I2PSocketManager socketManager;
|
|
||||||
public I2PServerSocket serverSocket;
|
|
||||||
private ServerSocket listener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
* @param S
|
|
||||||
* @param info
|
|
||||||
* @param database
|
|
||||||
* @param _log
|
|
||||||
*/
|
|
||||||
TCPlistener(ServerSocket listener, I2PSocketManager S, NamedDB info, NamedDB database, Log _log) {
|
|
||||||
this.database = database;
|
|
||||||
this.info = info;
|
|
||||||
this._log = _log;
|
|
||||||
this.socketManager = S;
|
|
||||||
this.listener = listener;
|
|
||||||
tgwatch = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simply listen on TCP port, and thread connections
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void run() {
|
|
||||||
boolean g = false;
|
|
||||||
boolean spin = true;
|
|
||||||
database.getReadLock();
|
|
||||||
info.getReadLock();
|
|
||||||
if(info.exists("OUTPORT")) {
|
|
||||||
tgwatch = 2;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Socket server = new Socket();
|
|
||||||
listener.setSoTimeout(1000);
|
|
||||||
info.releaseReadLock();
|
|
||||||
database.releaseReadLock();
|
|
||||||
while(spin) {
|
|
||||||
database.getReadLock();
|
|
||||||
info.getReadLock();
|
|
||||||
spin = info.get("RUNNING").equals(Boolean.TRUE);
|
|
||||||
info.releaseReadLock();
|
|
||||||
database.releaseReadLock();
|
|
||||||
try {
|
|
||||||
server = listener.accept();
|
|
||||||
g = true;
|
|
||||||
} catch(SocketTimeoutException ste) {
|
|
||||||
g = false;
|
|
||||||
}
|
|
||||||
if(g) {
|
|
||||||
// toss the connection to a new thread.
|
|
||||||
TCPtoI2P conn_c = new TCPtoI2P(socketManager, server, info, database);
|
|
||||||
Thread t = new Thread(conn_c, "BOBTCPtoI2P");
|
|
||||||
t.start();
|
|
||||||
g = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//System.out.println("TCPlistener: destroySession");
|
|
||||||
listener.close();
|
|
||||||
} catch(IOException ioe) {
|
|
||||||
try {
|
|
||||||
listener.close();
|
|
||||||
} catch(IOException e) {
|
|
||||||
}
|
|
||||||
// Fatal failure, cause a stop event
|
|
||||||
database.getReadLock();
|
|
||||||
info.getReadLock();
|
|
||||||
spin = info.get("RUNNING").equals(Boolean.TRUE);
|
|
||||||
info.releaseReadLock();
|
|
||||||
database.releaseReadLock();
|
|
||||||
if(spin) {
|
|
||||||
database.getWriteLock();
|
|
||||||
info.getWriteLock();
|
|
||||||
info.add("STOPPING", new Boolean(true));
|
|
||||||
info.add("RUNNING", new Boolean(false));
|
|
||||||
info.releaseWriteLock();
|
|
||||||
database.releaseWriteLock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// need to kill off the socket manager too.
|
|
||||||
I2PSession session = socketManager.getSession();
|
|
||||||
if(session != null) {
|
|
||||||
try {
|
|
||||||
session.destroySession();
|
|
||||||
} catch(I2PSessionException ex) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//System.out.println("TCPlistener: Waiting for children");
|
|
||||||
while(Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish
|
|
||||||
try {
|
|
||||||
Thread.sleep(100); //sleep for 100 ms (One tenth second)
|
|
||||||
} catch(Exception e) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//System.out.println("TCPlistener: Done.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
|||||||
/**
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* Version 2, December 2004
|
|
||||||
*
|
|
||||||
* Copyright (C) sponge
|
|
||||||
* Planet Earth
|
|
||||||
* Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
* copies of this license document, and changing it is allowed as long
|
|
||||||
* as the name is changed.
|
|
||||||
*
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
*
|
|
||||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
*
|
|
||||||
* See...
|
|
||||||
*
|
|
||||||
* http://sam.zoy.org/wtfpl/
|
|
||||||
* and
|
|
||||||
* http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
*
|
|
||||||
* ...for any additional details and liscense questions.
|
|
||||||
*/
|
|
||||||
package net.i2p.BOB;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InterruptedIOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.ConnectException;
|
|
||||||
import java.net.NoRouteToHostException;
|
|
||||||
import java.net.Socket;
|
|
||||||
import net.i2p.I2PException;
|
|
||||||
import net.i2p.client.streaming.I2PSocket;
|
|
||||||
import net.i2p.client.streaming.I2PSocketManager;
|
|
||||||
import net.i2p.data.Destination;
|
|
||||||
import net.i2p.i2ptunnel.I2PTunnel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Process TCP->I2P
|
|
||||||
*
|
|
||||||
* @author sponge
|
|
||||||
*/
|
|
||||||
public class TCPtoI2P implements Runnable {
|
|
||||||
|
|
||||||
private I2PSocket I2P;
|
|
||||||
private NamedDB info, database;
|
|
||||||
private Socket sock;
|
|
||||||
private I2PSocketManager socketManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a more forgiving readline,
|
|
||||||
* it works on unbuffered streams
|
|
||||||
*
|
|
||||||
* @param in
|
|
||||||
* @return line of text as a String
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private static String lnRead(InputStream in) throws Exception {
|
|
||||||
String S;
|
|
||||||
int b;
|
|
||||||
char c;
|
|
||||||
|
|
||||||
S = new String();
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
b = in.read();
|
|
||||||
if(b == 13) {
|
|
||||||
//skip CR
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(b < 20 || b > 126) {
|
|
||||||
// exit on anything not legal
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
c = (char)(b & 0x7f); // We only really give a fuck about ASCII
|
|
||||||
S = new String(S + c);
|
|
||||||
}
|
|
||||||
return S;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
* @param i2p
|
|
||||||
* @param socket
|
|
||||||
* @param info
|
|
||||||
* @param database
|
|
||||||
*/
|
|
||||||
TCPtoI2P(I2PSocketManager i2p, Socket socket, NamedDB info, NamedDB database) {
|
|
||||||
this.sock = socket;
|
|
||||||
this.info = info;
|
|
||||||
this.database = database;
|
|
||||||
this.socketManager = i2p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print an error message to out
|
|
||||||
*
|
|
||||||
* @param e
|
|
||||||
* @param out
|
|
||||||
* @throws java.io.IOException
|
|
||||||
*/
|
|
||||||
private void Emsg(String e, OutputStream out) throws IOException {
|
|
||||||
// Debugging System.out.println("ERROR TCPtoI2P: " + e);
|
|
||||||
out.write("ERROR".concat(e).getBytes());
|
|
||||||
out.write(13); // cr
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TCP stream to I2P stream thread starter
|
|
||||||
*/
|
|
||||||
public void run() {
|
|
||||||
String line, input;
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
InputStream in = sock.getInputStream();
|
|
||||||
OutputStream out = sock.getOutputStream();
|
|
||||||
try {
|
|
||||||
line = lnRead(in);
|
|
||||||
input = line.toLowerCase();
|
|
||||||
Destination dest = null;
|
|
||||||
|
|
||||||
if(input.endsWith(".i2p")) {
|
|
||||||
dest = I2PTunnel.destFromName(input);
|
|
||||||
line = dest.toBase64();
|
|
||||||
}
|
|
||||||
dest = new Destination();
|
|
||||||
dest.fromBase64(line);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// get a client socket
|
|
||||||
I2P = socketManager.connect(dest);
|
|
||||||
I2P.setReadTimeout(0); // temp bugfix, this *SHOULD* be the default
|
|
||||||
// make readers/writers
|
|
||||||
InputStream Iin = I2P.getInputStream();
|
|
||||||
OutputStream Iout = I2P.getOutputStream();
|
|
||||||
// setup to cross the streams
|
|
||||||
TCPio conn_c = new TCPio(in, Iout, info, database); // app -> I2P
|
|
||||||
TCPio conn_a = new TCPio(Iin, out, info, database); // I2P -> app
|
|
||||||
Thread t = new Thread(conn_c, "TCPioA");
|
|
||||||
Thread q = new Thread(conn_a, "TCPioB");
|
|
||||||
// Fire!
|
|
||||||
t.start();
|
|
||||||
q.start();
|
|
||||||
while(t.isAlive() && q.isAlive()) { // AND is used here to kill off the other thread
|
|
||||||
try {
|
|
||||||
Thread.sleep(10); //sleep for 10 ms
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// System.out.println("TCPtoI2P: Going away...");
|
|
||||||
|
|
||||||
} catch(I2PException e) {
|
|
||||||
Emsg("ERROR " + e.toString(), out);
|
|
||||||
} catch(ConnectException e) {
|
|
||||||
Emsg("ERROR " + e.toString(), out);
|
|
||||||
} catch(NoRouteToHostException e) {
|
|
||||||
Emsg("ERROR " + e.toString(), out);
|
|
||||||
} catch(InterruptedIOException e) {
|
|
||||||
Emsg("ERROR " + e.toString(), out);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch(Exception e) {
|
|
||||||
Emsg("ERROR " + e.toString(), out);
|
|
||||||
}
|
|
||||||
} catch(IOException ioe) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// System.out.println("TCPtoI2P: Close I2P");
|
|
||||||
I2P.close();
|
|
||||||
} catch(Exception e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// System.out.println("TCPtoI2P: Close sock");
|
|
||||||
sock.close();
|
|
||||||
} catch(Exception e) {
|
|
||||||
}
|
|
||||||
// System.out.println("TCPtoI2P: Done.");
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
/**
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* Version 2, December 2004
|
|
||||||
*
|
|
||||||
* Copyright (C) sponge
|
|
||||||
* Planet Earth
|
|
||||||
* Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
* copies of this license document, and changing it is allowed as long
|
|
||||||
* as the name is changed.
|
|
||||||
*
|
|
||||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
*
|
|
||||||
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
*
|
|
||||||
* See...
|
|
||||||
*
|
|
||||||
* http://sam.zoy.org/wtfpl/
|
|
||||||
* and
|
|
||||||
* http://en.wikipedia.org/wiki/WTFPL
|
|
||||||
*
|
|
||||||
* ...for any additional details and liscense questions.
|
|
||||||
*/
|
|
||||||
package net.i2p.BOB;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.Socket;
|
|
||||||
import net.i2p.client.I2PSession;
|
|
||||||
import net.i2p.client.I2PSessionException;
|
|
||||||
import net.i2p.client.I2PSessionListener;
|
|
||||||
import net.i2p.data.Destination;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UDP IO on I2P
|
|
||||||
*
|
|
||||||
* FIX ME: Untested, and incomplete!
|
|
||||||
* I have no personal need to UDP yet,
|
|
||||||
* however alot of p2p apps pretty much demand it.
|
|
||||||
* The skeletal frame is here, just needs to be finished.
|
|
||||||
*
|
|
||||||
* @author sponge
|
|
||||||
*/
|
|
||||||
public class UDPIOthread implements I2PSessionListener, Runnable {
|
|
||||||
|
|
||||||
private NamedDB info;
|
|
||||||
private Log _log;
|
|
||||||
private Socket socket;
|
|
||||||
private DataInputStream in;
|
|
||||||
private DataOutputStream out;
|
|
||||||
private I2PSession _session;
|
|
||||||
private Destination _peerDestination;
|
|
||||||
private boolean up;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
* @param info
|
|
||||||
* @param _log
|
|
||||||
* @param socket
|
|
||||||
* @param _session
|
|
||||||
*/
|
|
||||||
UDPIOthread(NamedDB info, Log _log, Socket socket, I2PSession _session) {
|
|
||||||
this.info = info;
|
|
||||||
this._log = _log;
|
|
||||||
this.socket = socket;
|
|
||||||
this._session = _session;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void run() {
|
|
||||||
byte data[] = new byte[1024];
|
|
||||||
up = true;
|
|
||||||
try {
|
|
||||||
in = new DataInputStream(socket.getInputStream());
|
|
||||||
out = new DataOutputStream(socket.getOutputStream());
|
|
||||||
while(up) {
|
|
||||||
int c = in.read(data);
|
|
||||||
// Note: could do a loopback test here with a wrapper.
|
|
||||||
boolean ok = _session.sendMessage(_peerDestination, data, 0, c);
|
|
||||||
|
|
||||||
if(!ok) {
|
|
||||||
up = false; // Is this the right thing to do??
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch(IOException ioe) {
|
|
||||||
_log.error("Error running", ioe);
|
|
||||||
} catch(I2PSessionException ise) {
|
|
||||||
_log.error("Error communicating", ise);
|
|
||||||
// } catch(DataFormatException dfe) {
|
|
||||||
// _log.error("Peer destination file is not valid", dfe);
|
|
||||||
} finally {
|
|
||||||
if(_session != null) {
|
|
||||||
try {
|
|
||||||
_session.destroySession();
|
|
||||||
} catch(I2PSessionException ise) {
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param session
|
|
||||||
* @param msgId
|
|
||||||
* @param size
|
|
||||||
*/
|
|
||||||
public void messageAvailable(I2PSession session, int msgId, long size) {
|
|
||||||
// _log.debug("Message available: id = " + msgId + " size = " + size);
|
|
||||||
try {
|
|
||||||
byte msg[] = session.receiveMessage(msgId);
|
|
||||||
out.write(msg);
|
|
||||||
out.flush();
|
|
||||||
} catch(I2PSessionException ise) {
|
|
||||||
up = false;
|
|
||||||
} catch(IOException ioe) {
|
|
||||||
up = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Great, can these be used to kill ourselves.
|
|
||||||
/** required by {@link I2PSessionListener I2PSessionListener} to notify of disconnect */
|
|
||||||
public void disconnected(I2PSession session) {
|
|
||||||
_log.debug("Disconnected");
|
|
||||||
// up = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** required by {@link I2PSessionListener I2PSessionListener} to notify of error */
|
|
||||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
|
||||||
_log.debug("Error occurred: " + message, error);
|
|
||||||
// up = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** required by {@link I2PSessionListener I2PSessionListener} to notify of abuse */
|
|
||||||
public void reportAbuse(I2PSession session, int severity) {
|
|
||||||
_log.debug("Abuse reported of severity " + severity);
|
|
||||||
// up = false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
<html>
|
|
||||||
<body>
|
|
||||||
<p>BOB, the Basic Open Bridge, allows TCP applications to talk over I2P.</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,43 +0,0 @@
|
|||||||
addressbook v2.0.2 - A simple name resolution mechanism for I2P
|
|
||||||
|
|
||||||
addressbook is a simple implementation of subscribable address books for I2P.
|
|
||||||
Addresses are stored in userhosts.txt and a second copy of the address book is
|
|
||||||
placed on your eepsite as hosts.txt.
|
|
||||||
|
|
||||||
subscriptions.txt contains a list of urls to check for new addresses.
|
|
||||||
Since the urls are checked in order, and conflicting addresses are not added,
|
|
||||||
addressbook.subscriptions can be considered to be ranked in order of trust.
|
|
||||||
|
|
||||||
The system created by addressbook is similar to the early days of DNS,
|
|
||||||
when everyone ran a local name server. The major difference is the lack of
|
|
||||||
authority. Name cannot be guaranteed to be globally unique, but in practise
|
|
||||||
they probably will be, for a variety of social reasons.
|
|
||||||
|
|
||||||
Requirements
|
|
||||||
************
|
|
||||||
|
|
||||||
i2p with a running http proxy
|
|
||||||
|
|
||||||
Installation and Usage
|
|
||||||
**********************
|
|
||||||
|
|
||||||
1. Unzip addressbook-%ver.zip into your i2p directory.
|
|
||||||
2. Restart your router.
|
|
||||||
|
|
||||||
The addressbook daemon will automatically run while the router is up.
|
|
||||||
|
|
||||||
Aside from the daemon itself, the other elements of the addressbook interface
|
|
||||||
are the config.txt, myhosts.txt, and subscriptions.txt files found in the addressbook
|
|
||||||
directory.
|
|
||||||
|
|
||||||
config.txt is the configuration file for addressbook.
|
|
||||||
|
|
||||||
myhosts.txt is the addressbook master address book. Addresses placed in this file
|
|
||||||
take precidence over those in the router address book and in remote address books.
|
|
||||||
If changes are made to this file, they will be reflected in the router address book
|
|
||||||
and published address book after the next update. Do not make changes directly to the
|
|
||||||
router address book, as they could be lost during an update.
|
|
||||||
|
|
||||||
subscriptions.txt is the subscription list for addressbook. Each entry is an absolute
|
|
||||||
url to a file in hosts.txt format. Since the list is checked in order, url's should be
|
|
||||||
listed in order of trust.
|
|
@ -1,71 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<project name="addressbook" default="war" basedir=".">
|
|
||||||
|
|
||||||
<property name="src" value="java/src"/>
|
|
||||||
<property name="build" value="build"/>
|
|
||||||
<property name="dist" location="dist"/>
|
|
||||||
<property name="jar" value="addressbook.jar"/>
|
|
||||||
<property name="war" value="addressbook.war"/>
|
|
||||||
|
|
||||||
<target name="init">
|
|
||||||
<mkdir dir="${build}"/>
|
|
||||||
<mkdir dir="${dist}"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="clean">
|
|
||||||
<delete dir="${build}"/>
|
|
||||||
<delete dir="${dist}"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="distclean" depends="clean" />
|
|
||||||
|
|
||||||
<condition property="depend.available">
|
|
||||||
<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" />
|
|
||||||
<pathelement location="../jetty/jettylib/javax.servlet.jar" />
|
|
||||||
</classpath>
|
|
||||||
</depend>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="compile" depends="init, depend">
|
|
||||||
<javac debug="true" deprecation="on" source="1.5" target="1.5"
|
|
||||||
srcdir="${src}" destdir="${build}">
|
|
||||||
<classpath>
|
|
||||||
<pathelement location="../../core/java/build/i2p.jar" />
|
|
||||||
<pathelement location="../jetty/jettylib/javax.servlet.jar" />
|
|
||||||
</classpath>
|
|
||||||
</javac>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="jar" depends="compile">
|
|
||||||
<jar basedir="${build}" destfile="${dist}/${jar}">
|
|
||||||
<manifest>
|
|
||||||
<attribute name="Main-Class" value="addressbook.Daemon"/>
|
|
||||||
</manifest>
|
|
||||||
</jar>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="war" depends="compile" unless="war.uptodate">
|
|
||||||
<mkdir dir="${dist}/tmp"/>
|
|
||||||
<mkdir dir="${dist}/tmp/WEB-INF"/>
|
|
||||||
<mkdir dir="${dist}/tmp/WEB-INF/classes"/>
|
|
||||||
<copy todir="${dist}/tmp/WEB-INF/classes">
|
|
||||||
<fileset dir="${build}"/>
|
|
||||||
</copy>
|
|
||||||
<war basedir="${dist}/tmp" webxml="web.xml" destfile="${dist}/${war}"/>
|
|
||||||
<delete dir="${dist}/tmp"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<uptodate property="war.uptodate" targetfile="${dist}/${war}">
|
|
||||||
<srcfiles dir= "." includes="${build}/**/*.class, web.xml"/>
|
|
||||||
</uptodate>
|
|
||||||
|
|
||||||
</project>
|
|
@ -1,48 +0,0 @@
|
|||||||
# This is the configuration file for addressbook.
|
|
||||||
#
|
|
||||||
# Options
|
|
||||||
# *******
|
|
||||||
# All paths are realitive to i2p/addressbook. Default value for
|
|
||||||
# each option is given in parentheses.
|
|
||||||
#
|
|
||||||
# proxy_host The hostname of your I2P http proxy.
|
|
||||||
# (localhost)
|
|
||||||
#
|
|
||||||
# proxy_port The port of your I2P http proxy. (4444)
|
|
||||||
#
|
|
||||||
# master_addressbook The path to your master address book, used for local
|
|
||||||
# changes only. (myhosts.txt)
|
|
||||||
#
|
|
||||||
# router_addressbook The path to the address book used by the router.
|
|
||||||
# Contains the addresses from your master address book
|
|
||||||
# and your subscribed address books. (../userhosts.txt)
|
|
||||||
#
|
|
||||||
# private_addressbook The path to the private address book used by the router.
|
|
||||||
# This is used only by the router and SusiDNS.
|
|
||||||
# It is not published by addressbook. (../privatehosts.txt)
|
|
||||||
#
|
|
||||||
# published_addressbook The path to the copy of your address book made
|
|
||||||
# available on i2p. (../eepsite/docroot/hosts.txt)
|
|
||||||
#
|
|
||||||
# log The path to your addressbook log. (log.txt)
|
|
||||||
#
|
|
||||||
# subscriptions The path to your subscription file. (subscriptions.txt)
|
|
||||||
#
|
|
||||||
# etags The path to the etags header storage file. (etags)
|
|
||||||
#
|
|
||||||
# last_modified The path to the last-modified header storage file.
|
|
||||||
# (last_modified)
|
|
||||||
#
|
|
||||||
# update_delay The time (in hours) between each update. (1)
|
|
||||||
|
|
||||||
proxy_host=localhost
|
|
||||||
proxy_port=4444
|
|
||||||
master_addressbook=myhosts.txt
|
|
||||||
router_addressbook=../userhosts.txt
|
|
||||||
private_addressbook=../privatehosts.txt
|
|
||||||
published_addressbook=../eepsite/docroot/hosts.txt
|
|
||||||
log=log.txt
|
|
||||||
subscriptions=subscriptions.txt
|
|
||||||
etags=etags
|
|
||||||
last_modified=last_modified
|
|
||||||
update_delay=1
|
|
@ -1,266 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2004 Ragnarok
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package addressbook;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
|
||||||
import net.i2p.util.EepGet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An address book for storing human readable names mapped to base64 i2p
|
|
||||||
* destinations. AddressBooks can be created from local and remote files, merged
|
|
||||||
* together, and written out to local files.
|
|
||||||
*
|
|
||||||
* @author Ragnarok
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class AddressBook {
|
|
||||||
|
|
||||||
private String location;
|
|
||||||
|
|
||||||
private Map addresses;
|
|
||||||
|
|
||||||
private boolean modified;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct an AddressBook from the contents of the Map addresses.
|
|
||||||
*
|
|
||||||
* @param addresses
|
|
||||||
* A Map containing human readable addresses as keys, mapped to
|
|
||||||
* base64 i2p destinations.
|
|
||||||
*/
|
|
||||||
public AddressBook(Map addresses) {
|
|
||||||
this.addresses = addresses;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Construct an AddressBook from the contents of the file at url. If the
|
|
||||||
* remote file cannot be read, construct an empty AddressBook
|
|
||||||
*
|
|
||||||
* @param url
|
|
||||||
* A URL pointing at a file with lines in the format "key=value",
|
|
||||||
* where key is a human readable name, and value is a base64 i2p
|
|
||||||
* destination.
|
|
||||||
*/
|
|
||||||
/* unused
|
|
||||||
public AddressBook(String url, String proxyHost, int proxyPort) {
|
|
||||||
this.location = url;
|
|
||||||
EepGet get = new EepGet(I2PAppContext.getGlobalContext(), true,
|
|
||||||
proxyHost, proxyPort, 0, "addressbook.tmp", url, true,
|
|
||||||
null);
|
|
||||||
get.fetch();
|
|
||||||
try {
|
|
||||||
this.addresses = ConfigParser.parse(new File("addressbook.tmp"));
|
|
||||||
} catch (IOException exp) {
|
|
||||||
this.addresses = new HashMap();
|
|
||||||
}
|
|
||||||
new File("addressbook.tmp").delete();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
static final long MAX_SUB_SIZE = 3 * 1024 * 1024l; //about 5,000 hosts
|
|
||||||
/**
|
|
||||||
* Construct an AddressBook from the Subscription subscription. If the
|
|
||||||
* address book at subscription has not changed since the last time it was
|
|
||||||
* read or cannot be read, return an empty AddressBook.
|
|
||||||
* Set a maximum size of the remote book to make it a little harder for a malicious book-sender.
|
|
||||||
*
|
|
||||||
* @param subscription
|
|
||||||
* A Subscription instance pointing at a remote address book.
|
|
||||||
* @param proxyHost hostname of proxy
|
|
||||||
* @param proxyPort port number of proxy
|
|
||||||
*/
|
|
||||||
public AddressBook(Subscription subscription, String proxyHost, int proxyPort) {
|
|
||||||
this.location = subscription.getLocation();
|
|
||||||
EepGet get = new EepGet(I2PAppContext.getGlobalContext(), true,
|
|
||||||
proxyHost, proxyPort, 0, -1l, MAX_SUB_SIZE, "addressbook.tmp", null,
|
|
||||||
subscription.getLocation(), true, subscription.getEtag(), subscription.getLastModified(), null);
|
|
||||||
if (get.fetch()) {
|
|
||||||
subscription.setEtag(get.getETag());
|
|
||||||
subscription.setLastModified(get.getLastModified());
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
this.addresses = ConfigParser.parse(new File("addressbook.tmp"));
|
|
||||||
} catch (IOException exp) {
|
|
||||||
this.addresses = new HashMap();
|
|
||||||
}
|
|
||||||
new File("addressbook.tmp").delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct an AddressBook from the contents of the file at file. If the
|
|
||||||
* file cannot be read, construct an empty AddressBook
|
|
||||||
*
|
|
||||||
* @param file
|
|
||||||
* A File pointing at a file with lines in the format
|
|
||||||
* "key=value", where key is a human readable name, and value is
|
|
||||||
* a base64 i2p destination.
|
|
||||||
*/
|
|
||||||
public AddressBook(File file) {
|
|
||||||
this.location = file.toString();
|
|
||||||
try {
|
|
||||||
this.addresses = ConfigParser.parse(file);
|
|
||||||
} catch (IOException exp) {
|
|
||||||
this.addresses = new HashMap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a Map containing the addresses in the AddressBook.
|
|
||||||
*
|
|
||||||
* @return A Map containing the addresses in the AddressBook, where the key
|
|
||||||
* is a human readable name, and the value is a base64 i2p
|
|
||||||
* destination.
|
|
||||||
*/
|
|
||||||
public Map getAddresses() {
|
|
||||||
return this.addresses;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the location of the file this AddressBook was constructed from.
|
|
||||||
*
|
|
||||||
* @return A String representing either an abstract path, or a url,
|
|
||||||
* depending on how the instance was constructed.
|
|
||||||
*/
|
|
||||||
public String getLocation() {
|
|
||||||
return this.location;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a string representation of the contents of the AddressBook.
|
|
||||||
*
|
|
||||||
* @return A String representing the contents of the AddressBook.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return this.addresses.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int MIN_DEST_LENGTH = 516;
|
|
||||||
private static final int MAX_DEST_LENGTH = MIN_DEST_LENGTH + 100; // longer than any known cert type for now
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do basic validation of the hostname and dest
|
|
||||||
* hostname was already converted to lower case by ConfigParser.parse()
|
|
||||||
*/
|
|
||||||
private static boolean valid(String host, String dest) {
|
|
||||||
return
|
|
||||||
host.endsWith(".i2p") &&
|
|
||||||
host.length() > 4 &&
|
|
||||||
host.length() <= 67 && // 63 + ".i2p"
|
|
||||||
(! host.startsWith(".")) &&
|
|
||||||
(! host.startsWith("-")) &&
|
|
||||||
host.indexOf(".-") < 0 &&
|
|
||||||
host.indexOf("-.") < 0 &&
|
|
||||||
host.indexOf("..") < 0 &&
|
|
||||||
// IDN - basic check, not complete validation
|
|
||||||
(host.indexOf("--") < 0 || host.startsWith("xn--") || host.indexOf(".xn--") > 0) &&
|
|
||||||
host.replaceAll("[a-z0-9.-]", "").length() == 0 &&
|
|
||||||
// Base32 spoofing (52chars.i2p)
|
|
||||||
(! (host.length() == 56 && host.substring(0,52).replaceAll("[a-z2-7]", "").length() == 0)) &&
|
|
||||||
// ... or maybe we do Base32 this way ...
|
|
||||||
(! host.equals("b32.i2p")) &&
|
|
||||||
(! host.endsWith(".b32.i2p")) &&
|
|
||||||
// some reserved names that may be used for local configuration someday
|
|
||||||
(! host.equals("proxy.i2p")) &&
|
|
||||||
(! host.equals("router.i2p")) &&
|
|
||||||
(! host.equals("console.i2p")) &&
|
|
||||||
(! host.endsWith(".proxy.i2p")) &&
|
|
||||||
(! host.endsWith(".router.i2p")) &&
|
|
||||||
(! host.endsWith(".console.i2p")) &&
|
|
||||||
|
|
||||||
((dest.length() == MIN_DEST_LENGTH && dest.endsWith("AAAA")) ||
|
|
||||||
(dest.length() > MIN_DEST_LENGTH && dest.length() <= MAX_DEST_LENGTH)) &&
|
|
||||||
dest.replaceAll("[a-zA-Z0-9~-]", "").length() == 0
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Merge this AddressBook with AddressBook other, writing messages about new
|
|
||||||
* addresses or conflicts to log. Addresses in AddressBook other that are
|
|
||||||
* not in this AddressBook are added to this AddressBook. In case of a
|
|
||||||
* conflict, addresses in this AddressBook take precedence
|
|
||||||
*
|
|
||||||
* @param other
|
|
||||||
* An AddressBook to merge with.
|
|
||||||
* @param overwrite True to overwrite
|
|
||||||
* @param log
|
|
||||||
* The log to write messages about new addresses or conflicts to.
|
|
||||||
*/
|
|
||||||
public void merge(AddressBook other, boolean overwrite, Log log) {
|
|
||||||
Iterator otherIter = other.addresses.keySet().iterator();
|
|
||||||
|
|
||||||
while (otherIter.hasNext()) {
|
|
||||||
String otherKey = (String) otherIter.next();
|
|
||||||
String otherValue = (String) other.addresses.get(otherKey);
|
|
||||||
|
|
||||||
if (valid(otherKey, otherValue)) {
|
|
||||||
if (this.addresses.containsKey(otherKey) && !overwrite) {
|
|
||||||
if (!this.addresses.get(otherKey).equals(otherValue)
|
|
||||||
&& log != null) {
|
|
||||||
log.append("Conflict for " + otherKey + " from "
|
|
||||||
+ other.location
|
|
||||||
+ ". Destination in remote address book is "
|
|
||||||
+ otherValue);
|
|
||||||
}
|
|
||||||
} else if (!this.addresses.containsKey(otherKey)
|
|
||||||
|| !this.addresses.get(otherKey).equals(otherValue)) {
|
|
||||||
this.addresses.put(otherKey, otherValue);
|
|
||||||
this.modified = true;
|
|
||||||
if (log != null) {
|
|
||||||
log.append("New address " + otherKey
|
|
||||||
+ " added to address book. From: " + other.location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the contents of this AddressBook out to the File file. If the file
|
|
||||||
* cannot be writen to, this method will silently fail.
|
|
||||||
*
|
|
||||||
* @param file
|
|
||||||
* The file to write the contents of this AddressBook too.
|
|
||||||
*/
|
|
||||||
public void write(File file) {
|
|
||||||
if (this.modified) {
|
|
||||||
try {
|
|
||||||
ConfigParser.write(this.addresses, file);
|
|
||||||
} catch (IOException exp) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write this AddressBook out to the file it was read from. Requires that
|
|
||||||
* AddressBook was constructed from a file on the local filesystem. If the
|
|
||||||
* file cannot be writen to, this method will silently fail.
|
|
||||||
*/
|
|
||||||
public void write() {
|
|
||||||
this.write(new File(this.location));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,319 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2004 Ragnarok
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package addressbook;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility class providing methods to parse and write files in config file
|
|
||||||
* format, and subscription file format.
|
|
||||||
*
|
|
||||||
* @author Ragnarok
|
|
||||||
*/
|
|
||||||
public class ConfigParser {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Strip the comments from a String. Lines that begin with '#' and ';' are
|
|
||||||
* considered comments, as well as any part of a line after a '#'.
|
|
||||||
*
|
|
||||||
* @param inputLine
|
|
||||||
* A String to strip comments from.
|
|
||||||
* @return A String without comments, but otherwise identical to inputLine.
|
|
||||||
*/
|
|
||||||
public static String stripComments(String inputLine) {
|
|
||||||
if (inputLine.startsWith(";")) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
if (inputLine.split("#").length > 0) {
|
|
||||||
return inputLine.split("#")[0];
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a Map using the contents of BufferedReader input. input must have
|
|
||||||
* a single key, value pair on each line, in the format: key=value. Lines
|
|
||||||
* starting with '#' or ';' are considered comments, and ignored. Lines that
|
|
||||||
* are obviously not in the format key=value are also ignored.
|
|
||||||
* The key is converted to lower case.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* A BufferedReader with lines in key=value format to parse into
|
|
||||||
* a Map.
|
|
||||||
* @return A Map containing the key, value pairs from input.
|
|
||||||
* @throws IOException
|
|
||||||
* if the BufferedReader cannot be read.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public static Map parse(BufferedReader input) throws IOException {
|
|
||||||
Map result = new HashMap();
|
|
||||||
String inputLine;
|
|
||||||
inputLine = input.readLine();
|
|
||||||
while (inputLine != null) {
|
|
||||||
inputLine = ConfigParser.stripComments(inputLine);
|
|
||||||
String[] splitLine = inputLine.split("=");
|
|
||||||
if (splitLine.length == 2) {
|
|
||||||
result.put(splitLine[0].trim().toLowerCase(), splitLine[1].trim());
|
|
||||||
}
|
|
||||||
inputLine = input.readLine();
|
|
||||||
}
|
|
||||||
input.close();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a Map using the contents of the File file. See parseBufferedReader
|
|
||||||
* for details of the input format.
|
|
||||||
*
|
|
||||||
* @param file
|
|
||||||
* A File to parse.
|
|
||||||
* @return A Map containing the key, value pairs from file.
|
|
||||||
* @throws IOException
|
|
||||||
* if file cannot be read.
|
|
||||||
*/
|
|
||||||
public static Map parse(File file) throws IOException {
|
|
||||||
FileInputStream fileStream = new FileInputStream(file);
|
|
||||||
BufferedReader input = new BufferedReader(new InputStreamReader(
|
|
||||||
fileStream));
|
|
||||||
Map rv = ConfigParser.parse(input);
|
|
||||||
try {
|
|
||||||
fileStream.close();
|
|
||||||
} catch (IOException ioe) {}
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a Map using the contents of the String string. See
|
|
||||||
* parseBufferedReader for details of the input format.
|
|
||||||
*
|
|
||||||
* @param string
|
|
||||||
* A String to parse.
|
|
||||||
* @return A Map containing the key, value pairs from string.
|
|
||||||
* @throws IOException
|
|
||||||
* if file cannot be read.
|
|
||||||
*/
|
|
||||||
public static Map parse(String string) throws IOException {
|
|
||||||
StringReader stringReader = new StringReader(string);
|
|
||||||
BufferedReader input = new BufferedReader(stringReader);
|
|
||||||
return ConfigParser.parse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a Map using the contents of the File file. If file cannot be read,
|
|
||||||
* use map instead, and write the result to where file should have been.
|
|
||||||
*
|
|
||||||
* @param file
|
|
||||||
* A File to attempt to parse.
|
|
||||||
* @param map
|
|
||||||
* A Map to use as the default, if file fails.
|
|
||||||
* @return A Map containing the key, value pairs from file, or if file
|
|
||||||
* cannot be read, map.
|
|
||||||
*/
|
|
||||||
public static Map parse(File file, Map map) {
|
|
||||||
Map result;
|
|
||||||
try {
|
|
||||||
result = ConfigParser.parse(file);
|
|
||||||
} catch (IOException exp) {
|
|
||||||
result = map;
|
|
||||||
try {
|
|
||||||
ConfigParser.write(result, file);
|
|
||||||
} catch (IOException exp2) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a List where each element is a line from the BufferedReader input.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* A BufferedReader to parse.
|
|
||||||
* @return A List consisting of one element for each line in input.
|
|
||||||
* @throws IOException
|
|
||||||
* if input cannot be read.
|
|
||||||
*/
|
|
||||||
public static List parseSubscriptions(BufferedReader input)
|
|
||||||
throws IOException {
|
|
||||||
List result = new LinkedList();
|
|
||||||
String inputLine = input.readLine();
|
|
||||||
while (inputLine != null) {
|
|
||||||
inputLine = ConfigParser.stripComments(inputLine).trim();
|
|
||||||
if (inputLine.length() > 0) {
|
|
||||||
result.add(inputLine);
|
|
||||||
}
|
|
||||||
inputLine = input.readLine();
|
|
||||||
}
|
|
||||||
input.close();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a List where each element is a line from the File file.
|
|
||||||
*
|
|
||||||
* @param file
|
|
||||||
* A File to parse.
|
|
||||||
* @return A List consisting of one element for each line in file.
|
|
||||||
* @throws IOException
|
|
||||||
* if file cannot be read.
|
|
||||||
*/
|
|
||||||
public static List parseSubscriptions(File file) throws IOException {
|
|
||||||
FileInputStream fileStream = new FileInputStream(file);
|
|
||||||
BufferedReader input = new BufferedReader(new InputStreamReader(
|
|
||||||
fileStream));
|
|
||||||
List rv = ConfigParser.parseSubscriptions(input);
|
|
||||||
try {
|
|
||||||
fileStream.close();
|
|
||||||
} catch (IOException ioe) {}
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a List where each element is a line from the String string.
|
|
||||||
*
|
|
||||||
* @param string
|
|
||||||
* A String to parse.
|
|
||||||
* @return A List consisting of one element for each line in string.
|
|
||||||
* @throws IOException
|
|
||||||
* if string cannot be read.
|
|
||||||
*/
|
|
||||||
public static List parseSubscriptions(String string) throws IOException {
|
|
||||||
StringReader stringReader = new StringReader(string);
|
|
||||||
BufferedReader input = new BufferedReader(stringReader);
|
|
||||||
return ConfigParser.parseSubscriptions(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a List using the contents of the File file. If file cannot be
|
|
||||||
* read, use list instead, and write the result to where file should have
|
|
||||||
* been.
|
|
||||||
*
|
|
||||||
* @param file
|
|
||||||
* A File to attempt to parse.
|
|
||||||
* @param list list of files to parse
|
|
||||||
* @return A List consisting of one element for each line in file, or if
|
|
||||||
* file cannot be read, list.
|
|
||||||
*/
|
|
||||||
public static List parseSubscriptions(File file, List list) {
|
|
||||||
List result;
|
|
||||||
try {
|
|
||||||
result = ConfigParser.parseSubscriptions(file);
|
|
||||||
} catch (IOException exp) {
|
|
||||||
result = list;
|
|
||||||
try {
|
|
||||||
ConfigParser.writeSubscriptions(result, file);
|
|
||||||
} catch (IOException exp2) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write contents of Map map to BufferedWriter output. Output is written
|
|
||||||
* with one key, value pair on each line, in the format: key=value.
|
|
||||||
*
|
|
||||||
* @param map
|
|
||||||
* A Map to write to output.
|
|
||||||
* @param output
|
|
||||||
* A BufferedWriter to write the Map to.
|
|
||||||
* @throws IOException
|
|
||||||
* if the BufferedWriter cannot be written to.
|
|
||||||
*/
|
|
||||||
public static void write(Map map, BufferedWriter output) throws IOException {
|
|
||||||
Iterator keyIter = map.keySet().iterator();
|
|
||||||
|
|
||||||
while (keyIter.hasNext()) {
|
|
||||||
String key = (String) keyIter.next();
|
|
||||||
output.write(key + "=" + (String) map.get(key));
|
|
||||||
output.newLine();
|
|
||||||
}
|
|
||||||
output.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write contents of Map map to the File file. Output is written
|
|
||||||
* with one key, value pair on each line, in the format: key=value.
|
|
||||||
*
|
|
||||||
* @param map
|
|
||||||
* A Map to write to file.
|
|
||||||
* @param file
|
|
||||||
* A File to write the Map to.
|
|
||||||
* @throws IOException
|
|
||||||
* if file cannot be written to.
|
|
||||||
*/
|
|
||||||
public static void write(Map map, File file) throws IOException {
|
|
||||||
ConfigParser
|
|
||||||
.write(map, new BufferedWriter(new FileWriter(file, false)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write contents of List list to BufferedReader output. Output is written
|
|
||||||
* with each element of list on a new line.
|
|
||||||
*
|
|
||||||
* @param list
|
|
||||||
* A List to write to file.
|
|
||||||
* @param output
|
|
||||||
* A BufferedReader to write list to.
|
|
||||||
* @throws IOException
|
|
||||||
* if output cannot be written to.
|
|
||||||
*/
|
|
||||||
public static void writeSubscriptions(List list, BufferedWriter output)
|
|
||||||
throws IOException {
|
|
||||||
Iterator iter = list.iterator();
|
|
||||||
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
output.write((String) iter.next());
|
|
||||||
output.newLine();
|
|
||||||
}
|
|
||||||
output.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write contents of List list to File file. Output is written with each
|
|
||||||
* element of list on a new line.
|
|
||||||
*
|
|
||||||
* @param list
|
|
||||||
* A List to write to file.
|
|
||||||
* @param file
|
|
||||||
* A File to write list to.
|
|
||||||
* @throws IOException
|
|
||||||
* if output cannot be written to.
|
|
||||||
*/
|
|
||||||
public static void writeSubscriptions(List list, File file)
|
|
||||||
throws IOException {
|
|
||||||
ConfigParser.writeSubscriptions(list, new BufferedWriter(
|
|
||||||
new FileWriter(file, false)));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,192 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2004 Ragnarok
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package addressbook;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main class of addressbook. Performs updates, and runs the main loop.
|
|
||||||
*
|
|
||||||
* @author Ragnarok
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class Daemon {
|
|
||||||
public static final String VERSION = "2.0.3";
|
|
||||||
private static final Daemon _instance = new Daemon();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the router and published address books using remote data from the
|
|
||||||
* subscribed address books listed in subscriptions.
|
|
||||||
*
|
|
||||||
* @param master
|
|
||||||
* The master AddressBook. This address book is never
|
|
||||||
* overwritten, so it is safe for the user to write to.
|
|
||||||
* @param router
|
|
||||||
* The router AddressBook. This is the address book read by
|
|
||||||
* client applications.
|
|
||||||
* @param published
|
|
||||||
* The published AddressBook. This address book is published on
|
|
||||||
* the user's eepsite so that others may subscribe to it.
|
|
||||||
* @param subscriptions
|
|
||||||
* A SubscriptionList listing the remote address books to update
|
|
||||||
* from.
|
|
||||||
* @param log
|
|
||||||
* The log to write changes and conflicts to.
|
|
||||||
*/
|
|
||||||
public void update(AddressBook master, AddressBook router,
|
|
||||||
File published, SubscriptionList subscriptions, Log log) {
|
|
||||||
router.merge(master, true, null);
|
|
||||||
Iterator iter = subscriptions.iterator();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
router.merge((AddressBook) iter.next(), false, log);
|
|
||||||
}
|
|
||||||
router.write();
|
|
||||||
if (published != null)
|
|
||||||
router.write(published);
|
|
||||||
subscriptions.write();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run an update, using the Map settings to provide the parameters.
|
|
||||||
*
|
|
||||||
* @param settings
|
|
||||||
* A Map containg the parameters needed by update.
|
|
||||||
* @param home
|
|
||||||
* The directory containing addressbook's configuration files.
|
|
||||||
*/
|
|
||||||
public void update(Map settings, String home) {
|
|
||||||
File masterFile = new File(home, (String) settings
|
|
||||||
.get("master_addressbook"));
|
|
||||||
File routerFile = new File(home, (String) settings
|
|
||||||
.get("router_addressbook"));
|
|
||||||
File published = null;
|
|
||||||
if ("true".equals(settings.get("should_publish")))
|
|
||||||
published = new File(home, (String) settings
|
|
||||||
.get("published_addressbook"));
|
|
||||||
File subscriptionFile = new File(home, (String) settings
|
|
||||||
.get("subscriptions"));
|
|
||||||
File logFile = new File(home, (String) settings.get("log"));
|
|
||||||
File etagsFile = new File(home, (String) settings.get("etags"));
|
|
||||||
File lastModifiedFile = new File(home, (String) settings
|
|
||||||
.get("last_modified"));
|
|
||||||
|
|
||||||
AddressBook master = new AddressBook(masterFile);
|
|
||||||
AddressBook router = new AddressBook(routerFile);
|
|
||||||
|
|
||||||
List defaultSubs = new LinkedList();
|
|
||||||
// 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");
|
|
||||||
|
|
||||||
SubscriptionList subscriptions = new SubscriptionList(subscriptionFile,
|
|
||||||
etagsFile, lastModifiedFile, defaultSubs, (String) settings
|
|
||||||
.get("proxy_host"), Integer.parseInt((String) settings.get("proxy_port")));
|
|
||||||
Log log = new Log(logFile);
|
|
||||||
|
|
||||||
update(master, router, published, subscriptions, log);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the settings, set the proxy, then enter into the main loop. The main
|
|
||||||
* loop performs an immediate update, and then an update every number of
|
|
||||||
* hours, as configured in the settings file.
|
|
||||||
*
|
|
||||||
* @param args
|
|
||||||
* Command line arguments. If there are any arguments provided,
|
|
||||||
* the first is taken as addressbook's home directory, and the
|
|
||||||
* others are ignored.
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
_instance.run(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run(String[] args) {
|
|
||||||
String settingsLocation = "config.txt";
|
|
||||||
String home;
|
|
||||||
if (args.length > 0) {
|
|
||||||
home = args[0];
|
|
||||||
} else {
|
|
||||||
home = ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
Map defaultSettings = new HashMap();
|
|
||||||
defaultSettings.put("proxy_host", "localhost");
|
|
||||||
defaultSettings.put("proxy_port", "4444");
|
|
||||||
defaultSettings.put("master_addressbook", "../userhosts.txt");
|
|
||||||
defaultSettings.put("router_addressbook", "../hosts.txt");
|
|
||||||
defaultSettings.put("published_addressbook", "../eepsite/docroot/hosts.txt");
|
|
||||||
defaultSettings.put("should_publish", "false");
|
|
||||||
defaultSettings.put("log", "log.txt");
|
|
||||||
defaultSettings.put("subscriptions", "subscriptions.txt");
|
|
||||||
defaultSettings.put("etags", "etags");
|
|
||||||
defaultSettings.put("last_modified", "last_modified");
|
|
||||||
defaultSettings.put("update_delay", "12");
|
|
||||||
|
|
||||||
File homeFile = new File(home);
|
|
||||||
if (!homeFile.exists()) {
|
|
||||||
boolean created = homeFile.mkdirs();
|
|
||||||
if (created)
|
|
||||||
System.out.println("INFO: Addressbook directory " + homeFile.getName() + " created");
|
|
||||||
else
|
|
||||||
System.out.println("ERROR: Addressbook directory " + homeFile.getName() + " could not be created");
|
|
||||||
}
|
|
||||||
|
|
||||||
File settingsFile = new File(homeFile, settingsLocation);
|
|
||||||
|
|
||||||
Map settings = ConfigParser.parse(settingsFile, defaultSettings);
|
|
||||||
// wait
|
|
||||||
try {
|
|
||||||
Thread.sleep(5*60*1000);
|
|
||||||
// Static method, and redundent Thread.currentThread().sleep(5*60*1000);
|
|
||||||
} catch (InterruptedException ie) {}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
long delay = Long.parseLong((String) settings.get("update_delay"));
|
|
||||||
if (delay < 1) {
|
|
||||||
delay = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
update(settings, home);
|
|
||||||
try {
|
|
||||||
synchronized (this) {
|
|
||||||
wait(delay * 60 * 60 * 1000);
|
|
||||||
}
|
|
||||||
} catch (InterruptedException exp) {
|
|
||||||
}
|
|
||||||
settings = ConfigParser.parse(settingsFile, defaultSettings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this to get the addressbook to reread its config and
|
|
||||||
* refetch its subscriptions.
|
|
||||||
*/
|
|
||||||
public static void wakeup() {
|
|
||||||
synchronized (_instance) {
|
|
||||||
_instance.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2004 Ragnarok
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package addressbook;
|
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple log with automatic time stamping.
|
|
||||||
*
|
|
||||||
* @author Ragnarok
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class Log {
|
|
||||||
|
|
||||||
private File file;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a Log instance that writes to the File file.
|
|
||||||
*
|
|
||||||
* @param file
|
|
||||||
* A File for the log to write to.
|
|
||||||
*/
|
|
||||||
public Log(File file) {
|
|
||||||
this.file = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write entry to a new line in the log, with appropriate time stamp.
|
|
||||||
*
|
|
||||||
* @param entry
|
|
||||||
* A String containing a message to append to the log.
|
|
||||||
*/
|
|
||||||
public void append(String entry) {
|
|
||||||
BufferedWriter bw = null;
|
|
||||||
try {
|
|
||||||
bw = new BufferedWriter(new FileWriter(this.file,
|
|
||||||
true));
|
|
||||||
String timestamp = new Date().toString();
|
|
||||||
bw.write(timestamp + " -- " + entry);
|
|
||||||
bw.newLine();
|
|
||||||
} catch (IOException exp) {
|
|
||||||
} finally {
|
|
||||||
if (bw != null)
|
|
||||||
try { bw.close(); } catch (IOException ioe) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the File that the Log is writing to.
|
|
||||||
*
|
|
||||||
* @return The File that the log is writing to.
|
|
||||||
*/
|
|
||||||
public File getFile() {
|
|
||||||
return this.file;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2004 Ragnarok
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package addressbook;
|
|
||||||
|
|
||||||
import javax.servlet.GenericServlet;
|
|
||||||
import javax.servlet.ServletConfig;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.ServletRequest;
|
|
||||||
import javax.servlet.ServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper for addressbook to allow it to be started as a web application.
|
|
||||||
*
|
|
||||||
* @author Ragnarok
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class Servlet extends GenericServlet {
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
|
|
||||||
*/
|
|
||||||
public void service(ServletRequest request, ServletResponse response) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void init(ServletConfig config) {
|
|
||||||
try {
|
|
||||||
super.init(config);
|
|
||||||
} catch (ServletException exp) {
|
|
||||||
}
|
|
||||||
String[] args = new String[1];
|
|
||||||
args[0] = config.getInitParameter("home");
|
|
||||||
DaemonThread thread = new DaemonThread(args);
|
|
||||||
thread.setDaemon(true);
|
|
||||||
thread.start();
|
|
||||||
System.out.println("INFO: Starting Addressbook " + Daemon.VERSION);
|
|
||||||
System.out.println("INFO: config root under " + args[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2004 Ragnarok
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package addressbook;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A subscription to a remote address book.
|
|
||||||
*
|
|
||||||
* @author Ragnarok
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class Subscription {
|
|
||||||
|
|
||||||
private String location;
|
|
||||||
|
|
||||||
private String etag;
|
|
||||||
|
|
||||||
private String lastModified;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a Subscription pointing to the address book at location, that
|
|
||||||
* was last read at the time represented by etag and lastModified.
|
|
||||||
*
|
|
||||||
* @param location
|
|
||||||
* A String representing a url to a remote address book.
|
|
||||||
* @param etag
|
|
||||||
* The etag header that we recieved the last time we read this
|
|
||||||
* subscription.
|
|
||||||
* @param lastModified
|
|
||||||
* the last-modified header we recieved the last time we read
|
|
||||||
* this subscription.
|
|
||||||
*/
|
|
||||||
public Subscription(String location, String etag, String lastModified) {
|
|
||||||
this.location = location;
|
|
||||||
this.etag = etag;
|
|
||||||
this.lastModified = lastModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the location this Subscription points at.
|
|
||||||
*
|
|
||||||
* @return A String representing a url to a remote address book.
|
|
||||||
*/
|
|
||||||
public String getLocation() {
|
|
||||||
return this.location;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the etag header that we recieved the last time we read this
|
|
||||||
* subscription.
|
|
||||||
*
|
|
||||||
* @return A String containing the etag header.
|
|
||||||
*/
|
|
||||||
public String getEtag() {
|
|
||||||
return this.etag;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the etag header.
|
|
||||||
*
|
|
||||||
* @param etag
|
|
||||||
* A String containing the etag header.
|
|
||||||
*/
|
|
||||||
public void setEtag(String etag) {
|
|
||||||
this.etag = etag;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the last-modified header that we recieved the last time we read
|
|
||||||
* this subscription.
|
|
||||||
*
|
|
||||||
* @return A String containing the last-modified header.
|
|
||||||
*/
|
|
||||||
public String getLastModified() {
|
|
||||||
return this.lastModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the last-modified header.
|
|
||||||
*
|
|
||||||
* @param lastModified
|
|
||||||
* A String containing the last-modified header.
|
|
||||||
*/
|
|
||||||
public void setLastModified(String lastModified) {
|
|
||||||
this.lastModified = lastModified;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2004 Ragnarok
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package addressbook;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An iterator over the subscriptions in a SubscriptionList. Note that this iterator
|
|
||||||
* returns AddressBook objects, and not Subscription objects.
|
|
||||||
*
|
|
||||||
* @author Ragnarok
|
|
||||||
*/
|
|
||||||
public class SubscriptionIterator implements Iterator {
|
|
||||||
|
|
||||||
private Iterator subIterator;
|
|
||||||
private String proxyHost;
|
|
||||||
private int proxyPort;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a SubscriptionIterator using the Subscriprions in List subscriptions.
|
|
||||||
*
|
|
||||||
* @param subscriptions
|
|
||||||
* List of Subscription objects that represent address books.
|
|
||||||
* @param proxyHost proxy hostname
|
|
||||||
* @param proxyPort proxt port number
|
|
||||||
*/
|
|
||||||
public SubscriptionIterator(List subscriptions, String proxyHost, int proxyPort) {
|
|
||||||
this.subIterator = subscriptions.iterator();
|
|
||||||
this.proxyHost = proxyHost;
|
|
||||||
this.proxyPort = proxyPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see java.util.Iterator#hasNext()
|
|
||||||
*/
|
|
||||||
public boolean hasNext() {
|
|
||||||
return this.subIterator.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see java.util.Iterator#next()
|
|
||||||
*/
|
|
||||||
public Object next() {
|
|
||||||
Subscription sub = (Subscription) this.subIterator.next();
|
|
||||||
return new AddressBook(sub, this.proxyHost, this.proxyPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see java.util.Iterator#remove()
|
|
||||||
*/
|
|
||||||
public void remove() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,132 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2004 Ragnarok
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package addressbook;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A list of Subscriptions loaded from a file.
|
|
||||||
*
|
|
||||||
* @author Ragnarok
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class SubscriptionList {
|
|
||||||
|
|
||||||
private List subscriptions;
|
|
||||||
|
|
||||||
private File etagsFile;
|
|
||||||
|
|
||||||
private File lastModifiedFile;
|
|
||||||
|
|
||||||
private String proxyHost;
|
|
||||||
|
|
||||||
private int proxyPort;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a SubscriptionList using the urls from locationsFile and, if
|
|
||||||
* available, the etags and last-modified headers loaded from etagsFile and
|
|
||||||
* lastModifiedFile.
|
|
||||||
*
|
|
||||||
* @param locationsFile
|
|
||||||
* A file containing one url on each line.
|
|
||||||
* @param etagsFile
|
|
||||||
* A file containg the etag headers used for conditional GET. The
|
|
||||||
* file is in the format "url=etag".
|
|
||||||
* @param lastModifiedFile
|
|
||||||
* A file containg the last-modified headers used for conditional
|
|
||||||
* GET. The file is in the format "url=leastmodified".
|
|
||||||
* @param defaultSubs default subscription file
|
|
||||||
* @param proxyHost proxy hostname
|
|
||||||
* @param proxyPort proxy port number
|
|
||||||
*/
|
|
||||||
public SubscriptionList(File locationsFile, File etagsFile,
|
|
||||||
File lastModifiedFile, List defaultSubs, String proxyHost,
|
|
||||||
int proxyPort) {
|
|
||||||
this.subscriptions = new LinkedList();
|
|
||||||
this.etagsFile = etagsFile;
|
|
||||||
this.lastModifiedFile = lastModifiedFile;
|
|
||||||
this.proxyHost = proxyHost;
|
|
||||||
this.proxyPort = proxyPort;
|
|
||||||
Map etags;
|
|
||||||
Map lastModified;
|
|
||||||
String location;
|
|
||||||
List locations = ConfigParser.parseSubscriptions(locationsFile,
|
|
||||||
defaultSubs);
|
|
||||||
try {
|
|
||||||
etags = ConfigParser.parse(etagsFile);
|
|
||||||
} catch (IOException exp) {
|
|
||||||
etags = new HashMap();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
lastModified = ConfigParser.parse(lastModifiedFile);
|
|
||||||
} catch (IOException exp) {
|
|
||||||
lastModified = new HashMap();
|
|
||||||
}
|
|
||||||
Iterator iter = locations.iterator();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
location = (String) iter.next();
|
|
||||||
this.subscriptions.add(new Subscription(location, (String) etags
|
|
||||||
.get(location), (String) lastModified.get(location)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an iterator over the AddressBooks represented by the Subscriptions
|
|
||||||
* in this SubscriptionList.
|
|
||||||
*
|
|
||||||
* @return A SubscriptionIterator.
|
|
||||||
*/
|
|
||||||
public SubscriptionIterator iterator() {
|
|
||||||
return new SubscriptionIterator(this.subscriptions, this.proxyHost,
|
|
||||||
this.proxyPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the etag and last-modified headers for each Subscription to files.
|
|
||||||
*/
|
|
||||||
public void write() {
|
|
||||||
Iterator iter = this.subscriptions.iterator();
|
|
||||||
Subscription sub;
|
|
||||||
Map etags = new HashMap();
|
|
||||||
Map lastModified = new HashMap();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
sub = (Subscription) iter.next();
|
|
||||||
if (sub.getEtag() != null) {
|
|
||||||
etags.put(sub.getLocation(), sub.getEtag());
|
|
||||||
}
|
|
||||||
if (sub.getLastModified() != null) {
|
|
||||||
lastModified.put(sub.getLocation(), sub.getLastModified());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
ConfigParser.write(etags, this.etagsFile);
|
|
||||||
ConfigParser.write(lastModified, this.lastModifiedFile);
|
|
||||||
} catch (IOException exp) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
# addressbook master address book. Addresses placed in this file take precidence
|
|
||||||
# over those in the router address book and in remote address books. If changes
|
|
||||||
# are made to this file, they will be reflected in the router address book and
|
|
||||||
# published address book after the next update.
|
|
||||||
#
|
|
||||||
# Do not make changes directly to the router address book, as they could be lost
|
|
||||||
# during an update.
|
|
||||||
#
|
|
||||||
# This file takes addresses in the hosts.txt format, i.e.
|
|
||||||
# example.i2p=somereallylongbase64thingAAAA
|
|
@ -1,7 +0,0 @@
|
|||||||
# Subscription list for addressbook
|
|
||||||
#
|
|
||||||
# Each entry is an absolute url to a file in hosts.txt format.
|
|
||||||
# Since the list is checked in order, url's should be listed in order of trust.
|
|
||||||
#
|
|
||||||
http://dev.i2p/i2p/hosts.txt
|
|
||||||
http://duck.i2p/hosts.txt
|
|
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE web-app
|
|
||||||
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
|
|
||||||
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
|
|
||||||
|
|
||||||
<web-app>
|
|
||||||
<servlet>
|
|
||||||
<servlet-name>addressbook</servlet-name>
|
|
||||||
<servlet-class>addressbook.Servlet</servlet-class>
|
|
||||||
<init-param>
|
|
||||||
<param-name>home</param-name>
|
|
||||||
<param-value>./addressbook</param-value>
|
|
||||||
</init-param>
|
|
||||||
<load-on-startup>1</load-on-startup>
|
|
||||||
</servlet>
|
|
||||||
</web-app>
|
|
@ -1,381 +0,0 @@
|
|||||||
package net.i2p.client;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* free (adj.): unencumbered; not under the control of others
|
|
||||||
* Written by jrandom in 2003 and released into the public domain
|
|
||||||
* with no warranty of any kind, either expressed or implied.
|
|
||||||
* It probably won't make your computer catch on fire, or eat
|
|
||||||
* your children, but it might. Use at your own risk.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
|
||||||
import net.i2p.I2PException;
|
|
||||||
import net.i2p.data.DataFormatException;
|
|
||||||
import net.i2p.data.Destination;
|
|
||||||
import net.i2p.util.Clock;
|
|
||||||
import net.i2p.util.I2PThread;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ATalk - anonymous talk, demonstrating a trivial I2P usage scenario.
|
|
||||||
* Run this class with no arguments for a manual.
|
|
||||||
*
|
|
||||||
* @author jrandom
|
|
||||||
*/
|
|
||||||
public class ATalk implements I2PSessionListener, Runnable {
|
|
||||||
/** logging hook - status messages are piped to this */
|
|
||||||
private final static Log _log = new Log(ATalk.class);
|
|
||||||
|
|
||||||
/** platform independent newline */
|
|
||||||
private final static String NL = System.getProperty("line.separator");
|
|
||||||
|
|
||||||
/** the current session */
|
|
||||||
private I2PSession _session;
|
|
||||||
|
|
||||||
/** who am i */
|
|
||||||
private Destination _myDestination;
|
|
||||||
|
|
||||||
/** who are you? */
|
|
||||||
private Destination _peerDestination;
|
|
||||||
|
|
||||||
/** location of my secret key file */
|
|
||||||
private String _myKeyFile;
|
|
||||||
|
|
||||||
/** location of their public key */
|
|
||||||
private String _theirDestinationFile;
|
|
||||||
|
|
||||||
/** where the application reads input from. currently set to standard input */
|
|
||||||
private BufferedReader _in;
|
|
||||||
|
|
||||||
/** where the application sends output to. currently set to standard output */
|
|
||||||
private BufferedWriter _out;
|
|
||||||
|
|
||||||
/** string that messages must begin with to be treated as files */
|
|
||||||
private final static String FILE_COMMAND = ".file: ";
|
|
||||||
|
|
||||||
/** the, erm, manual */
|
|
||||||
private final static String MANUAL = "ATalk: Anonymous Talk, a demo program for the Invisible Internet Project SDK"
|
|
||||||
+ NL
|
|
||||||
+ "To generate a new destination:"
|
|
||||||
+ NL
|
|
||||||
+ "\tATalk [fileToSavePrivateKeyIn] [fileToSavePublicKeyIn]"
|
|
||||||
+ NL
|
|
||||||
+ "To talk to another destination:"
|
|
||||||
+ NL
|
|
||||||
+ "\tATalk [myPrivateKeyFile] [peerPublicKey] [shouldLogToScreen]"
|
|
||||||
+ NL
|
|
||||||
+ "shouldLogToScreen is 'true' or 'false', depending on whether you want log info on the screen"
|
|
||||||
+ NL
|
|
||||||
+ "When talking to another destination, messages are sent after you hit return"
|
|
||||||
+ NL
|
|
||||||
+ "To send a file, send a message saying:"
|
|
||||||
+ NL
|
|
||||||
+ "\t"
|
|
||||||
+ FILE_COMMAND
|
|
||||||
+ "[filenameToSend]"
|
|
||||||
+ NL
|
|
||||||
+ "The peer will then recieve the file and be notified of where it has been saved"
|
|
||||||
+ NL
|
|
||||||
+ "To end the talk session, enter a period on a line by itself and hit return"
|
|
||||||
+ NL;
|
|
||||||
|
|
||||||
public final static String PROP_CONFIG_LOCATION = "configFile";
|
|
||||||
|
|
||||||
private static final SimpleDateFormat _fmt = new SimpleDateFormat("hh:mm:ss.SSS");
|
|
||||||
|
|
||||||
/** Construct the talk engine, but don't connect yet */
|
|
||||||
public ATalk(String myKeyFile, String theirDestFile) {
|
|
||||||
_myKeyFile = myKeyFile;
|
|
||||||
_theirDestinationFile = theirDestFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Actually start up the connection to the I2P network.
|
|
||||||
* Successful connect does not mean the peer is online or reachable.
|
|
||||||
*
|
|
||||||
* @throws IOException if there is a problem reading in the keys from the files specified
|
|
||||||
* @throws DataFormatException if the key files are not in the valid format
|
|
||||||
* @throws I2PSessionException if there is a problem contacting the I2P router
|
|
||||||
*/
|
|
||||||
public void connect() throws IOException, I2PSessionException, DataFormatException {
|
|
||||||
I2PClient client = I2PClientFactory.createClient();
|
|
||||||
File myFile = new File(_myKeyFile);
|
|
||||||
Properties props = new Properties();
|
|
||||||
String configLocation = System.getProperty(PROP_CONFIG_LOCATION, "atalk.config");
|
|
||||||
try {
|
|
||||||
props.load(new FileInputStream(configLocation));
|
|
||||||
} catch (FileNotFoundException fnfe) {
|
|
||||||
_log.warn("Unable to load up the ATalk config file " + configLocation);
|
|
||||||
}
|
|
||||||
// Provide any router or client API configuration here.
|
|
||||||
if (!props.containsKey(I2PClient.PROP_TCP_HOST))
|
|
||||||
props.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
|
|
||||||
if (!props.containsKey(I2PClient.PROP_TCP_PORT))
|
|
||||||
props.setProperty(I2PClient.PROP_TCP_PORT, "7654");
|
|
||||||
if (!props.containsKey(I2PClient.PROP_RELIABILITY))
|
|
||||||
props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
|
|
||||||
_session = client.createSession(new FileInputStream(myFile), props);
|
|
||||||
_session.setSessionListener(this);
|
|
||||||
_session.connect();
|
|
||||||
|
|
||||||
File peerDestFile = new File(_theirDestinationFile);
|
|
||||||
_peerDestination = new Destination();
|
|
||||||
_peerDestination.readBytes(new FileInputStream(peerDestFile));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Actual bulk processing of the application, reading in user input,
|
|
||||||
* sending messages, and displaying results. When this function exits, the
|
|
||||||
* application is complete.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
connect();
|
|
||||||
_in = new BufferedReader(new InputStreamReader(System.in));
|
|
||||||
_out = new BufferedWriter(new OutputStreamWriter(System.out));
|
|
||||||
|
|
||||||
_out.write("Starting up anonymous talk session" + NL);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
String line = _in.readLine();
|
|
||||||
if ((line == null) || (line.trim().length() <= 0)) continue;
|
|
||||||
if (".".equals(line)) {
|
|
||||||
boolean ok = _session.sendMessage(_peerDestination, ("Peer disconnected at " + now()).getBytes());
|
|
||||||
// ignore ok, we're closing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (line.startsWith(FILE_COMMAND) && (line.trim().length() > FILE_COMMAND.length())) {
|
|
||||||
try {
|
|
||||||
String file = line.substring(FILE_COMMAND.length());
|
|
||||||
boolean sent = sendFile(file);
|
|
||||||
if (!sent) {
|
|
||||||
_out.write("Failed sending the file: " + file + NL);
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_out.write("Error sending the file: " + ioe.getMessage() + NL);
|
|
||||||
_log.error("Error sending the file", ioe);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
boolean ok = _session.sendMessage(_peerDestination, ("[" + now() + "] " + line).getBytes());
|
|
||||||
if (!ok) {
|
|
||||||
_out.write("Failed sending message. Peer disconnected?" + NL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.error("Error running", ioe);
|
|
||||||
} catch (I2PSessionException ise) {
|
|
||||||
_log.error("Error communicating", ise);
|
|
||||||
} catch (DataFormatException dfe) {
|
|
||||||
_log.error("Peer destination file is not valid", dfe);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
_log.debug("Exiting anonymous talk session");
|
|
||||||
if (_out != null) _out.write("Exiting anonymous talk session");
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
if (_session != null) {
|
|
||||||
try {
|
|
||||||
_session.destroySession();
|
|
||||||
} catch (I2PSessionException ise) {
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Thread.sleep(5000);
|
|
||||||
} catch (InterruptedException ie) { // nop!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String now() {
|
|
||||||
Date now = new Date(Clock.getInstance().now());
|
|
||||||
return _fmt.format(now);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Send the given file to the current peer. This works by sending a message
|
|
||||||
* saying ".file: filename\nbodyOfFile", where filename is the name of the file
|
|
||||||
* (which the recipient will be shown), and the bodyOfFile is the set of raw
|
|
||||||
* bytes in the file.
|
|
||||||
*
|
|
||||||
* @throws IOException if the file could not be found or read
|
|
||||||
* @return false if the file could not be sent to the peer
|
|
||||||
*/
|
|
||||||
private boolean sendFile(String filename) throws IOException, I2PSessionException {
|
|
||||||
_log.debug("Sending file [" + filename + "]");
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
|
|
||||||
baos.write((FILE_COMMAND + filename + "\n").getBytes());
|
|
||||||
FileInputStream fin = new FileInputStream(filename);
|
|
||||||
byte buf[] = new byte[4096];
|
|
||||||
try {
|
|
||||||
while (true) {
|
|
||||||
int len = fin.read(buf);
|
|
||||||
if (len == -1) break;
|
|
||||||
baos.write(buf, 0, len);
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.debug("Failed reading the file", ioe);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
baos.close();
|
|
||||||
byte val[] = baos.toByteArray();
|
|
||||||
_log.debug("Sending " + filename + " with a full payload of " + val.length);
|
|
||||||
try {
|
|
||||||
boolean rv = _session.sendMessage(_peerDestination, val);
|
|
||||||
_log.debug("Sending " + filename + " complete: rv = " + rv);
|
|
||||||
return rv;
|
|
||||||
} catch (Throwable t) {
|
|
||||||
_log.error("Error sending file", t);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** I2PSessionListener.messageAvailable requires this method to be called whenever
|
|
||||||
* I2P wants to tell the session that a message is available. ATalk always grabs
|
|
||||||
* the message immediately and either processes it as a "send file" command (passing
|
|
||||||
* it off to handleRecieveFile(..) or simply displays the
|
|
||||||
* message to the user.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void messageAvailable(I2PSession session, int msgId, long size) {
|
|
||||||
_log.debug("Message available: id = " + msgId + " size = " + size);
|
|
||||||
try {
|
|
||||||
byte msg[] = session.receiveMessage(msgId);
|
|
||||||
// inefficient way to just read the first line of text, but its easy
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(msg)));
|
|
||||||
String line = reader.readLine();
|
|
||||||
if (line.startsWith(FILE_COMMAND)) {
|
|
||||||
handleRecieveFile(line, msg);
|
|
||||||
} else {
|
|
||||||
// not a file command, so just plop 'er out on the screen
|
|
||||||
_out.write(now() + " --> " + new String(msg));
|
|
||||||
_out.write(NL);
|
|
||||||
_out.flush();
|
|
||||||
}
|
|
||||||
} catch (I2PSessionException ise) {
|
|
||||||
_log.error("Error fetching available message", ise);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.error("Error writing out the message", ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** React to a file being sent our way from the peer via {@link #sendFile sendFile}
|
|
||||||
* by saving the file to a temporary location and displaying where, what, and how large
|
|
||||||
* it is.
|
|
||||||
*
|
|
||||||
* @param firstline the first line of the message that, according to the sendFile
|
|
||||||
* implementation, contains the command and the filename that it was stored
|
|
||||||
* at on the peer's computer
|
|
||||||
* @param msg the entire message recieved, including the firstline
|
|
||||||
*/
|
|
||||||
private void handleRecieveFile(String firstline, byte msg[]) throws IOException {
|
|
||||||
_log.debug("handleRecieveFile called");
|
|
||||||
File f = File.createTempFile("recieve", ".dat", new File("."));
|
|
||||||
FileOutputStream fos = new FileOutputStream(f);
|
|
||||||
int lineLen = firstline.getBytes().length + "\n".getBytes().length;
|
|
||||||
int lenToCopy = msg.length - lineLen;
|
|
||||||
byte buf[] = new byte[lenToCopy];
|
|
||||||
System.arraycopy(msg, lineLen, buf, 0, lenToCopy);
|
|
||||||
fos.write(buf);
|
|
||||||
fos.close();
|
|
||||||
String name = firstline.substring(FILE_COMMAND.length());
|
|
||||||
_out.write("Recieved a file called [" + name + "] of size [" + lenToCopy + "] bytes, saved as ["
|
|
||||||
+ f.getAbsolutePath() + "]" + NL);
|
|
||||||
_out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** driver */
|
|
||||||
public static void main(String args[]) {
|
|
||||||
I2PAppContext context = new I2PAppContext();
|
|
||||||
if (args.length == 2) {
|
|
||||||
String myKeyFile = args[0];
|
|
||||||
String myDestinationFile = args[1];
|
|
||||||
boolean success = generateKeys(myKeyFile, myDestinationFile);
|
|
||||||
if (success)
|
|
||||||
_log.debug("Keys generated (private key file: " + myKeyFile + " destination file: " + myDestinationFile
|
|
||||||
+ ")");
|
|
||||||
else
|
|
||||||
_log.debug("Keys generation failed");
|
|
||||||
try {
|
|
||||||
Thread.sleep(5000);
|
|
||||||
} catch (InterruptedException ie) { // nop
|
|
||||||
}
|
|
||||||
} else if (args.length == 3) {
|
|
||||||
_log.debug("Starting chat");
|
|
||||||
String myKeyfile = args[0];
|
|
||||||
String peerDestFile = args[1];
|
|
||||||
String shouldLog = args[2];
|
|
||||||
if (Boolean.TRUE.toString().equalsIgnoreCase(shouldLog))
|
|
||||||
context.logManager().setDisplayOnScreen(true);
|
|
||||||
else
|
|
||||||
context.logManager().setDisplayOnScreen(false);
|
|
||||||
String logFile = args[2];
|
|
||||||
Thread talkThread = new I2PThread(new ATalk(myKeyfile, peerDestFile));
|
|
||||||
talkThread.start();
|
|
||||||
} else {
|
|
||||||
System.out.println(MANUAL);
|
|
||||||
try {
|
|
||||||
Thread.sleep(5000);
|
|
||||||
} catch (InterruptedException ie) { // nop
|
|
||||||
}
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Generate a new Destination, saving that destination and the associated
|
|
||||||
* private keys in the privKeyFile, and also saving the destination without
|
|
||||||
* any private keys to destinationFile.
|
|
||||||
*
|
|
||||||
* @param privKeyFile private key file, including the destination and the various
|
|
||||||
* private keys, as defined by {@link I2PClient#createDestination I2PClient.createDestination}
|
|
||||||
* @param destinationFile file in which the Destination is serialized in
|
|
||||||
*/
|
|
||||||
private static boolean generateKeys(String privKeyFile, String destinationFile) {
|
|
||||||
try {
|
|
||||||
Destination d = I2PClientFactory.createClient().createDestination(new FileOutputStream(privKeyFile));
|
|
||||||
FileOutputStream fos = new FileOutputStream(destinationFile);
|
|
||||||
d.writeBytes(fos);
|
|
||||||
fos.flush();
|
|
||||||
fos.close();
|
|
||||||
return true;
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.error("Error generating keys", ioe);
|
|
||||||
} catch (I2PException ipe) {
|
|
||||||
_log.error("Error generating keys", ipe);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** required by {@link I2PSessionListener I2PSessionListener} to notify of disconnect */
|
|
||||||
public void disconnected(I2PSession session) {
|
|
||||||
_log.debug("Disconnected");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** required by {@link I2PSessionListener I2PSessionListener} to notify of error */
|
|
||||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
|
||||||
_log.debug("Error occurred: " + message, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** required by {@link I2PSessionListener I2PSessionListener} to notify of abuse */
|
|
||||||
public void reportAbuse(I2PSession session, int severity) {
|
|
||||||
_log.debug("Abuse reported of severity " + severity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project basedir="." default="all" name="fortuna">
|
|
||||||
|
|
||||||
<property name="cvs.base.dir" value="java/gnu-crypto" />
|
|
||||||
<property name="cvs.etc.dir" value="${cvs.base.dir}/etc" />
|
|
||||||
<property name="cvs.lib.dir" value="${cvs.base.dir}/lib" />
|
|
||||||
<property name="cvs.object.dir" value="${cvs.base.dir}/classes" />
|
|
||||||
<property name="cvs.base.crypto.object.dir" value="${cvs.object.dir}/gnu/crypto" />
|
|
||||||
<property name="cvs.cipher.object.dir" value="${cvs.base.crypto.object.dir}/cipher" />
|
|
||||||
<property name="cvs.hash.object.dir" value="${cvs.base.crypto.object.dir}/hash" />
|
|
||||||
<property name="cvs.prng.object.dir" value="${cvs.base.crypto.object.dir}/prng" />
|
|
||||||
|
|
||||||
<patternset id="fortuna.files">
|
|
||||||
<include name="${cvs.base.crypto.object.dir}/Registry.class"/>
|
|
||||||
<include name="${cvs.prng.object.dir}/Fortuna*.class"/>
|
|
||||||
<include name="${cvs.prng.object.dir}/BasePRNG.class"/>
|
|
||||||
<include name="${cvs.prng.object.dir}/RandomEventListener.class"/>
|
|
||||||
<include name="${cvs.prng.object.dir}/IRandom.class"/>
|
|
||||||
<include name="${cvs.cipher.object.dir}/CipherFactory.class"/>
|
|
||||||
<include name="${cvs.cipher.object.dir}/IBlockCipher.class"/>
|
|
||||||
<include name="${cvs.hash.object.dir}/HashFactory.class"/>
|
|
||||||
<include name="${cvs.hash.object.dir}/IMessageDigest.class"/>
|
|
||||||
</patternset>
|
|
||||||
|
|
||||||
<target name="all" depends="build,jar"
|
|
||||||
description="Create and test the custom Fortuna library" />
|
|
||||||
|
|
||||||
<target name="build" depends="-init,checkout"
|
|
||||||
description="Build the source and tests">
|
|
||||||
<ant dir="${cvs.base.dir}" target="jar" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="builddep" />
|
|
||||||
|
|
||||||
<target name="checkout" depends="-init" unless="cvs.source.available"
|
|
||||||
description="Check out GNU Crypto sources from CVS HEAD">
|
|
||||||
<cvs cvsRoot=":ext:anoncvs@savannah.gnu.org:/cvsroot/gnu-crypto"
|
|
||||||
cvsRsh="ssh"
|
|
||||||
dest="java"
|
|
||||||
package="gnu-crypto" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="clean"
|
|
||||||
description="Remove generated tests and object files">
|
|
||||||
<ant dir="${cvs.base.dir}" target="clean" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="cleandep" />
|
|
||||||
|
|
||||||
<target name="compile" />
|
|
||||||
|
|
||||||
<target name="distclean" depends="clean"
|
|
||||||
description="Remove all generated files">
|
|
||||||
<delete dir="build" />
|
|
||||||
<delete dir="jartemp" />
|
|
||||||
<!--
|
|
||||||
Annoyingly the GNU Crypto distclean task called here doesn't clean
|
|
||||||
*all* derived files from java/gnu-crypto/lib like it should.....
|
|
||||||
-->
|
|
||||||
<ant dir="${cvs.base.dir}" target="distclean" />
|
|
||||||
<!--
|
|
||||||
.....and so we mop up the rest ourselves.
|
|
||||||
-->
|
|
||||||
<delete dir="${cvs.lib.dir}" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="-init">
|
|
||||||
<available property="cvs.source.available" file="${cvs.base.dir}" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="jar" depends="build"
|
|
||||||
description="Create the custom Fortuna jar library">
|
|
||||||
<delete dir="build" />
|
|
||||||
<delete dir="jartemp" />
|
|
||||||
<mkdir dir="build" />
|
|
||||||
<mkdir dir="jartemp/${cvs.object.dir}" />
|
|
||||||
<copy todir="jartemp">
|
|
||||||
<fileset dir=".">
|
|
||||||
<patternset refid="fortuna.files" />
|
|
||||||
</fileset>
|
|
||||||
</copy>
|
|
||||||
<jar basedir="jartemp/${cvs.object.dir}" jarfile="build/fortuna.jar">
|
|
||||||
<manifest>
|
|
||||||
<section name="fortuna">
|
|
||||||
<attribute name="Implementation-Title" value="I2P Custom GNU Crypto Fortuna Library" />
|
|
||||||
<attribute name="Implementation-Version" value="CVS HEAD" />
|
|
||||||
<attribute name="Implementation-Vendor" value="Free Software Foundation" />
|
|
||||||
<attribute name="Implementation-Vendor-Id" value="FSF" />
|
|
||||||
<attribute name="Implementation-URL" value="http://www.gnu.org/software/gnu-crypto" />
|
|
||||||
</section>
|
|
||||||
</manifest>
|
|
||||||
</jar>
|
|
||||||
<delete dir="jartemp" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="test" depends="jar"
|
|
||||||
description="Perform crypto tests on custom Fortuna jar library" />
|
|
||||||
<!--
|
|
||||||
Add this when Fortuna tests are added to GNU Crypto, else write some
|
|
||||||
-->
|
|
||||||
|
|
||||||
<target name="update" depends="checkout"
|
|
||||||
description="Update GNU Crypto sources to latest CVS HEAD">
|
|
||||||
<cvs command="update -d" cvsRsh="ssh" dest="java/gnu-crypto" />
|
|
||||||
</target>
|
|
||||||
</project>
|
|
@ -1,340 +0,0 @@
|
|||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
Version 2, June 1991
|
|
||||||
|
|
||||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
|
||||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The licenses for most software are designed to take away your
|
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
|
||||||
License is intended to guarantee your freedom to share and change free
|
|
||||||
software--to make sure the software is free for all its users. This
|
|
||||||
General Public License applies to most of the Free Software
|
|
||||||
Foundation's software and to any other program whose authors commit to
|
|
||||||
using it. (Some other Free Software Foundation software is covered by
|
|
||||||
the GNU Library General Public License instead.) You can apply it to
|
|
||||||
your programs, too.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
|
||||||
have the freedom to distribute copies of free software (and charge for
|
|
||||||
this service if you wish), that you receive source code or can get it
|
|
||||||
if you want it, that you can change the software or use pieces of it
|
|
||||||
in new free programs; and that you know you can do these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to make restrictions that forbid
|
|
||||||
anyone to deny you these rights or to ask you to surrender the rights.
|
|
||||||
These restrictions translate to certain responsibilities for you if you
|
|
||||||
distribute copies of the software, or if you modify it.
|
|
||||||
|
|
||||||
For example, if you distribute copies of such a program, whether
|
|
||||||
gratis or for a fee, you must give the recipients all the rights that
|
|
||||||
you have. You must make sure that they, too, receive or can get the
|
|
||||||
source code. And you must show them these terms so they know their
|
|
||||||
rights.
|
|
||||||
|
|
||||||
We protect your rights with two steps: (1) copyright the software, and
|
|
||||||
(2) offer you this license which gives you legal permission to copy,
|
|
||||||
distribute and/or modify the software.
|
|
||||||
|
|
||||||
Also, for each author's protection and ours, we want to make certain
|
|
||||||
that everyone understands that there is no warranty for this free
|
|
||||||
software. If the software is modified by someone else and passed on, we
|
|
||||||
want its recipients to know that what they have is not the original, so
|
|
||||||
that any problems introduced by others will not reflect on the original
|
|
||||||
authors' reputations.
|
|
||||||
|
|
||||||
Finally, any free program is threatened constantly by software
|
|
||||||
patents. We wish to avoid the danger that redistributors of a free
|
|
||||||
program will individually obtain patent licenses, in effect making the
|
|
||||||
program proprietary. To prevent this, we have made it clear that any
|
|
||||||
patent must be licensed for everyone's free use or not licensed at all.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow.
|
|
||||||
|
|
||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. This License applies to any program or other work which contains
|
|
||||||
a notice placed by the copyright holder saying it may be distributed
|
|
||||||
under the terms of this General Public License. The "Program", below,
|
|
||||||
refers to any such program or work, and a "work based on the Program"
|
|
||||||
means either the Program or any derivative work under copyright law:
|
|
||||||
that is to say, a work containing the Program or a portion of it,
|
|
||||||
either verbatim or with modifications and/or translated into another
|
|
||||||
language. (Hereinafter, translation is included without limitation in
|
|
||||||
the term "modification".) Each licensee is addressed as "you".
|
|
||||||
|
|
||||||
Activities other than copying, distribution and modification are not
|
|
||||||
covered by this License; they are outside its scope. The act of
|
|
||||||
running the Program is not restricted, and the output from the Program
|
|
||||||
is covered only if its contents constitute a work based on the
|
|
||||||
Program (independent of having been made by running the Program).
|
|
||||||
Whether that is true depends on what the Program does.
|
|
||||||
|
|
||||||
1. You may copy and distribute verbatim copies of the Program's
|
|
||||||
source code as you receive it, in any medium, provided that you
|
|
||||||
conspicuously and appropriately publish on each copy an appropriate
|
|
||||||
copyright notice and disclaimer of warranty; keep intact all the
|
|
||||||
notices that refer to this License and to the absence of any warranty;
|
|
||||||
and give any other recipients of the Program a copy of this License
|
|
||||||
along with the Program.
|
|
||||||
|
|
||||||
You may charge a fee for the physical act of transferring a copy, and
|
|
||||||
you may at your option offer warranty protection in exchange for a fee.
|
|
||||||
|
|
||||||
2. You may modify your copy or copies of the Program or any portion
|
|
||||||
of it, thus forming a work based on the Program, and copy and
|
|
||||||
distribute such modifications or work under the terms of Section 1
|
|
||||||
above, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) You must cause the modified files to carry prominent notices
|
|
||||||
stating that you changed the files and the date of any change.
|
|
||||||
|
|
||||||
b) You must cause any work that you distribute or publish, that in
|
|
||||||
whole or in part contains or is derived from the Program or any
|
|
||||||
part thereof, to be licensed as a whole at no charge to all third
|
|
||||||
parties under the terms of this License.
|
|
||||||
|
|
||||||
c) If the modified program normally reads commands interactively
|
|
||||||
when run, you must cause it, when started running for such
|
|
||||||
interactive use in the most ordinary way, to print or display an
|
|
||||||
announcement including an appropriate copyright notice and a
|
|
||||||
notice that there is no warranty (or else, saying that you provide
|
|
||||||
a warranty) and that users may redistribute the program under
|
|
||||||
these conditions, and telling the user how to view a copy of this
|
|
||||||
License. (Exception: if the Program itself is interactive but
|
|
||||||
does not normally print such an announcement, your work based on
|
|
||||||
the Program is not required to print an announcement.)
|
|
||||||
|
|
||||||
These requirements apply to the modified work as a whole. If
|
|
||||||
identifiable sections of that work are not derived from the Program,
|
|
||||||
and can be reasonably considered independent and separate works in
|
|
||||||
themselves, then this License, and its terms, do not apply to those
|
|
||||||
sections when you distribute them as separate works. But when you
|
|
||||||
distribute the same sections as part of a whole which is a work based
|
|
||||||
on the Program, the distribution of the whole must be on the terms of
|
|
||||||
this License, whose permissions for other licensees extend to the
|
|
||||||
entire whole, and thus to each and every part regardless of who wrote it.
|
|
||||||
|
|
||||||
Thus, it is not the intent of this section to claim rights or contest
|
|
||||||
your rights to work written entirely by you; rather, the intent is to
|
|
||||||
exercise the right to control the distribution of derivative or
|
|
||||||
collective works based on the Program.
|
|
||||||
|
|
||||||
In addition, mere aggregation of another work not based on the Program
|
|
||||||
with the Program (or with a work based on the Program) on a volume of
|
|
||||||
a storage or distribution medium does not bring the other work under
|
|
||||||
the scope of this License.
|
|
||||||
|
|
||||||
3. You may copy and distribute the Program (or a work based on it,
|
|
||||||
under Section 2) in object code or executable form under the terms of
|
|
||||||
Sections 1 and 2 above provided that you also do one of the following:
|
|
||||||
|
|
||||||
a) Accompany it with the complete corresponding machine-readable
|
|
||||||
source code, which must be distributed under the terms of Sections
|
|
||||||
1 and 2 above on a medium customarily used for software interchange; or,
|
|
||||||
|
|
||||||
b) Accompany it with a written offer, valid for at least three
|
|
||||||
years, to give any third party, for a charge no more than your
|
|
||||||
cost of physically performing source distribution, a complete
|
|
||||||
machine-readable copy of the corresponding source code, to be
|
|
||||||
distributed under the terms of Sections 1 and 2 above on a medium
|
|
||||||
customarily used for software interchange; or,
|
|
||||||
|
|
||||||
c) Accompany it with the information you received as to the offer
|
|
||||||
to distribute corresponding source code. (This alternative is
|
|
||||||
allowed only for noncommercial distribution and only if you
|
|
||||||
received the program in object code or executable form with such
|
|
||||||
an offer, in accord with Subsection b above.)
|
|
||||||
|
|
||||||
The source code for a work means the preferred form of the work for
|
|
||||||
making modifications to it. For an executable work, complete source
|
|
||||||
code means all the source code for all modules it contains, plus any
|
|
||||||
associated interface definition files, plus the scripts used to
|
|
||||||
control compilation and installation of the executable. However, as a
|
|
||||||
special exception, the source code distributed need not include
|
|
||||||
anything that is normally distributed (in either source or binary
|
|
||||||
form) with the major components (compiler, kernel, and so on) of the
|
|
||||||
operating system on which the executable runs, unless that component
|
|
||||||
itself accompanies the executable.
|
|
||||||
|
|
||||||
If distribution of executable or object code is made by offering
|
|
||||||
access to copy from a designated place, then offering equivalent
|
|
||||||
access to copy the source code from the same place counts as
|
|
||||||
distribution of the source code, even though third parties are not
|
|
||||||
compelled to copy the source along with the object code.
|
|
||||||
|
|
||||||
4. You may not copy, modify, sublicense, or distribute the Program
|
|
||||||
except as expressly provided under this License. Any attempt
|
|
||||||
otherwise to copy, modify, sublicense or distribute the Program is
|
|
||||||
void, and will automatically terminate your rights under this License.
|
|
||||||
However, parties who have received copies, or rights, from you under
|
|
||||||
this License will not have their licenses terminated so long as such
|
|
||||||
parties remain in full compliance.
|
|
||||||
|
|
||||||
5. You are not required to accept this License, since you have not
|
|
||||||
signed it. However, nothing else grants you permission to modify or
|
|
||||||
distribute the Program or its derivative works. These actions are
|
|
||||||
prohibited by law if you do not accept this License. Therefore, by
|
|
||||||
modifying or distributing the Program (or any work based on the
|
|
||||||
Program), you indicate your acceptance of this License to do so, and
|
|
||||||
all its terms and conditions for copying, distributing or modifying
|
|
||||||
the Program or works based on it.
|
|
||||||
|
|
||||||
6. Each time you redistribute the Program (or any work based on the
|
|
||||||
Program), the recipient automatically receives a license from the
|
|
||||||
original licensor to copy, distribute or modify the Program subject to
|
|
||||||
these terms and conditions. You may not impose any further
|
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
|
||||||
You are not responsible for enforcing compliance by third parties to
|
|
||||||
this License.
|
|
||||||
|
|
||||||
7. If, as a consequence of a court judgment or allegation of patent
|
|
||||||
infringement or for any other reason (not limited to patent issues),
|
|
||||||
conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot
|
|
||||||
distribute so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you
|
|
||||||
may not distribute the Program at all. For example, if a patent
|
|
||||||
license would not permit royalty-free redistribution of the Program by
|
|
||||||
all those who receive copies directly or indirectly through you, then
|
|
||||||
the only way you could satisfy both it and this License would be to
|
|
||||||
refrain entirely from distribution of the Program.
|
|
||||||
|
|
||||||
If any portion of this section is held invalid or unenforceable under
|
|
||||||
any particular circumstance, the balance of the section is intended to
|
|
||||||
apply and the section as a whole is intended to apply in other
|
|
||||||
circumstances.
|
|
||||||
|
|
||||||
It is not the purpose of this section to induce you to infringe any
|
|
||||||
patents or other property right claims or to contest validity of any
|
|
||||||
such claims; this section has the sole purpose of protecting the
|
|
||||||
integrity of the free software distribution system, which is
|
|
||||||
implemented by public license practices. Many people have made
|
|
||||||
generous contributions to the wide range of software distributed
|
|
||||||
through that system in reliance on consistent application of that
|
|
||||||
system; it is up to the author/donor to decide if he or she is willing
|
|
||||||
to distribute software through any other system and a licensee cannot
|
|
||||||
impose that choice.
|
|
||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
|
||||||
be a consequence of the rest of this License.
|
|
||||||
|
|
||||||
8. If the distribution and/or use of the Program is restricted in
|
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
|
||||||
original copyright holder who places the Program under this License
|
|
||||||
may add an explicit geographical distribution limitation excluding
|
|
||||||
those countries, so that distribution is permitted only in or among
|
|
||||||
countries not thus excluded. In such case, this License incorporates
|
|
||||||
the limitation as if written in the body of this License.
|
|
||||||
|
|
||||||
9. The Free Software Foundation may publish revised and/or new versions
|
|
||||||
of the General Public License from time to time. Such new versions will
|
|
||||||
be similar in spirit to the present version, but may differ in detail to
|
|
||||||
address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the Program
|
|
||||||
specifies a version number of this License which applies to it and "any
|
|
||||||
later version", you have the option of following the terms and conditions
|
|
||||||
either of that version or of any later version published by the Free
|
|
||||||
Software Foundation. If the Program does not specify a version number of
|
|
||||||
this License, you may choose any version ever published by the Free Software
|
|
||||||
Foundation.
|
|
||||||
|
|
||||||
10. If you wish to incorporate parts of the Program into other free
|
|
||||||
programs whose distribution conditions are different, write to the author
|
|
||||||
to ask for permission. For software which is copyrighted by the Free
|
|
||||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
|
||||||
make exceptions for this. Our decision will be guided by the two goals
|
|
||||||
of preserving the free status of all derivatives of our free software and
|
|
||||||
of promoting the sharing and reuse of software generally.
|
|
||||||
|
|
||||||
NO WARRANTY
|
|
||||||
|
|
||||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
|
||||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
|
||||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
|
||||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
|
||||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
|
||||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
|
||||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
|
||||||
REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
|
||||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
|
||||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
|
||||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
|
||||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
|
||||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
|
||||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
|
||||||
POSSIBILITY OF SUCH DAMAGES.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
|
||||||
possible use to the public, the best way to achieve this is to make it
|
|
||||||
free software which everyone can redistribute and change under these terms.
|
|
||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest
|
|
||||||
to attach them to the start of each source file to most effectively
|
|
||||||
convey the exclusion of warranty; and each file should have at least
|
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
If the program is interactive, make it output a short notice like this
|
|
||||||
when it starts in an interactive mode:
|
|
||||||
|
|
||||||
Gnomovision version 69, Copyright (C) year name of author
|
|
||||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
|
||||||
This is free software, and you are welcome to redistribute it
|
|
||||||
under certain conditions; type `show c' for details.
|
|
||||||
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
||||||
parts of the General Public License. Of course, the commands you use may
|
|
||||||
be called something other than `show w' and `show c'; they could even be
|
|
||||||
mouse-clicks or menu items--whatever suits your program.
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or your
|
|
||||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
|
||||||
necessary. Here is a sample; alter the names:
|
|
||||||
|
|
||||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
|
||||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
|
||||||
|
|
||||||
<signature of Ty Coon>, 1 April 1989
|
|
||||||
Ty Coon, President of Vice
|
|
||||||
|
|
||||||
This General Public License does not permit incorporating your program into
|
|
||||||
proprietary programs. If your program is a subroutine library, you may
|
|
||||||
consider it more useful to permit linking proprietary applications with the
|
|
||||||
library. If this is what you want to do, use the GNU Library General
|
|
||||||
Public License instead of this License.
|
|
@ -1,24 +0,0 @@
|
|||||||
- I2PSnark:
|
|
||||||
- add multitorrent support by checking the metainfo hash in the
|
|
||||||
PeerAcceptor and feeding it off to the appropriate coordinator
|
|
||||||
- add a web interface
|
|
||||||
|
|
||||||
- BEncode
|
|
||||||
- Byte array length indicator can overflow.
|
|
||||||
- Support really big BigNums (only 256 chars allowed now)
|
|
||||||
- Better BEValue toString(). Uses stupid heuristic now for debugging.
|
|
||||||
- Implemented bencoding.
|
|
||||||
- Remove application level hack to calculate sha1 hash for metainfo
|
|
||||||
(But can it be done as efficiently?)
|
|
||||||
|
|
||||||
- Storage
|
|
||||||
- Check file name filter.
|
|
||||||
|
|
||||||
- TrackerClient
|
|
||||||
- Support undocumented &numwant= request.
|
|
||||||
|
|
||||||
- PeerCoordinator
|
|
||||||
- Disconnect from other seeds as soon as you are a seed yourself.
|
|
||||||
|
|
||||||
- Text UI
|
|
||||||
- Make it completely silent.
|
|
@ -1 +0,0 @@
|
|||||||
Mark Wielaard <mark@klomp.org>
|
|
@ -1,487 +0,0 @@
|
|||||||
2003-06-27 14:24 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* README: Update version number and explain new features.
|
|
||||||
|
|
||||||
2003-06-27 13:51 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* Makefile, org/klomp/snark/GnomeInfoWindow.java,
|
|
||||||
org/klomp/snark/GnomePeerList.java,
|
|
||||||
org/klomp/snark/PeerCoordinator.java,
|
|
||||||
org/klomp/snark/SnarkGnome.java: Add GnomeInfoWindow.
|
|
||||||
|
|
||||||
2003-06-27 00:37 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Snark.java: Implement 'info' and 'list' commands.
|
|
||||||
|
|
||||||
2003-06-27 00:05 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* Makefile, org/klomp/snark/GnomePeerList.java,
|
|
||||||
org/klomp/snark/SnarkGnome.java: Add GnomePeerList to show state of
|
|
||||||
connected peers.
|
|
||||||
|
|
||||||
2003-06-27 00:04 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: Peer.java, PeerID.java: Make Comparable.
|
|
||||||
|
|
||||||
2003-06-23 23:32 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerMonitorTask.java: Correctly update
|
|
||||||
lastDownloaded and lastUploaded.
|
|
||||||
|
|
||||||
2003-06-23 23:20 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Snark.java: When checking storage use the
|
|
||||||
MetaInfo from the storage.
|
|
||||||
|
|
||||||
2003-06-23 21:47 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Storage.java: Fill piece hashes, not info hashes.
|
|
||||||
|
|
||||||
2003-06-23 21:42 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/MetaInfo.java: New package private
|
|
||||||
getPieceHashes() method.
|
|
||||||
|
|
||||||
2003-06-22 19:49 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* README, TODO, org/klomp/snark/Snark.java: Add new command line
|
|
||||||
switch --no-commands. Don't read interactive commands or show
|
|
||||||
usage info.
|
|
||||||
|
|
||||||
2003-06-22 19:26 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* Makefile, org/klomp/snark/PeerCheckerTask.java,
|
|
||||||
org/klomp/snark/PeerMonitorTask.java, org/klomp/snark/Snark.java:
|
|
||||||
Split peer statistic reporting from PeerCheckerTask into
|
|
||||||
PeerMonitorTask. Use new task in Snark text ui.
|
|
||||||
|
|
||||||
2003-06-22 18:32 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Snark.java: Only print peer id when debug level
|
|
||||||
is INFO or higher.
|
|
||||||
|
|
||||||
2003-06-22 18:00 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/ShutdownListener.java: Add new ShutdownListener
|
|
||||||
interface.
|
|
||||||
|
|
||||||
2003-06-22 17:18 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* TODO: Text UI item to not read from stdin.
|
|
||||||
|
|
||||||
2003-06-22 17:18 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* snark-gnome.sh: kaffe java-gnome support (but crashes hard at the
|
|
||||||
moment).
|
|
||||||
|
|
||||||
2003-06-22 14:04 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* Makefile, org/klomp/snark/CoordinatorListener.java,
|
|
||||||
org/klomp/snark/PeerCoordinator.java,
|
|
||||||
org/klomp/snark/ProgressListener.java, org/klomp/snark/Snark.java,
|
|
||||||
org/klomp/snark/SnarkGnome.java,
|
|
||||||
org/klomp/snark/SnarkShutdown.java, org/klomp/snark/Storage.java,
|
|
||||||
org/klomp/snark/StorageListener.java: Split ProgressListener into
|
|
||||||
Storage, Coordinator and Shutdown listener.
|
|
||||||
|
|
||||||
2003-06-20 19:06 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: PeerCoordinator.java, Snark.java,
|
|
||||||
SnarkGnome.java, Storage.java: Progress listeners for both Storage
|
|
||||||
and PeerCoordinator.
|
|
||||||
|
|
||||||
2003-06-20 14:50 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* Makefile, org/klomp/snark/PeerCoordinator.java,
|
|
||||||
org/klomp/snark/ProgressListener.java,
|
|
||||||
org/klomp/snark/SnarkGnome.java: Add ProgressListener.
|
|
||||||
|
|
||||||
2003-06-20 13:22 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/SnarkGnome.java: Add Pieces collected field.
|
|
||||||
|
|
||||||
2003-06-20 12:26 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: PeerCoordinator.java, PeerListener.java,
|
|
||||||
PeerState.java: Add PeerListener.downloaded() which gets called on
|
|
||||||
chunk updates. Keep PeerCoordinator.downloaded up to date using
|
|
||||||
this remove adjusting in gotPiece() except when we receive a bad
|
|
||||||
piece.
|
|
||||||
|
|
||||||
2003-06-16 00:27 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* Makefile, snark-gnome.sh, org/klomp/snark/Snark.java,
|
|
||||||
org/klomp/snark/SnarkGnome.java: Start of a Gnome GUI.
|
|
||||||
|
|
||||||
2003-06-05 13:19 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerCoordinator.java: Don't remove a BAD piece
|
|
||||||
from the wantedPieces list. Revert to synchronizing on
|
|
||||||
wantedPieces for all relevant sections.
|
|
||||||
|
|
||||||
2003-06-03 21:09 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Snark.java: Only call readLine() when !quit.
|
|
||||||
Always print exception when fatal() is called.
|
|
||||||
|
|
||||||
2003-06-01 23:12 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* README: Set release version to 0.4.
|
|
||||||
|
|
||||||
2003-06-01 22:59 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerConnectionIn.java: Handle negative length
|
|
||||||
prefixes (terminates connection).
|
|
||||||
|
|
||||||
2003-06-01 21:34 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: Snark.java, SnarkShutdown.java: Implement
|
|
||||||
correct shutdown and read commands from stdin.
|
|
||||||
|
|
||||||
2003-06-01 21:34 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/TrackerInfo.java: Check that interval and peers
|
|
||||||
list actually exist.
|
|
||||||
|
|
||||||
2003-06-01 21:33 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Storage.java: Implement close().
|
|
||||||
|
|
||||||
2003-06-01 21:05 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerState.java: Fix debug logging.
|
|
||||||
|
|
||||||
2003-06-01 20:55 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerCoordinator.java: Implement halt().
|
|
||||||
|
|
||||||
2003-06-01 20:55 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/ConnectionAcceptor.java: Rename stop() to halt().
|
|
||||||
|
|
||||||
2003-06-01 17:35 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerState.java: Drop lock on this when calling
|
|
||||||
addRequest() from havePiece().
|
|
||||||
|
|
||||||
2003-06-01 14:46 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* README, org/klomp/snark/ConnectionAcceptor.java,
|
|
||||||
org/klomp/snark/HttpAcceptor.java, org/klomp/snark/Peer.java,
|
|
||||||
org/klomp/snark/PeerCheckerTask.java,
|
|
||||||
org/klomp/snark/PeerConnectionIn.java,
|
|
||||||
org/klomp/snark/PeerConnectionOut.java,
|
|
||||||
org/klomp/snark/PeerCoordinator.java,
|
|
||||||
org/klomp/snark/PeerState.java, org/klomp/snark/Snark.java,
|
|
||||||
org/klomp/snark/SnarkShutdown.java, org/klomp/snark/Storage.java,
|
|
||||||
org/klomp/snark/Tracker.java, org/klomp/snark/TrackerClient.java:
|
|
||||||
Add debug/log level.
|
|
||||||
|
|
||||||
2003-05-31 23:04 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: PeerCheckerTask.java, PeerCoordinator.java: Use
|
|
||||||
just one lock (peers) for all synchronization (even for
|
|
||||||
wantedPieces). Let PeerChecker handle real disconnect and keep
|
|
||||||
count of uploaders.
|
|
||||||
|
|
||||||
2003-05-31 22:29 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: Peer.java, PeerConnectionIn.java: Set state to
|
|
||||||
null on first disconnect() call. So always check whether it might
|
|
||||||
already be null. Helps disconnect check.
|
|
||||||
|
|
||||||
2003-05-31 22:27 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerConnectionOut.java: Don't explicitly close
|
|
||||||
the DataOutputStream (if another thread is using it libgcj seems to
|
|
||||||
not like it very much).
|
|
||||||
|
|
||||||
2003-05-30 21:33 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerConnectionOut.java: Cancel
|
|
||||||
(un)interested/(un)choke when (inverse) is still in send queue.
|
|
||||||
Remove pieces from send queue when choke message is actaully send.
|
|
||||||
|
|
||||||
2003-05-30 19:32 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerState.java: Make sure listener.wantPiece(int)
|
|
||||||
is never called while lock on this is held.
|
|
||||||
|
|
||||||
2003-05-30 19:00 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerConnectionOut.java: Indentation cleanup.
|
|
||||||
|
|
||||||
2003-05-30 17:50 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Storage.java: Only synchronize on bitfield as
|
|
||||||
long as necessary.
|
|
||||||
|
|
||||||
2003-05-30 17:43 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Tracker.java: Identing cleanup.
|
|
||||||
|
|
||||||
2003-05-30 16:32 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerState.java: Better error message.
|
|
||||||
|
|
||||||
2003-05-30 15:11 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerState.java: Make sure not to hold the lock on
|
|
||||||
this when calling the listener to prevent deadlocks. Implement
|
|
||||||
handling and sending of cancel messages.
|
|
||||||
|
|
||||||
2003-05-30 14:50 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerCoordinator.java: First check if we still
|
|
||||||
want a piece before trying to add it to the Storage.
|
|
||||||
|
|
||||||
2003-05-30 14:49 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerConnectionOut.java: Implement
|
|
||||||
sendCancel(Request). Add cancelRequest(int, int, int).
|
|
||||||
|
|
||||||
2003-05-30 14:46 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Request.java: Add hashCode() and equals(Object)
|
|
||||||
methods.
|
|
||||||
|
|
||||||
2003-05-30 14:45 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Peer.java: Fix wheter -> whether javadoc
|
|
||||||
comments. Mark state null immediatly after calling
|
|
||||||
listener.disconnected(). Call PeerState.havePiece() not
|
|
||||||
PeerConnectionOut.sendHave() directly.
|
|
||||||
|
|
||||||
2003-05-25 19:23 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* TODO: Add PeerCoordinator TODO for connecting to seeds.
|
|
||||||
|
|
||||||
2003-05-23 12:12 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* Makefile: Create class files with jikes again.
|
|
||||||
|
|
||||||
2003-05-18 22:01 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: PeerCheckerTask.java, PeerCoordinator.java:
|
|
||||||
Prefer to (optimistically) unchoke first those peers that unchoked
|
|
||||||
us. And make sure to not unchoke a peer that we just choked.
|
|
||||||
|
|
||||||
2003-05-18 21:48 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Peer.java: Fix isChoked() to not always return
|
|
||||||
true.
|
|
||||||
|
|
||||||
2003-05-18 14:46 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: Peer.java, PeerCheckerTask.java,
|
|
||||||
PeerCoordinator.java, PeerState.java: Remove separate Peer
|
|
||||||
downloading/uploading states. Keep choke and interest always up to
|
|
||||||
date. Uploading is now just when we are not choking the peer.
|
|
||||||
Downloading is now defined as being unchoked and interesting.
|
|
||||||
CHECK_PERIOD is now 20 seconds. MAX_CONNECTIONS is now 24.
|
|
||||||
MAX_DOWNLOADERS doesn't exists anymore. We download whenever we can
|
|
||||||
from peers.
|
|
||||||
|
|
||||||
2003-05-18 13:57 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerConnectionOut.java: Remove piece messages
|
|
||||||
from queue when we are choking. (They will have to be rerequested
|
|
||||||
when we unchoke the peer again.)
|
|
||||||
|
|
||||||
2003-05-15 00:08 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerState.java: Ignore missed chunk requests,
|
|
||||||
don't requeue them.
|
|
||||||
|
|
||||||
2003-05-15 00:06 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Request.java: Add sanity check
|
|
||||||
|
|
||||||
2003-05-10 15:47 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Snark.java: Add extra '(' to usage message.
|
|
||||||
|
|
||||||
2003-05-10 15:22 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* README: Set version to 0.3 (The Bakers Tale).
|
|
||||||
|
|
||||||
2003-05-10 15:17 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerState.java: Mention received piece in warning
|
|
||||||
message.
|
|
||||||
|
|
||||||
2003-05-10 03:20 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: PeerConnectionIn.java, PeerState.java,
|
|
||||||
Request.java: Remove currentRequest and handle all piece messages
|
|
||||||
from the lastRequested list.
|
|
||||||
|
|
||||||
2003-05-09 20:02 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerState.java: Fix nothing requested warning
|
|
||||||
message.
|
|
||||||
|
|
||||||
2003-05-09 19:59 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerConnectionOut.java: Piece messages are big.
|
|
||||||
So if there are other (control) messages make sure they are send
|
|
||||||
first. Also remove request messages from the queue if we are
|
|
||||||
currently being choked to prevent them from being send even if we
|
|
||||||
get unchoked a little later. (Since we will resent them anyway in
|
|
||||||
that case.)
|
|
||||||
|
|
||||||
2003-05-09 18:33 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: Peer.java, PeerCheckerTask.java,
|
|
||||||
PeerCoordinator.java, PeerID.java: New definition of PeerID.equals
|
|
||||||
(port + address + id) and new method PeerID.sameID (only id). These
|
|
||||||
are used to really see if we already have a connection to a certain
|
|
||||||
peer (active setup vs passive setup).
|
|
||||||
|
|
||||||
2003-05-08 03:05 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerState.java: Use Snark.debug() not
|
|
||||||
System.out.println().
|
|
||||||
|
|
||||||
2003-05-06 20:29 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerState.java: s/noting/nothing/
|
|
||||||
|
|
||||||
2003-05-06 20:28 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* Makefile: s/lagacy/legacy/
|
|
||||||
|
|
||||||
2003-05-05 23:17 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* README: Set version to 0.2, explain new functionality and add
|
|
||||||
examples.
|
|
||||||
|
|
||||||
2003-05-05 22:42 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* .cvsignore, Makefile, org/klomp/snark/StaticSnark.java: Enable
|
|
||||||
-static binary creation.
|
|
||||||
|
|
||||||
2003-05-05 22:42 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Tracker.java: Disable --ip support.
|
|
||||||
|
|
||||||
2003-05-05 21:02 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: HttpAcceptor.java, PeerCheckerTask.java,
|
|
||||||
PeerCoordinator.java, TrackerClient.java: Use Snark.debug() not
|
|
||||||
System.out.println().
|
|
||||||
|
|
||||||
2003-05-05 21:01 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerConnectionIn.java: Be prepared to handle the
|
|
||||||
case where currentRequest is null.
|
|
||||||
|
|
||||||
2003-05-05 21:00 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Snark.java: Improve argument parsing errors.
|
|
||||||
|
|
||||||
2003-05-05 21:00 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* Makefile: Use gcj -C again for creating the class files.
|
|
||||||
|
|
||||||
2003-05-05 09:24 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerState.java: Just clear outstandingRequests,
|
|
||||||
never make it null.
|
|
||||||
|
|
||||||
2003-05-05 02:55 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/TrackerClient.java: Always retry both first
|
|
||||||
started event and every other event as long the TrackerClient is
|
|
||||||
not stopped.
|
|
||||||
|
|
||||||
2003-05-05 02:54 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Snark.java: Remove double assignment port.
|
|
||||||
|
|
||||||
2003-05-05 02:54 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* TODO: Add Tracker TODO item.
|
|
||||||
|
|
||||||
2003-05-04 23:38 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: ConnectionAcceptor.java, MetaInfo.java,
|
|
||||||
Snark.java, Storage.java, Tracker.java: Add info hash calcultation
|
|
||||||
to MetaInfo. Add torrent creation to Storage. Add ip parameter
|
|
||||||
handling to Tracker. Make ConnectionAcceptor handle
|
|
||||||
null/non-existing HttpAcceptors. Add debug output, --ip handling
|
|
||||||
and all the above to Snark.
|
|
||||||
|
|
||||||
2003-05-04 23:36 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/TrackerClient.java: Handle all failing requests
|
|
||||||
the same (print a warning).
|
|
||||||
|
|
||||||
2003-05-03 15:46 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: Peer.java, PeerID.java, TrackerInfo.java: Split
|
|
||||||
Peer and PeerID a little more.
|
|
||||||
|
|
||||||
2003-05-03 15:44 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/MetaInfo.java: Add reannounce() and
|
|
||||||
getTorrentData().
|
|
||||||
|
|
||||||
2003-05-03 15:38 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/: PeerCheckerTask.java, PeerCoordinator.java:
|
|
||||||
More concise verbose/debug output. Always use addUpDownloader() to
|
|
||||||
set peers upload or download state to true.
|
|
||||||
|
|
||||||
2003-05-03 13:38 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/TrackerClient.java: Compile fixes.
|
|
||||||
|
|
||||||
2003-05-03 13:32 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/TrackerClient.java: Only generate fatal() call on
|
|
||||||
first Tracker access. Otherwise just print a warning error message.
|
|
||||||
|
|
||||||
2003-05-03 03:10 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerState.java: Better handle resending
|
|
||||||
outstanding pieces and try to recover better from unrequested
|
|
||||||
pieces.
|
|
||||||
|
|
||||||
2003-05-02 21:33 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* Makefile, org/klomp/snark/HttpAcceptor.java,
|
|
||||||
org/klomp/snark/MetaInfo.java, org/klomp/snark/PeerID.java,
|
|
||||||
org/klomp/snark/Snark.java, org/klomp/snark/Tracker.java,
|
|
||||||
org/klomp/snark/TrackerClient.java,
|
|
||||||
org/klomp/snark/bencode/BEncoder.java: Add Tracker, PeerID and
|
|
||||||
BEncoder.
|
|
||||||
|
|
||||||
2003-05-01 20:17 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* Makefile, org/klomp/snark/ConnectionAcceptor.java,
|
|
||||||
org/klomp/snark/HttpAcceptor.java, org/klomp/snark/Peer.java,
|
|
||||||
org/klomp/snark/PeerAcceptor.java, org/klomp/snark/Snark.java: Add
|
|
||||||
ConnectionAcceptor that handles both PeerAcceptor and HttpAcceptor.
|
|
||||||
|
|
||||||
2003-05-01 18:39 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/PeerCoordinator.java: connected() synchronize on
|
|
||||||
peers.
|
|
||||||
|
|
||||||
2003-04-28 02:56 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/SnarkShutdown.java: Wait some time before
|
|
||||||
returning...
|
|
||||||
|
|
||||||
2003-04-28 02:56 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* TODO: More items.
|
|
||||||
|
|
||||||
2003-04-28 02:56 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* org/klomp/snark/Snark.java: Calculate real random ID.
|
|
||||||
|
|
||||||
2003-04-27 Mark Wielaard <mark@klomp.org>
|
|
||||||
|
|
||||||
* snark: Initial (0.1) version.
|
|
@ -1 +0,0 @@
|
|||||||
i2psnark.dir=i2psnark
|
|
@ -1,111 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project basedir="." default="all" name="i2psnark">
|
|
||||||
<target name="all" depends="clean, build" />
|
|
||||||
<target name="build" depends="builddep, jar, war" />
|
|
||||||
<target name="builddep">
|
|
||||||
<ant dir="../../jetty/" target="build" />
|
|
||||||
<ant dir="../../streaming/java/" target="build" />
|
|
||||||
<!-- streaming will build ministreaming and core -->
|
|
||||||
</target>
|
|
||||||
<condition property="depend.available">
|
|
||||||
<typefound name="depend" />
|
|
||||||
</condition>
|
|
||||||
<target name="depend" if="depend.available">
|
|
||||||
<depend
|
|
||||||
cache="../../../build"
|
|
||||||
srcdir="./src"
|
|
||||||
destdir="./build/obj" >
|
|
||||||
<!-- Depend on classes instead of jars where available -->
|
|
||||||
<classpath>
|
|
||||||
<pathelement location="../../../core/java/build/obj" />
|
|
||||||
<pathelement location="../../../router/java/build/obj" />
|
|
||||||
<pathelement location="../../ministreaming/java/build/obj" />
|
|
||||||
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
|
|
||||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
|
||||||
</classpath>
|
|
||||||
</depend>
|
|
||||||
</target>
|
|
||||||
<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"
|
|
||||||
destdir="./build/obj"
|
|
||||||
classpath="../../../core/java/build/i2p.jar:../../../router/java/build/router.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar:../../ministreaming/java/build/mstreaming.jar" />
|
|
||||||
</target>
|
|
||||||
<target name="jar" depends="builddep, compile">
|
|
||||||
<jar destfile="./build/i2psnark.jar" basedir="./build/obj" includes="**/*.class" excludes="**/*Servlet.class">
|
|
||||||
<manifest>
|
|
||||||
<attribute name="Main-Class" value="org.klomp.snark.Snark" />
|
|
||||||
<attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" />
|
|
||||||
</manifest>
|
|
||||||
</jar>
|
|
||||||
</target>
|
|
||||||
<target name="war" depends="jar">
|
|
||||||
<war destfile="../i2psnark.war" webxml="../web.xml">
|
|
||||||
<classes dir="./build/obj" includes="**/*" />
|
|
||||||
</war>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="standalone" depends="standalone_prep">
|
|
||||||
<zip destfile="i2psnark-standalone.zip">
|
|
||||||
<zipfileset dir="./dist/" prefix="i2psnark/" />
|
|
||||||
</zip>
|
|
||||||
</target>
|
|
||||||
<target name="standalone_prep" depends="war">
|
|
||||||
<javac debug="true" deprecation="on" source="1.5" target="1.5"
|
|
||||||
destdir="./build" srcdir="src/" includes="org/klomp/snark/web/RunStandalone.java" >
|
|
||||||
<classpath>
|
|
||||||
<pathelement location="../../jetty/jettylib/commons-logging.jar" />
|
|
||||||
<pathelement location="../../jetty/jettylib/commons-el.jar" />
|
|
||||||
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
|
|
||||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
|
||||||
<pathelement location="../../../core/java/build/i2p.jar" />
|
|
||||||
</classpath>
|
|
||||||
</javac>
|
|
||||||
|
|
||||||
<jar destfile="./build/launch-i2psnark.jar" basedir="./build/" includes="org/klomp/snark/web/RunStandalone.class">
|
|
||||||
<manifest>
|
|
||||||
<attribute name="Main-Class" value="org.klomp.snark.web.RunStandalone" />
|
|
||||||
<attribute name="Class-Path" value="lib/i2p.jar lib/mstreaming.jar lib/streaming.jar lib/commons-el.jar lib/commons-logging.jar lib/jasper-compiler.jar lib/jasper-runtime.jar lib/javax.servlet.jar lib/org.mortbay.jetty.jar" />
|
|
||||||
</manifest>
|
|
||||||
</jar>
|
|
||||||
|
|
||||||
<delete dir="./dist" />
|
|
||||||
<mkdir dir="./dist" />
|
|
||||||
<copy file="./build/launch-i2psnark.jar" tofile="./dist/launch-i2psnark.jar" />
|
|
||||||
<mkdir dir="./dist/webapps" />
|
|
||||||
<copy file="../i2psnark.war" tofile="./dist/webapps/i2psnark.war" />
|
|
||||||
<mkdir dir="./dist/lib" />
|
|
||||||
<copy file="../../../core/java/build/i2p.jar" tofile="./dist/lib/i2p.jar" />
|
|
||||||
<copy file="../../jetty/jettylib/commons-el.jar" tofile="./dist/lib/commons-el.jar" />
|
|
||||||
<copy file="../../jetty/jettylib/commons-logging.jar" tofile="./dist/lib/commons-logging.jar" />
|
|
||||||
<copy file="../../jetty/jettylib/javax.servlet.jar" tofile="./dist/lib/javax.servlet.jar" />
|
|
||||||
<copy file="../../jetty/jettylib/org.mortbay.jetty.jar" tofile="./dist/lib/org.mortbay.jetty.jar" />
|
|
||||||
<copy file="../../jetty/jettylib/jasper-compiler.jar" tofile="./dist/lib/jasper-compiler.jar" />
|
|
||||||
<copy file="../../jetty/jettylib/jasper-runtime.jar" tofile="./dist/lib/jasper-runtime.jar" />
|
|
||||||
<copy file="../../ministreaming/java/build/mstreaming.jar" tofile="./dist/lib/mstreaming.jar" />
|
|
||||||
<copy file="../../streaming/java/build/streaming.jar" tofile="./dist/lib/streaming.jar" />
|
|
||||||
<copy file="../jetty-i2psnark.xml" tofile="./dist/jetty-i2psnark.xml" />
|
|
||||||
<copy file="../readme-standalone.txt" tofile="./dist/readme.txt" />
|
|
||||||
<mkdir dir="./dist/work" />
|
|
||||||
<mkdir dir="./dist/logs" />
|
|
||||||
|
|
||||||
<zip destfile="i2psnark-standalone.zip">
|
|
||||||
<zipfileset dir="./dist/" prefix="i2psnark/" />
|
|
||||||
</zip>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="clean">
|
|
||||||
<delete dir="./build" />
|
|
||||||
<delete file="../i2psnark.war" />
|
|
||||||
<delete file="./i2psnark-standalone.zip" />
|
|
||||||
</target>
|
|
||||||
<target name="cleandep" depends="clean">
|
|
||||||
<ant dir="../../ministreaming/java/" target="distclean" />
|
|
||||||
</target>
|
|
||||||
<target name="distclean" depends="clean">
|
|
||||||
<ant dir="../../ministreaming/java/" target="distclean" />
|
|
||||||
</target>
|
|
||||||
</project>
|
|
@ -1,156 +0,0 @@
|
|||||||
/* BitField - Container of a byte array representing set and unset bits.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Container of a byte array representing set and unset bits.
|
|
||||||
*/
|
|
||||||
public class BitField
|
|
||||||
{
|
|
||||||
|
|
||||||
private final byte[] bitfield;
|
|
||||||
private final int size;
|
|
||||||
private int count;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new BitField that represents <code>size</code> unset bits.
|
|
||||||
*/
|
|
||||||
public BitField(int size)
|
|
||||||
{
|
|
||||||
this.size = size;
|
|
||||||
int arraysize = ((size-1)/8)+1;
|
|
||||||
bitfield = new byte[arraysize];
|
|
||||||
this.count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new BitField that represents <code>size</code> bits
|
|
||||||
* as set by the given byte array. This will make a copy of the array.
|
|
||||||
* Extra bytes will be ignored.
|
|
||||||
*
|
|
||||||
* @exception ArrayOutOfBoundsException if give byte array is not large
|
|
||||||
* enough.
|
|
||||||
*/
|
|
||||||
public BitField(byte[] bitfield, int size)
|
|
||||||
{
|
|
||||||
this.size = size;
|
|
||||||
int arraysize = ((size-1)/8)+1;
|
|
||||||
this.bitfield = new byte[arraysize];
|
|
||||||
|
|
||||||
// XXX - More correct would be to check that unused bits are
|
|
||||||
// cleared or clear them explicitly ourselves.
|
|
||||||
System.arraycopy(bitfield, 0, this.bitfield, 0, arraysize);
|
|
||||||
|
|
||||||
this.count = 0;
|
|
||||||
for (int i = 0; i < size; i++)
|
|
||||||
if (get(i))
|
|
||||||
this.count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This returns the actual byte array used. Changes to this array
|
|
||||||
* effect this BitField. Note that some bits at the end of the byte
|
|
||||||
* array are supposed to be always unset if they represent bits
|
|
||||||
* bigger then the size of the bitfield.
|
|
||||||
*/
|
|
||||||
public byte[] getFieldBytes()
|
|
||||||
{
|
|
||||||
return bitfield;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the size of the BitField. The returned value is one bigger
|
|
||||||
* then the last valid bit number (since bit numbers are counted
|
|
||||||
* from zero).
|
|
||||||
*/
|
|
||||||
public int size()
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the given bit to true.
|
|
||||||
*
|
|
||||||
* @exception IndexOutOfBoundsException if bit is smaller then zero
|
|
||||||
* bigger then size (inclusive).
|
|
||||||
*/
|
|
||||||
public void set(int bit)
|
|
||||||
{
|
|
||||||
if (bit < 0 || bit >= size)
|
|
||||||
throw new IndexOutOfBoundsException(Integer.toString(bit));
|
|
||||||
int index = bit/8;
|
|
||||||
int mask = 128 >> (bit % 8);
|
|
||||||
if ((bitfield[index] & mask) == 0) {
|
|
||||||
count++;
|
|
||||||
bitfield[index] |= mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the bit is set or false if it is not.
|
|
||||||
*
|
|
||||||
* @exception IndexOutOfBoundsException if bit is smaller then zero
|
|
||||||
* bigger then size (inclusive).
|
|
||||||
*/
|
|
||||||
public boolean get(int bit)
|
|
||||||
{
|
|
||||||
if (bit < 0 || bit >= size)
|
|
||||||
throw new IndexOutOfBoundsException(Integer.toString(bit));
|
|
||||||
|
|
||||||
int index = bit/8;
|
|
||||||
int mask = 128 >> (bit % 8);
|
|
||||||
return (bitfield[index] & mask) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the number of set bits.
|
|
||||||
*/
|
|
||||||
public int count()
|
|
||||||
{
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if all bits are set.
|
|
||||||
*/
|
|
||||||
public boolean complete()
|
|
||||||
{
|
|
||||||
return count >= size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
// Not very efficient
|
|
||||||
StringBuffer sb = new StringBuffer("BitField(");
|
|
||||||
sb.append(size).append(")[");
|
|
||||||
for (int i = 0; i < size; i++)
|
|
||||||
if (get(i))
|
|
||||||
{
|
|
||||||
sb.append(' ');
|
|
||||||
sb.append(i);
|
|
||||||
}
|
|
||||||
sb.append(" ]");
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,190 +0,0 @@
|
|||||||
/* ConnectionAcceptor - Accepts connections and routes them to sub-acceptors.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
import net.i2p.I2PException;
|
|
||||||
import net.i2p.client.streaming.I2PServerSocket;
|
|
||||||
import net.i2p.client.streaming.I2PSocket;
|
|
||||||
import net.i2p.util.I2PAppThread;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accepts connections on a TCP port and routes them to sub-acceptors.
|
|
||||||
*/
|
|
||||||
public class ConnectionAcceptor implements Runnable
|
|
||||||
{
|
|
||||||
private Log _log = new Log(ConnectionAcceptor.class);
|
|
||||||
private I2PServerSocket serverSocket;
|
|
||||||
private PeerAcceptor peeracceptor;
|
|
||||||
private Thread thread;
|
|
||||||
private I2PSnarkUtil _util;
|
|
||||||
|
|
||||||
private boolean stop;
|
|
||||||
private boolean socketChanged;
|
|
||||||
|
|
||||||
public ConnectionAcceptor(I2PSnarkUtil util) { _util = util; }
|
|
||||||
|
|
||||||
public synchronized void startAccepting(PeerCoordinatorSet set, I2PServerSocket socket) {
|
|
||||||
if (serverSocket != socket) {
|
|
||||||
if ( (peeracceptor == null) || (peeracceptor.coordinators != set) )
|
|
||||||
peeracceptor = new PeerAcceptor(set);
|
|
||||||
serverSocket = socket;
|
|
||||||
stop = false;
|
|
||||||
socketChanged = true;
|
|
||||||
if (thread == null) {
|
|
||||||
thread = new I2PAppThread(this, "I2PSnark acceptor");
|
|
||||||
thread.setDaemon(true);
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionAcceptor(I2PSnarkUtil util, I2PServerSocket serverSocket,
|
|
||||||
PeerAcceptor peeracceptor)
|
|
||||||
{
|
|
||||||
this.serverSocket = serverSocket;
|
|
||||||
this.peeracceptor = peeracceptor;
|
|
||||||
_util = util;
|
|
||||||
|
|
||||||
socketChanged = false;
|
|
||||||
stop = false;
|
|
||||||
thread = new I2PAppThread(this, "I2PSnark acceptor");
|
|
||||||
thread.setDaemon(true);
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void halt()
|
|
||||||
{
|
|
||||||
if (stop) return;
|
|
||||||
stop = true;
|
|
||||||
|
|
||||||
I2PServerSocket ss = serverSocket;
|
|
||||||
if (ss != null)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ss.close();
|
|
||||||
}
|
|
||||||
catch(I2PException ioe) { }
|
|
||||||
|
|
||||||
Thread t = thread;
|
|
||||||
if (t != null)
|
|
||||||
t.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restart() {
|
|
||||||
serverSocket = _util.getServerSocket();
|
|
||||||
socketChanged = true;
|
|
||||||
Thread t = thread;
|
|
||||||
if (t != null)
|
|
||||||
t.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPort()
|
|
||||||
{
|
|
||||||
return 6881; // serverSocket.getLocalPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
while(!stop)
|
|
||||||
{
|
|
||||||
if (socketChanged) {
|
|
||||||
// ok, already updated
|
|
||||||
socketChanged = false;
|
|
||||||
}
|
|
||||||
while ( (serverSocket == null) && (!stop)) {
|
|
||||||
serverSocket = _util.getServerSocket();
|
|
||||||
if (serverSocket == null)
|
|
||||||
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
|
|
||||||
}
|
|
||||||
if(stop)
|
|
||||||
break;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
I2PSocket socket = serverSocket.accept();
|
|
||||||
if (socket == null) {
|
|
||||||
if (socketChanged) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
I2PServerSocket ss = _util.getServerSocket();
|
|
||||||
if (ss != serverSocket) {
|
|
||||||
serverSocket = ss;
|
|
||||||
socketChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Thread t = new I2PAppThread(new Handler(socket), "Connection-" + socket);
|
|
||||||
t.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (I2PException ioe)
|
|
||||||
{
|
|
||||||
if (!socketChanged) {
|
|
||||||
_util.debug("Error while accepting: " + ioe, Snark.ERROR);
|
|
||||||
stop = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
_util.debug("Error while accepting: " + ioe, Snark.ERROR);
|
|
||||||
stop = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (serverSocket != null)
|
|
||||||
serverSocket.close();
|
|
||||||
}
|
|
||||||
catch (I2PException ignored) { }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Handler implements Runnable {
|
|
||||||
private I2PSocket _socket;
|
|
||||||
public Handler(I2PSocket socket) {
|
|
||||||
_socket = socket;
|
|
||||||
}
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
InputStream in = _socket.getInputStream();
|
|
||||||
OutputStream out = _socket.getOutputStream();
|
|
||||||
|
|
||||||
if (true) {
|
|
||||||
in = new BufferedInputStream(in);
|
|
||||||
//out = new BufferedOutputStream(out);
|
|
||||||
}
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Handling socket from " + _socket.getPeerDestination().calculateHash().toBase64());
|
|
||||||
peeracceptor.connection(_socket, in, out);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Error handling connection from " + _socket.getPeerDestination().calculateHash().toBase64(), ioe);
|
|
||||||
try { _socket.close(); } catch (IOException ignored) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
/* CoordinatorListener.java - Callback when a peer changes state
|
|
||||||
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback used when some peer changes state.
|
|
||||||
*/
|
|
||||||
public interface CoordinatorListener
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Called when the PeerCoordinator notices a change in the state of a peer.
|
|
||||||
*/
|
|
||||||
void peerChange(PeerCoordinator coordinator, Peer peer);
|
|
||||||
|
|
||||||
public boolean overUploadLimit(int uploaders);
|
|
||||||
public boolean overUpBWLimit();
|
|
||||||
public boolean overUpBWLimit(long total);
|
|
||||||
}
|
|
@ -1,378 +0,0 @@
|
|||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
|
||||||
import net.i2p.I2PException;
|
|
||||||
import net.i2p.client.I2PSession;
|
|
||||||
import net.i2p.client.streaming.I2PServerSocket;
|
|
||||||
import net.i2p.client.streaming.I2PSocket;
|
|
||||||
import net.i2p.client.streaming.I2PSocketManager;
|
|
||||||
import net.i2p.client.streaming.I2PSocketManagerFactory;
|
|
||||||
import net.i2p.data.DataFormatException;
|
|
||||||
import net.i2p.data.Destination;
|
|
||||||
import net.i2p.data.Hash;
|
|
||||||
import net.i2p.util.EepGet;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
import net.i2p.util.SimpleTimer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* I2P specific helpers for I2PSnark
|
|
||||||
* We use this class as a sort of context for i2psnark
|
|
||||||
* so we can run multiple instances of single Snarks
|
|
||||||
* (but not multiple SnarkManagers, it is still static)
|
|
||||||
*/
|
|
||||||
public class I2PSnarkUtil {
|
|
||||||
private I2PAppContext _context;
|
|
||||||
private Log _log;
|
|
||||||
|
|
||||||
private boolean _shouldProxy;
|
|
||||||
private String _proxyHost;
|
|
||||||
private int _proxyPort;
|
|
||||||
private String _i2cpHost;
|
|
||||||
private int _i2cpPort;
|
|
||||||
private Map _opts;
|
|
||||||
private I2PSocketManager _manager;
|
|
||||||
private boolean _configured;
|
|
||||||
private Set _shitlist;
|
|
||||||
private int _maxUploaders;
|
|
||||||
private int _maxUpBW;
|
|
||||||
private int _maxConnections;
|
|
||||||
|
|
||||||
public static final String PROP_USE_OPENTRACKERS = "i2psnark.useOpentrackers";
|
|
||||||
public static final boolean DEFAULT_USE_OPENTRACKERS = true;
|
|
||||||
public static final String PROP_OPENTRACKERS = "i2psnark.opentrackers";
|
|
||||||
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 I2PSnarkUtil(I2PAppContext ctx) {
|
|
||||||
_context = ctx;
|
|
||||||
_log = _context.logManager().getLog(Snark.class);
|
|
||||||
_opts = new HashMap();
|
|
||||||
setProxy("127.0.0.1", 4444);
|
|
||||||
setI2CPConfig("127.0.0.1", 7654, null);
|
|
||||||
_shitlist = new HashSet(64);
|
|
||||||
_configured = false;
|
|
||||||
_maxUploaders = Snark.MAX_TOTAL_UPLOADERS;
|
|
||||||
_maxUpBW = DEFAULT_MAX_UP_BW;
|
|
||||||
_maxConnections = MAX_CONNECTIONS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify what HTTP proxy tracker requests should go through (specify a null
|
|
||||||
* host for no proxying)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void setProxy(String host, int port) {
|
|
||||||
if ( (host != null) && (port > 0) ) {
|
|
||||||
_shouldProxy = true;
|
|
||||||
_proxyHost = host;
|
|
||||||
_proxyPort = port;
|
|
||||||
} else {
|
|
||||||
_shouldProxy = false;
|
|
||||||
_proxyHost = null;
|
|
||||||
_proxyPort = -1;
|
|
||||||
}
|
|
||||||
_configured = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean configured() { return _configured; }
|
|
||||||
|
|
||||||
public void setI2CPConfig(String i2cpHost, int i2cpPort, Map opts) {
|
|
||||||
if (i2cpHost != null)
|
|
||||||
_i2cpHost = i2cpHost;
|
|
||||||
if (i2cpPort > 0)
|
|
||||||
_i2cpPort = i2cpPort;
|
|
||||||
if (opts != null)
|
|
||||||
_opts.putAll(opts);
|
|
||||||
_configured = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMaxUploaders(int limit) {
|
|
||||||
_maxUploaders = limit;
|
|
||||||
_configured = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMaxUpBW(int limit) {
|
|
||||||
_maxUpBW = limit;
|
|
||||||
_configured = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMaxConnections(int limit) {
|
|
||||||
_maxConnections = limit;
|
|
||||||
_configured = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getI2CPHost() { return _i2cpHost; }
|
|
||||||
public int getI2CPPort() { return _i2cpPort; }
|
|
||||||
public Map getI2CPOptions() { return _opts; }
|
|
||||||
public String getEepProxyHost() { return _proxyHost; }
|
|
||||||
public int getEepProxyPort() { return _proxyPort; }
|
|
||||||
public boolean getEepProxySet() { return _shouldProxy; }
|
|
||||||
public int getMaxUploaders() { return _maxUploaders; }
|
|
||||||
public int getMaxUpBW() { return _maxUpBW; }
|
|
||||||
public int getMaxConnections() { return _maxConnections; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect to the router, if we aren't already
|
|
||||||
*/
|
|
||||||
synchronized public boolean connect() {
|
|
||||||
if (_manager == null) {
|
|
||||||
Properties opts = new Properties();
|
|
||||||
if (_opts != null) {
|
|
||||||
for (Iterator iter = _opts.keySet().iterator(); iter.hasNext(); ) {
|
|
||||||
String key = (String)iter.next();
|
|
||||||
opts.setProperty(key, _opts.get(key).toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opts.getProperty("inbound.nickname") == null)
|
|
||||||
opts.setProperty("inbound.nickname", "I2PSnark");
|
|
||||||
if (opts.getProperty("outbound.nickname") == null)
|
|
||||||
opts.setProperty("outbound.nickname", "I2PSnark");
|
|
||||||
if (opts.getProperty("i2p.streaming.inactivityTimeout") == null)
|
|
||||||
opts.setProperty("i2p.streaming.inactivityTimeout", "240000");
|
|
||||||
if (opts.getProperty("i2p.streaming.inactivityAction") == null)
|
|
||||||
opts.setProperty("i2p.streaming.inactivityAction", "1"); // 1 == disconnect, 2 == ping
|
|
||||||
if (opts.getProperty("i2p.streaming.initialWindowSize") == null)
|
|
||||||
opts.setProperty("i2p.streaming.initialWindowSize", "1");
|
|
||||||
if (opts.getProperty("i2p.streaming.slowStartGrowthRateFactor") == null)
|
|
||||||
opts.setProperty("i2p.streaming.slowStartGrowthRateFactor", "1");
|
|
||||||
//if (opts.getProperty("i2p.streaming.writeTimeout") == null)
|
|
||||||
// opts.setProperty("i2p.streaming.writeTimeout", "90000");
|
|
||||||
//if (opts.getProperty("i2p.streaming.readTimeout") == null)
|
|
||||||
// opts.setProperty("i2p.streaming.readTimeout", "120000");
|
|
||||||
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
|
|
||||||
}
|
|
||||||
return (_manager != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean connected() { return _manager != null; }
|
|
||||||
/**
|
|
||||||
* Destroy the destination itself
|
|
||||||
*/
|
|
||||||
public void disconnect() {
|
|
||||||
I2PSocketManager mgr = _manager;
|
|
||||||
_manager = null;
|
|
||||||
_shitlist.clear();
|
|
||||||
mgr.destroySocketManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** connect to the given destination */
|
|
||||||
I2PSocket connect(PeerID peer) throws IOException {
|
|
||||||
Hash dest = peer.getAddress().calculateHash();
|
|
||||||
synchronized (_shitlist) {
|
|
||||||
if (_shitlist.contains(dest))
|
|
||||||
throw new IOException("Not trying to contact " + dest.toBase64() + ", as they are shitlisted");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
I2PSocket rv = _manager.connect(peer.getAddress());
|
|
||||||
if (rv != null) synchronized (_shitlist) { _shitlist.remove(dest); }
|
|
||||||
return rv;
|
|
||||||
} catch (I2PException ie) {
|
|
||||||
synchronized (_shitlist) {
|
|
||||||
_shitlist.add(dest);
|
|
||||||
}
|
|
||||||
SimpleTimer.getInstance().addEvent(new Unshitlist(dest), 10*60*1000);
|
|
||||||
throw new IOException("Unable to reach the peer " + peer + ": " + ie.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Unshitlist implements SimpleTimer.TimedEvent {
|
|
||||||
private Hash _dest;
|
|
||||||
public Unshitlist(Hash dest) { _dest = dest; }
|
|
||||||
public void timeReached() { synchronized (_shitlist) { _shitlist.remove(_dest); } }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* fetch the given URL, returning the file it is stored in, or null on error
|
|
||||||
*/
|
|
||||||
public File get(String url) { return get(url, true, 0); }
|
|
||||||
public File get(String url, boolean rewrite) { return get(url, rewrite, 0); }
|
|
||||||
public File get(String url, int retries) { return get(url, true, retries); }
|
|
||||||
public File get(String url, boolean rewrite, int retries) {
|
|
||||||
_log.debug("Fetching [" + url + "] proxy=" + _proxyHost + ":" + _proxyPort + ": " + _shouldProxy);
|
|
||||||
File out = null;
|
|
||||||
try {
|
|
||||||
out = File.createTempFile("i2psnark", "url", new File("."));
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
ioe.printStackTrace();
|
|
||||||
if (out != null)
|
|
||||||
out.delete();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
out.deleteOnExit();
|
|
||||||
String fetchURL = url;
|
|
||||||
if (rewrite)
|
|
||||||
fetchURL = rewriteAnnounce(url);
|
|
||||||
//_log.debug("Rewritten url [" + fetchURL + "]");
|
|
||||||
EepGet get = new EepGet(_context, _shouldProxy, _proxyHost, _proxyPort, retries, out.getAbsolutePath(), fetchURL);
|
|
||||||
if (get.fetch()) {
|
|
||||||
_log.debug("Fetch successful [" + url + "]: size=" + out.length());
|
|
||||||
return out;
|
|
||||||
} else {
|
|
||||||
_log.warn("Fetch failed [" + url + "]");
|
|
||||||
out.delete();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public I2PServerSocket getServerSocket() {
|
|
||||||
I2PSocketManager mgr = _manager;
|
|
||||||
if (mgr != null)
|
|
||||||
return mgr.getServerSocket();
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getOurIPString() {
|
|
||||||
if (_manager == null)
|
|
||||||
return "unknown";
|
|
||||||
I2PSession sess = _manager.getSession();
|
|
||||||
if (sess != null) {
|
|
||||||
Destination dest = sess.getMyDestination();
|
|
||||||
if (dest != null)
|
|
||||||
return dest.toBase64();
|
|
||||||
}
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Base64 only - static (no naming service) */
|
|
||||||
static Destination getDestinationFromBase64(String ip) {
|
|
||||||
if (ip == null) return null;
|
|
||||||
if (ip.endsWith(".i2p")) {
|
|
||||||
if (ip.length() < 520)
|
|
||||||
return null;
|
|
||||||
try {
|
|
||||||
return new Destination(ip.substring(0, ip.length()-4)); // sans .i2p
|
|
||||||
} catch (DataFormatException dfe) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
return new Destination(ip);
|
|
||||||
} catch (DataFormatException dfe) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Base64 Hash or Hash.i2p or name.i2p using naming service */
|
|
||||||
Destination getDestination(String ip) {
|
|
||||||
if (ip == null) return null;
|
|
||||||
if (ip.endsWith(".i2p")) {
|
|
||||||
if (ip.length() < 520) { // key + ".i2p"
|
|
||||||
Destination dest = _context.namingService().lookup(ip);
|
|
||||||
if (dest != null)
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return new Destination(ip.substring(0, ip.length()-4)); // sans .i2p
|
|
||||||
} catch (DataFormatException dfe) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
return new Destination(ip);
|
|
||||||
} catch (DataFormatException dfe) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String lookup(String name) {
|
|
||||||
Destination dest = getDestination(name);
|
|
||||||
if (dest == null)
|
|
||||||
return null;
|
|
||||||
return dest.toBase64();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given http://KEY.i2p/foo/announce turn it into http://i2p/KEY/foo/announce
|
|
||||||
* Given http://tracker.blah.i2p/foo/announce leave it alone
|
|
||||||
*/
|
|
||||||
String rewriteAnnounce(String origAnnounce) {
|
|
||||||
int destStart = "http://".length();
|
|
||||||
int destEnd = origAnnounce.indexOf(".i2p");
|
|
||||||
if (destEnd < destStart + 516)
|
|
||||||
return origAnnounce;
|
|
||||||
int pathStart = origAnnounce.indexOf('/', destEnd);
|
|
||||||
String rv = "http://i2p/" + origAnnounce.substring(destStart, destEnd) + origAnnounce.substring(pathStart);
|
|
||||||
//_log.debug("Rewriting [" + origAnnounce + "] as [" + rv + "]");
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOpenTrackerString() {
|
|
||||||
String rv = (String) _opts.get(PROP_OPENTRACKERS);
|
|
||||||
if (rv == null)
|
|
||||||
return DEFAULT_OPENTRACKERS;
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** comma delimited list open trackers to use as backups */
|
|
||||||
/** sorted map of name to announceURL=baseURL */
|
|
||||||
public List getOpenTrackers() {
|
|
||||||
if (!shouldUseOpenTrackers())
|
|
||||||
return null;
|
|
||||||
List rv = new ArrayList(1);
|
|
||||||
String trackers = getOpenTrackerString();
|
|
||||||
StringTokenizer tok = new StringTokenizer(trackers, ", ");
|
|
||||||
while (tok.hasMoreTokens())
|
|
||||||
rv.add(tok.nextToken());
|
|
||||||
|
|
||||||
if (rv.size() <= 0)
|
|
||||||
return null;
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean shouldUseOpenTrackers() {
|
|
||||||
String rv = (String) _opts.get(PROP_USE_OPENTRACKERS);
|
|
||||||
if (rv == null)
|
|
||||||
return DEFAULT_USE_OPENTRACKERS;
|
|
||||||
return Boolean.valueOf(rv).booleanValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** hook between snark's logger and an i2p log */
|
|
||||||
void debug(String msg, int snarkDebugLevel) {
|
|
||||||
debug(msg, snarkDebugLevel, null);
|
|
||||||
}
|
|
||||||
void debug(String msg, int snarkDebugLevel, Throwable t) {
|
|
||||||
if (t instanceof OutOfMemoryError) {
|
|
||||||
try { Thread.sleep(100); } catch (InterruptedException ie) {}
|
|
||||||
try {
|
|
||||||
t.printStackTrace();
|
|
||||||
} catch (Throwable tt) {}
|
|
||||||
try {
|
|
||||||
System.out.println("OOM thread: " + Thread.currentThread().getName());
|
|
||||||
} catch (Throwable tt) {}
|
|
||||||
}
|
|
||||||
switch (snarkDebugLevel) {
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
_log.error(msg, t);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
_log.warn(msg, t);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
case 4:
|
|
||||||
_log.info(msg, t);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
case 6:
|
|
||||||
default:
|
|
||||||
_log.debug(msg, t);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,141 +0,0 @@
|
|||||||
/* Message - A protocol message which can be send through a DataOutputStream.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import net.i2p.util.SimpleTimer;
|
|
||||||
|
|
||||||
// Used to queue outgoing connections
|
|
||||||
// sendMessage() should be used to translate them to wire format.
|
|
||||||
class Message
|
|
||||||
{
|
|
||||||
final static byte KEEP_ALIVE = -1;
|
|
||||||
final static byte CHOKE = 0;
|
|
||||||
final static byte UNCHOKE = 1;
|
|
||||||
final static byte INTERESTED = 2;
|
|
||||||
final static byte UNINTERESTED = 3;
|
|
||||||
final static byte HAVE = 4;
|
|
||||||
final static byte BITFIELD = 5;
|
|
||||||
final static byte REQUEST = 6;
|
|
||||||
final static byte PIECE = 7;
|
|
||||||
final static byte CANCEL = 8;
|
|
||||||
|
|
||||||
// Not all fields are used for every message.
|
|
||||||
// KEEP_ALIVE doesn't have a real wire representation
|
|
||||||
byte type;
|
|
||||||
|
|
||||||
// Used for HAVE, REQUEST, PIECE and CANCEL messages.
|
|
||||||
int piece;
|
|
||||||
|
|
||||||
// Used for REQUEST, PIECE and CANCEL messages.
|
|
||||||
int begin;
|
|
||||||
int length;
|
|
||||||
|
|
||||||
// Used for PIECE and BITFIELD messages
|
|
||||||
byte[] data;
|
|
||||||
int off;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
SimpleTimer.TimedEvent expireEvent;
|
|
||||||
|
|
||||||
/** Utility method for sending a message through a DataStream. */
|
|
||||||
void sendMessage(DataOutputStream dos) throws IOException
|
|
||||||
{
|
|
||||||
// KEEP_ALIVE is special.
|
|
||||||
if (type == KEEP_ALIVE)
|
|
||||||
{
|
|
||||||
dos.writeInt(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the total length in bytes
|
|
||||||
|
|
||||||
// Type is one byte.
|
|
||||||
int datalen = 1;
|
|
||||||
|
|
||||||
// piece is 4 bytes.
|
|
||||||
if (type == HAVE || type == REQUEST || type == PIECE || type == CANCEL)
|
|
||||||
datalen += 4;
|
|
||||||
|
|
||||||
// begin/offset is 4 bytes
|
|
||||||
if (type == REQUEST || type == PIECE || type == CANCEL)
|
|
||||||
datalen += 4;
|
|
||||||
|
|
||||||
// length is 4 bytes
|
|
||||||
if (type == REQUEST || type == CANCEL)
|
|
||||||
datalen += 4;
|
|
||||||
|
|
||||||
// add length of data for piece or bitfield array.
|
|
||||||
if (type == BITFIELD || type == PIECE)
|
|
||||||
datalen += len;
|
|
||||||
|
|
||||||
// Send length
|
|
||||||
dos.writeInt(datalen);
|
|
||||||
dos.writeByte(type & 0xFF);
|
|
||||||
|
|
||||||
// Send additional info (piece number)
|
|
||||||
if (type == HAVE || type == REQUEST || type == PIECE || type == CANCEL)
|
|
||||||
dos.writeInt(piece);
|
|
||||||
|
|
||||||
// Send additional info (begin/offset)
|
|
||||||
if (type == REQUEST || type == PIECE || type == CANCEL)
|
|
||||||
dos.writeInt(begin);
|
|
||||||
|
|
||||||
// Send additional info (length); for PIECE this is implicit.
|
|
||||||
if (type == REQUEST || type == CANCEL)
|
|
||||||
dos.writeInt(length);
|
|
||||||
|
|
||||||
// Send actual data
|
|
||||||
if (type == BITFIELD || type == PIECE)
|
|
||||||
dos.write(data, off, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case KEEP_ALIVE:
|
|
||||||
return "KEEP_ALIVE";
|
|
||||||
case CHOKE:
|
|
||||||
return "CHOKE";
|
|
||||||
case UNCHOKE:
|
|
||||||
return "UNCHOKE";
|
|
||||||
case INTERESTED:
|
|
||||||
return "INTERESTED";
|
|
||||||
case UNINTERESTED:
|
|
||||||
return "UNINTERESTED";
|
|
||||||
case HAVE:
|
|
||||||
return "HAVE(" + piece + ")";
|
|
||||||
case BITFIELD:
|
|
||||||
return "BITFIELD";
|
|
||||||
case REQUEST:
|
|
||||||
return "REQUEST(" + piece + "," + begin + "," + length + ")";
|
|
||||||
case PIECE:
|
|
||||||
return "PIECE(" + piece + "," + begin + "," + length + ")";
|
|
||||||
case CANCEL:
|
|
||||||
return "CANCEL(" + piece + "," + begin + "," + length + ")";
|
|
||||||
default:
|
|
||||||
return "<UNKNOWN>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,464 +0,0 @@
|
|||||||
/* MetaInfo - Holds all information gotten from a torrent file.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import net.i2p.crypto.SHA1;
|
|
||||||
import net.i2p.data.Base64;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
import org.klomp.snark.bencode.BDecoder;
|
|
||||||
import org.klomp.snark.bencode.BEValue;
|
|
||||||
import org.klomp.snark.bencode.BEncoder;
|
|
||||||
import org.klomp.snark.bencode.InvalidBEncodingException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: this class is buggy, as it doesn't propogate custom meta fields into the bencoded
|
|
||||||
* info data, and from there to the info_hash. At the moment, though, it seems to work with
|
|
||||||
* torrents created by I2P-BT, I2PRufus and Azureus.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class MetaInfo
|
|
||||||
{
|
|
||||||
private static final Log _log = new Log(MetaInfo.class);
|
|
||||||
private final String announce;
|
|
||||||
private final byte[] info_hash;
|
|
||||||
private final String name;
|
|
||||||
private final String name_utf8;
|
|
||||||
private final List files;
|
|
||||||
private final List files_utf8;
|
|
||||||
private final List lengths;
|
|
||||||
private final int piece_length;
|
|
||||||
private final byte[] piece_hashes;
|
|
||||||
private final long length;
|
|
||||||
private final Map infoMap;
|
|
||||||
|
|
||||||
private byte[] torrentdata;
|
|
||||||
|
|
||||||
MetaInfo(String announce, String name, String name_utf8, List files, List lengths,
|
|
||||||
int piece_length, byte[] piece_hashes, long length)
|
|
||||||
{
|
|
||||||
this.announce = announce;
|
|
||||||
this.name = name;
|
|
||||||
this.name_utf8 = name_utf8;
|
|
||||||
this.files = files;
|
|
||||||
this.files_utf8 = null;
|
|
||||||
this.lengths = lengths;
|
|
||||||
this.piece_length = piece_length;
|
|
||||||
this.piece_hashes = piece_hashes;
|
|
||||||
this.length = length;
|
|
||||||
|
|
||||||
this.info_hash = calculateInfoHash();
|
|
||||||
infoMap = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new MetaInfo from the given InputStream. The
|
|
||||||
* InputStream must start with a correctly bencoded dictonary
|
|
||||||
* describing the torrent.
|
|
||||||
*/
|
|
||||||
public MetaInfo(InputStream in) throws IOException
|
|
||||||
{
|
|
||||||
this(new BDecoder(in));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new MetaInfo from the given BDecoder. The BDecoder
|
|
||||||
* must have a complete dictionary describing the torrent.
|
|
||||||
*/
|
|
||||||
public MetaInfo(BDecoder be) throws IOException
|
|
||||||
{
|
|
||||||
// Note that evaluation order matters here...
|
|
||||||
this(be.bdecodeMap().getMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new MetaInfo from a Map of BEValues and the SHA1 over
|
|
||||||
* the original bencoded info dictonary (this is a hack, we could
|
|
||||||
* reconstruct the bencoded stream and recalculate the hash). Will
|
|
||||||
* throw a InvalidBEncodingException if the given map does not
|
|
||||||
* contain a valid announce string or info dictonary.
|
|
||||||
*/
|
|
||||||
public MetaInfo(Map m) throws InvalidBEncodingException
|
|
||||||
{
|
|
||||||
_log.debug("Creating a metaInfo: " + m, new Exception("source"));
|
|
||||||
BEValue val = (BEValue)m.get("announce");
|
|
||||||
if (val == null)
|
|
||||||
throw new InvalidBEncodingException("Missing announce string");
|
|
||||||
this.announce = val.getString();
|
|
||||||
|
|
||||||
val = (BEValue)m.get("info");
|
|
||||||
if (val == null)
|
|
||||||
throw new InvalidBEncodingException("Missing info map");
|
|
||||||
Map info = val.getMap();
|
|
||||||
infoMap = info;
|
|
||||||
|
|
||||||
val = (BEValue)info.get("name");
|
|
||||||
if (val == null)
|
|
||||||
throw new InvalidBEncodingException("Missing name string");
|
|
||||||
name = val.getString();
|
|
||||||
|
|
||||||
val = (BEValue)info.get("name.utf-8");
|
|
||||||
if (val != null)
|
|
||||||
name_utf8 = val.getString();
|
|
||||||
else
|
|
||||||
name_utf8 = null;
|
|
||||||
|
|
||||||
val = (BEValue)info.get("piece length");
|
|
||||||
if (val == null)
|
|
||||||
throw new InvalidBEncodingException("Missing piece length number");
|
|
||||||
piece_length = val.getInt();
|
|
||||||
|
|
||||||
val = (BEValue)info.get("pieces");
|
|
||||||
if (val == null)
|
|
||||||
throw new InvalidBEncodingException("Missing piece bytes");
|
|
||||||
piece_hashes = val.getBytes();
|
|
||||||
|
|
||||||
val = (BEValue)info.get("length");
|
|
||||||
if (val != null)
|
|
||||||
{
|
|
||||||
// Single file case.
|
|
||||||
length = val.getLong();
|
|
||||||
files = null;
|
|
||||||
files_utf8 = null;
|
|
||||||
lengths = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Multi file case.
|
|
||||||
val = (BEValue)info.get("files");
|
|
||||||
if (val == null)
|
|
||||||
throw new InvalidBEncodingException
|
|
||||||
("Missing length number and/or files list");
|
|
||||||
|
|
||||||
List list = val.getList();
|
|
||||||
int size = list.size();
|
|
||||||
if (size == 0)
|
|
||||||
throw new InvalidBEncodingException("zero size files list");
|
|
||||||
|
|
||||||
files = new ArrayList(size);
|
|
||||||
files_utf8 = new ArrayList(size);
|
|
||||||
lengths = new ArrayList(size);
|
|
||||||
long l = 0;
|
|
||||||
for (int i = 0; i < list.size(); i++)
|
|
||||||
{
|
|
||||||
Map desc = ((BEValue)list.get(i)).getMap();
|
|
||||||
val = (BEValue)desc.get("length");
|
|
||||||
if (val == null)
|
|
||||||
throw new InvalidBEncodingException("Missing length number");
|
|
||||||
long len = val.getLong();
|
|
||||||
lengths.add(new Long(len));
|
|
||||||
l += len;
|
|
||||||
|
|
||||||
val = (BEValue)desc.get("path");
|
|
||||||
if (val == null)
|
|
||||||
throw new InvalidBEncodingException("Missing path list");
|
|
||||||
List path_list = val.getList();
|
|
||||||
int path_length = path_list.size();
|
|
||||||
if (path_length == 0)
|
|
||||||
throw new InvalidBEncodingException("zero size file path list");
|
|
||||||
|
|
||||||
List file = new ArrayList(path_length);
|
|
||||||
Iterator it = path_list.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
file.add(((BEValue)it.next()).getString());
|
|
||||||
|
|
||||||
files.add(file);
|
|
||||||
|
|
||||||
val = (BEValue)desc.get("path.utf-8");
|
|
||||||
if (val != null) {
|
|
||||||
path_list = val.getList();
|
|
||||||
path_length = path_list.size();
|
|
||||||
if (path_length > 0) {
|
|
||||||
file = new ArrayList(path_length);
|
|
||||||
it = path_list.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
file.add(((BEValue)it.next()).getString());
|
|
||||||
files_utf8.add(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
length = l;
|
|
||||||
}
|
|
||||||
|
|
||||||
info_hash = calculateInfoHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the string representing the URL of the tracker for this torrent.
|
|
||||||
*/
|
|
||||||
public String getAnnounce()
|
|
||||||
{
|
|
||||||
return announce;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the original 20 byte SHA1 hash over the bencoded info map.
|
|
||||||
*/
|
|
||||||
public byte[] getInfoHash()
|
|
||||||
{
|
|
||||||
// XXX - Should we return a clone, just to be sure?
|
|
||||||
return info_hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the piece hashes. Only used by storage so package local.
|
|
||||||
*/
|
|
||||||
byte[] getPieceHashes()
|
|
||||||
{
|
|
||||||
return piece_hashes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the requested name for the file or toplevel directory.
|
|
||||||
* If it is a toplevel directory name getFiles() will return a
|
|
||||||
* non-null List of file name hierarchy name.
|
|
||||||
*/
|
|
||||||
public String getName()
|
|
||||||
{
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of lists of file name hierarchies or null if it is
|
|
||||||
* a single name. It has the same size as the list returned by
|
|
||||||
* getLengths().
|
|
||||||
*/
|
|
||||||
public List getFiles()
|
|
||||||
{
|
|
||||||
// XXX - Immutable?
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of Longs indication the size of the individual
|
|
||||||
* files, or null if it is a single file. It has the same size as
|
|
||||||
* the list returned by getFiles().
|
|
||||||
*/
|
|
||||||
public List getLengths()
|
|
||||||
{
|
|
||||||
// XXX - Immutable?
|
|
||||||
return lengths;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of pieces.
|
|
||||||
*/
|
|
||||||
public int getPieces()
|
|
||||||
{
|
|
||||||
return piece_hashes.length/20;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the length of a piece. All pieces are of equal length
|
|
||||||
* except for the last one (<code>getPieces()-1</code>).
|
|
||||||
*
|
|
||||||
* @exception IndexOutOfBoundsException when piece is equal to or
|
|
||||||
* greater then the number of pieces in the torrent.
|
|
||||||
*/
|
|
||||||
public int getPieceLength(int piece)
|
|
||||||
{
|
|
||||||
int pieces = getPieces();
|
|
||||||
if (piece >= 0 && piece < pieces -1)
|
|
||||||
return piece_length;
|
|
||||||
else if (piece == pieces -1)
|
|
||||||
return (int)(length - piece * piece_length);
|
|
||||||
else
|
|
||||||
throw new IndexOutOfBoundsException("no piece: " + piece);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks that the given piece has the same SHA1 hash as the given
|
|
||||||
* byte array. Returns random results or IndexOutOfBoundsExceptions
|
|
||||||
* when the piece number is unknown.
|
|
||||||
*/
|
|
||||||
public boolean checkPiece(int piece, byte[] bs, int off, int length)
|
|
||||||
{
|
|
||||||
if (true)
|
|
||||||
return fast_checkPiece(piece, bs, off, length);
|
|
||||||
else
|
|
||||||
return orig_checkPiece(piece, bs, off, length);
|
|
||||||
}
|
|
||||||
private boolean orig_checkPiece(int piece, byte[] bs, int off, int length) {
|
|
||||||
// Check digest
|
|
||||||
MessageDigest sha1;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
sha1 = MessageDigest.getInstance("SHA");
|
|
||||||
}
|
|
||||||
catch (NoSuchAlgorithmException nsae)
|
|
||||||
{
|
|
||||||
throw new InternalError("No SHA digest available: " + nsae);
|
|
||||||
}
|
|
||||||
|
|
||||||
sha1.update(bs, off, length);
|
|
||||||
byte[] hash = sha1.digest();
|
|
||||||
for (int i = 0; i < 20; i++)
|
|
||||||
if (hash[i] != piece_hashes[20 * piece + i])
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean fast_checkPiece(int piece, byte[] bs, int off, int length) {
|
|
||||||
SHA1 sha1 = new SHA1();
|
|
||||||
|
|
||||||
sha1.update(bs, off, length);
|
|
||||||
byte[] hash = sha1.digest();
|
|
||||||
for (int i = 0; i < 20; i++)
|
|
||||||
if (hash[i] != piece_hashes[20 * piece + i])
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the total length of the torrent in bytes.
|
|
||||||
*/
|
|
||||||
public long getTotalLength()
|
|
||||||
{
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "MetaInfo[info_hash='" + hexencode(info_hash)
|
|
||||||
+ "', announce='" + announce
|
|
||||||
+ "', name='" + name
|
|
||||||
+ "', files=" + files
|
|
||||||
+ ", #pieces='" + piece_hashes.length/20
|
|
||||||
+ "', piece_length='" + piece_length
|
|
||||||
+ "', length='" + length
|
|
||||||
+ "']";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a byte array as a hex encoded string.
|
|
||||||
*/
|
|
||||||
private static String hexencode(byte[] bs)
|
|
||||||
{
|
|
||||||
StringBuffer sb = new StringBuffer(bs.length*2);
|
|
||||||
for (int i = 0; i < bs.length; i++)
|
|
||||||
{
|
|
||||||
int c = bs[i] & 0xFF;
|
|
||||||
if (c < 16)
|
|
||||||
sb.append('0');
|
|
||||||
sb.append(Integer.toHexString(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a copy of this MetaInfo that shares everything except the
|
|
||||||
* announce URL.
|
|
||||||
*/
|
|
||||||
public MetaInfo reannounce(String announce)
|
|
||||||
{
|
|
||||||
return new MetaInfo(announce, name, name_utf8, files,
|
|
||||||
lengths, piece_length,
|
|
||||||
piece_hashes, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getTorrentData()
|
|
||||||
{
|
|
||||||
if (torrentdata == null)
|
|
||||||
{
|
|
||||||
Map m = new HashMap();
|
|
||||||
m.put("announce", announce);
|
|
||||||
Map info = createInfoMap();
|
|
||||||
m.put("info", info);
|
|
||||||
torrentdata = BEncoder.bencode(m);
|
|
||||||
}
|
|
||||||
return torrentdata;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map createInfoMap()
|
|
||||||
{
|
|
||||||
Map info = new HashMap();
|
|
||||||
if (infoMap != null) {
|
|
||||||
info.putAll(infoMap);
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
info.put("name", name);
|
|
||||||
if (name_utf8 != null)
|
|
||||||
info.put("name.utf-8", name_utf8);
|
|
||||||
info.put("piece length", Integer.valueOf(piece_length));
|
|
||||||
info.put("pieces", piece_hashes);
|
|
||||||
if (files == null)
|
|
||||||
info.put("length", new Long(length));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
List l = new ArrayList();
|
|
||||||
for (int i = 0; i < files.size(); i++)
|
|
||||||
{
|
|
||||||
Map file = new HashMap();
|
|
||||||
file.put("path", files.get(i));
|
|
||||||
if ( (files_utf8 != null) && (files_utf8.size() > i) )
|
|
||||||
file.put("path.utf-8", files_utf8.get(i));
|
|
||||||
file.put("length", lengths.get(i));
|
|
||||||
l.add(file);
|
|
||||||
}
|
|
||||||
info.put("files", l);
|
|
||||||
}
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] calculateInfoHash()
|
|
||||||
{
|
|
||||||
Map info = createInfoMap();
|
|
||||||
StringBuffer buf = new StringBuffer(128);
|
|
||||||
buf.append("info: ");
|
|
||||||
for (Iterator iter = info.entrySet().iterator(); iter.hasNext(); ) {
|
|
||||||
Map.Entry entry = (Map.Entry)iter.next();
|
|
||||||
String key = (String)entry.getKey();
|
|
||||||
Object val = entry.getValue();
|
|
||||||
buf.append(key).append('=');
|
|
||||||
if (val instanceof byte[])
|
|
||||||
buf.append(Base64.encode((byte[])val, true));
|
|
||||||
else
|
|
||||||
buf.append(val.toString());
|
|
||||||
}
|
|
||||||
_log.debug(buf.toString());
|
|
||||||
byte[] infoBytes = BEncoder.bencode(info);
|
|
||||||
//_log.debug("info bencoded: [" + Base64.encode(infoBytes, true) + "]");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MessageDigest digest = MessageDigest.getInstance("SHA");
|
|
||||||
byte hash[] = digest.digest(infoBytes);
|
|
||||||
_log.debug("info hash: [" + net.i2p.data.Base64.encode(hash) + "]");
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
catch(NoSuchAlgorithmException nsa)
|
|
||||||
{
|
|
||||||
throw new InternalError(nsa.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,573 +0,0 @@
|
|||||||
/* Peer - All public information concerning a peer.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import net.i2p.client.streaming.I2PSocket;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
public class Peer implements Comparable
|
|
||||||
{
|
|
||||||
private Log _log = new Log(Peer.class);
|
|
||||||
// Identifying property, the peer id of the other side.
|
|
||||||
private final PeerID peerID;
|
|
||||||
|
|
||||||
private final byte[] my_id;
|
|
||||||
final MetaInfo metainfo;
|
|
||||||
|
|
||||||
// The data in/output streams set during the handshake and used by
|
|
||||||
// the actual connections.
|
|
||||||
private DataInputStream din;
|
|
||||||
private DataOutputStream dout;
|
|
||||||
|
|
||||||
// Keeps state for in/out connections. Non-null when the handshake
|
|
||||||
// was successful, the connection setup and runs
|
|
||||||
PeerState state;
|
|
||||||
|
|
||||||
private I2PSocket sock;
|
|
||||||
|
|
||||||
private boolean deregister = true;
|
|
||||||
private static long __id;
|
|
||||||
private long _id;
|
|
||||||
final static long CHECK_PERIOD = PeerCoordinator.CHECK_PERIOD; // 40 seconds
|
|
||||||
final static int RATE_DEPTH = PeerCoordinator.RATE_DEPTH; // make following arrays RATE_DEPTH long
|
|
||||||
private long uploaded_old[] = {-1,-1,-1,-1,-1,-1};
|
|
||||||
private long downloaded_old[] = {-1,-1,-1,-1,-1,-1};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a disconnected peer given a PeerID, your own id and the
|
|
||||||
* relevant MetaInfo.
|
|
||||||
*/
|
|
||||||
public Peer(PeerID peerID, byte[] my_id, MetaInfo metainfo)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
this.peerID = peerID;
|
|
||||||
this.my_id = my_id;
|
|
||||||
this.metainfo = metainfo;
|
|
||||||
_id = ++__id;
|
|
||||||
//_log.debug("Creating a new peer with " + peerID.getAddress().calculateHash().toBase64(), new Exception("creating"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a unconnected peer from the input and output stream got
|
|
||||||
* from the socket. Note that the complete handshake (which can take
|
|
||||||
* some time or block indefinitely) is done in the calling Thread to
|
|
||||||
* get the remote peer id. To completely start the connection call
|
|
||||||
* the connect() method.
|
|
||||||
*
|
|
||||||
* @exception IOException when an error occurred during the handshake.
|
|
||||||
*/
|
|
||||||
public Peer(final I2PSocket sock, InputStream in, OutputStream out, byte[] my_id, MetaInfo metainfo)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
this.my_id = my_id;
|
|
||||||
this.metainfo = metainfo;
|
|
||||||
this.sock = sock;
|
|
||||||
|
|
||||||
byte[] id = handshake(in, out);
|
|
||||||
this.peerID = new PeerID(id, sock.getPeerDestination());
|
|
||||||
_id = ++__id;
|
|
||||||
_log.debug("Creating a new peer with " + peerID.getAddress().calculateHash().toBase64(), new Exception("creating " + _id));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the id of the peer.
|
|
||||||
*/
|
|
||||||
public PeerID getPeerID()
|
|
||||||
{
|
|
||||||
return peerID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the String representation of the peerID.
|
|
||||||
*/
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
if (peerID != null)
|
|
||||||
return peerID.toString() + ' ' + _id;
|
|
||||||
else
|
|
||||||
return "[unknown id] " + _id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns socket (for debug printing)
|
|
||||||
*/
|
|
||||||
public String getSocket()
|
|
||||||
{
|
|
||||||
return sock.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The hash code of a Peer is the hash code of the peerID.
|
|
||||||
*/
|
|
||||||
public int hashCode()
|
|
||||||
{
|
|
||||||
return peerID.hashCode() ^ (2 << _id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Two Peers are equal when they have the same PeerID.
|
|
||||||
* All other properties are ignored.
|
|
||||||
*/
|
|
||||||
public boolean equals(Object o)
|
|
||||||
{
|
|
||||||
if (o instanceof Peer)
|
|
||||||
{
|
|
||||||
Peer p = (Peer)o;
|
|
||||||
return _id == p._id && peerID.equals(p.peerID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares the PeerIDs.
|
|
||||||
*/
|
|
||||||
public int compareTo(Object o)
|
|
||||||
{
|
|
||||||
Peer p = (Peer)o;
|
|
||||||
int rv = peerID.compareTo(p.peerID);
|
|
||||||
if (rv == 0) {
|
|
||||||
if (_id > p._id) return 1;
|
|
||||||
else if (_id < p._id) return -1;
|
|
||||||
else return 0;
|
|
||||||
} else {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the connection to the other peer. This method does not
|
|
||||||
* return until the connection is terminated.
|
|
||||||
*
|
|
||||||
* When the connection is correctly started the connected() method
|
|
||||||
* of the given PeerListener is called. If the connection ends or
|
|
||||||
* the connection could not be setup correctly the disconnected()
|
|
||||||
* method is called.
|
|
||||||
*
|
|
||||||
* If the given BitField is non-null it is send to the peer as first
|
|
||||||
* message.
|
|
||||||
*/
|
|
||||||
public void runConnection(I2PSnarkUtil util, PeerListener listener, BitField bitfield)
|
|
||||||
{
|
|
||||||
if (state != null)
|
|
||||||
throw new IllegalStateException("Peer already started");
|
|
||||||
|
|
||||||
_log.debug("Running connection to " + peerID.getAddress().calculateHash().toBase64(), new Exception("connecting"));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Do we need to handshake?
|
|
||||||
if (din == null)
|
|
||||||
{
|
|
||||||
sock = util.connect(peerID);
|
|
||||||
_log.debug("Connected to " + peerID + ": " + sock);
|
|
||||||
if ((sock == null) || (sock.isClosed())) {
|
|
||||||
throw new IOException("Unable to reach " + peerID);
|
|
||||||
}
|
|
||||||
InputStream in = sock.getInputStream();
|
|
||||||
OutputStream out = sock.getOutputStream(); //new BufferedOutputStream(sock.getOutputStream());
|
|
||||||
if (true) {
|
|
||||||
// buffered output streams are internally synchronized, so we can't get through to the underlying
|
|
||||||
// I2PSocket's MessageOutputStream to close() it if we are blocking on a write(...). Oh, and the
|
|
||||||
// buffer is unnecessary anyway, as unbuffered access lets the streaming lib do the 'right thing'.
|
|
||||||
//out = new BufferedOutputStream(out);
|
|
||||||
in = new BufferedInputStream(sock.getInputStream());
|
|
||||||
}
|
|
||||||
//BufferedInputStream bis
|
|
||||||
// = new BufferedInputStream(sock.getInputStream());
|
|
||||||
//BufferedOutputStream bos
|
|
||||||
// = new BufferedOutputStream(sock.getOutputStream());
|
|
||||||
byte [] id = handshake(in, out); //handshake(bis, bos);
|
|
||||||
byte [] expected_id = peerID.getID();
|
|
||||||
if (!Arrays.equals(expected_id, id))
|
|
||||||
throw new IOException("Unexpected peerID '"
|
|
||||||
+ PeerID.idencode(id)
|
|
||||||
+ "' expected '"
|
|
||||||
+ PeerID.idencode(expected_id) + "'");
|
|
||||||
_log.debug("Handshake got matching IDs with " + toString());
|
|
||||||
} else {
|
|
||||||
_log.debug("Already have din [" + sock + "] with " + toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
PeerConnectionIn in = new PeerConnectionIn(this, din);
|
|
||||||
PeerConnectionOut out = new PeerConnectionOut(this, dout);
|
|
||||||
PeerState s = new PeerState(this, listener, metainfo, in, out);
|
|
||||||
|
|
||||||
// Send our bitmap
|
|
||||||
if (bitfield != null)
|
|
||||||
s.out.sendBitfield(bitfield);
|
|
||||||
|
|
||||||
// We are up and running!
|
|
||||||
state = s;
|
|
||||||
listener.connected(this);
|
|
||||||
|
|
||||||
_log.debug("Start running the reader with " + toString());
|
|
||||||
// Use this thread for running the incomming connection.
|
|
||||||
// The outgoing connection creates its own Thread.
|
|
||||||
out.startup();
|
|
||||||
Thread.currentThread().setName("Snark reader from " + peerID);
|
|
||||||
s.in.run();
|
|
||||||
}
|
|
||||||
catch(IOException eofe)
|
|
||||||
{
|
|
||||||
// Ignore, probably just the other side closing the connection.
|
|
||||||
// Or refusing the connection, timing out, etc.
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug(this.toString(), eofe);
|
|
||||||
}
|
|
||||||
catch(Throwable t)
|
|
||||||
{
|
|
||||||
_log.error(this + ": " + t.getMessage(), t);
|
|
||||||
if (t instanceof OutOfMemoryError)
|
|
||||||
throw (OutOfMemoryError)t;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (deregister) listener.disconnected(this);
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets DataIn/OutputStreams, does the handshake and returns the id
|
|
||||||
* reported by the other side.
|
|
||||||
*/
|
|
||||||
private byte[] handshake(InputStream in, OutputStream out) //BufferedInputStream bis, BufferedOutputStream bos)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
din = new DataInputStream(in);
|
|
||||||
dout = new DataOutputStream(out);
|
|
||||||
|
|
||||||
// Handshake write - header
|
|
||||||
dout.write(19);
|
|
||||||
dout.write("BitTorrent protocol".getBytes("UTF-8"));
|
|
||||||
// Handshake write - zeros
|
|
||||||
byte[] zeros = new byte[8];
|
|
||||||
dout.write(zeros);
|
|
||||||
// Handshake write - metainfo hash
|
|
||||||
byte[] shared_hash = metainfo.getInfoHash();
|
|
||||||
dout.write(shared_hash);
|
|
||||||
// Handshake write - peer id
|
|
||||||
dout.write(my_id);
|
|
||||||
dout.flush();
|
|
||||||
|
|
||||||
_log.debug("Wrote my shared hash and ID to " + toString());
|
|
||||||
|
|
||||||
// Handshake read - header
|
|
||||||
byte b = din.readByte();
|
|
||||||
if (b != 19)
|
|
||||||
throw new IOException("Handshake failure, expected 19, got "
|
|
||||||
+ (b & 0xff) + " on " + sock);
|
|
||||||
|
|
||||||
byte[] bs = new byte[19];
|
|
||||||
din.readFully(bs);
|
|
||||||
String bittorrentProtocol = new String(bs, "UTF-8");
|
|
||||||
if (!"BitTorrent protocol".equals(bittorrentProtocol))
|
|
||||||
throw new IOException("Handshake failure, expected "
|
|
||||||
+ "'Bittorrent protocol', got '"
|
|
||||||
+ bittorrentProtocol + "'");
|
|
||||||
|
|
||||||
// Handshake read - zeros
|
|
||||||
din.readFully(zeros);
|
|
||||||
|
|
||||||
// Handshake read - metainfo hash
|
|
||||||
bs = new byte[20];
|
|
||||||
din.readFully(bs);
|
|
||||||
if (!Arrays.equals(shared_hash, bs))
|
|
||||||
throw new IOException("Unexpected MetaInfo hash");
|
|
||||||
|
|
||||||
// Handshake read - peer id
|
|
||||||
din.readFully(bs);
|
|
||||||
_log.debug("Read the remote side's hash and peerID fully from " + toString());
|
|
||||||
return bs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isConnected()
|
|
||||||
{
|
|
||||||
return state != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnects this peer if it was connected. If deregister is
|
|
||||||
* true, PeerListener.disconnected() will be called when the
|
|
||||||
* connection is completely terminated. Otherwise the connection is
|
|
||||||
* silently terminated.
|
|
||||||
*/
|
|
||||||
public void disconnect(boolean deregister)
|
|
||||||
{
|
|
||||||
// Both in and out connection will call this.
|
|
||||||
this.deregister = deregister;
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
void disconnect()
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
if (s != null)
|
|
||||||
{
|
|
||||||
// try to save partial piece
|
|
||||||
if (this.deregister) {
|
|
||||||
PeerListener p = s.listener;
|
|
||||||
if (p != null) {
|
|
||||||
p.savePeerPartial(s);
|
|
||||||
p.markUnrequested(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state = null;
|
|
||||||
|
|
||||||
PeerConnectionIn in = s.in;
|
|
||||||
if (in != null)
|
|
||||||
in.disconnect();
|
|
||||||
PeerConnectionOut out = s.out;
|
|
||||||
if (out != null)
|
|
||||||
out.disconnect();
|
|
||||||
PeerListener pl = s.listener;
|
|
||||||
if (pl != null)
|
|
||||||
pl.disconnected(this);
|
|
||||||
}
|
|
||||||
I2PSocket csock = sock;
|
|
||||||
sock = null;
|
|
||||||
if ( (csock != null) && (!csock.isClosed()) ) {
|
|
||||||
try {
|
|
||||||
csock.close();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.warn("Error disconnecting " + toString(), ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tell the peer we have another piece.
|
|
||||||
*/
|
|
||||||
public void have(int piece)
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
if (s != null)
|
|
||||||
s.havePiece(piece);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the peer is interested in pieces we have. Returns
|
|
||||||
* false if not connected.
|
|
||||||
*/
|
|
||||||
public boolean isInterested()
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
return (s != null) && s.interested;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether or not we are interested in pieces from this peer.
|
|
||||||
* Defaults to false. When interest is true and this peer unchokes
|
|
||||||
* us then we start downloading from it. Has no effect when not connected.
|
|
||||||
*/
|
|
||||||
public void setInteresting(boolean interest)
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
if (s != null)
|
|
||||||
s.setInteresting(interest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the peer has pieces we want from it. Returns false
|
|
||||||
* if not connected.
|
|
||||||
*/
|
|
||||||
public boolean isInteresting()
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
return (s != null) && s.interesting;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether or not we are choking the peer. Defaults to
|
|
||||||
* true. When choke is false and the peer requests some pieces we
|
|
||||||
* upload them, otherwise requests of this peer are ignored.
|
|
||||||
*/
|
|
||||||
public void setChoking(boolean choke)
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
if (s != null)
|
|
||||||
s.setChoking(choke);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not we are choking the peer. Returns true when not connected.
|
|
||||||
*/
|
|
||||||
public boolean isChoking()
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
return (s == null) || s.choking;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the peer choked us. Returns true when not connected.
|
|
||||||
*/
|
|
||||||
public boolean isChoked()
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
return (s == null) || s.choked;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of bytes that have been downloaded.
|
|
||||||
* Can be reset to zero with <code>resetCounters()</code>/
|
|
||||||
*/
|
|
||||||
public long getDownloaded()
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
return (s != null) ? s.downloaded : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of bytes that have been uploaded.
|
|
||||||
* Can be reset to zero with <code>resetCounters()</code>/
|
|
||||||
*/
|
|
||||||
public long getUploaded()
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
return (s != null) ? s.uploaded : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the downloaded and uploaded counters to zero.
|
|
||||||
*/
|
|
||||||
public void resetCounters()
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
if (s != null)
|
|
||||||
{
|
|
||||||
s.downloaded = 0;
|
|
||||||
s.uploaded = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getInactiveTime() {
|
|
||||||
PeerState s = state;
|
|
||||||
if (s != null) {
|
|
||||||
PeerConnectionIn in = s.in;
|
|
||||||
PeerConnectionOut out = s.out;
|
|
||||||
if (in != null && out != null) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
return Math.max(now - out.lastSent, now - in.lastRcvd);
|
|
||||||
} else
|
|
||||||
return -1; //"state, no out";
|
|
||||||
} else {
|
|
||||||
return -1; //"no state";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send keepalive
|
|
||||||
*/
|
|
||||||
public void keepAlive()
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
if (s != null)
|
|
||||||
s.keepAlive();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retransmit outstanding requests if necessary
|
|
||||||
*/
|
|
||||||
public void retransmitRequests()
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
if (s != null)
|
|
||||||
s.retransmitRequests();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return how much the peer has
|
|
||||||
*/
|
|
||||||
public int completed()
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
if (s == null || s.bitfield == null)
|
|
||||||
return 0;
|
|
||||||
return s.bitfield.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return if a peer is a seeder
|
|
||||||
*/
|
|
||||||
public boolean isCompleted()
|
|
||||||
{
|
|
||||||
PeerState s = state;
|
|
||||||
if (s == null || s.bitfield == null)
|
|
||||||
return false;
|
|
||||||
return s.bitfield.complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Push the total uploaded/downloaded onto a RATE_DEPTH deep stack
|
|
||||||
*/
|
|
||||||
public void setRateHistory(long up, long down)
|
|
||||||
{
|
|
||||||
setRate(up, uploaded_old);
|
|
||||||
setRate(down, downloaded_old);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setRate(long val, long array[])
|
|
||||||
{
|
|
||||||
synchronized(array) {
|
|
||||||
for (int i = RATE_DEPTH-1; i > 0; i--)
|
|
||||||
array[i] = array[i-1];
|
|
||||||
array[0] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the 4-minute-average rate in Bps
|
|
||||||
*/
|
|
||||||
public long getUploadRate()
|
|
||||||
{
|
|
||||||
return getRate(uploaded_old);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getDownloadRate()
|
|
||||||
{
|
|
||||||
return getRate(downloaded_old);
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getRate(long array[])
|
|
||||||
{
|
|
||||||
long rate = 0;
|
|
||||||
int i = 0;
|
|
||||||
synchronized(array) {
|
|
||||||
for ( ; i < RATE_DEPTH; i++){
|
|
||||||
if (array[i] < 0)
|
|
||||||
break;
|
|
||||||
rate += array[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i == 0)
|
|
||||||
return 0;
|
|
||||||
return rate / (i * CHECK_PERIOD / 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,149 +0,0 @@
|
|||||||
/* PeerAcceptor - Accepts incomming connections from peers.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.SequenceInputStream;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
import net.i2p.client.streaming.I2PSocket;
|
|
||||||
import net.i2p.data.Base64;
|
|
||||||
import net.i2p.data.DataHelper;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accepts incomming connections from peers. The ConnectionAcceptor
|
|
||||||
* will call the connection() method when it detects an incomming BT
|
|
||||||
* protocol connection. The PeerAcceptor will then create a new peer
|
|
||||||
* if the PeerCoordinator wants more peers.
|
|
||||||
*/
|
|
||||||
public class PeerAcceptor
|
|
||||||
{
|
|
||||||
private static final Log _log = new Log(PeerAcceptor.class);
|
|
||||||
private final PeerCoordinator coordinator;
|
|
||||||
final PeerCoordinatorSet coordinators;
|
|
||||||
|
|
||||||
public PeerAcceptor(PeerCoordinator coordinator)
|
|
||||||
{
|
|
||||||
this.coordinator = coordinator;
|
|
||||||
this.coordinators = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PeerAcceptor(PeerCoordinatorSet coordinators)
|
|
||||||
{
|
|
||||||
this.coordinators = coordinators;
|
|
||||||
this.coordinator = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connection(I2PSocket socket,
|
|
||||||
InputStream in, OutputStream out)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
// inside this Peer constructor's handshake is where you'd deal with the other
|
|
||||||
// side saying they want to communicate with another torrent - aka multitorrent
|
|
||||||
// support, but because of how the protocol works, we can get away with just reading
|
|
||||||
// ahead the first $LOOKAHEAD_SIZE bytes to figure out which infohash they want to
|
|
||||||
// talk about, and we can just look for that in our list of active torrents.
|
|
||||||
byte peerInfoHash[] = null;
|
|
||||||
if (in instanceof BufferedInputStream) {
|
|
||||||
in.mark(LOOKAHEAD_SIZE);
|
|
||||||
peerInfoHash = readHash(in);
|
|
||||||
in.reset();
|
|
||||||
} else {
|
|
||||||
// is this working right?
|
|
||||||
try {
|
|
||||||
peerInfoHash = readHash(in);
|
|
||||||
_log.info("infohash read from " + socket.getPeerDestination().calculateHash().toBase64()
|
|
||||||
+ ": " + Base64.encode(peerInfoHash));
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.info("Unable to read the infohash from " + socket.getPeerDestination().calculateHash().toBase64());
|
|
||||||
throw ioe;
|
|
||||||
}
|
|
||||||
in = new SequenceInputStream(new ByteArrayInputStream(peerInfoHash), in);
|
|
||||||
}
|
|
||||||
if (coordinator != null) {
|
|
||||||
// single torrent capability
|
|
||||||
MetaInfo meta = coordinator.getMetaInfo();
|
|
||||||
if (DataHelper.eq(meta.getInfoHash(), peerInfoHash)) {
|
|
||||||
if (coordinator.needPeers())
|
|
||||||
{
|
|
||||||
Peer peer = new Peer(socket, in, out, coordinator.getID(),
|
|
||||||
coordinator.getMetaInfo());
|
|
||||||
coordinator.addPeer(peer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
socket.close();
|
|
||||||
} else {
|
|
||||||
// its for another infohash, but we are only single torrent capable. b0rk.
|
|
||||||
throw new IOException("Peer wants another torrent (" + Base64.encode(peerInfoHash)
|
|
||||||
+ ") while we only support (" + Base64.encode(meta.getInfoHash()) + ")");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// multitorrent capable, so lets see what we can handle
|
|
||||||
for (Iterator iter = coordinators.iterator(); iter.hasNext(); ) {
|
|
||||||
PeerCoordinator cur = (PeerCoordinator)iter.next();
|
|
||||||
MetaInfo meta = cur.getMetaInfo();
|
|
||||||
|
|
||||||
if (DataHelper.eq(meta.getInfoHash(), peerInfoHash)) {
|
|
||||||
if (cur.needPeers())
|
|
||||||
{
|
|
||||||
Peer peer = new Peer(socket, in, out, cur.getID(),
|
|
||||||
cur.getMetaInfo());
|
|
||||||
cur.addPeer(peer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Rejecting new peer for " + cur.snark.torrent);
|
|
||||||
socket.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// this is only reached if none of the coordinators match the infohash
|
|
||||||
throw new IOException("Peer wants another torrent (" + Base64.encode(peerInfoHash)
|
|
||||||
+ ") while we don't support that hash");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int LOOKAHEAD_SIZE = "19".length() +
|
|
||||||
"BitTorrent protocol".length() +
|
|
||||||
8 + // blank, reserved
|
|
||||||
20; // infohash
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read ahead to the infohash, throwing an exception if there isn't enough data
|
|
||||||
*/
|
|
||||||
private byte[] readHash(InputStream in) throws IOException {
|
|
||||||
byte buf[] = new byte[LOOKAHEAD_SIZE];
|
|
||||||
int read = DataHelper.read(in, buf);
|
|
||||||
if (read != buf.length)
|
|
||||||
throw new IOException("Unable to read the hash (read " + read + ")");
|
|
||||||
byte rv[] = new byte[20];
|
|
||||||
System.arraycopy(buf, buf.length-rv.length-1, rv, 0, rv.length);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,258 +0,0 @@
|
|||||||
/* PeerCheckTasks - TimerTask that checks for good/bad up/downloaders.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TimerTask that checks for good/bad up/downloader. Works together
|
|
||||||
* with the PeerCoordinator to select which Peers get (un)choked.
|
|
||||||
*/
|
|
||||||
class PeerCheckerTask extends TimerTask
|
|
||||||
{
|
|
||||||
private static final long KILOPERSECOND = 1024*(PeerCoordinator.CHECK_PERIOD/1000);
|
|
||||||
|
|
||||||
private final PeerCoordinator coordinator;
|
|
||||||
public I2PSnarkUtil _util;
|
|
||||||
|
|
||||||
PeerCheckerTask(I2PSnarkUtil util, PeerCoordinator coordinator)
|
|
||||||
{
|
|
||||||
_util = util;
|
|
||||||
this.coordinator = coordinator;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Random random = new Random();
|
|
||||||
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
synchronized(coordinator.peers)
|
|
||||||
{
|
|
||||||
Iterator it = coordinator.peers.iterator();
|
|
||||||
if ((!it.hasNext()) || coordinator.halted()) {
|
|
||||||
coordinator.peerCount = 0;
|
|
||||||
coordinator.interestedAndChoking = 0;
|
|
||||||
coordinator.setRateHistory(0, 0);
|
|
||||||
coordinator.uploaders = 0;
|
|
||||||
if (coordinator.halted())
|
|
||||||
cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate total uploading and worst downloader.
|
|
||||||
long worstdownload = Long.MAX_VALUE;
|
|
||||||
Peer worstDownloader = null;
|
|
||||||
|
|
||||||
int peers = 0;
|
|
||||||
int uploaders = 0;
|
|
||||||
int downloaders = 0;
|
|
||||||
int removedCount = 0;
|
|
||||||
|
|
||||||
long uploaded = 0;
|
|
||||||
long downloaded = 0;
|
|
||||||
|
|
||||||
// Keep track of peers we remove now,
|
|
||||||
// we will add them back to the end of the list.
|
|
||||||
List removed = new ArrayList();
|
|
||||||
int uploadLimit = coordinator.allowedUploaders();
|
|
||||||
boolean overBWLimit = coordinator.overUpBWLimit();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Peer peer = (Peer)it.next();
|
|
||||||
|
|
||||||
// Remove dying peers
|
|
||||||
if (!peer.isConnected())
|
|
||||||
{
|
|
||||||
it.remove();
|
|
||||||
coordinator.removePeerFromPieces(peer);
|
|
||||||
coordinator.peerCount = coordinator.peers.size();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
peers++;
|
|
||||||
|
|
||||||
if (!peer.isChoking())
|
|
||||||
uploaders++;
|
|
||||||
if (!peer.isChoked() && peer.isInteresting())
|
|
||||||
downloaders++;
|
|
||||||
|
|
||||||
long upload = peer.getUploaded();
|
|
||||||
uploaded += upload;
|
|
||||||
long download = peer.getDownloaded();
|
|
||||||
downloaded += download;
|
|
||||||
peer.setRateHistory(upload, download);
|
|
||||||
peer.resetCounters();
|
|
||||||
|
|
||||||
_util.debug(peer + ":", Snark.DEBUG);
|
|
||||||
_util.debug(" ul: " + upload/KILOPERSECOND
|
|
||||||
+ " dl: " + download/KILOPERSECOND
|
|
||||||
+ " i: " + peer.isInterested()
|
|
||||||
+ " I: " + peer.isInteresting()
|
|
||||||
+ " c: " + peer.isChoking()
|
|
||||||
+ " C: " + peer.isChoked(),
|
|
||||||
Snark.DEBUG);
|
|
||||||
|
|
||||||
// Choke half of them rather than all so it isn't so drastic...
|
|
||||||
// unless this torrent is over the limit all by itself.
|
|
||||||
boolean overBWLimitChoke = upload > 0 &&
|
|
||||||
((overBWLimit && random.nextBoolean()) ||
|
|
||||||
(coordinator.overUpBWLimit(uploaded)));
|
|
||||||
|
|
||||||
// If we are at our max uploaders and we have lots of other
|
|
||||||
// interested peers try to make some room.
|
|
||||||
// (Note use of coordinator.uploaders)
|
|
||||||
if (((coordinator.uploaders == uploadLimit
|
|
||||||
&& coordinator.interestedAndChoking > 0)
|
|
||||||
|| coordinator.uploaders > uploadLimit
|
|
||||||
|| overBWLimitChoke)
|
|
||||||
&& !peer.isChoking())
|
|
||||||
{
|
|
||||||
// Check if it still wants pieces from us.
|
|
||||||
if (!peer.isInterested())
|
|
||||||
{
|
|
||||||
_util.debug("Choke uninterested peer: " + peer,
|
|
||||||
Snark.INFO);
|
|
||||||
peer.setChoking(true);
|
|
||||||
uploaders--;
|
|
||||||
coordinator.uploaders--;
|
|
||||||
|
|
||||||
// Put it at the back of the list
|
|
||||||
it.remove();
|
|
||||||
removed.add(peer);
|
|
||||||
}
|
|
||||||
else if (overBWLimitChoke)
|
|
||||||
{
|
|
||||||
_util.debug("BW limit (" + upload + "/" + uploaded + "), choke peer: " + peer,
|
|
||||||
Snark.INFO);
|
|
||||||
peer.setChoking(true);
|
|
||||||
uploaders--;
|
|
||||||
coordinator.uploaders--;
|
|
||||||
removedCount++;
|
|
||||||
|
|
||||||
// Put it at the back of the list for fairness, even though we won't be unchoking this time
|
|
||||||
it.remove();
|
|
||||||
removed.add(peer);
|
|
||||||
}
|
|
||||||
else if (peer.isInteresting() && peer.isChoked())
|
|
||||||
{
|
|
||||||
// If they are choking us make someone else a downloader
|
|
||||||
_util.debug("Choke choking peer: " + peer, Snark.DEBUG);
|
|
||||||
peer.setChoking(true);
|
|
||||||
uploaders--;
|
|
||||||
coordinator.uploaders--;
|
|
||||||
removedCount++;
|
|
||||||
|
|
||||||
// Put it at the back of the list
|
|
||||||
it.remove();
|
|
||||||
removed.add(peer);
|
|
||||||
}
|
|
||||||
else if (!peer.isInteresting() && !coordinator.completed())
|
|
||||||
{
|
|
||||||
// If they aren't interesting make someone else a downloader
|
|
||||||
_util.debug("Choke uninteresting peer: " + peer, Snark.DEBUG);
|
|
||||||
peer.setChoking(true);
|
|
||||||
uploaders--;
|
|
||||||
coordinator.uploaders--;
|
|
||||||
removedCount++;
|
|
||||||
|
|
||||||
// Put it at the back of the list
|
|
||||||
it.remove();
|
|
||||||
removed.add(peer);
|
|
||||||
}
|
|
||||||
else if (peer.isInteresting()
|
|
||||||
&& !peer.isChoked()
|
|
||||||
&& download == 0)
|
|
||||||
{
|
|
||||||
// We are downloading but didn't receive anything...
|
|
||||||
_util.debug("Choke downloader that doesn't deliver:"
|
|
||||||
+ peer, Snark.DEBUG);
|
|
||||||
peer.setChoking(true);
|
|
||||||
uploaders--;
|
|
||||||
coordinator.uploaders--;
|
|
||||||
removedCount++;
|
|
||||||
|
|
||||||
// Put it at the back of the list
|
|
||||||
it.remove();
|
|
||||||
removed.add(peer);
|
|
||||||
}
|
|
||||||
else if (peer.isInteresting() && !peer.isChoked() &&
|
|
||||||
download < worstdownload)
|
|
||||||
{
|
|
||||||
// Make sure download is good if we are uploading
|
|
||||||
worstdownload = download;
|
|
||||||
worstDownloader = peer;
|
|
||||||
}
|
|
||||||
else if (upload < worstdownload && coordinator.completed())
|
|
||||||
{
|
|
||||||
// Make sure upload is good if we are seeding
|
|
||||||
worstdownload = upload;
|
|
||||||
worstDownloader = peer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
peer.retransmitRequests();
|
|
||||||
peer.keepAlive();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resync actual uploaders value
|
|
||||||
// (can shift a bit by disconnecting peers)
|
|
||||||
coordinator.uploaders = uploaders;
|
|
||||||
|
|
||||||
// Remove the worst downloader if needed. (uploader if seeding)
|
|
||||||
if (((uploaders == uploadLimit
|
|
||||||
&& coordinator.interestedAndChoking > 0)
|
|
||||||
|| uploaders > uploadLimit)
|
|
||||||
&& worstDownloader != null)
|
|
||||||
{
|
|
||||||
_util.debug("Choke worst downloader: " + worstDownloader,
|
|
||||||
Snark.DEBUG);
|
|
||||||
|
|
||||||
worstDownloader.setChoking(true);
|
|
||||||
coordinator.uploaders--;
|
|
||||||
removedCount++;
|
|
||||||
|
|
||||||
// Put it at the back of the list
|
|
||||||
coordinator.peers.remove(worstDownloader);
|
|
||||||
coordinator.peerCount = coordinator.peers.size();
|
|
||||||
removed.add(worstDownloader);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optimistically unchoke a peer
|
|
||||||
if ((!overBWLimit) && !coordinator.overUpBWLimit(uploaded))
|
|
||||||
coordinator.unchokePeer();
|
|
||||||
|
|
||||||
// Put peers back at the end of the list that we removed earlier.
|
|
||||||
coordinator.peers.addAll(removed);
|
|
||||||
coordinator.peerCount = coordinator.peers.size();
|
|
||||||
coordinator.interestedAndChoking += removedCount;
|
|
||||||
|
|
||||||
// store the rates
|
|
||||||
coordinator.setRateHistory(uploaded, downloaded);
|
|
||||||
|
|
||||||
// close out unused files, but we don't need to do it every time
|
|
||||||
if (random.nextInt(4) == 0)
|
|
||||||
coordinator.getStorage().cleanRAFs();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,199 +0,0 @@
|
|||||||
/* PeerConnectionIn - Handles incomming messages and hands them to PeerState.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
class PeerConnectionIn implements Runnable
|
|
||||||
{
|
|
||||||
private Log _log = new Log(PeerConnectionIn.class);
|
|
||||||
private final Peer peer;
|
|
||||||
private final DataInputStream din;
|
|
||||||
|
|
||||||
private Thread thread;
|
|
||||||
private volatile boolean quit;
|
|
||||||
|
|
||||||
long lastRcvd;
|
|
||||||
|
|
||||||
public PeerConnectionIn(Peer peer, DataInputStream din)
|
|
||||||
{
|
|
||||||
this.peer = peer;
|
|
||||||
this.din = din;
|
|
||||||
lastRcvd = System.currentTimeMillis();
|
|
||||||
quit = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void disconnect()
|
|
||||||
{
|
|
||||||
if (quit == true)
|
|
||||||
return;
|
|
||||||
|
|
||||||
quit = true;
|
|
||||||
Thread t = thread;
|
|
||||||
if (t != null)
|
|
||||||
t.interrupt();
|
|
||||||
if (din != null) {
|
|
||||||
try {
|
|
||||||
din.close();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.warn("Error closing the stream from " + peer, ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
thread = Thread.currentThread();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
PeerState ps = peer.state;
|
|
||||||
while (!quit && ps != null)
|
|
||||||
{
|
|
||||||
// Common variables used for some messages.
|
|
||||||
int piece;
|
|
||||||
int begin;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
// Wait till we hear something...
|
|
||||||
// The length of a complete message in bytes.
|
|
||||||
// The biggest is the piece message, for which the length is the
|
|
||||||
// request size (32K) plus 9. (we could also check if Storage.MAX_PIECES / 8
|
|
||||||
// in the bitfield message is bigger but it's currently 5000/8 = 625 so don't bother)
|
|
||||||
int i = din.readInt();
|
|
||||||
lastRcvd = System.currentTimeMillis();
|
|
||||||
if (i < 0 || i > PeerState.PARTSIZE + 9)
|
|
||||||
throw new IOException("Unexpected length prefix: " + i);
|
|
||||||
|
|
||||||
if (i == 0)
|
|
||||||
{
|
|
||||||
ps.keepAliveMessage();
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Received keepalive from " + peer + " on " + peer.metainfo.getName());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte b = din.readByte();
|
|
||||||
Message m = new Message();
|
|
||||||
m.type = b;
|
|
||||||
switch (b)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
ps.chokeMessage(true);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Received choke from " + peer + " on " + peer.metainfo.getName());
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
ps.chokeMessage(false);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Received unchoke from " + peer + " on " + peer.metainfo.getName());
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
ps.interestedMessage(true);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Received interested from " + peer + " on " + peer.metainfo.getName());
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
ps.interestedMessage(false);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Received not interested from " + peer + " on " + peer.metainfo.getName());
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
piece = din.readInt();
|
|
||||||
ps.haveMessage(piece);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Received havePiece(" + piece + ") from " + peer + " on " + peer.metainfo.getName());
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
byte[] bitmap = new byte[i-1];
|
|
||||||
din.readFully(bitmap);
|
|
||||||
ps.bitfieldMessage(bitmap);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Received bitmap from " + peer + " on " + peer.metainfo.getName() + ": size=" + (i-1) + ": " + ps.bitfield);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
piece = din.readInt();
|
|
||||||
begin = din.readInt();
|
|
||||||
len = din.readInt();
|
|
||||||
ps.requestMessage(piece, begin, len);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Received request(" + piece + "," + begin + ") from " + peer + " on " + peer.metainfo.getName());
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
piece = din.readInt();
|
|
||||||
begin = din.readInt();
|
|
||||||
len = i-9;
|
|
||||||
Request req = ps.getOutstandingRequest(piece, begin, len);
|
|
||||||
byte[] piece_bytes;
|
|
||||||
if (req != null)
|
|
||||||
{
|
|
||||||
piece_bytes = req.bs;
|
|
||||||
din.readFully(piece_bytes, begin, len);
|
|
||||||
ps.pieceMessage(req);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Received data(" + piece + "," + begin + ") from " + peer + " on " + peer.metainfo.getName());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// XXX - Consume but throw away afterwards.
|
|
||||||
piece_bytes = new byte[len];
|
|
||||||
din.readFully(piece_bytes);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Received UNWANTED data(" + piece + "," + begin + ") from " + peer + " on " + peer.metainfo.getName());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
piece = din.readInt();
|
|
||||||
begin = din.readInt();
|
|
||||||
len = din.readInt();
|
|
||||||
ps.cancelMessage(piece, begin, len);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Received cancel(" + piece + "," + begin + ") from " + peer + " on " + peer.metainfo.getName());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
byte[] bs = new byte[i-1];
|
|
||||||
din.readFully(bs);
|
|
||||||
ps.unknownMessage(b, bs);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Received unknown message from " + peer + " on " + peer.metainfo.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
// Ignore, probably the other side closed connection.
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("IOError talking with " + peer, ioe);
|
|
||||||
}
|
|
||||||
catch (Throwable t)
|
|
||||||
{
|
|
||||||
_log.error("Error talking with " + peer, t);
|
|
||||||
if (t instanceof OutOfMemoryError)
|
|
||||||
throw (OutOfMemoryError)t;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
peer.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,472 +0,0 @@
|
|||||||
/* PeerConnectionOut - Keeps a queue of outgoing messages and delivers them.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.i2p.util.I2PAppThread;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
import net.i2p.util.SimpleTimer;
|
|
||||||
|
|
||||||
class PeerConnectionOut implements Runnable
|
|
||||||
{
|
|
||||||
private Log _log = new Log(PeerConnectionOut.class);
|
|
||||||
private final Peer peer;
|
|
||||||
private final DataOutputStream dout;
|
|
||||||
|
|
||||||
private Thread thread;
|
|
||||||
private boolean quit;
|
|
||||||
|
|
||||||
// Contains Messages.
|
|
||||||
private List sendQueue = new ArrayList();
|
|
||||||
|
|
||||||
private static long __id = 0;
|
|
||||||
private long _id;
|
|
||||||
|
|
||||||
long lastSent;
|
|
||||||
|
|
||||||
public PeerConnectionOut(Peer peer, DataOutputStream dout)
|
|
||||||
{
|
|
||||||
this.peer = peer;
|
|
||||||
this.dout = dout;
|
|
||||||
_id = ++__id;
|
|
||||||
|
|
||||||
lastSent = System.currentTimeMillis();
|
|
||||||
quit = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startup() {
|
|
||||||
thread = new I2PAppThread(this, "Snark sender " + _id + ": " + peer);
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Continuesly monitors for more outgoing messages that have to be send.
|
|
||||||
* Stops if quit is true of an IOException occurs.
|
|
||||||
*/
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (!quit && peer.isConnected())
|
|
||||||
{
|
|
||||||
Message m = null;
|
|
||||||
PeerState state = null;
|
|
||||||
boolean shouldFlush;
|
|
||||||
synchronized(sendQueue)
|
|
||||||
{
|
|
||||||
shouldFlush = !quit && peer.isConnected() && sendQueue.isEmpty();
|
|
||||||
}
|
|
||||||
if (shouldFlush)
|
|
||||||
// Make sure everything will reach the other side.
|
|
||||||
// flush while not holding lock, could take a long time
|
|
||||||
dout.flush();
|
|
||||||
|
|
||||||
synchronized(sendQueue)
|
|
||||||
{
|
|
||||||
while (!quit && peer.isConnected() && sendQueue.isEmpty())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Make sure everything will reach the other side.
|
|
||||||
// don't flush while holding lock, could take a long time
|
|
||||||
// dout.flush();
|
|
||||||
|
|
||||||
// Wait till more data arrives.
|
|
||||||
sendQueue.wait(60*1000);
|
|
||||||
}
|
|
||||||
catch (InterruptedException ie)
|
|
||||||
{
|
|
||||||
/* ignored */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state = peer.state;
|
|
||||||
if (!quit && state != null && peer.isConnected())
|
|
||||||
{
|
|
||||||
// Piece messages are big. So if there are other
|
|
||||||
// (control) messages make sure they are send first.
|
|
||||||
// Also remove request messages from the queue if
|
|
||||||
// we are currently being choked to prevent them from
|
|
||||||
// being send even if we get unchoked a little later.
|
|
||||||
// (Since we will resent them anyway in that case.)
|
|
||||||
// And remove piece messages if we are choking.
|
|
||||||
|
|
||||||
// this should get fixed for starvation
|
|
||||||
Iterator it = sendQueue.iterator();
|
|
||||||
while (m == null && it.hasNext())
|
|
||||||
{
|
|
||||||
Message nm = (Message)it.next();
|
|
||||||
if (nm.type == Message.PIECE)
|
|
||||||
{
|
|
||||||
if (state.choking) {
|
|
||||||
it.remove();
|
|
||||||
SimpleTimer.getInstance().removeEvent(nm.expireEvent);
|
|
||||||
}
|
|
||||||
nm = null;
|
|
||||||
}
|
|
||||||
else if (nm.type == Message.REQUEST && state.choked)
|
|
||||||
{
|
|
||||||
it.remove();
|
|
||||||
SimpleTimer.getInstance().removeEvent(nm.expireEvent);
|
|
||||||
nm = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m == null && nm != null)
|
|
||||||
{
|
|
||||||
m = nm;
|
|
||||||
SimpleTimer.getInstance().removeEvent(nm.expireEvent);
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m == null && sendQueue.size() > 0) {
|
|
||||||
m = (Message)sendQueue.remove(0);
|
|
||||||
SimpleTimer.getInstance().removeEvent(m.expireEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m != null)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Send " + peer + ": " + m + " on " + peer.metainfo.getName());
|
|
||||||
m.sendMessage(dout);
|
|
||||||
lastSent = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// Remove all piece messages after sending a choke message.
|
|
||||||
if (m.type == Message.CHOKE)
|
|
||||||
removeMessage(Message.PIECE);
|
|
||||||
|
|
||||||
// XXX - Should also register overhead...
|
|
||||||
if (m.type == Message.PIECE)
|
|
||||||
state.uploaded(m.len);
|
|
||||||
|
|
||||||
m = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
// Ignore, probably other side closed connection.
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("IOError sending to " + peer, ioe);
|
|
||||||
}
|
|
||||||
catch (Throwable t)
|
|
||||||
{
|
|
||||||
_log.error("Error sending to " + peer, t);
|
|
||||||
if (t instanceof OutOfMemoryError)
|
|
||||||
throw (OutOfMemoryError)t;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
quit = true;
|
|
||||||
peer.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disconnect()
|
|
||||||
{
|
|
||||||
synchronized(sendQueue)
|
|
||||||
{
|
|
||||||
//if (quit == true)
|
|
||||||
// return;
|
|
||||||
|
|
||||||
quit = true;
|
|
||||||
if (thread != null)
|
|
||||||
thread.interrupt();
|
|
||||||
|
|
||||||
sendQueue.clear();
|
|
||||||
sendQueue.notify();
|
|
||||||
}
|
|
||||||
if (dout != null) {
|
|
||||||
try {
|
|
||||||
dout.close();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.warn("Error closing the stream to " + peer, ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a message to the sendQueue and notifies the method waiting
|
|
||||||
* on the sendQueue to change.
|
|
||||||
* If a PIECE message only, add a timeout.
|
|
||||||
*/
|
|
||||||
private void addMessage(Message m)
|
|
||||||
{
|
|
||||||
if (m.type == Message.PIECE)
|
|
||||||
SimpleTimer.getInstance().addEvent(new RemoveTooSlow(m), SEND_TIMEOUT);
|
|
||||||
synchronized(sendQueue)
|
|
||||||
{
|
|
||||||
sendQueue.add(m);
|
|
||||||
sendQueue.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** remove messages not sent in 3m */
|
|
||||||
private static final int SEND_TIMEOUT = 3*60*1000;
|
|
||||||
private class RemoveTooSlow implements SimpleTimer.TimedEvent {
|
|
||||||
private Message _m;
|
|
||||||
public RemoveTooSlow(Message m) {
|
|
||||||
_m = m;
|
|
||||||
m.expireEvent = RemoveTooSlow.this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void timeReached() {
|
|
||||||
boolean removed = false;
|
|
||||||
synchronized (sendQueue) {
|
|
||||||
removed = sendQueue.remove(_m);
|
|
||||||
sendQueue.notifyAll();
|
|
||||||
}
|
|
||||||
if (removed)
|
|
||||||
_log.info("Took too long to send " + _m + " to " + peer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a particular message type from the queue.
|
|
||||||
*
|
|
||||||
* @param type the Message type to remove.
|
|
||||||
* @returns true when a message of the given type was removed, false
|
|
||||||
* otherwise.
|
|
||||||
*/
|
|
||||||
private boolean removeMessage(int type)
|
|
||||||
{
|
|
||||||
boolean removed = false;
|
|
||||||
synchronized(sendQueue)
|
|
||||||
{
|
|
||||||
Iterator it = sendQueue.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Message m = (Message)it.next();
|
|
||||||
if (m.type == type)
|
|
||||||
{
|
|
||||||
it.remove();
|
|
||||||
removed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sendQueue.notifyAll();
|
|
||||||
}
|
|
||||||
return removed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendAlive()
|
|
||||||
{
|
|
||||||
Message m = new Message();
|
|
||||||
m.type = Message.KEEP_ALIVE;
|
|
||||||
// addMessage(m);
|
|
||||||
synchronized(sendQueue)
|
|
||||||
{
|
|
||||||
if(sendQueue.isEmpty())
|
|
||||||
sendQueue.add(m);
|
|
||||||
sendQueue.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendChoke(boolean choke)
|
|
||||||
{
|
|
||||||
// We cancel the (un)choke but keep PIECE messages.
|
|
||||||
// PIECE messages are purged if a choke is actually send.
|
|
||||||
synchronized(sendQueue)
|
|
||||||
{
|
|
||||||
int inverseType = choke ? Message.UNCHOKE
|
|
||||||
: Message.CHOKE;
|
|
||||||
if (!removeMessage(inverseType))
|
|
||||||
{
|
|
||||||
Message m = new Message();
|
|
||||||
if (choke)
|
|
||||||
m.type = Message.CHOKE;
|
|
||||||
else
|
|
||||||
m.type = Message.UNCHOKE;
|
|
||||||
addMessage(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendInterest(boolean interest)
|
|
||||||
{
|
|
||||||
synchronized(sendQueue)
|
|
||||||
{
|
|
||||||
int inverseType = interest ? Message.UNINTERESTED
|
|
||||||
: Message.INTERESTED;
|
|
||||||
if (!removeMessage(inverseType))
|
|
||||||
{
|
|
||||||
Message m = new Message();
|
|
||||||
if (interest)
|
|
||||||
m.type = Message.INTERESTED;
|
|
||||||
else
|
|
||||||
m.type = Message.UNINTERESTED;
|
|
||||||
addMessage(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendHave(int piece)
|
|
||||||
{
|
|
||||||
Message m = new Message();
|
|
||||||
m.type = Message.HAVE;
|
|
||||||
m.piece = piece;
|
|
||||||
addMessage(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendBitfield(BitField bitfield)
|
|
||||||
{
|
|
||||||
Message m = new Message();
|
|
||||||
m.type = Message.BITFIELD;
|
|
||||||
m.data = bitfield.getFieldBytes();
|
|
||||||
m.off = 0;
|
|
||||||
m.len = m.data.length;
|
|
||||||
addMessage(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** reransmit requests not received in 7m */
|
|
||||||
private static final int REQ_TIMEOUT = (2 * SEND_TIMEOUT) + (60 * 1000);
|
|
||||||
void retransmitRequests(List requests)
|
|
||||||
{
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
Iterator it = requests.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Request req = (Request)it.next();
|
|
||||||
if(now > req.sendTime + REQ_TIMEOUT) {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Retransmit request " + req + " to peer " + peer);
|
|
||||||
sendRequest(req);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendRequests(List requests)
|
|
||||||
{
|
|
||||||
Iterator it = requests.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Request req = (Request)it.next();
|
|
||||||
sendRequest(req);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendRequest(Request req)
|
|
||||||
{
|
|
||||||
// Check for duplicate requests to deal with fibrillating i2p-bt
|
|
||||||
// (multiple choke/unchokes received cause duplicate requests in the queue)
|
|
||||||
synchronized(sendQueue)
|
|
||||||
{
|
|
||||||
Iterator it = sendQueue.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Message m = (Message)it.next();
|
|
||||||
if (m.type == Message.REQUEST && m.piece == req.piece &&
|
|
||||||
m.begin == req.off && m.length == req.len)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Discarding duplicate request " + req + " to peer " + peer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Message m = new Message();
|
|
||||||
m.type = Message.REQUEST;
|
|
||||||
m.piece = req.piece;
|
|
||||||
m.begin = req.off;
|
|
||||||
m.length = req.len;
|
|
||||||
addMessage(m);
|
|
||||||
req.sendTime = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used by PeerState to limit pipelined requests
|
|
||||||
int queuedBytes()
|
|
||||||
{
|
|
||||||
int total = 0;
|
|
||||||
synchronized(sendQueue)
|
|
||||||
{
|
|
||||||
Iterator it = sendQueue.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Message m = (Message)it.next();
|
|
||||||
if (m.type == Message.PIECE)
|
|
||||||
total += m.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendPiece(int piece, int begin, int length, byte[] bytes)
|
|
||||||
{
|
|
||||||
Message m = new Message();
|
|
||||||
m.type = Message.PIECE;
|
|
||||||
m.piece = piece;
|
|
||||||
m.begin = begin;
|
|
||||||
m.length = length;
|
|
||||||
m.data = bytes;
|
|
||||||
m.off = 0;
|
|
||||||
m.len = length;
|
|
||||||
addMessage(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendCancel(Request req)
|
|
||||||
{
|
|
||||||
// See if it is still in our send queue
|
|
||||||
synchronized(sendQueue)
|
|
||||||
{
|
|
||||||
Iterator it = sendQueue.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Message m = (Message)it.next();
|
|
||||||
if (m.type == Message.REQUEST
|
|
||||||
&& m.piece == req.piece
|
|
||||||
&& m.begin == req.off
|
|
||||||
&& m.length == req.len)
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always send, just to be sure it it is really canceled.
|
|
||||||
Message m = new Message();
|
|
||||||
m.type = Message.CANCEL;
|
|
||||||
m.piece = req.piece;
|
|
||||||
m.begin = req.off;
|
|
||||||
m.length = req.len;
|
|
||||||
addMessage(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by the PeerState when the other side doesn't want this
|
|
||||||
// request to be handled anymore. Removes any pending Piece Message
|
|
||||||
// from out send queue.
|
|
||||||
void cancelRequest(int piece, int begin, int length)
|
|
||||||
{
|
|
||||||
synchronized (sendQueue)
|
|
||||||
{
|
|
||||||
Iterator it = sendQueue.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Message m = (Message)it.next();
|
|
||||||
if (m.type == Message.PIECE
|
|
||||||
&& m.piece == piece
|
|
||||||
&& m.begin == begin
|
|
||||||
&& m.length == length)
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,874 +0,0 @@
|
|||||||
/* PeerCoordinator - Coordinates which peers do what (up and downloading).
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Timer;
|
|
||||||
|
|
||||||
import net.i2p.util.I2PAppThread;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Coordinates what peer does what.
|
|
||||||
*/
|
|
||||||
public class PeerCoordinator implements PeerListener
|
|
||||||
{
|
|
||||||
private final Log _log = new Log(PeerCoordinator.class);
|
|
||||||
final MetaInfo metainfo;
|
|
||||||
final Storage storage;
|
|
||||||
final Snark snark;
|
|
||||||
|
|
||||||
// package local for access by CheckDownLoadersTask
|
|
||||||
final static long CHECK_PERIOD = 40*1000; // 40 seconds
|
|
||||||
final static int MAX_UPLOADERS = 6;
|
|
||||||
|
|
||||||
// Approximation of the number of current uploaders.
|
|
||||||
// Resynced by PeerChecker once in a while.
|
|
||||||
int uploaders = 0;
|
|
||||||
int interestedAndChoking = 0;
|
|
||||||
|
|
||||||
// final static int MAX_DOWNLOADERS = MAX_CONNECTIONS;
|
|
||||||
// int downloaders = 0;
|
|
||||||
|
|
||||||
private long uploaded;
|
|
||||||
private long downloaded;
|
|
||||||
final static int RATE_DEPTH = 6; // make following arrays RATE_DEPTH long
|
|
||||||
private long uploaded_old[] = {-1,-1,-1,-1,-1,-1};
|
|
||||||
private long downloaded_old[] = {-1,-1,-1,-1,-1,-1};
|
|
||||||
|
|
||||||
// synchronize on this when changing peers or downloaders
|
|
||||||
final List peers = new ArrayList();
|
|
||||||
/** estimate of the peers, without requiring any synchronization */
|
|
||||||
volatile int peerCount;
|
|
||||||
|
|
||||||
/** Timer to handle all periodical tasks. */
|
|
||||||
private final Timer timer = new Timer(true);
|
|
||||||
|
|
||||||
private final byte[] id;
|
|
||||||
|
|
||||||
// Some random wanted pieces
|
|
||||||
private List wantedPieces;
|
|
||||||
|
|
||||||
private boolean halted = false;
|
|
||||||
|
|
||||||
private final CoordinatorListener listener;
|
|
||||||
public I2PSnarkUtil _util;
|
|
||||||
|
|
||||||
public String trackerProblems = null;
|
|
||||||
public int trackerSeenPeers = 0;
|
|
||||||
|
|
||||||
public PeerCoordinator(I2PSnarkUtil util, byte[] id, MetaInfo metainfo, Storage storage,
|
|
||||||
CoordinatorListener listener, Snark torrent)
|
|
||||||
{
|
|
||||||
_util = util;
|
|
||||||
this.id = id;
|
|
||||||
this.metainfo = metainfo;
|
|
||||||
this.storage = storage;
|
|
||||||
this.listener = listener;
|
|
||||||
this.snark = torrent;
|
|
||||||
|
|
||||||
setWantedPieces();
|
|
||||||
|
|
||||||
// Install a timer to check the uploaders.
|
|
||||||
// Randomize the first start time so multiple tasks are spread out,
|
|
||||||
// this will help the behavior with global limits
|
|
||||||
Random r = new Random();
|
|
||||||
timer.schedule(new PeerCheckerTask(_util, this), (CHECK_PERIOD / 2) + r.nextInt((int) CHECK_PERIOD), CHECK_PERIOD);
|
|
||||||
}
|
|
||||||
|
|
||||||
// only called externally from Storage after the double-check fails
|
|
||||||
public void setWantedPieces()
|
|
||||||
{
|
|
||||||
// Make a list of pieces
|
|
||||||
wantedPieces = new ArrayList();
|
|
||||||
BitField bitfield = storage.getBitField();
|
|
||||||
for(int i = 0; i < metainfo.getPieces(); i++)
|
|
||||||
if (!bitfield.get(i))
|
|
||||||
wantedPieces.add(new Piece(i));
|
|
||||||
Collections.shuffle(wantedPieces);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Storage getStorage() { return storage; }
|
|
||||||
public CoordinatorListener getListener() { return listener; }
|
|
||||||
|
|
||||||
// for web page detailed stats
|
|
||||||
public List peerList()
|
|
||||||
{
|
|
||||||
synchronized(peers)
|
|
||||||
{
|
|
||||||
return new ArrayList(peers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getID()
|
|
||||||
{
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean completed()
|
|
||||||
{
|
|
||||||
return storage.complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPeerCount() { return peerCount; }
|
|
||||||
|
|
||||||
public int getPeers()
|
|
||||||
{
|
|
||||||
synchronized(peers)
|
|
||||||
{
|
|
||||||
int rv = peers.size();
|
|
||||||
peerCount = rv;
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns how many bytes are still needed to get the complete file.
|
|
||||||
*/
|
|
||||||
public long getLeft()
|
|
||||||
{
|
|
||||||
// XXX - Only an approximation.
|
|
||||||
return ((long) storage.needed()) * metainfo.getPieceLength(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the total number of uploaded bytes of all peers.
|
|
||||||
*/
|
|
||||||
public long getUploaded()
|
|
||||||
{
|
|
||||||
return uploaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the total number of downloaded bytes of all peers.
|
|
||||||
*/
|
|
||||||
public long getDownloaded()
|
|
||||||
{
|
|
||||||
return downloaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Push the total uploaded/downloaded onto a RATE_DEPTH deep stack
|
|
||||||
*/
|
|
||||||
public void setRateHistory(long up, long down)
|
|
||||||
{
|
|
||||||
setRate(up, uploaded_old);
|
|
||||||
setRate(down, downloaded_old);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void setRate(long val, long array[])
|
|
||||||
{
|
|
||||||
synchronized(array) {
|
|
||||||
for (int i = RATE_DEPTH-1; i > 0; i--)
|
|
||||||
array[i] = array[i-1];
|
|
||||||
array[0] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the 4-minute-average rate in Bps
|
|
||||||
*/
|
|
||||||
public long getDownloadRate()
|
|
||||||
{
|
|
||||||
return getRate(downloaded_old);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getUploadRate()
|
|
||||||
{
|
|
||||||
return getRate(uploaded_old);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCurrentUploadRate()
|
|
||||||
{
|
|
||||||
// no need to synchronize, only one value
|
|
||||||
long r = uploaded_old[0];
|
|
||||||
if (r <= 0)
|
|
||||||
return 0;
|
|
||||||
return (r * 1000) / CHECK_PERIOD;
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getRate(long array[])
|
|
||||||
{
|
|
||||||
long rate = 0;
|
|
||||||
int i = 0;
|
|
||||||
synchronized(array) {
|
|
||||||
for ( ; i < RATE_DEPTH; i++) {
|
|
||||||
if (array[i] < 0)
|
|
||||||
break;
|
|
||||||
rate += array[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i == 0)
|
|
||||||
return 0;
|
|
||||||
return rate / (i * CHECK_PERIOD / 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MetaInfo getMetaInfo()
|
|
||||||
{
|
|
||||||
return metainfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean needPeers()
|
|
||||||
{
|
|
||||||
synchronized(peers)
|
|
||||||
{
|
|
||||||
return !halted && peers.size() < _util.getMaxConnections();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean halted() { return halted; }
|
|
||||||
|
|
||||||
public void halt()
|
|
||||||
{
|
|
||||||
halted = true;
|
|
||||||
List removed = new ArrayList();
|
|
||||||
synchronized(peers)
|
|
||||||
{
|
|
||||||
// Stop peer checker task.
|
|
||||||
timer.cancel();
|
|
||||||
|
|
||||||
// Stop peers.
|
|
||||||
removed.addAll(peers);
|
|
||||||
peers.clear();
|
|
||||||
peerCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (removed.size() > 0) {
|
|
||||||
Peer peer = (Peer)removed.remove(0);
|
|
||||||
peer.disconnect();
|
|
||||||
removePeerFromPieces(peer);
|
|
||||||
}
|
|
||||||
// delete any saved orphan partial piece
|
|
||||||
savedRequest = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connected(Peer peer)
|
|
||||||
{
|
|
||||||
if (halted)
|
|
||||||
{
|
|
||||||
peer.disconnect(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Peer toDisconnect = null;
|
|
||||||
synchronized(peers)
|
|
||||||
{
|
|
||||||
Peer old = peerIDInList(peer.getPeerID(), peers);
|
|
||||||
if ( (old != null) && (old.getInactiveTime() > 8*60*1000) ) {
|
|
||||||
// idle for 8 minutes, kill the old con (32KB/8min = 68B/sec minimum for one block)
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Remomving old peer: " + peer + ": " + old + ", inactive for " + old.getInactiveTime());
|
|
||||||
peers.remove(old);
|
|
||||||
toDisconnect = old;
|
|
||||||
old = null;
|
|
||||||
}
|
|
||||||
if (old != null)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Already connected to: " + peer + ": " + old + ", inactive for " + old.getInactiveTime());
|
|
||||||
// toDisconnect = peer to get out of synchronized(peers)
|
|
||||||
peer.disconnect(false); // Don't deregister this connection/peer.
|
|
||||||
}
|
|
||||||
// This is already checked in addPeer() but we could have gone over the limit since then
|
|
||||||
else if (peers.size() >= _util.getMaxConnections())
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Already at MAX_CONNECTIONS in connected() with peer: " + peer);
|
|
||||||
// toDisconnect = peer to get out of synchronized(peers)
|
|
||||||
peer.disconnect(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("New connection to peer: " + peer + " for " + metainfo.getName());
|
|
||||||
|
|
||||||
// Add it to the beginning of the list.
|
|
||||||
// And try to optimistically make it a uploader.
|
|
||||||
peers.add(0, peer);
|
|
||||||
peerCount = peers.size();
|
|
||||||
unchokePeer();
|
|
||||||
|
|
||||||
if (listener != null)
|
|
||||||
listener.peerChange(this, peer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (toDisconnect != null) {
|
|
||||||
toDisconnect.disconnect(false);
|
|
||||||
removePeerFromPieces(toDisconnect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// caller must synchronize on peers
|
|
||||||
private static Peer peerIDInList(PeerID pid, List peers)
|
|
||||||
{
|
|
||||||
Iterator it = peers.iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
Peer cur = (Peer)it.next();
|
|
||||||
if (pid.sameID(cur.getPeerID()))
|
|
||||||
return cur;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns true if actual attempt to add peer occurs
|
|
||||||
public boolean addPeer(final Peer peer)
|
|
||||||
{
|
|
||||||
if (halted)
|
|
||||||
{
|
|
||||||
peer.disconnect(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean need_more;
|
|
||||||
int peersize = 0;
|
|
||||||
synchronized(peers)
|
|
||||||
{
|
|
||||||
peersize = peers.size();
|
|
||||||
// This isn't a strict limit, as we may have several pending connections;
|
|
||||||
// thus there is an additional check in connected()
|
|
||||||
need_more = (!peer.isConnected()) && peersize < _util.getMaxConnections();
|
|
||||||
// Check if we already have this peer before we build the connection
|
|
||||||
Peer old = peerIDInList(peer.getPeerID(), peers);
|
|
||||||
need_more = need_more && ((old == null) || (old.getInactiveTime() > 8*60*1000));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (need_more)
|
|
||||||
{
|
|
||||||
_log.debug("Adding a peer " + peer.getPeerID().getAddress().calculateHash().toBase64() + " for " + metainfo.getName(), new Exception("add/run"));
|
|
||||||
|
|
||||||
// Run the peer with us as listener and the current bitfield.
|
|
||||||
final PeerListener listener = this;
|
|
||||||
final BitField bitfield = storage.getBitField();
|
|
||||||
Runnable r = new Runnable()
|
|
||||||
{
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
peer.runConnection(_util, listener, bitfield);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
String threadName = peer.toString();
|
|
||||||
new I2PAppThread(r, threadName).start();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (_log.shouldLog(Log.DEBUG)) {
|
|
||||||
if (peer.isConnected())
|
|
||||||
_log.info("Add peer already connected: " + peer);
|
|
||||||
else
|
|
||||||
_log.info("Connections: " + peersize + "/" + _util.getMaxConnections()
|
|
||||||
+ " not accepting extra peer: " + peer);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// (Optimistically) unchoke. Should be called with peers synchronized
|
|
||||||
void unchokePeer()
|
|
||||||
{
|
|
||||||
// linked list will contain all interested peers that we choke.
|
|
||||||
// At the start are the peers that have us unchoked at the end the
|
|
||||||
// other peer that are interested, but are choking us.
|
|
||||||
List interested = new LinkedList();
|
|
||||||
synchronized (peers) {
|
|
||||||
int count = 0;
|
|
||||||
int unchokedCount = 0;
|
|
||||||
int maxUploaders = allowedUploaders();
|
|
||||||
Iterator it = peers.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Peer peer = (Peer)it.next();
|
|
||||||
if (peer.isChoking() && peer.isInterested())
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
if (uploaders < maxUploaders)
|
|
||||||
{
|
|
||||||
if (!peer.isChoked())
|
|
||||||
interested.add(unchokedCount++, peer);
|
|
||||||
else
|
|
||||||
interested.add(peer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (uploaders < maxUploaders && interested.size() > 0)
|
|
||||||
{
|
|
||||||
Peer peer = (Peer)interested.remove(0);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Unchoke: " + peer);
|
|
||||||
peer.setChoking(false);
|
|
||||||
uploaders++;
|
|
||||||
count--;
|
|
||||||
// Put peer back at the end of the list.
|
|
||||||
peers.remove(peer);
|
|
||||||
peers.add(peer);
|
|
||||||
peerCount = peers.size();
|
|
||||||
}
|
|
||||||
interestedAndChoking = count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getBitMap()
|
|
||||||
{
|
|
||||||
return storage.getBitField().getFieldBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if we don't have the given piece yet.
|
|
||||||
*/
|
|
||||||
public boolean gotHave(Peer peer, int piece)
|
|
||||||
{
|
|
||||||
if (listener != null)
|
|
||||||
listener.peerChange(this, peer);
|
|
||||||
|
|
||||||
synchronized(wantedPieces)
|
|
||||||
{
|
|
||||||
return wantedPieces.contains(new Piece(piece));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given bitfield contains at least one piece we
|
|
||||||
* are interested in.
|
|
||||||
*/
|
|
||||||
public boolean gotBitField(Peer peer, BitField bitfield)
|
|
||||||
{
|
|
||||||
if (listener != null)
|
|
||||||
listener.peerChange(this, peer);
|
|
||||||
|
|
||||||
synchronized(wantedPieces)
|
|
||||||
{
|
|
||||||
Iterator it = wantedPieces.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Piece p = (Piece)it.next();
|
|
||||||
int i = p.getId();
|
|
||||||
if (bitfield.get(i)) {
|
|
||||||
p.addPeer(peer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns one of pieces in the given BitField that is still wanted or
|
|
||||||
* -1 if none of the given pieces are wanted.
|
|
||||||
*/
|
|
||||||
public int wantPiece(Peer peer, BitField havePieces)
|
|
||||||
{
|
|
||||||
if (halted) {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("We don't want anything from the peer, as we are halted! peer=" + peer);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized(wantedPieces)
|
|
||||||
{
|
|
||||||
Piece piece = null;
|
|
||||||
Collections.sort(wantedPieces); // Sort in order of rarest first.
|
|
||||||
List requested = new ArrayList();
|
|
||||||
Iterator it = wantedPieces.iterator();
|
|
||||||
while (piece == null && it.hasNext())
|
|
||||||
{
|
|
||||||
Piece p = (Piece)it.next();
|
|
||||||
if (havePieces.get(p.getId()) && !p.isRequested())
|
|
||||||
{
|
|
||||||
piece = p;
|
|
||||||
}
|
|
||||||
else if (p.isRequested())
|
|
||||||
{
|
|
||||||
requested.add(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Only request a piece we've requested before if there's no other choice.
|
|
||||||
if (piece == null) {
|
|
||||||
// let's not all get on the same piece
|
|
||||||
Collections.shuffle(requested);
|
|
||||||
Iterator it2 = requested.iterator();
|
|
||||||
while (piece == null && it2.hasNext())
|
|
||||||
{
|
|
||||||
Piece p = (Piece)it2.next();
|
|
||||||
if (havePieces.get(p.getId()))
|
|
||||||
{
|
|
||||||
piece = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (piece == null) {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("nothing to even rerequest from " + peer + ": requested = " + requested);
|
|
||||||
// _log.warn("nothing to even rerequest from " + peer + ": requested = " + requested
|
|
||||||
// + " wanted = " + wantedPieces + " peerHas = " + havePieces);
|
|
||||||
return -1; //If we still can't find a piece we want, so be it.
|
|
||||||
} else {
|
|
||||||
// Should be a lot smarter here - limit # of parallel attempts and
|
|
||||||
// share blocks rather than starting from 0 with each peer.
|
|
||||||
// This is where the flaws of the snark data model are really exposed.
|
|
||||||
// Could also randomize within the duplicate set rather than strict rarest-first
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("parallel request (end game?) for " + peer + ": piece = " + piece);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
piece.setRequested(true);
|
|
||||||
return piece.getId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a byte array containing the requested piece or null of
|
|
||||||
* the piece is unknown.
|
|
||||||
*/
|
|
||||||
public byte[] gotRequest(Peer peer, int piece, int off, int len)
|
|
||||||
{
|
|
||||||
if (halted)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return storage.getPiece(piece, off, len);
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
snark.stopTorrent();
|
|
||||||
_log.error("Error reading the storage for " + metainfo.getName(), ioe);
|
|
||||||
throw new RuntimeException("B0rked");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a peer has uploaded some bytes of a piece.
|
|
||||||
*/
|
|
||||||
public void uploaded(Peer peer, int size)
|
|
||||||
{
|
|
||||||
uploaded += size;
|
|
||||||
|
|
||||||
if (listener != null)
|
|
||||||
listener.peerChange(this, peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a peer has downloaded some bytes of a piece.
|
|
||||||
*/
|
|
||||||
public void downloaded(Peer peer, int size)
|
|
||||||
{
|
|
||||||
downloaded += size;
|
|
||||||
|
|
||||||
if (listener != null)
|
|
||||||
listener.peerChange(this, peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns false if the piece is no good (according to the hash).
|
|
||||||
* In that case the peer that supplied the piece should probably be
|
|
||||||
* blacklisted.
|
|
||||||
*/
|
|
||||||
public boolean gotPiece(Peer peer, int piece, byte[] bs)
|
|
||||||
{
|
|
||||||
if (halted) {
|
|
||||||
_log.info("Got while-halted piece " + piece + "/" + metainfo.getPieces() +" from " + peer + " for " + metainfo.getName());
|
|
||||||
return true; // We don't actually care anymore.
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized(wantedPieces)
|
|
||||||
{
|
|
||||||
Piece p = new Piece(piece);
|
|
||||||
if (!wantedPieces.contains(p))
|
|
||||||
{
|
|
||||||
_log.info("Got unwanted piece " + piece + "/" + metainfo.getPieces() +" from " + peer + " for " + metainfo.getName());
|
|
||||||
|
|
||||||
// No need to announce have piece to peers.
|
|
||||||
// Assume we got a good piece, we don't really care anymore.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (storage.putPiece(piece, bs))
|
|
||||||
{
|
|
||||||
_log.info("Got valid piece " + piece + "/" + metainfo.getPieces() +" from " + peer + " for " + metainfo.getName());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Oops. We didn't actually download this then... :(
|
|
||||||
downloaded -= metainfo.getPieceLength(piece);
|
|
||||||
_log.warn("Got BAD piece " + piece + "/" + metainfo.getPieces() + " from " + peer + " for " + metainfo.getName());
|
|
||||||
return false; // No need to announce BAD piece to peers.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
snark.stopTorrent();
|
|
||||||
_log.error("Error writing storage for " + metainfo.getName(), ioe);
|
|
||||||
throw new RuntimeException("B0rked");
|
|
||||||
}
|
|
||||||
wantedPieces.remove(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Announce to the world we have it!
|
|
||||||
// Disconnect from other seeders when we get the last piece
|
|
||||||
synchronized(peers)
|
|
||||||
{
|
|
||||||
List toDisconnect = new ArrayList();
|
|
||||||
Iterator it = peers.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Peer p = (Peer)it.next();
|
|
||||||
if (p.isConnected())
|
|
||||||
{
|
|
||||||
if (completed() && p.isCompleted())
|
|
||||||
toDisconnect.add(p);
|
|
||||||
else
|
|
||||||
p.have(piece);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it = toDisconnect.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Peer p = (Peer)it.next();
|
|
||||||
p.disconnect(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void gotChoke(Peer peer, boolean choke)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Got choke(" + choke + "): " + peer);
|
|
||||||
|
|
||||||
if (listener != null)
|
|
||||||
listener.peerChange(this, peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void gotInterest(Peer peer, boolean interest)
|
|
||||||
{
|
|
||||||
if (interest)
|
|
||||||
{
|
|
||||||
synchronized(peers)
|
|
||||||
{
|
|
||||||
if (uploaders < allowedUploaders())
|
|
||||||
{
|
|
||||||
if(peer.isChoking())
|
|
||||||
{
|
|
||||||
uploaders++;
|
|
||||||
peer.setChoking(false);
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Unchoke: " + peer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listener != null)
|
|
||||||
listener.peerChange(this, peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disconnected(Peer peer)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Disconnected " + peer, new Exception("Disconnected by"));
|
|
||||||
|
|
||||||
synchronized(peers)
|
|
||||||
{
|
|
||||||
// Make sure it is no longer in our lists
|
|
||||||
if (peers.remove(peer))
|
|
||||||
{
|
|
||||||
// Unchoke some random other peer
|
|
||||||
unchokePeer();
|
|
||||||
removePeerFromPieces(peer);
|
|
||||||
}
|
|
||||||
peerCount = peers.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listener != null)
|
|
||||||
listener.peerChange(this, peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Called when a peer is removed, to prevent it from being used in
|
|
||||||
* rarest-first calculations.
|
|
||||||
*/
|
|
||||||
public void removePeerFromPieces(Peer peer) {
|
|
||||||
synchronized(wantedPieces) {
|
|
||||||
for(Iterator iter = wantedPieces.iterator(); iter.hasNext(); ) {
|
|
||||||
Piece piece = (Piece)iter.next();
|
|
||||||
piece.removePeer(peer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Simple method to save a partial piece on peer disconnection
|
|
||||||
* and hopefully restart it later.
|
|
||||||
* Only one partial piece is saved at a time.
|
|
||||||
* Replace it if a new one is bigger or the old one is too old.
|
|
||||||
* Storage method is private so we can expand to save multiple partials
|
|
||||||
* if we wish.
|
|
||||||
*/
|
|
||||||
private Request savedRequest = null;
|
|
||||||
private long savedRequestTime = 0;
|
|
||||||
public void savePeerPartial(PeerState state)
|
|
||||||
{
|
|
||||||
if (halted)
|
|
||||||
return;
|
|
||||||
Request req = state.getPartialRequest();
|
|
||||||
if (req == null)
|
|
||||||
return;
|
|
||||||
if (savedRequest == null ||
|
|
||||||
req.off > savedRequest.off ||
|
|
||||||
System.currentTimeMillis() > savedRequestTime + (15 * 60 * 1000)) {
|
|
||||||
if (savedRequest == null || (req.piece != savedRequest.piece && req.off != savedRequest.off)) {
|
|
||||||
if (_log.shouldLog(Log.DEBUG)) {
|
|
||||||
_log.debug(" Saving orphaned partial piece " + req);
|
|
||||||
if (savedRequest != null)
|
|
||||||
_log.debug(" (Discarding previously saved orphan) " + savedRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
savedRequest = req;
|
|
||||||
savedRequestTime = System.currentTimeMillis();
|
|
||||||
} else {
|
|
||||||
if (req.piece != savedRequest.piece)
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug(" Discarding orphaned partial piece " + req);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return partial piece if it's still wanted and peer has it.
|
|
||||||
*/
|
|
||||||
public Request getPeerPartial(BitField havePieces) {
|
|
||||||
if (savedRequest == null)
|
|
||||||
return null;
|
|
||||||
if (! havePieces.get(savedRequest.piece)) {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Peer doesn't have orphaned piece " + savedRequest);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
synchronized(wantedPieces)
|
|
||||||
{
|
|
||||||
for(Iterator iter = wantedPieces.iterator(); iter.hasNext(); ) {
|
|
||||||
Piece piece = (Piece)iter.next();
|
|
||||||
if (piece.getId() == savedRequest.piece) {
|
|
||||||
Request req = savedRequest;
|
|
||||||
piece.setRequested(true);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Restoring orphaned partial piece " + req);
|
|
||||||
savedRequest = null;
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("We no longer want orphaned piece " + savedRequest);
|
|
||||||
savedRequest = null;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Clear the requested flag for a piece if the peer
|
|
||||||
** is the only one requesting it
|
|
||||||
*/
|
|
||||||
private void markUnrequestedIfOnlyOne(Peer peer, int piece)
|
|
||||||
{
|
|
||||||
// see if anybody else is requesting
|
|
||||||
synchronized (peers)
|
|
||||||
{
|
|
||||||
Iterator it = peers.iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
Peer p = (Peer)it.next();
|
|
||||||
if (p.equals(peer))
|
|
||||||
continue;
|
|
||||||
if (p.state == null)
|
|
||||||
continue;
|
|
||||||
int[] arr = p.state.getRequestedPieces();
|
|
||||||
for (int i = 0; arr[i] >= 0; i++)
|
|
||||||
if(arr[i] == piece) {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Another peer is requesting piece " + piece);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// nobody is, so mark unrequested
|
|
||||||
synchronized(wantedPieces)
|
|
||||||
{
|
|
||||||
Iterator it = wantedPieces.iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
Piece p = (Piece)it.next();
|
|
||||||
if (p.getId() == piece) {
|
|
||||||
p.setRequested(false);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Removing from request list piece " + piece);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Mark a peer's requested pieces unrequested when it is disconnected
|
|
||||||
** Once for each piece
|
|
||||||
** This is enough trouble, maybe would be easier just to regenerate
|
|
||||||
** the requested list from scratch instead.
|
|
||||||
*/
|
|
||||||
public void markUnrequested(Peer peer)
|
|
||||||
{
|
|
||||||
if (halted || peer.state == null)
|
|
||||||
return;
|
|
||||||
int[] arr = peer.state.getRequestedPieces();
|
|
||||||
for (int i = 0; arr[i] >= 0; i++)
|
|
||||||
markUnrequestedIfOnlyOne(peer, arr[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return number of allowed uploaders for this torrent.
|
|
||||||
** Check with Snark to see if we are over the total upload limit.
|
|
||||||
*/
|
|
||||||
public int allowedUploaders()
|
|
||||||
{
|
|
||||||
if (listener != null && listener.overUploadLimit(uploaders)) {
|
|
||||||
// if (_log.shouldLog(Log.DEBUG))
|
|
||||||
// _log.debug("Over limit, uploaders was: " + uploaders);
|
|
||||||
return uploaders - 1;
|
|
||||||
} else if (uploaders < MAX_UPLOADERS)
|
|
||||||
return uploaders + 1;
|
|
||||||
else
|
|
||||||
return MAX_UPLOADERS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean overUpBWLimit()
|
|
||||||
{
|
|
||||||
if (listener != null)
|
|
||||||
return listener.overUpBWLimit();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean overUpBWLimit(long total)
|
|
||||||
{
|
|
||||||
if (listener != null)
|
|
||||||
return listener.overUpBWLimit(total * 1000 / CHECK_PERIOD);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
|||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hmm, any guesses as to what this is? Used by the multitorrent functionality
|
|
||||||
* in the PeerAcceptor to pick the right PeerCoordinator to accept the con for.
|
|
||||||
* Each PeerCoordinator is added to the set from within the Snark (and removed
|
|
||||||
* from it there too)
|
|
||||||
*/
|
|
||||||
public class PeerCoordinatorSet {
|
|
||||||
private Set _coordinators;
|
|
||||||
|
|
||||||
public PeerCoordinatorSet() {
|
|
||||||
_coordinators = new HashSet();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterator iterator() {
|
|
||||||
synchronized (_coordinators) {
|
|
||||||
return new ArrayList(_coordinators).iterator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(PeerCoordinator coordinator) {
|
|
||||||
synchronized (_coordinators) {
|
|
||||||
_coordinators.add(coordinator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void remove(PeerCoordinator coordinator) {
|
|
||||||
synchronized (_coordinators) {
|
|
||||||
_coordinators.remove(coordinator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,210 +0,0 @@
|
|||||||
/* PeerID - All public information concerning a peer.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import net.i2p.data.Base64;
|
|
||||||
import net.i2p.data.Destination;
|
|
||||||
|
|
||||||
import org.klomp.snark.bencode.BDecoder;
|
|
||||||
import org.klomp.snark.bencode.BEValue;
|
|
||||||
import org.klomp.snark.bencode.InvalidBEncodingException;
|
|
||||||
|
|
||||||
public class PeerID implements Comparable
|
|
||||||
{
|
|
||||||
private final byte[] id;
|
|
||||||
private final Destination address;
|
|
||||||
private final int port;
|
|
||||||
|
|
||||||
private final int hash;
|
|
||||||
|
|
||||||
public PeerID(byte[] id, Destination address)
|
|
||||||
{
|
|
||||||
this.id = id;
|
|
||||||
this.address = address;
|
|
||||||
this.port = 6881;
|
|
||||||
|
|
||||||
hash = calculateHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a PeerID from a BDecoder.
|
|
||||||
*/
|
|
||||||
public PeerID(BDecoder be)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
this(be.bdecodeMap().getMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a PeerID from a Map containing BEncoded peer id, ip and
|
|
||||||
* port.
|
|
||||||
*/
|
|
||||||
public PeerID(Map m)
|
|
||||||
throws InvalidBEncodingException, UnknownHostException
|
|
||||||
{
|
|
||||||
BEValue bevalue = (BEValue)m.get("peer id");
|
|
||||||
if (bevalue == null)
|
|
||||||
throw new InvalidBEncodingException("peer id missing");
|
|
||||||
id = bevalue.getBytes();
|
|
||||||
|
|
||||||
bevalue = (BEValue)m.get("ip");
|
|
||||||
if (bevalue == null)
|
|
||||||
throw new InvalidBEncodingException("ip missing");
|
|
||||||
address = I2PSnarkUtil.getDestinationFromBase64(bevalue.getString());
|
|
||||||
if (address == null)
|
|
||||||
throw new InvalidBEncodingException("Invalid destination [" + bevalue.getString() + "]");
|
|
||||||
|
|
||||||
port = 6881;
|
|
||||||
|
|
||||||
hash = calculateHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getID()
|
|
||||||
{
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Destination getAddress()
|
|
||||||
{
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPort()
|
|
||||||
{
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int calculateHash()
|
|
||||||
{
|
|
||||||
int b = 0;
|
|
||||||
for (int i = 0; i < id.length; i++)
|
|
||||||
b ^= id[i];
|
|
||||||
return (b ^ address.hashCode()) ^ port;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The hash code of a PeerID is the exclusive or of all id bytes.
|
|
||||||
*/
|
|
||||||
public int hashCode()
|
|
||||||
{
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if and only if this peerID and the given peerID have
|
|
||||||
* the same 20 bytes as ID.
|
|
||||||
*/
|
|
||||||
public boolean sameID(PeerID pid)
|
|
||||||
{
|
|
||||||
boolean equal = true;
|
|
||||||
for (int i = 0; equal && i < id.length; i++)
|
|
||||||
equal = id[i] == pid.id[i];
|
|
||||||
return equal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Two PeerIDs are equal when they have the same id, address and port.
|
|
||||||
*/
|
|
||||||
public boolean equals(Object o)
|
|
||||||
{
|
|
||||||
if (o instanceof PeerID)
|
|
||||||
{
|
|
||||||
PeerID pid = (PeerID)o;
|
|
||||||
|
|
||||||
return port == pid.port
|
|
||||||
&& address.equals(pid.address)
|
|
||||||
&& sameID(pid);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares port, address and id.
|
|
||||||
*/
|
|
||||||
public int compareTo(Object o)
|
|
||||||
{
|
|
||||||
PeerID pid = (PeerID)o;
|
|
||||||
|
|
||||||
int result = port - pid.port;
|
|
||||||
if (result != 0)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
result = address.hashCode() - pid.address.hashCode();
|
|
||||||
if (result != 0)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
for (int i = 0; i < id.length; i++)
|
|
||||||
{
|
|
||||||
result = id[i] - pid.id[i];
|
|
||||||
if (result != 0)
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* should match what the bytemonsoon tracker reports on its web pages.
|
|
||||||
*/
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
int nonZero = 0;
|
|
||||||
for (int i = 0; i < id.length; i++) {
|
|
||||||
if (id[i] != 0) {
|
|
||||||
nonZero = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Base64.encode(id, nonZero, id.length-nonZero).substring(0,4) + "@" + address.toBase64().substring(0,6);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode an id as a hex encoded string and remove leading zeros.
|
|
||||||
*/
|
|
||||||
public static String idencode(byte[] bs)
|
|
||||||
{
|
|
||||||
boolean leading_zeros = true;
|
|
||||||
|
|
||||||
StringBuffer sb = new StringBuffer(bs.length*2);
|
|
||||||
for (int i = 0; i < bs.length; i++)
|
|
||||||
{
|
|
||||||
int c = bs[i] & 0xFF;
|
|
||||||
if (leading_zeros && c == 0)
|
|
||||||
continue;
|
|
||||||
else
|
|
||||||
leading_zeros = false;
|
|
||||||
|
|
||||||
if (c < 16)
|
|
||||||
sb.append('0');
|
|
||||||
sb.append(Integer.toHexString(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,172 +0,0 @@
|
|||||||
/* PeerListener - Interface for listening to peer events.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener for Peer events.
|
|
||||||
*/
|
|
||||||
public interface PeerListener
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Called when the connection to the peer has started and the
|
|
||||||
* handshake was successfull.
|
|
||||||
*
|
|
||||||
* @param peer the Peer that just got connected.
|
|
||||||
*/
|
|
||||||
void connected(Peer peer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the connection to the peer was terminated or the
|
|
||||||
* connection handshake failed.
|
|
||||||
*
|
|
||||||
* @param peer the Peer that just got disconnected.
|
|
||||||
*/
|
|
||||||
void disconnected(Peer peer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a choke message is received.
|
|
||||||
*
|
|
||||||
* @param peer the Peer that got the message.
|
|
||||||
* @param choke true when the peer got a choke message, false when
|
|
||||||
* the peer got an unchoke message.
|
|
||||||
*/
|
|
||||||
void gotChoke(Peer peer, boolean choke);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when an interested message is received.
|
|
||||||
*
|
|
||||||
* @param peer the Peer that got the message.
|
|
||||||
* @param interest true when the peer got a interested message, false when
|
|
||||||
* the peer got an uninterested message.
|
|
||||||
*/
|
|
||||||
void gotInterest(Peer peer, boolean interest);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a have piece message is received. If the method
|
|
||||||
* returns true and the peer has not yet received a interested
|
|
||||||
* message or we indicated earlier to be not interested then an
|
|
||||||
* interested message will be send.
|
|
||||||
*
|
|
||||||
* @param peer the Peer that got the message.
|
|
||||||
* @param piece the piece number that the per just got.
|
|
||||||
*
|
|
||||||
* @return true when it is a piece that we want, false if the piece is
|
|
||||||
* already known.
|
|
||||||
*/
|
|
||||||
boolean gotHave(Peer peer, int piece);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a bitmap message is received. If this method returns
|
|
||||||
* true a interested message will be send back to the peer.
|
|
||||||
*
|
|
||||||
* @param peer the Peer that got the message.
|
|
||||||
* @param bitfield a BitField containing the pieces that the other
|
|
||||||
* side has.
|
|
||||||
*
|
|
||||||
* @return true when the BitField contains pieces we want, false if
|
|
||||||
* the piece is already known.
|
|
||||||
*/
|
|
||||||
boolean gotBitField(Peer peer, BitField bitfield);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a piece is received from the peer. The piece must be
|
|
||||||
* requested by Peer.request() first. If this method returns false
|
|
||||||
* that means the Peer provided a corrupted piece and the connection
|
|
||||||
* will be closed.
|
|
||||||
*
|
|
||||||
* @param peer the Peer that got the piece.
|
|
||||||
* @param piece the piece number received.
|
|
||||||
* @param bs the byte array containing the piece.
|
|
||||||
*
|
|
||||||
* @return true when the bytes represent the piece, false otherwise.
|
|
||||||
*/
|
|
||||||
boolean gotPiece(Peer peer, int piece, byte[] bs);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the peer wants (part of) a piece from us. Only called
|
|
||||||
* when the peer is not choked by us (<code>peer.choke(false)</code>
|
|
||||||
* was called).
|
|
||||||
*
|
|
||||||
* @param peer the Peer that wants the piece.
|
|
||||||
* @param piece the piece number requested.
|
|
||||||
* @param off byte offset into the piece.
|
|
||||||
* @param len length of the chunk requested.
|
|
||||||
*
|
|
||||||
* @return a byte array containing the piece or null when the piece
|
|
||||||
* is not available (which is a protocol error).
|
|
||||||
*/
|
|
||||||
byte[] gotRequest(Peer peer, int piece, int off, int len);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a (partial) piece has been downloaded from the peer.
|
|
||||||
*
|
|
||||||
* @param peer the Peer from which size bytes where downloaded.
|
|
||||||
* @param size the number of bytes that where downloaded.
|
|
||||||
*/
|
|
||||||
void downloaded(Peer peer, int size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a (partial) piece has been uploaded to the peer.
|
|
||||||
*
|
|
||||||
* @param peer the Peer to which size bytes where uploaded.
|
|
||||||
* @param size the number of bytes that where uploaded.
|
|
||||||
*/
|
|
||||||
void uploaded(Peer peer, int size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when we are downloading from the peer and need to ask for
|
|
||||||
* a new piece. Might be called multiple times before
|
|
||||||
* <code>gotPiece()</code> is called.
|
|
||||||
*
|
|
||||||
* @param peer the Peer that will be asked to provide the piece.
|
|
||||||
* @param bitfield a BitField containing the pieces that the other
|
|
||||||
* side has.
|
|
||||||
*
|
|
||||||
* @return one of the pieces from the bitfield that we want or -1 if
|
|
||||||
* we are no longer interested in the peer.
|
|
||||||
*/
|
|
||||||
int wantPiece(Peer peer, BitField bitfield);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the peer has disconnected and the peer task may have a partially
|
|
||||||
* downloaded piece that the PeerCoordinator can save
|
|
||||||
*
|
|
||||||
* @param state the PeerState for the peer
|
|
||||||
*/
|
|
||||||
void savePeerPartial(PeerState state);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a peer has connected and there may be a partially
|
|
||||||
* downloaded piece that the coordinatorator can give the peer task
|
|
||||||
*
|
|
||||||
* @param havePieces the have-pieces bitmask for the peer
|
|
||||||
*
|
|
||||||
* @return request (contains the partial data and valid length)
|
|
||||||
*/
|
|
||||||
Request getPeerPartial(BitField havePieces);
|
|
||||||
|
|
||||||
/** Mark a peer's requested pieces unrequested when it is disconnected
|
|
||||||
* This prevents premature end game
|
|
||||||
*
|
|
||||||
* @param peer the peer that is disconnecting
|
|
||||||
*/
|
|
||||||
void markUnrequested(Peer peer);
|
|
||||||
}
|
|
@ -1,129 +0,0 @@
|
|||||||
/* PeerMonitorTasks - TimerTask that monitors the peers and total up/down speed
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TimerTask that monitors the peers and total up/download speeds.
|
|
||||||
* Works together with the main Snark class to report periodical statistics.
|
|
||||||
*/
|
|
||||||
class PeerMonitorTask extends TimerTask
|
|
||||||
{
|
|
||||||
final static long MONITOR_PERIOD = 10 * 1000; // Ten seconds.
|
|
||||||
private static final long KILOPERSECOND = 1024 * (MONITOR_PERIOD / 1000);
|
|
||||||
|
|
||||||
private final PeerCoordinator coordinator;
|
|
||||||
|
|
||||||
private long lastDownloaded = 0;
|
|
||||||
private long lastUploaded = 0;
|
|
||||||
|
|
||||||
PeerMonitorTask(PeerCoordinator coordinator)
|
|
||||||
{
|
|
||||||
this.coordinator = coordinator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
// Get some statistics
|
|
||||||
int peers = 0;
|
|
||||||
int uploaders = 0;
|
|
||||||
int downloaders = 0;
|
|
||||||
int interested = 0;
|
|
||||||
int interesting = 0;
|
|
||||||
int choking = 0;
|
|
||||||
int choked = 0;
|
|
||||||
|
|
||||||
synchronized(coordinator.peers)
|
|
||||||
{
|
|
||||||
Iterator it = coordinator.peers.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Peer peer = (Peer)it.next();
|
|
||||||
|
|
||||||
// Don't list dying peers
|
|
||||||
if (!peer.isConnected())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
peers++;
|
|
||||||
|
|
||||||
if (!peer.isChoking())
|
|
||||||
uploaders++;
|
|
||||||
if (!peer.isChoked() && peer.isInteresting())
|
|
||||||
downloaders++;
|
|
||||||
if (peer.isInterested())
|
|
||||||
interested++;
|
|
||||||
if (peer.isInteresting())
|
|
||||||
interesting++;
|
|
||||||
if (peer.isChoking())
|
|
||||||
choking++;
|
|
||||||
if (peer.isChoked())
|
|
||||||
choked++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print some statistics
|
|
||||||
long downloaded = coordinator.getDownloaded();
|
|
||||||
String totalDown;
|
|
||||||
if (downloaded >= 10 * 1024 * 1024)
|
|
||||||
totalDown = (downloaded / (1024 * 1024)) + "MB";
|
|
||||||
else
|
|
||||||
totalDown = (downloaded / 1024 )+ "KB";
|
|
||||||
long uploaded = coordinator.getUploaded();
|
|
||||||
String totalUp;
|
|
||||||
if (uploaded >= 10 * 1024 * 1024)
|
|
||||||
totalUp = (uploaded / (1024 * 1024)) + "MB";
|
|
||||||
else
|
|
||||||
totalUp = (uploaded / 1024) + "KB";
|
|
||||||
|
|
||||||
int needP = coordinator.storage.needed();
|
|
||||||
long needMB
|
|
||||||
= needP * coordinator.metainfo.getPieceLength(0) / (1024 * 1024);
|
|
||||||
int totalP = coordinator.metainfo.getPieces();
|
|
||||||
long totalMB = coordinator.metainfo.getTotalLength() / (1024 * 1024);
|
|
||||||
|
|
||||||
System.out.println();
|
|
||||||
System.out.println("Down: "
|
|
||||||
+ (downloaded - lastDownloaded) / KILOPERSECOND
|
|
||||||
+ "KB/s"
|
|
||||||
+ " (" + totalDown + ")"
|
|
||||||
+ " Up: "
|
|
||||||
+ (uploaded - lastUploaded) / KILOPERSECOND
|
|
||||||
+ "KB/s"
|
|
||||||
+ " (" + totalUp + ")"
|
|
||||||
+ " Need " + needP
|
|
||||||
+ " (" + needMB + "MB)"
|
|
||||||
+ " of " + totalP
|
|
||||||
+ " (" + totalMB + "MB)"
|
|
||||||
+ " pieces");
|
|
||||||
System.out.println(peers + ": Download #" + downloaders
|
|
||||||
+ " Upload #" + uploaders
|
|
||||||
+ " Interested #" + interested
|
|
||||||
+ " Interesting #" + interesting
|
|
||||||
+ " Choking #" + choking
|
|
||||||
+ " Choked #" + choked);
|
|
||||||
System.out.println();
|
|
||||||
|
|
||||||
lastDownloaded = downloaded;
|
|
||||||
lastUploaded = uploaded;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,608 +0,0 @@
|
|||||||
/* PeerState - Keeps track of the Peer state through connection callbacks.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
class PeerState
|
|
||||||
{
|
|
||||||
private Log _log = new Log(PeerState.class);
|
|
||||||
final Peer peer;
|
|
||||||
final PeerListener listener;
|
|
||||||
final MetaInfo metainfo;
|
|
||||||
|
|
||||||
// Interesting and choking describes whether we are interested in or
|
|
||||||
// are choking the other side.
|
|
||||||
boolean interesting = false;
|
|
||||||
boolean choking = true;
|
|
||||||
|
|
||||||
// Interested and choked describes whether the other side is
|
|
||||||
// interested in us or choked us.
|
|
||||||
boolean interested = false;
|
|
||||||
boolean choked = true;
|
|
||||||
|
|
||||||
// Package local for use by Peer.
|
|
||||||
long downloaded;
|
|
||||||
long uploaded;
|
|
||||||
|
|
||||||
BitField bitfield;
|
|
||||||
|
|
||||||
// Package local for use by Peer.
|
|
||||||
final PeerConnectionIn in;
|
|
||||||
final PeerConnectionOut out;
|
|
||||||
|
|
||||||
// Outstanding request
|
|
||||||
private final List outstandingRequests = new ArrayList();
|
|
||||||
private Request lastRequest = null;
|
|
||||||
|
|
||||||
// If we have te resend outstanding requests (true after we got choked).
|
|
||||||
private boolean resend = false;
|
|
||||||
|
|
||||||
private final static int MAX_PIPELINE = 2; // this is for outbound requests
|
|
||||||
private final static int MAX_PIPELINE_BYTES = 128*1024; // this is for inbound requests
|
|
||||||
public final static int PARTSIZE = 32*1024; // Snark was 16K, i2p-bt uses 64KB
|
|
||||||
private final static int MAX_PARTSIZE = 64*1024; // Don't let anybody request more than this
|
|
||||||
|
|
||||||
PeerState(Peer peer, PeerListener listener, MetaInfo metainfo,
|
|
||||||
PeerConnectionIn in, PeerConnectionOut out)
|
|
||||||
{
|
|
||||||
this.peer = peer;
|
|
||||||
this.listener = listener;
|
|
||||||
this.metainfo = metainfo;
|
|
||||||
|
|
||||||
this.in = in;
|
|
||||||
this.out = out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE Methods that inspect or change the state synchronize (on this).
|
|
||||||
|
|
||||||
void keepAliveMessage()
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug(peer + " rcv alive");
|
|
||||||
/* XXX - ignored */
|
|
||||||
}
|
|
||||||
|
|
||||||
void chokeMessage(boolean choke)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug(peer + " rcv " + (choke ? "" : "un") + "choked");
|
|
||||||
|
|
||||||
choked = choke;
|
|
||||||
if (choked)
|
|
||||||
resend = true;
|
|
||||||
|
|
||||||
listener.gotChoke(peer, choke);
|
|
||||||
|
|
||||||
if (!choked && interesting)
|
|
||||||
request();
|
|
||||||
}
|
|
||||||
|
|
||||||
void interestedMessage(boolean interest)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug(peer + " rcv " + (interest ? "" : "un")
|
|
||||||
+ "interested");
|
|
||||||
interested = interest;
|
|
||||||
listener.gotInterest(peer, interest);
|
|
||||||
}
|
|
||||||
|
|
||||||
void haveMessage(int piece)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug(peer + " rcv have(" + piece + ")");
|
|
||||||
// Sanity check
|
|
||||||
if (piece < 0 || piece >= metainfo.getPieces())
|
|
||||||
{
|
|
||||||
// XXX disconnect?
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Got strange 'have: " + piece + "' message from " + peer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized(this)
|
|
||||||
{
|
|
||||||
// Can happen if the other side never send a bitfield message.
|
|
||||||
if (bitfield == null)
|
|
||||||
bitfield = new BitField(metainfo.getPieces());
|
|
||||||
|
|
||||||
bitfield.set(piece);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listener.gotHave(peer, piece))
|
|
||||||
setInteresting(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bitfieldMessage(byte[] bitmap)
|
|
||||||
{
|
|
||||||
synchronized(this)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug(peer + " rcv bitfield");
|
|
||||||
if (bitfield != null)
|
|
||||||
{
|
|
||||||
// XXX - Be liberal in what you except?
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Got unexpected bitfield message from " + peer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX - Check for weird bitfield and disconnect?
|
|
||||||
bitfield = new BitField(bitmap, metainfo.getPieces());
|
|
||||||
}
|
|
||||||
setInteresting(listener.gotBitField(peer, bitfield));
|
|
||||||
}
|
|
||||||
|
|
||||||
void requestMessage(int piece, int begin, int length)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug(peer + " rcv request("
|
|
||||||
+ piece + ", " + begin + ", " + length + ") ");
|
|
||||||
if (choking)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Request received, but choking " + peer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
if (piece < 0
|
|
||||||
|| piece >= metainfo.getPieces()
|
|
||||||
|| begin < 0
|
|
||||||
|| begin > metainfo.getPieceLength(piece)
|
|
||||||
|| length <= 0
|
|
||||||
|| length > MAX_PARTSIZE)
|
|
||||||
{
|
|
||||||
// XXX - Protocol error -> disconnect?
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Got strange 'request: " + piece
|
|
||||||
+ ", " + begin
|
|
||||||
+ ", " + length
|
|
||||||
+ "' message from " + peer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limit total pipelined requests to MAX_PIPELINE bytes
|
|
||||||
// to conserve memory and prevent DOS
|
|
||||||
if (out.queuedBytes() + length > MAX_PIPELINE_BYTES)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Discarding request over pipeline limit from " + peer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] pieceBytes = listener.gotRequest(peer, piece, begin, length);
|
|
||||||
if (pieceBytes == null)
|
|
||||||
{
|
|
||||||
// XXX - Protocol error-> diconnect?
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Got request for unknown piece: " + piece);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// More sanity checks
|
|
||||||
if (length != pieceBytes.length)
|
|
||||||
{
|
|
||||||
// XXX - Protocol error-> disconnect?
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Got out of range 'request: " + piece
|
|
||||||
+ ", " + begin
|
|
||||||
+ ", " + length
|
|
||||||
+ "' message from " + peer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Sending (" + piece + ", " + begin + ", "
|
|
||||||
+ length + ")" + " to " + peer);
|
|
||||||
out.sendPiece(piece, begin, length, pieceBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when some bytes have left the outgoing connection.
|
|
||||||
* XXX - Should indicate whether it was a real piece or overhead.
|
|
||||||
*/
|
|
||||||
void uploaded(int size)
|
|
||||||
{
|
|
||||||
uploaded += size;
|
|
||||||
listener.uploaded(peer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is used to flag that we have to back up from the firstOutstandingRequest
|
|
||||||
// when calculating how far we've gotten
|
|
||||||
Request pendingRequest = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a partial piece request has been handled by
|
|
||||||
* PeerConnectionIn.
|
|
||||||
*/
|
|
||||||
void pieceMessage(Request req)
|
|
||||||
{
|
|
||||||
int size = req.len;
|
|
||||||
downloaded += size;
|
|
||||||
listener.downloaded(peer, size);
|
|
||||||
|
|
||||||
pendingRequest = null;
|
|
||||||
|
|
||||||
// Last chunk needed for this piece?
|
|
||||||
if (getFirstOutstandingRequest(req.piece) == -1)
|
|
||||||
{
|
|
||||||
if (listener.gotPiece(peer, req.piece, req.bs))
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Got " + req.piece + ": " + peer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Got BAD " + req.piece + " from " + peer);
|
|
||||||
// XXX ARGH What now !?!
|
|
||||||
downloaded = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized private int getFirstOutstandingRequest(int piece)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < outstandingRequests.size(); i++)
|
|
||||||
if (((Request)outstandingRequests.get(i)).piece == piece)
|
|
||||||
return i;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a piece message is being processed by the incoming
|
|
||||||
* connection. Returns null when there was no such request. It also
|
|
||||||
* requeues/sends requests when it thinks that they must have been
|
|
||||||
* lost.
|
|
||||||
*/
|
|
||||||
Request getOutstandingRequest(int piece, int begin, int length)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("getChunk("
|
|
||||||
+ piece + "," + begin + "," + length + ") "
|
|
||||||
+ peer);
|
|
||||||
|
|
||||||
int r = getFirstOutstandingRequest(piece);
|
|
||||||
|
|
||||||
// Unrequested piece number?
|
|
||||||
if (r == -1)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Unrequested 'piece: " + piece + ", "
|
|
||||||
+ begin + ", " + length + "' received from "
|
|
||||||
+ peer);
|
|
||||||
downloaded = 0; // XXX - punishment?
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup the correct piece chunk request from the list.
|
|
||||||
Request req;
|
|
||||||
synchronized(this)
|
|
||||||
{
|
|
||||||
req = (Request)outstandingRequests.get(r);
|
|
||||||
while (req.piece == piece && req.off != begin
|
|
||||||
&& r < outstandingRequests.size() - 1)
|
|
||||||
{
|
|
||||||
r++;
|
|
||||||
req = (Request)outstandingRequests.get(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Something wrong?
|
|
||||||
if (req.piece != piece || req.off != begin || req.len != length)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Unrequested or unneeded 'piece: "
|
|
||||||
+ piece + ", "
|
|
||||||
+ begin + ", "
|
|
||||||
+ length + "' received from "
|
|
||||||
+ peer);
|
|
||||||
downloaded = 0; // XXX - punishment?
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Report missing requests.
|
|
||||||
if (r != 0)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Some requests dropped, got " + req
|
|
||||||
+ ", wanted for peer: " + peer);
|
|
||||||
for (int i = 0; i < r; i++)
|
|
||||||
{
|
|
||||||
Request dropReq = (Request)outstandingRequests.remove(0);
|
|
||||||
outstandingRequests.add(dropReq);
|
|
||||||
if (!choked)
|
|
||||||
out.sendRequest(dropReq);
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("dropped " + dropReq + " with peer " + peer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
outstandingRequests.remove(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request more if necessary to keep the pipeline filled.
|
|
||||||
addRequest();
|
|
||||||
|
|
||||||
pendingRequest = req;
|
|
||||||
return req;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// get longest partial piece
|
|
||||||
Request getPartialRequest()
|
|
||||||
{
|
|
||||||
Request req = null;
|
|
||||||
for (int i = 0; i < outstandingRequests.size(); i++) {
|
|
||||||
Request r1 = (Request)outstandingRequests.get(i);
|
|
||||||
int j = getFirstOutstandingRequest(r1.piece);
|
|
||||||
if (j == -1)
|
|
||||||
continue;
|
|
||||||
Request r2 = (Request)outstandingRequests.get(j);
|
|
||||||
if (r2.off > 0 && ((req == null) || (r2.off > req.off)))
|
|
||||||
req = r2;
|
|
||||||
}
|
|
||||||
if (pendingRequest != null && req != null && pendingRequest.off < req.off) {
|
|
||||||
if (pendingRequest.off != 0)
|
|
||||||
req = pendingRequest;
|
|
||||||
else
|
|
||||||
req = null;
|
|
||||||
}
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
// return array of pieces terminated by -1
|
|
||||||
// remove most duplicates
|
|
||||||
// but still could be some duplicates, not guaranteed
|
|
||||||
int[] getRequestedPieces()
|
|
||||||
{
|
|
||||||
int size = outstandingRequests.size();
|
|
||||||
int[] arr = new int[size+2];
|
|
||||||
int pc = -1;
|
|
||||||
int pos = 0;
|
|
||||||
if (pendingRequest != null) {
|
|
||||||
pc = pendingRequest.piece;
|
|
||||||
arr[pos++] = pc;
|
|
||||||
}
|
|
||||||
Request req = null;
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
Request r1 = (Request)outstandingRequests.get(i);
|
|
||||||
if (pc != r1.piece) {
|
|
||||||
pc = r1.piece;
|
|
||||||
arr[pos++] = pc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arr[pos] = -1;
|
|
||||||
return(arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cancelMessage(int piece, int begin, int length)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Got cancel message ("
|
|
||||||
+ piece + ", " + begin + ", " + length + ")");
|
|
||||||
out.cancelRequest(piece, begin, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void unknownMessage(int type, byte[] bs)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Warning: Ignoring unknown message type: " + type
|
|
||||||
+ " length: " + bs.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void havePiece(int piece)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Tell " + peer + " havePiece(" + piece + ")");
|
|
||||||
|
|
||||||
synchronized(this)
|
|
||||||
{
|
|
||||||
// Tell the other side that we are no longer interested in any of
|
|
||||||
// the outstanding requests for this piece.
|
|
||||||
if (lastRequest != null && lastRequest.piece == piece)
|
|
||||||
lastRequest = null;
|
|
||||||
|
|
||||||
Iterator it = outstandingRequests.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Request req = (Request)it.next();
|
|
||||||
if (req.piece == piece)
|
|
||||||
{
|
|
||||||
it.remove();
|
|
||||||
// Send cancel even when we are choked to make sure that it is
|
|
||||||
// really never ever send.
|
|
||||||
out.sendCancel(req);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tell the other side that we really have this piece.
|
|
||||||
out.sendHave(piece);
|
|
||||||
|
|
||||||
// Request something else if necessary.
|
|
||||||
addRequest();
|
|
||||||
|
|
||||||
synchronized(this)
|
|
||||||
{
|
|
||||||
// Is the peer still interesting?
|
|
||||||
if (lastRequest == null)
|
|
||||||
setInteresting(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Starts or resumes requesting pieces.
|
|
||||||
private void request()
|
|
||||||
{
|
|
||||||
// Are there outstanding requests that have to be resend?
|
|
||||||
if (resend)
|
|
||||||
{
|
|
||||||
synchronized (this) {
|
|
||||||
out.sendRequests(outstandingRequests);
|
|
||||||
}
|
|
||||||
resend = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add/Send some more requests if necessary.
|
|
||||||
addRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new request to the outstanding requests list.
|
|
||||||
*/
|
|
||||||
synchronized private void addRequest()
|
|
||||||
{
|
|
||||||
boolean more_pieces = true;
|
|
||||||
while (more_pieces)
|
|
||||||
{
|
|
||||||
more_pieces = outstandingRequests.size() < MAX_PIPELINE;
|
|
||||||
// We want something and we don't have outstanding requests?
|
|
||||||
if (more_pieces && lastRequest == null)
|
|
||||||
more_pieces = requestNextPiece();
|
|
||||||
else if (more_pieces) // We want something
|
|
||||||
{
|
|
||||||
int pieceLength;
|
|
||||||
boolean isLastChunk;
|
|
||||||
pieceLength = metainfo.getPieceLength(lastRequest.piece);
|
|
||||||
isLastChunk = lastRequest.off + lastRequest.len == pieceLength;
|
|
||||||
|
|
||||||
// Last part of a piece?
|
|
||||||
if (isLastChunk)
|
|
||||||
more_pieces = requestNextPiece();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int nextPiece = lastRequest.piece;
|
|
||||||
int nextBegin = lastRequest.off + PARTSIZE;
|
|
||||||
byte[] bs = lastRequest.bs;
|
|
||||||
int maxLength = pieceLength - nextBegin;
|
|
||||||
int nextLength = maxLength > PARTSIZE ? PARTSIZE
|
|
||||||
: maxLength;
|
|
||||||
Request req
|
|
||||||
= new Request(nextPiece, bs, nextBegin, nextLength);
|
|
||||||
outstandingRequests.add(req);
|
|
||||||
if (!choked)
|
|
||||||
out.sendRequest(req);
|
|
||||||
lastRequest = req;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug(peer + " requests " + outstandingRequests);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Starts requesting first chunk of next piece. Returns true if
|
|
||||||
// something has been added to the requests, false otherwise.
|
|
||||||
private boolean requestNextPiece()
|
|
||||||
{
|
|
||||||
// Check that we already know what the other side has.
|
|
||||||
if (bitfield != null)
|
|
||||||
{
|
|
||||||
// Check for adopting an orphaned partial piece
|
|
||||||
Request r = listener.getPeerPartial(bitfield);
|
|
||||||
if (r != null) {
|
|
||||||
// Check that r not already in outstandingRequests
|
|
||||||
int[] arr = getRequestedPieces();
|
|
||||||
boolean found = false;
|
|
||||||
for (int i = 0; arr[i] >= 0; i++) {
|
|
||||||
if (arr[i] == r.piece) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
outstandingRequests.add(r);
|
|
||||||
if (!choked)
|
|
||||||
out.sendRequest(r);
|
|
||||||
lastRequest = r;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int nextPiece = listener.wantPiece(peer, bitfield);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug(peer + " want piece " + nextPiece);
|
|
||||||
if (nextPiece != -1
|
|
||||||
&& (lastRequest == null || lastRequest.piece != nextPiece))
|
|
||||||
{
|
|
||||||
int piece_length = metainfo.getPieceLength(nextPiece);
|
|
||||||
//Catch a common place for OOMs esp. on 1MB pieces
|
|
||||||
byte[] bs;
|
|
||||||
try {
|
|
||||||
bs = new byte[piece_length];
|
|
||||||
} catch (OutOfMemoryError oom) {
|
|
||||||
_log.warn("Out of memory, can't request piece " + nextPiece, oom);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = Math.min(piece_length, PARTSIZE);
|
|
||||||
Request req = new Request(nextPiece, bs, 0, length);
|
|
||||||
outstandingRequests.add(req);
|
|
||||||
if (!choked)
|
|
||||||
out.sendRequest(req);
|
|
||||||
lastRequest = req;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void setInteresting(boolean interest)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug(peer + " setInteresting(" + interest + ")");
|
|
||||||
|
|
||||||
if (interest != interesting)
|
|
||||||
{
|
|
||||||
interesting = interest;
|
|
||||||
out.sendInterest(interest);
|
|
||||||
|
|
||||||
if (interesting && !choked)
|
|
||||||
request();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void setChoking(boolean choke)
|
|
||||||
{
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug(peer + " setChoking(" + choke + ")");
|
|
||||||
|
|
||||||
if (choking != choke)
|
|
||||||
{
|
|
||||||
choking = choke;
|
|
||||||
out.sendChoke(choke);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void keepAlive()
|
|
||||||
{
|
|
||||||
out.sendAlive();
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void retransmitRequests()
|
|
||||||
{
|
|
||||||
if (interesting && !choked)
|
|
||||||
out.retransmitRequests(outstandingRequests);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class Piece implements Comparable {
|
|
||||||
|
|
||||||
private int id;
|
|
||||||
private Set peers;
|
|
||||||
private boolean requested;
|
|
||||||
|
|
||||||
public Piece(int id) {
|
|
||||||
this.id = id;
|
|
||||||
this.peers = Collections.synchronizedSet(new HashSet());
|
|
||||||
this.requested = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int compareTo(Object o) throws ClassCastException {
|
|
||||||
return this.peers.size() - ((Piece)o).peers.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (o == null) return false;
|
|
||||||
try {
|
|
||||||
return this.id == ((Piece)o).id;
|
|
||||||
} catch (ClassCastException cce) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getId() { return this.id; }
|
|
||||||
public Set getPeers() { return this.peers; }
|
|
||||||
public boolean addPeer(Peer peer) { return this.peers.add(peer.getPeerID()); }
|
|
||||||
public boolean removePeer(Peer peer) { return this.peers.remove(peer.getPeerID()); }
|
|
||||||
public boolean isRequested() { return this.requested; }
|
|
||||||
public void setRequested(boolean requested) { this.requested = requested; }
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return String.valueOf(id);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
/* Request - Holds all information needed for a (partial) piece request.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds all information needed for a partial piece request.
|
|
||||||
*/
|
|
||||||
class Request
|
|
||||||
{
|
|
||||||
final int piece;
|
|
||||||
final byte[] bs;
|
|
||||||
final int off;
|
|
||||||
final int len;
|
|
||||||
long sendTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new Request.
|
|
||||||
*
|
|
||||||
* @param piece Piece number requested.
|
|
||||||
* @param bs byte array where response should be stored.
|
|
||||||
* @param off the offset in the array.
|
|
||||||
* @param len the number of bytes requested.
|
|
||||||
*/
|
|
||||||
Request(int piece, byte[] bs, int off, int len)
|
|
||||||
{
|
|
||||||
this.piece = piece;
|
|
||||||
this.bs = bs;
|
|
||||||
this.off = off;
|
|
||||||
this.len = len;
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
if (piece < 0 || off < 0 || len <= 0 || off + len > bs.length)
|
|
||||||
throw new IndexOutOfBoundsException("Illegal Request " + toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public int hashCode()
|
|
||||||
{
|
|
||||||
return piece ^ off ^ len;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals(Object o)
|
|
||||||
{
|
|
||||||
if (o instanceof Request)
|
|
||||||
{
|
|
||||||
Request req = (Request)o;
|
|
||||||
return req.piece == piece && req.off == off && req.len == len;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "(" + piece + "," + off + "," + len + ")";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
/* ShutdownListener - Callback for end of shutdown sequence
|
|
||||||
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for end of shutdown sequence.
|
|
||||||
*/
|
|
||||||
interface ShutdownListener
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Called when the SnarkShutdown hook has finished shutting down all
|
|
||||||
* subcomponents.
|
|
||||||
*/
|
|
||||||
void shutdown();
|
|
||||||
}
|
|
@ -1,878 +0,0 @@
|
|||||||
/* Snark - Main snark program startup class.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
|
||||||
import net.i2p.router.client.ClientManagerFacadeImpl;
|
|
||||||
import net.i2p.client.streaming.I2PServerSocket;
|
|
||||||
import net.i2p.data.Destination;
|
|
||||||
import net.i2p.util.I2PThread;
|
|
||||||
|
|
||||||
import org.klomp.snark.bencode.BDecoder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main Snark program startup class.
|
|
||||||
*
|
|
||||||
* @author Mark Wielaard (mark@klomp.org)
|
|
||||||
*/
|
|
||||||
public class Snark
|
|
||||||
implements StorageListener, CoordinatorListener, ShutdownListener
|
|
||||||
{
|
|
||||||
private final static int MIN_PORT = 6881;
|
|
||||||
private final static int MAX_PORT = 6889;
|
|
||||||
|
|
||||||
// Error messages (non-fatal)
|
|
||||||
public final static int ERROR = 1;
|
|
||||||
|
|
||||||
// Warning messages
|
|
||||||
public final static int WARNING = 2;
|
|
||||||
|
|
||||||
// Notices (peer level)
|
|
||||||
public final static int NOTICE = 3;
|
|
||||||
|
|
||||||
// Info messages (protocol policy level)
|
|
||||||
public final static int INFO = 4;
|
|
||||||
|
|
||||||
// Debug info (protocol level)
|
|
||||||
public final static int DEBUG = 5;
|
|
||||||
|
|
||||||
// Very low level stuff (network level)
|
|
||||||
public final static int ALL = 6;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* What level of debug info to show.
|
|
||||||
*/
|
|
||||||
//public static int debug = NOTICE;
|
|
||||||
|
|
||||||
// Whether or not to ask the user for commands while sharing
|
|
||||||
private static boolean command_interpreter = true;
|
|
||||||
|
|
||||||
private static final String newline = System.getProperty("line.separator");
|
|
||||||
|
|
||||||
private static final String copyright =
|
|
||||||
"The Hunting of the Snark Project - Copyright (C) 2003 Mark J. Wielaard"
|
|
||||||
+ newline + newline
|
|
||||||
+ "Snark comes with ABSOLUTELY NO WARRANTY. This is free software, and"
|
|
||||||
+ newline
|
|
||||||
+ "you are welcome to redistribute it under certain conditions; read the"
|
|
||||||
+ newline
|
|
||||||
+ "COPYING file for details." + newline + newline
|
|
||||||
+ "This is the I2P port, allowing anonymous bittorrent (http://www.i2p.net/)" + newline
|
|
||||||
+ "It will not work with normal torrents, so don't even try ;)";
|
|
||||||
|
|
||||||
private static final String usage =
|
|
||||||
"Press return for help. Type \"quit\" and return to stop.";
|
|
||||||
private static final String help =
|
|
||||||
"Commands: 'info', 'list', 'quit'.";
|
|
||||||
|
|
||||||
// String indicating main activity
|
|
||||||
String activity = "Not started";
|
|
||||||
|
|
||||||
private static class OOMListener implements I2PThread.OOMEventListener {
|
|
||||||
public void outOfMemory(OutOfMemoryError err) {
|
|
||||||
try {
|
|
||||||
err.printStackTrace();
|
|
||||||
System.out.println("OOM in the snark" + err);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
System.out.println("OOM in the OOM");
|
|
||||||
}
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args)
|
|
||||||
{
|
|
||||||
System.out.println(copyright);
|
|
||||||
System.out.println();
|
|
||||||
|
|
||||||
if ( (args.length > 0) && ("--config".equals(args[0])) ) {
|
|
||||||
I2PThread.addOOMEventListener(new OOMListener());
|
|
||||||
SnarkManager sm = SnarkManager.instance();
|
|
||||||
if (args.length > 1)
|
|
||||||
sm.loadConfig(args[1]);
|
|
||||||
System.out.println("Running in multitorrent mode");
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
synchronized (sm) {
|
|
||||||
sm.wait();
|
|
||||||
}
|
|
||||||
} catch (InterruptedException ie) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse debug, share/ip and torrent file options.
|
|
||||||
Snark snark = parseArguments(args);
|
|
||||||
|
|
||||||
SnarkShutdown snarkhook
|
|
||||||
= new SnarkShutdown(snark.storage,
|
|
||||||
snark.coordinator,
|
|
||||||
snark.acceptor,
|
|
||||||
snark.trackerclient,
|
|
||||||
snark);
|
|
||||||
//Runtime.getRuntime().addShutdownHook(snarkhook);
|
|
||||||
|
|
||||||
Timer timer = new Timer(true);
|
|
||||||
TimerTask monitor = new PeerMonitorTask(snark.coordinator);
|
|
||||||
timer.schedule(monitor,
|
|
||||||
PeerMonitorTask.MONITOR_PERIOD,
|
|
||||||
PeerMonitorTask.MONITOR_PERIOD);
|
|
||||||
|
|
||||||
// Start command interpreter
|
|
||||||
if (Snark.command_interpreter)
|
|
||||||
{
|
|
||||||
boolean quit = false;
|
|
||||||
|
|
||||||
System.out.println();
|
|
||||||
System.out.println(usage);
|
|
||||||
System.out.println();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
BufferedReader br = new BufferedReader
|
|
||||||
(new InputStreamReader(System.in));
|
|
||||||
String line = br.readLine();
|
|
||||||
while(!quit && line != null)
|
|
||||||
{
|
|
||||||
line = line.toLowerCase();
|
|
||||||
if ("quit".equals(line))
|
|
||||||
quit = true;
|
|
||||||
else if ("list".equals(line))
|
|
||||||
{
|
|
||||||
synchronized(snark.coordinator.peers)
|
|
||||||
{
|
|
||||||
System.out.println(snark.coordinator.peers.size()
|
|
||||||
+ " peers -"
|
|
||||||
+ " (i)nterested,"
|
|
||||||
+ " (I)nteresting,"
|
|
||||||
+ " (c)hoking,"
|
|
||||||
+ " (C)hoked:");
|
|
||||||
Iterator it = snark.coordinator.peers.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Peer peer = (Peer)it.next();
|
|
||||||
System.out.println(peer);
|
|
||||||
System.out.println("\ti: " + peer.isInterested()
|
|
||||||
+ " I: " + peer.isInteresting()
|
|
||||||
+ " c: " + peer.isChoking()
|
|
||||||
+ " C: " + peer.isChoked());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ("info".equals(line))
|
|
||||||
{
|
|
||||||
System.out.println("Name: " + snark.meta.getName());
|
|
||||||
System.out.println("Torrent: " + snark.torrent);
|
|
||||||
System.out.println("Tracker: " + snark.meta.getAnnounce());
|
|
||||||
List files = snark.meta.getFiles();
|
|
||||||
System.out.println("Files: "
|
|
||||||
+ ((files == null) ? 1 : files.size()));
|
|
||||||
System.out.println("Pieces: " + snark.meta.getPieces());
|
|
||||||
System.out.println("Piece size: "
|
|
||||||
+ snark.meta.getPieceLength(0) / 1024
|
|
||||||
+ " KB");
|
|
||||||
System.out.println("Total size: "
|
|
||||||
+ snark.meta.getTotalLength() / (1024 * 1024)
|
|
||||||
+ " MB");
|
|
||||||
}
|
|
||||||
else if ("".equals(line) || "help".equals(line))
|
|
||||||
{
|
|
||||||
System.out.println(usage);
|
|
||||||
System.out.println(help);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
System.out.println("Unknown command: " + line);
|
|
||||||
System.out.println(usage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!quit)
|
|
||||||
{
|
|
||||||
System.out.println();
|
|
||||||
line = br.readLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(IOException ioe)
|
|
||||||
{
|
|
||||||
System.out.println("ERROR while reading stdin: " + ioe);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explicit shutdown.
|
|
||||||
Runtime.getRuntime().removeShutdownHook(snarkhook);
|
|
||||||
snarkhook.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String PROP_MAX_CONNECTIONS = "i2psnark.maxConnections";
|
|
||||||
public String torrent;
|
|
||||||
public MetaInfo meta;
|
|
||||||
public Storage storage;
|
|
||||||
public PeerCoordinator coordinator;
|
|
||||||
public ConnectionAcceptor acceptor;
|
|
||||||
public TrackerClient trackerclient;
|
|
||||||
public String rootDataDir = ".";
|
|
||||||
public CompleteListener completeListener;
|
|
||||||
public boolean stopped;
|
|
||||||
byte[] id;
|
|
||||||
public I2PSnarkUtil _util;
|
|
||||||
private PeerCoordinatorSet _peerCoordinatorSet;
|
|
||||||
|
|
||||||
/** from main() via parseArguments() single torrent */
|
|
||||||
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 */
|
|
||||||
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);
|
|
||||||
String host = opts.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_HOST);
|
|
||||||
int port = 0;
|
|
||||||
String s = opts.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_PORT);
|
|
||||||
if (s != null) {
|
|
||||||
try {
|
|
||||||
port = Integer.parseInt(s);
|
|
||||||
} catch (NumberFormatException nfe) {}
|
|
||||||
}
|
|
||||||
_util.setI2CPConfig(host, port, opts);
|
|
||||||
s = opts.getProperty(SnarkManager.PROP_UPBW_MAX);
|
|
||||||
if (s != null) {
|
|
||||||
try {
|
|
||||||
int v = Integer.parseInt(s);
|
|
||||||
_util.setMaxUpBW(v);
|
|
||||||
} catch (NumberFormatException nfe) {}
|
|
||||||
}
|
|
||||||
s = opts.getProperty(PROP_MAX_CONNECTIONS);
|
|
||||||
if (s != null) {
|
|
||||||
try {
|
|
||||||
int v = Integer.parseInt(s);
|
|
||||||
_util.setMaxConnections(v);
|
|
||||||
} catch (NumberFormatException nfe) {}
|
|
||||||
}
|
|
||||||
if (start)
|
|
||||||
this.startTorrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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)
|
|
||||||
{
|
|
||||||
if (slistener == null)
|
|
||||||
slistener = this;
|
|
||||||
|
|
||||||
completeListener = complistener;
|
|
||||||
_util = util;
|
|
||||||
_peerCoordinatorSet = peerCoordinatorSet;
|
|
||||||
acceptor = connectionAcceptor;
|
|
||||||
|
|
||||||
this.torrent = torrent;
|
|
||||||
this.rootDataDir = rootDir;
|
|
||||||
|
|
||||||
stopped = true;
|
|
||||||
activity = "Network setup";
|
|
||||||
|
|
||||||
// "Taking Three as the subject to reason about--
|
|
||||||
// A convenient number to state--
|
|
||||||
// We add Seven, and Ten, and then multiply out
|
|
||||||
// By One Thousand diminished by Eight.
|
|
||||||
//
|
|
||||||
// "The result we proceed to divide, as you see,
|
|
||||||
// By Nine Hundred and Ninety Two:
|
|
||||||
// Then subtract Seventeen, and the answer must be
|
|
||||||
// Exactly and perfectly true.
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
byte snark = (((3 + 7 + 10) * (1000 - 8)) / 992) - 17;
|
|
||||||
id = new byte[20];
|
|
||||||
Random random = new Random();
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 9; i++)
|
|
||||||
id[i] = 0;
|
|
||||||
id[i++] = snark;
|
|
||||||
id[i++] = snark;
|
|
||||||
id[i++] = snark;
|
|
||||||
while (i < 20)
|
|
||||||
id[i++] = (byte)random.nextInt(256);
|
|
||||||
|
|
||||||
debug("My peer id: " + PeerID.idencode(id), Snark.INFO);
|
|
||||||
|
|
||||||
int port;
|
|
||||||
IOException lastException = null;
|
|
||||||
/*
|
|
||||||
* Don't start a tunnel if the torrent isn't going to be started.
|
|
||||||
* If we are starting,
|
|
||||||
* startTorrent() will force a connect.
|
|
||||||
*
|
|
||||||
boolean ok = util.connect();
|
|
||||||
if (!ok) fatal("Unable to connect to I2P");
|
|
||||||
I2PServerSocket serversocket = util.getServerSocket();
|
|
||||||
if (serversocket == null)
|
|
||||||
fatal("Unable to listen for I2P connections");
|
|
||||||
else {
|
|
||||||
Destination d = serversocket.getManager().getSession().getMyDestination();
|
|
||||||
debug("Listening on I2P destination " + d.toBase64() + " / " + d.calculateHash().toBase64(), NOTICE);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Figure out what the torrent argument represents.
|
|
||||||
meta = null;
|
|
||||||
File f = null;
|
|
||||||
InputStream in = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
f = new File(torrent);
|
|
||||||
if (f.exists())
|
|
||||||
in = new FileInputStream(f);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
activity = "Getting torrent";
|
|
||||||
File torrentFile = _util.get(torrent, 3);
|
|
||||||
if (torrentFile == null) {
|
|
||||||
fatal("Unable to fetch " + torrent);
|
|
||||||
if (false) return; // never reached - fatal(..) throws
|
|
||||||
} else {
|
|
||||||
torrentFile.deleteOnExit();
|
|
||||||
in = new FileInputStream(torrentFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
meta = new MetaInfo(new BDecoder(in));
|
|
||||||
}
|
|
||||||
catch(IOException ioe)
|
|
||||||
{
|
|
||||||
// OK, so it wasn't a torrent metainfo file.
|
|
||||||
if (f != null && f.exists())
|
|
||||||
if (ip == null)
|
|
||||||
fatal("'" + torrent + "' exists,"
|
|
||||||
+ " but is not a valid torrent metainfo file."
|
|
||||||
+ System.getProperty("line.separator"), ioe);
|
|
||||||
else
|
|
||||||
fatal("I2PSnark does not support creating and tracking a torrent at the moment");
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
// Try to create a new metainfo file
|
|
||||||
debug
|
|
||||||
("Trying to create metainfo torrent for '" + torrent + "'",
|
|
||||||
NOTICE);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
activity = "Creating torrent";
|
|
||||||
storage = new Storage
|
|
||||||
(f, "http://" + ip + ":" + port + "/announce", slistener);
|
|
||||||
storage.create();
|
|
||||||
meta = storage.getMetaInfo();
|
|
||||||
}
|
|
||||||
catch (IOException ioe2)
|
|
||||||
{
|
|
||||||
fatal("Could not create torrent for '" + torrent + "'", ioe2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
else
|
|
||||||
fatal("Cannot open '" + torrent + "'", ioe);
|
|
||||||
} finally {
|
|
||||||
if (in != null)
|
|
||||||
try { in.close(); } catch (IOException ioe) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug(meta.toString(), INFO);
|
|
||||||
|
|
||||||
// When the metainfo torrent was created from an existing file/dir
|
|
||||||
// it already exists.
|
|
||||||
if (storage == null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
activity = "Checking storage";
|
|
||||||
storage = new Storage(_util, meta, slistener);
|
|
||||||
if (completeListener != null) {
|
|
||||||
storage.check(rootDataDir,
|
|
||||||
completeListener.getSavedTorrentTime(this),
|
|
||||||
completeListener.getSavedTorrentBitField(this));
|
|
||||||
} else {
|
|
||||||
storage.check(rootDataDir);
|
|
||||||
}
|
|
||||||
// have to figure out when to reopen
|
|
||||||
// if (!start)
|
|
||||||
// storage.close();
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
try { storage.close(); } catch (IOException ioee) {
|
|
||||||
ioee.printStackTrace();
|
|
||||||
}
|
|
||||||
fatal("Could not create storage", ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* see comment above
|
|
||||||
*
|
|
||||||
activity = "Collecting pieces";
|
|
||||||
coordinator = new PeerCoordinator(id, meta, storage, clistener, this);
|
|
||||||
PeerCoordinatorSet set = PeerCoordinatorSet.instance();
|
|
||||||
set.add(coordinator);
|
|
||||||
ConnectionAcceptor acceptor = ConnectionAcceptor.instance();
|
|
||||||
acceptor.startAccepting(set, serversocket);
|
|
||||||
trackerclient = new TrackerClient(meta, coordinator);
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (start)
|
|
||||||
startTorrent();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Start up contacting peers and querying the tracker
|
|
||||||
*/
|
|
||||||
public void startTorrent() {
|
|
||||||
boolean ok = _util.connect();
|
|
||||||
if (!ok) fatal("Unable to connect to I2P");
|
|
||||||
if (coordinator == null) {
|
|
||||||
I2PServerSocket serversocket = _util.getServerSocket();
|
|
||||||
if (serversocket == null)
|
|
||||||
fatal("Unable to listen for I2P connections");
|
|
||||||
else {
|
|
||||||
Destination d = serversocket.getManager().getSession().getMyDestination();
|
|
||||||
debug("Listening on I2P destination " + d.toBase64() + " / " + d.calculateHash().toBase64(), NOTICE);
|
|
||||||
}
|
|
||||||
debug("Starting PeerCoordinator, ConnectionAcceptor, and TrackerClient", NOTICE);
|
|
||||||
activity = "Collecting pieces";
|
|
||||||
coordinator = new PeerCoordinator(_util, id, meta, storage, this, this);
|
|
||||||
if (_peerCoordinatorSet != null) {
|
|
||||||
// multitorrent
|
|
||||||
_peerCoordinatorSet.add(coordinator);
|
|
||||||
if (acceptor != null) {
|
|
||||||
acceptor.startAccepting(_peerCoordinatorSet, serversocket);
|
|
||||||
} else {
|
|
||||||
// error
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// single torrent
|
|
||||||
acceptor = new ConnectionAcceptor(_util, serversocket, new PeerAcceptor(coordinator));
|
|
||||||
}
|
|
||||||
trackerclient = new TrackerClient(_util, meta, coordinator);
|
|
||||||
}
|
|
||||||
|
|
||||||
stopped = false;
|
|
||||||
boolean coordinatorChanged = false;
|
|
||||||
if (coordinator.halted()) {
|
|
||||||
// ok, we have already started and stopped, but the coordinator seems a bit annoying to
|
|
||||||
// restart safely, so lets build a new one to replace the old
|
|
||||||
if (_peerCoordinatorSet != null)
|
|
||||||
_peerCoordinatorSet.remove(coordinator);
|
|
||||||
PeerCoordinator newCoord = new PeerCoordinator(_util, coordinator.getID(), coordinator.getMetaInfo(),
|
|
||||||
coordinator.getStorage(), coordinator.getListener(), this);
|
|
||||||
if (_peerCoordinatorSet != null)
|
|
||||||
_peerCoordinatorSet.add(newCoord);
|
|
||||||
coordinator = newCoord;
|
|
||||||
coordinatorChanged = true;
|
|
||||||
}
|
|
||||||
if (!trackerclient.started() && !coordinatorChanged) {
|
|
||||||
trackerclient.start();
|
|
||||||
} else if (trackerclient.halted() || coordinatorChanged) {
|
|
||||||
try
|
|
||||||
{
|
|
||||||
storage.reopen(rootDataDir);
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
try { storage.close(); } catch (IOException ioee) {
|
|
||||||
ioee.printStackTrace();
|
|
||||||
}
|
|
||||||
fatal("Could not reopen storage", ioe);
|
|
||||||
}
|
|
||||||
TrackerClient newClient = new TrackerClient(_util, coordinator.getMetaInfo(), coordinator);
|
|
||||||
if (!trackerclient.halted())
|
|
||||||
trackerclient.halt();
|
|
||||||
trackerclient = newClient;
|
|
||||||
trackerclient.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Stop contacting the tracker and talking with peers
|
|
||||||
*/
|
|
||||||
public void stopTorrent() {
|
|
||||||
stopped = true;
|
|
||||||
TrackerClient tc = trackerclient;
|
|
||||||
if (tc != null)
|
|
||||||
tc.halt();
|
|
||||||
PeerCoordinator pc = coordinator;
|
|
||||||
if (pc != null)
|
|
||||||
pc.halt();
|
|
||||||
Storage st = storage;
|
|
||||||
if (st != null) {
|
|
||||||
boolean changed = storage.changed;
|
|
||||||
try {
|
|
||||||
storage.close();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
System.out.println("Error closing " + torrent);
|
|
||||||
ioe.printStackTrace();
|
|
||||||
}
|
|
||||||
if (changed && completeListener != null)
|
|
||||||
completeListener.updateStatus(this);
|
|
||||||
}
|
|
||||||
if (pc != null && _peerCoordinatorSet != null)
|
|
||||||
_peerCoordinatorSet.remove(pc);
|
|
||||||
if (_peerCoordinatorSet == null)
|
|
||||||
_util.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
static Snark parseArguments(String[] args)
|
|
||||||
{
|
|
||||||
return parseArguments(args, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets debug, ip and torrent variables then creates a Snark
|
|
||||||
* instance. Calls usage(), which terminates the program, if
|
|
||||||
* non-valid argument list. The given listeners will be
|
|
||||||
* passed to all components that take one.
|
|
||||||
*/
|
|
||||||
static Snark parseArguments(String[] args,
|
|
||||||
StorageListener slistener,
|
|
||||||
CoordinatorListener clistener)
|
|
||||||
{
|
|
||||||
int user_port = -1;
|
|
||||||
String ip = null;
|
|
||||||
String torrent = null;
|
|
||||||
|
|
||||||
I2PSnarkUtil util = new I2PSnarkUtil(I2PAppContext.getGlobalContext());
|
|
||||||
boolean configured = util.configured();
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
while (i < args.length)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
if (args[i].equals("--debug"))
|
|
||||||
{
|
|
||||||
debug = INFO;
|
|
||||||
i++;
|
|
||||||
|
|
||||||
// Try if there is an level argument.
|
|
||||||
if (i < args.length)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int level = Integer.parseInt(args[i]);
|
|
||||||
if (level >= 0)
|
|
||||||
{
|
|
||||||
debug = level;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (NumberFormatException nfe) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else */ if (args[i].equals("--port"))
|
|
||||||
{
|
|
||||||
if (args.length - 1 < i + 1)
|
|
||||||
usage("--port needs port number to listen on");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
user_port = Integer.parseInt(args[i + 1]);
|
|
||||||
}
|
|
||||||
catch (NumberFormatException nfe)
|
|
||||||
{
|
|
||||||
usage("--port argument must be a number (" + nfe + ")");
|
|
||||||
}
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
else if (args[i].equals("--no-commands"))
|
|
||||||
{
|
|
||||||
command_interpreter = false;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
else if (args[i].equals("--eepproxy"))
|
|
||||||
{
|
|
||||||
String proxyHost = args[i+1];
|
|
||||||
String proxyPort = args[i+2];
|
|
||||||
if (!configured)
|
|
||||||
util.setProxy(proxyHost, Integer.parseInt(proxyPort));
|
|
||||||
i += 3;
|
|
||||||
}
|
|
||||||
else if (args[i].equals("--i2cp"))
|
|
||||||
{
|
|
||||||
String i2cpHost = args[i+1];
|
|
||||||
String i2cpPort = args[i+2];
|
|
||||||
Properties opts = null;
|
|
||||||
if (i+3 < args.length) {
|
|
||||||
if (!args[i+3].startsWith("--")) {
|
|
||||||
opts = new Properties();
|
|
||||||
StringTokenizer tok = new StringTokenizer(args[i+3], " \t");
|
|
||||||
while (tok.hasMoreTokens()) {
|
|
||||||
String str = tok.nextToken();
|
|
||||||
int split = str.indexOf('=');
|
|
||||||
if (split > 0) {
|
|
||||||
opts.setProperty(str.substring(0, split), str.substring(split+1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!configured)
|
|
||||||
util.setI2CPConfig(i2cpHost, Integer.parseInt(i2cpPort), opts);
|
|
||||||
i += 3 + (opts != null ? 1 : 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
torrent = args[i];
|
|
||||||
i++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (torrent == null || i != args.length)
|
|
||||||
if (torrent != null && torrent.startsWith("-"))
|
|
||||||
usage("Unknow option '" + torrent + "'.");
|
|
||||||
else
|
|
||||||
usage("Need exactly one <url>, <file> or <dir>.");
|
|
||||||
|
|
||||||
return new Snark(util, torrent, ip, user_port, slistener, clistener);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void usage(String s)
|
|
||||||
{
|
|
||||||
System.out.println("snark: " + s);
|
|
||||||
usage();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void usage()
|
|
||||||
{
|
|
||||||
System.out.println
|
|
||||||
("Usage: snark [--debug [level]] [--no-commands] [--port <port>]");
|
|
||||||
System.out.println
|
|
||||||
(" [--eepproxy hostname portnum]");
|
|
||||||
System.out.println
|
|
||||||
(" [--i2cp routerHost routerPort ['name=val name=val name=val']]");
|
|
||||||
System.out.println
|
|
||||||
(" (<url>|<file>)");
|
|
||||||
System.out.println
|
|
||||||
(" --debug\tShows some extra info and stacktraces");
|
|
||||||
System.out.println
|
|
||||||
(" level\tHow much debug details to show");
|
|
||||||
System.out.println
|
|
||||||
(" \t(defaults to "
|
|
||||||
+ NOTICE + ", with --debug to "
|
|
||||||
+ INFO + ", highest level is "
|
|
||||||
+ ALL + ").");
|
|
||||||
System.out.println
|
|
||||||
(" --no-commands\tDon't read interactive commands or show usage info.");
|
|
||||||
System.out.println
|
|
||||||
(" --port\tThe port to listen on for incomming connections");
|
|
||||||
System.out.println
|
|
||||||
(" \t(if not given defaults to first free port between "
|
|
||||||
+ MIN_PORT + "-" + MAX_PORT + ").");
|
|
||||||
System.out.println
|
|
||||||
(" --share\tStart torrent tracker on <ip> address or <host> name.");
|
|
||||||
System.out.println
|
|
||||||
(" --eepproxy\thttp proxy to use (default of 127.0.0.1 port 4444)");
|
|
||||||
System.out.println
|
|
||||||
(" --i2cp\tlocation of your I2P router (default of 127.0.0.1 port 7654)");
|
|
||||||
System.out.println
|
|
||||||
(" \toptional settings may be included, such as");
|
|
||||||
System.out.println
|
|
||||||
(" \tinbound.length=2 outbound.length=2 inbound.lengthVariance=-1 ");
|
|
||||||
System.out.println
|
|
||||||
(" <url> \tURL pointing to .torrent metainfo file to download/share.");
|
|
||||||
System.out.println
|
|
||||||
(" <file> \tEither a local .torrent metainfo file to download");
|
|
||||||
System.out.println
|
|
||||||
(" \tor (with --share) a file to share.");
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Aborts program abnormally.
|
|
||||||
*/
|
|
||||||
public void fatal(String s)
|
|
||||||
{
|
|
||||||
fatal(s, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Aborts program abnormally.
|
|
||||||
*/
|
|
||||||
public void fatal(String s, Throwable t)
|
|
||||||
{
|
|
||||||
_util.debug(s, ERROR, t);
|
|
||||||
//System.err.println("snark: " + s + ((t == null) ? "" : (": " + t)));
|
|
||||||
//if (debug >= INFO && t != null)
|
|
||||||
// t.printStackTrace();
|
|
||||||
stopTorrent();
|
|
||||||
throw new RuntimeException("die bart die");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show debug info if debug is true.
|
|
||||||
*/
|
|
||||||
private void debug(String s, int level)
|
|
||||||
{
|
|
||||||
_util.debug(s, level, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** coordinatorListener */
|
|
||||||
public void peerChange(PeerCoordinator coordinator, Peer peer)
|
|
||||||
{
|
|
||||||
// System.out.println(peer.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean allocating = false;
|
|
||||||
public void storageCreateFile(Storage storage, String name, long length)
|
|
||||||
{
|
|
||||||
//if (allocating)
|
|
||||||
// System.out.println(); // Done with last file.
|
|
||||||
|
|
||||||
//System.out.print("Creating file '" + name
|
|
||||||
// + "' of length " + length + ": ");
|
|
||||||
allocating = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// How much storage space has been allocated
|
|
||||||
private long allocated = 0;
|
|
||||||
|
|
||||||
public void storageAllocated(Storage storage, long length)
|
|
||||||
{
|
|
||||||
allocating = true;
|
|
||||||
//System.out.print(".");
|
|
||||||
allocated += length;
|
|
||||||
//if (allocated == meta.getTotalLength())
|
|
||||||
// System.out.println(); // We have all the disk space we need.
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean allChecked = false;
|
|
||||||
boolean checking = false;
|
|
||||||
boolean prechecking = true;
|
|
||||||
public void storageChecked(Storage storage, int num, boolean checked)
|
|
||||||
{
|
|
||||||
allocating = false;
|
|
||||||
if (!allChecked && !checking)
|
|
||||||
{
|
|
||||||
// Use the MetaInfo from the storage since our own might not
|
|
||||||
// yet be setup correctly.
|
|
||||||
//MetaInfo meta = storage.getMetaInfo();
|
|
||||||
//if (meta != null)
|
|
||||||
// System.out.print("Checking existing "
|
|
||||||
// + meta.getPieces()
|
|
||||||
// + " pieces: ");
|
|
||||||
checking = true;
|
|
||||||
}
|
|
||||||
if (!checking)
|
|
||||||
debug("Got " + (checked ? "" : "BAD ") + "piece: " + num,
|
|
||||||
Snark.INFO);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void storageAllChecked(Storage storage)
|
|
||||||
{
|
|
||||||
//if (checking)
|
|
||||||
// System.out.println();
|
|
||||||
|
|
||||||
allChecked = true;
|
|
||||||
checking = false;
|
|
||||||
if (storage.changed && completeListener != null)
|
|
||||||
completeListener.updateStatus(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void storageCompleted(Storage storage)
|
|
||||||
{
|
|
||||||
debug("Completely received " + torrent, Snark.INFO);
|
|
||||||
//storage.close();
|
|
||||||
//System.out.println("Completely received: " + torrent);
|
|
||||||
if (completeListener != null)
|
|
||||||
completeListener.torrentComplete(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWantedPieces(Storage storage)
|
|
||||||
{
|
|
||||||
coordinator.setWantedPieces();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void shutdown()
|
|
||||||
{
|
|
||||||
// Should not be necessary since all non-deamon threads should
|
|
||||||
// have died. But in reality this does not always happen.
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface CompleteListener {
|
|
||||||
public void torrentComplete(Snark snark);
|
|
||||||
public void updateStatus(Snark snark);
|
|
||||||
// not really listeners but the easiest way to get back to an optional SnarkManager
|
|
||||||
public long getSavedTorrentTime(Snark snark);
|
|
||||||
public BitField getSavedTorrentBitField(Snark snark);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Maintain a configurable total uploader cap
|
|
||||||
* coordinatorListener
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
int totalUploaders = 0;
|
|
||||||
for (Iterator iter = _peerCoordinatorSet.iterator(); iter.hasNext(); ) {
|
|
||||||
PeerCoordinator c = (PeerCoordinator)iter.next();
|
|
||||||
if (!c.halted())
|
|
||||||
totalUploaders += c.uploaders;
|
|
||||||
}
|
|
||||||
int limit = _util.getMaxUploaders();
|
|
||||||
// debug("Total uploaders: " + totalUploaders + " Limit: " + limit, Snark.DEBUG);
|
|
||||||
return totalUploaders > limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean overUpBWLimit() {
|
|
||||||
if (_peerCoordinatorSet == null)
|
|
||||||
return false;
|
|
||||||
long total = 0;
|
|
||||||
for (Iterator iter = _peerCoordinatorSet.iterator(); iter.hasNext(); ) {
|
|
||||||
PeerCoordinator c = (PeerCoordinator)iter.next();
|
|
||||||
if (!c.halted())
|
|
||||||
total += c.getCurrentUploadRate();
|
|
||||||
}
|
|
||||||
long limit = 1024l * _util.getMaxUpBW();
|
|
||||||
debug("Total up bw: " + total + " Limit: " + limit, Snark.WARNING);
|
|
||||||
return total > limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean overUpBWLimit(long total) {
|
|
||||||
long limit = 1024l * _util.getMaxUpBW();
|
|
||||||
return total > limit;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,749 +0,0 @@
|
|||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FilenameFilter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
|
||||||
import net.i2p.data.Base64;
|
|
||||||
import net.i2p.data.DataHelper;
|
|
||||||
import net.i2p.router.RouterContext;
|
|
||||||
import net.i2p.util.I2PAppThread;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manage multiple snarks
|
|
||||||
*/
|
|
||||||
public class SnarkManager implements Snark.CompleteListener {
|
|
||||||
private static SnarkManager _instance = new SnarkManager();
|
|
||||||
public static SnarkManager instance() { return _instance; }
|
|
||||||
|
|
||||||
/** map of (canonical) filename to Snark instance (unsynchronized) */
|
|
||||||
private Map _snarks;
|
|
||||||
private Object _addSnarkLock;
|
|
||||||
private String _configFile = "i2psnark.config";
|
|
||||||
private Properties _config;
|
|
||||||
private I2PAppContext _context;
|
|
||||||
private Log _log;
|
|
||||||
private List _messages;
|
|
||||||
private I2PSnarkUtil _util;
|
|
||||||
private PeerCoordinatorSet _peerCoordinatorSet;
|
|
||||||
private ConnectionAcceptor _connectionAcceptor;
|
|
||||||
|
|
||||||
public static final String PROP_I2CP_HOST = "i2psnark.i2cpHost";
|
|
||||||
public static final String PROP_I2CP_PORT = "i2psnark.i2cpPort";
|
|
||||||
public static final String PROP_I2CP_OPTS = "i2psnark.i2cpOptions";
|
|
||||||
public static final String PROP_EEP_HOST = "i2psnark.eepHost";
|
|
||||||
public static final String PROP_EEP_PORT = "i2psnark.eepPort";
|
|
||||||
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_AUTO_START = "i2snark.autoStart"; // oops
|
|
||||||
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:///";
|
|
||||||
|
|
||||||
public static final int MIN_UP_BW = 2;
|
|
||||||
public static final int DEFAULT_MAX_UP_BW = 10;
|
|
||||||
|
|
||||||
private SnarkManager() {
|
|
||||||
_snarks = new HashMap();
|
|
||||||
_addSnarkLock = new Object();
|
|
||||||
_context = I2PAppContext.getGlobalContext();
|
|
||||||
_log = _context.logManager().getLog(SnarkManager.class);
|
|
||||||
_messages = new ArrayList(16);
|
|
||||||
_util = new I2PSnarkUtil(_context);
|
|
||||||
loadConfig(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Caller _must_ call loadConfig(file) before this if setting new values
|
|
||||||
* for i2cp host/port or i2psnark.dir
|
|
||||||
*/
|
|
||||||
public void start() {
|
|
||||||
_peerCoordinatorSet = new PeerCoordinatorSet();
|
|
||||||
_connectionAcceptor = new ConnectionAcceptor(_util);
|
|
||||||
int minutes = getStartupDelayMinutes();
|
|
||||||
_messages.add("Adding torrents in " + minutes + (minutes == 1 ? " minute" : " minutes"));
|
|
||||||
I2PAppThread monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor");
|
|
||||||
monitor.setDaemon(true);
|
|
||||||
monitor.start();
|
|
||||||
if (_context instanceof RouterContext)
|
|
||||||
((RouterContext)_context).router().addShutdownTask(new SnarkManagerShutdown());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** hook to I2PSnarkUtil for the servlet */
|
|
||||||
public I2PSnarkUtil util() { return _util; }
|
|
||||||
|
|
||||||
private static final int MAX_MESSAGES = 5;
|
|
||||||
public void addMessage(String message) {
|
|
||||||
synchronized (_messages) {
|
|
||||||
_messages.add(message);
|
|
||||||
while (_messages.size() > MAX_MESSAGES)
|
|
||||||
_messages.remove(0);
|
|
||||||
}
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("MSG: " + message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** newest last */
|
|
||||||
public List getMessages() {
|
|
||||||
synchronized (_messages) {
|
|
||||||
return new ArrayList(_messages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean shouldAutoStart() {
|
|
||||||
return Boolean.valueOf(_config.getProperty(PROP_AUTO_START, DEFAULT_AUTO_START+"")).booleanValue();
|
|
||||||
}
|
|
||||||
public String linkPrefix() {
|
|
||||||
return _config.getProperty(PROP_LINK_PREFIX, DEFAULT_LINK_PREFIX + getDataDir().getAbsolutePath() + File.separatorChar);
|
|
||||||
}
|
|
||||||
private int getStartupDelayMinutes() { return 3; }
|
|
||||||
public File getDataDir() {
|
|
||||||
String dir = _config.getProperty(PROP_DIR);
|
|
||||||
if ( (dir == null) || (dir.trim().length() <= 0) )
|
|
||||||
dir = "i2psnark";
|
|
||||||
return new File(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** null to set initial defaults */
|
|
||||||
public void loadConfig(String filename) {
|
|
||||||
if (_config == null)
|
|
||||||
_config = new Properties();
|
|
||||||
if (filename != null) {
|
|
||||||
_configFile = filename;
|
|
||||||
File cfg = new File(filename);
|
|
||||||
if (cfg.exists()) {
|
|
||||||
try {
|
|
||||||
DataHelper.loadProps(_config, cfg);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.error("Error loading I2PSnark config '" + filename + "'", ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// now add sane defaults
|
|
||||||
if (!_config.containsKey(PROP_I2CP_HOST))
|
|
||||||
_config.setProperty(PROP_I2CP_HOST, "localhost");
|
|
||||||
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");
|
|
||||||
if (!_config.containsKey(PROP_EEP_HOST))
|
|
||||||
_config.setProperty(PROP_EEP_HOST, "localhost");
|
|
||||||
if (!_config.containsKey(PROP_EEP_PORT))
|
|
||||||
_config.setProperty(PROP_EEP_PORT, "4444");
|
|
||||||
if (!_config.containsKey(PROP_UPLOADERS_TOTAL))
|
|
||||||
_config.setProperty(PROP_UPLOADERS_TOTAL, "" + Snark.MAX_TOTAL_UPLOADERS);
|
|
||||||
if (!_config.containsKey(PROP_UPBW_MAX)) {
|
|
||||||
try {
|
|
||||||
if (_context instanceof RouterContext)
|
|
||||||
_config.setProperty(PROP_UPBW_MAX, "" + (((RouterContext)_context).bandwidthLimiter().getOutboundKBytesPerSecond() / 2));
|
|
||||||
else
|
|
||||||
_config.setProperty(PROP_UPBW_MAX, "" + DEFAULT_MAX_UP_BW);
|
|
||||||
} catch (NoClassDefFoundError ncdfe) {
|
|
||||||
_config.setProperty(PROP_UPBW_MAX, "" + DEFAULT_MAX_UP_BW);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!_config.containsKey(PROP_DIR))
|
|
||||||
_config.setProperty(PROP_DIR, "i2psnark");
|
|
||||||
if (!_config.containsKey(PROP_AUTO_START))
|
|
||||||
_config.setProperty(PROP_AUTO_START, DEFAULT_AUTO_START);
|
|
||||||
updateConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateConfig() {
|
|
||||||
String i2cpHost = _config.getProperty(PROP_I2CP_HOST);
|
|
||||||
int i2cpPort = getInt(PROP_I2CP_PORT, 7654);
|
|
||||||
String opts = _config.getProperty(PROP_I2CP_OPTS);
|
|
||||||
Map i2cpOpts = new HashMap();
|
|
||||||
if (opts != null) {
|
|
||||||
StringTokenizer tok = new StringTokenizer(opts, " ");
|
|
||||||
while (tok.hasMoreTokens()) {
|
|
||||||
String pair = tok.nextToken();
|
|
||||||
int split = pair.indexOf('=');
|
|
||||||
if (split > 0)
|
|
||||||
i2cpOpts.put(pair.substring(0, split), pair.substring(split+1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i2cpHost != null) {
|
|
||||||
_util.setI2CPConfig(i2cpHost, i2cpPort, i2cpOpts);
|
|
||||||
_log.debug("Configuring with I2CP options " + i2cpOpts);
|
|
||||||
}
|
|
||||||
//I2PSnarkUtil.instance().setI2CPConfig("66.111.51.110", 7654, new Properties());
|
|
||||||
String eepHost = _config.getProperty(PROP_EEP_HOST);
|
|
||||||
int eepPort = getInt(PROP_EEP_PORT, 4444);
|
|
||||||
if (eepHost != null)
|
|
||||||
_util.setProxy(eepHost, eepPort);
|
|
||||||
_util.setMaxUploaders(getInt(PROP_UPLOADERS_TOTAL, Snark.MAX_TOTAL_UPLOADERS));
|
|
||||||
_util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW));
|
|
||||||
getDataDir().mkdirs();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getInt(String prop, int defaultVal) {
|
|
||||||
String p = _config.getProperty(prop);
|
|
||||||
try {
|
|
||||||
if ( (p != null) && (p.trim().length() > 0) )
|
|
||||||
return Integer.parseInt(p.trim());
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return defaultVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateConfig(String dataDir, boolean autoStart, String seedPct, String eepHost,
|
|
||||||
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
|
|
||||||
String upLimit, String upBW, boolean useOpenTrackers, String openTrackers) {
|
|
||||||
boolean changed = false;
|
|
||||||
if (eepHost != null) {
|
|
||||||
int port = _util.getEepProxyPort();
|
|
||||||
try { port = Integer.parseInt(eepPort); } catch (NumberFormatException nfe) {}
|
|
||||||
String host = _util.getEepProxyHost();
|
|
||||||
if ( (eepHost.trim().length() > 0) && (port > 0) &&
|
|
||||||
((!host.equals(eepHost) || (port != _util.getEepProxyPort()) )) ) {
|
|
||||||
_util.setProxy(eepHost, port);
|
|
||||||
changed = true;
|
|
||||||
_config.setProperty(PROP_EEP_HOST, eepHost);
|
|
||||||
_config.setProperty(PROP_EEP_PORT, eepPort+"");
|
|
||||||
addMessage("EepProxy location changed to " + eepHost + ":" + port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (upLimit != null) {
|
|
||||||
int limit = _util.getMaxUploaders();
|
|
||||||
try { limit = Integer.parseInt(upLimit); } catch (NumberFormatException nfe) {}
|
|
||||||
if ( limit != _util.getMaxUploaders()) {
|
|
||||||
if ( limit >= Snark.MIN_TOTAL_UPLOADERS ) {
|
|
||||||
_util.setMaxUploaders(limit);
|
|
||||||
changed = true;
|
|
||||||
_config.setProperty(PROP_UPLOADERS_TOTAL, "" + limit);
|
|
||||||
addMessage("Total uploaders limit changed to " + limit);
|
|
||||||
} else {
|
|
||||||
addMessage("Minimum total uploaders limit is " + Snark.MIN_TOTAL_UPLOADERS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (upBW != null) {
|
|
||||||
int limit = _util.getMaxUpBW();
|
|
||||||
try { limit = Integer.parseInt(upBW); } catch (NumberFormatException nfe) {}
|
|
||||||
if ( limit != _util.getMaxUpBW()) {
|
|
||||||
if ( limit >= MIN_UP_BW ) {
|
|
||||||
_util.setMaxUpBW(limit);
|
|
||||||
changed = true;
|
|
||||||
_config.setProperty(PROP_UPBW_MAX, "" + limit);
|
|
||||||
addMessage("Up BW limit changed to " + limit + "KBps");
|
|
||||||
} else {
|
|
||||||
addMessage("Minimum Up BW limit is " + MIN_UP_BW + "KBps");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i2cpHost != null) {
|
|
||||||
int oldI2CPPort = _util.getI2CPPort();
|
|
||||||
String oldI2CPHost = _util.getI2CPHost();
|
|
||||||
int port = oldI2CPPort;
|
|
||||||
try { port = Integer.parseInt(i2cpPort); } catch (NumberFormatException nfe) {}
|
|
||||||
String host = oldI2CPHost;
|
|
||||||
Map opts = new HashMap();
|
|
||||||
if (i2cpOpts == null) i2cpOpts = "";
|
|
||||||
StringTokenizer tok = new StringTokenizer(i2cpOpts, " \t\n");
|
|
||||||
while (tok.hasMoreTokens()) {
|
|
||||||
String pair = tok.nextToken();
|
|
||||||
int split = pair.indexOf('=');
|
|
||||||
if (split > 0)
|
|
||||||
opts.put(pair.substring(0, split), pair.substring(split+1));
|
|
||||||
}
|
|
||||||
Map oldOpts = new HashMap();
|
|
||||||
String oldI2CPOpts = _config.getProperty(PROP_I2CP_OPTS);
|
|
||||||
if (oldI2CPOpts == null) oldI2CPOpts = "";
|
|
||||||
tok = new StringTokenizer(oldI2CPOpts, " \t\n");
|
|
||||||
while (tok.hasMoreTokens()) {
|
|
||||||
String pair = tok.nextToken();
|
|
||||||
int split = pair.indexOf('=');
|
|
||||||
if (split > 0)
|
|
||||||
oldOpts.put(pair.substring(0, split), pair.substring(split+1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (i2cpHost.trim().length() > 0) && (port > 0) &&
|
|
||||||
((!host.equals(i2cpHost) ||
|
|
||||||
(port != _util.getI2CPPort()) ||
|
|
||||||
(!oldOpts.equals(opts)))) ) {
|
|
||||||
boolean snarksActive = false;
|
|
||||||
Set names = listTorrentFiles();
|
|
||||||
for (Iterator iter = names.iterator(); iter.hasNext(); ) {
|
|
||||||
Snark snark = getTorrent((String)iter.next());
|
|
||||||
if ( (snark != null) && (!snark.stopped) ) {
|
|
||||||
snarksActive = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (snarksActive) {
|
|
||||||
addMessage("Cannot change the I2CP settings while torrents are active");
|
|
||||||
_log.debug("i2cp host [" + i2cpHost + "] i2cp port " + port + " opts [" + opts
|
|
||||||
+ "] oldOpts [" + oldOpts + "]");
|
|
||||||
} else {
|
|
||||||
if (_util.connected()) {
|
|
||||||
_util.disconnect();
|
|
||||||
addMessage("Disconnecting old I2CP destination");
|
|
||||||
}
|
|
||||||
Properties p = new Properties();
|
|
||||||
p.putAll(opts);
|
|
||||||
addMessage("I2CP settings changed to " + i2cpHost + ":" + port + " (" + i2cpOpts.trim() + ")");
|
|
||||||
_util.setI2CPConfig(i2cpHost, port, p);
|
|
||||||
boolean ok = _util.connect();
|
|
||||||
if (!ok) {
|
|
||||||
addMessage("Unable to connect with the new settings, reverting to the old I2CP settings");
|
|
||||||
_util.setI2CPConfig(oldI2CPHost, oldI2CPPort, oldOpts);
|
|
||||||
ok = _util.connect();
|
|
||||||
if (!ok)
|
|
||||||
addMessage("Unable to reconnect with the old settings!");
|
|
||||||
} else {
|
|
||||||
addMessage("Reconnected on the new I2CP destination");
|
|
||||||
_config.setProperty(PROP_I2CP_HOST, i2cpHost.trim());
|
|
||||||
_config.setProperty(PROP_I2CP_PORT, "" + port);
|
|
||||||
_config.setProperty(PROP_I2CP_OPTS, i2cpOpts.trim());
|
|
||||||
changed = true;
|
|
||||||
// no PeerAcceptors/I2PServerSockets to deal with, since all snarks are inactive
|
|
||||||
for (Iterator iter = names.iterator(); iter.hasNext(); ) {
|
|
||||||
String name = (String)iter.next();
|
|
||||||
Snark snark = getTorrent(name);
|
|
||||||
if ( (snark != null) && (snark.acceptor != null) ) {
|
|
||||||
snark.acceptor.restart();
|
|
||||||
addMessage("I2CP listener restarted for " + snark.meta.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (shouldAutoStart() != autoStart) {
|
|
||||||
_config.setProperty(PROP_AUTO_START, autoStart + "");
|
|
||||||
addMessage("Adjusted autostart to " + autoStart);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (_util.shouldUseOpenTrackers() != useOpenTrackers) {
|
|
||||||
_config.setProperty(I2PSnarkUtil.PROP_USE_OPENTRACKERS, useOpenTrackers + "");
|
|
||||||
addMessage((useOpenTrackers ? "En" : "Dis") + "abled open trackers - torrent restart required to take effect");
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (openTrackers != null) {
|
|
||||||
if (openTrackers.trim().length() > 0 && !openTrackers.trim().equals(_util.getOpenTrackerString())) {
|
|
||||||
_config.setProperty(I2PSnarkUtil.PROP_OPENTRACKERS, openTrackers.trim());
|
|
||||||
addMessage("Open Tracker list changed - torrent restart required to take effect");
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
saveConfig();
|
|
||||||
} else {
|
|
||||||
addMessage("Configuration unchanged");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveConfig() {
|
|
||||||
try {
|
|
||||||
synchronized (_configFile) {
|
|
||||||
DataHelper.storeProps(_config, new File(_configFile));
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
addMessage("Unable to save the config to '" + _configFile + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Properties getConfig() { return _config; }
|
|
||||||
|
|
||||||
/** hardcoded for sanity. perhaps this should be customizable, for people who increase their ulimit, etc. */
|
|
||||||
private static final int MAX_FILES_PER_TORRENT = 256;
|
|
||||||
|
|
||||||
/** set of filenames that we are dealing with */
|
|
||||||
public Set listTorrentFiles() { synchronized (_snarks) { return new HashSet(_snarks.keySet()); } }
|
|
||||||
/**
|
|
||||||
* Grab the torrent given the (canonical) filename
|
|
||||||
*/
|
|
||||||
public Snark getTorrent(String filename) { synchronized (_snarks) { return (Snark)_snarks.get(filename); } }
|
|
||||||
public void addTorrent(String filename) { addTorrent(filename, false); }
|
|
||||||
public void addTorrent(String filename, boolean dontAutoStart) {
|
|
||||||
if ((!dontAutoStart) && !_util.connected()) {
|
|
||||||
addMessage("Connecting to I2P");
|
|
||||||
boolean ok = _util.connect();
|
|
||||||
if (!ok) {
|
|
||||||
addMessage("Error connecting to I2P - check your I2CP settings");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File sfile = new File(filename);
|
|
||||||
try {
|
|
||||||
filename = sfile.getCanonicalPath();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.error("Unable to add the torrent " + filename, ioe);
|
|
||||||
addMessage("ERR: Could not add the torrent '" + filename + "': " + ioe.getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
File dataDir = getDataDir();
|
|
||||||
Snark torrent = null;
|
|
||||||
synchronized (_snarks) {
|
|
||||||
torrent = (Snark)_snarks.get(filename);
|
|
||||||
}
|
|
||||||
// don't hold the _snarks lock while verifying the torrent
|
|
||||||
if (torrent == null) {
|
|
||||||
synchronized (_addSnarkLock) {
|
|
||||||
// double-check
|
|
||||||
synchronized (_snarks) {
|
|
||||||
if(_snarks.get(filename) != null)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileInputStream fis = null;
|
|
||||||
try {
|
|
||||||
fis = new FileInputStream(sfile);
|
|
||||||
MetaInfo info = new MetaInfo(fis);
|
|
||||||
fis.close();
|
|
||||||
fis = null;
|
|
||||||
|
|
||||||
String rejectMessage = locked_validateTorrent(info);
|
|
||||||
if (rejectMessage != null) {
|
|
||||||
sfile.delete();
|
|
||||||
addMessage(rejectMessage);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
torrent = new Snark(_util, filename, null, -1, null, null, this,
|
|
||||||
_peerCoordinatorSet, _connectionAcceptor,
|
|
||||||
false, dataDir.getPath());
|
|
||||||
torrent.completeListener = this;
|
|
||||||
synchronized (_snarks) {
|
|
||||||
_snarks.put(filename, torrent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
addMessage("Torrent in " + sfile.getName() + " is invalid: " + ioe.getMessage());
|
|
||||||
if (sfile.exists())
|
|
||||||
sfile.delete();
|
|
||||||
return;
|
|
||||||
} finally {
|
|
||||||
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// ok, snark created, now lets start it up or configure it further
|
|
||||||
File f = new File(filename);
|
|
||||||
if (!dontAutoStart && shouldAutoStart()) {
|
|
||||||
torrent.startTorrent();
|
|
||||||
addMessage("Torrent added and started: '" + f.getName() + "'");
|
|
||||||
} else {
|
|
||||||
addMessage("Torrent added: '" + f.getName() + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the timestamp for a torrent from the config file
|
|
||||||
*/
|
|
||||||
public long getSavedTorrentTime(Snark snark) {
|
|
||||||
MetaInfo metainfo = snark.meta;
|
|
||||||
byte[] ih = metainfo.getInfoHash();
|
|
||||||
String infohash = Base64.encode(ih);
|
|
||||||
infohash = infohash.replace('=', '$');
|
|
||||||
String time = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the saved bitfield for a torrent from the config file.
|
|
||||||
* Convert "." to a full bitfield.
|
|
||||||
*/
|
|
||||||
public BitField getSavedTorrentBitField(Snark snark) {
|
|
||||||
MetaInfo metainfo = snark.meta;
|
|
||||||
byte[] ih = metainfo.getInfoHash();
|
|
||||||
String infohash = Base64.encode(ih);
|
|
||||||
infohash = infohash.replace('=', '$');
|
|
||||||
String bf = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
|
|
||||||
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);
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
bitfield.set(i);
|
|
||||||
return bitfield;
|
|
||||||
}
|
|
||||||
byte[] bitfield = Base64.decode(bf);
|
|
||||||
if (bitfield == null)
|
|
||||||
return null;
|
|
||||||
if (bitfield.length * 8 < len)
|
|
||||||
return null;
|
|
||||||
return new BitField(bitfield, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 '='.
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield) {
|
|
||||||
byte[] ih = metainfo.getInfoHash();
|
|
||||||
String infohash = Base64.encode(ih);
|
|
||||||
infohash = infohash.replace('=', '$');
|
|
||||||
String now = "" + System.currentTimeMillis();
|
|
||||||
String bfs;
|
|
||||||
if (bitfield.complete()) {
|
|
||||||
bfs = ".";
|
|
||||||
} else {
|
|
||||||
byte[] bf = bitfield.getFieldBytes();
|
|
||||||
bfs = Base64.encode(bf);
|
|
||||||
}
|
|
||||||
_config.setProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX, now + "," + bfs);
|
|
||||||
saveConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the status of a torrent from the config file.
|
|
||||||
* This may help the config file from growing too big.
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
saveConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String locked_validateTorrent(MetaInfo info) throws IOException {
|
|
||||||
String announce = info.getAnnounce();
|
|
||||||
// basic validation of url
|
|
||||||
if ((!announce.startsWith("http://")) ||
|
|
||||||
(announce.indexOf(".i2p/") < 0))
|
|
||||||
return "Non-i2p tracker in " + info.getName() + ", deleting it";
|
|
||||||
List files = info.getFiles();
|
|
||||||
if ( (files != null) && (files.size() > MAX_FILES_PER_TORRENT) ) {
|
|
||||||
return "Too many files in " + info.getName() + " (" + files.size() + "), deleting it";
|
|
||||||
} else if (info.getPieces() <= 0) {
|
|
||||||
return "No pieces in " + info.getName() + "? deleting it";
|
|
||||||
} else if (info.getPieceLength(0) > 1*1024*1024) {
|
|
||||||
return "Pieces are too large in " + info.getName() + " (" + info.getPieceLength(0)/1024 + "KB), deleting it";
|
|
||||||
} else if (info.getTotalLength() > 10*1024*1024*1024l) {
|
|
||||||
System.out.println("torrent info: " + info.toString());
|
|
||||||
List lengths = info.getLengths();
|
|
||||||
if (lengths != null)
|
|
||||||
for (int i = 0; i < lengths.size(); i++)
|
|
||||||
System.out.println("File " + i + " is " + lengths.get(i) + " long");
|
|
||||||
|
|
||||||
return "Torrents larger than 10GB are not supported yet (because we're paranoid): " + info.getName() + ", deleting it";
|
|
||||||
} else {
|
|
||||||
// ok
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop the torrent, leaving it on the list of torrents unless told to remove it
|
|
||||||
*/
|
|
||||||
public Snark stopTorrent(String filename, boolean shouldRemove) {
|
|
||||||
File sfile = new File(filename);
|
|
||||||
try {
|
|
||||||
filename = sfile.getCanonicalPath();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.error("Unable to remove the torrent " + filename, ioe);
|
|
||||||
addMessage("ERR: Could not remove the torrent '" + filename + "': " + ioe.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int remaining = 0;
|
|
||||||
Snark torrent = null;
|
|
||||||
synchronized (_snarks) {
|
|
||||||
if (shouldRemove)
|
|
||||||
torrent = (Snark)_snarks.remove(filename);
|
|
||||||
else
|
|
||||||
torrent = (Snark)_snarks.get(filename);
|
|
||||||
remaining = _snarks.size();
|
|
||||||
}
|
|
||||||
if (torrent != null) {
|
|
||||||
boolean wasStopped = torrent.stopped;
|
|
||||||
torrent.stopTorrent();
|
|
||||||
if (remaining == 0) {
|
|
||||||
// should we disconnect/reconnect here (taking care to deal with the other thread's
|
|
||||||
// I2PServerSocket.accept() call properly?)
|
|
||||||
////_util.
|
|
||||||
}
|
|
||||||
if (!wasStopped)
|
|
||||||
addMessage("Torrent stopped: '" + sfile.getName() + "'");
|
|
||||||
}
|
|
||||||
return torrent;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Stop the torrent and delete the torrent file itself, but leaving the data
|
|
||||||
* behind.
|
|
||||||
*/
|
|
||||||
public void removeTorrent(String filename) {
|
|
||||||
Snark torrent = stopTorrent(filename, true);
|
|
||||||
if (torrent != null) {
|
|
||||||
File torrentFile = new File(filename);
|
|
||||||
torrentFile.delete();
|
|
||||||
if (torrent.storage != null)
|
|
||||||
removeTorrentStatus(torrent.storage.getMetaInfo());
|
|
||||||
addMessage("Torrent removed: '" + torrentFile.getName() + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DirMonitor implements Runnable {
|
|
||||||
public void run() {
|
|
||||||
try { Thread.sleep(60*1000*getStartupDelayMinutes()); } catch (InterruptedException ie) {}
|
|
||||||
// the first message was a "We are starting up in 1m"
|
|
||||||
synchronized (_messages) {
|
|
||||||
if (_messages.size() == 1)
|
|
||||||
_messages.remove(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
File dir = getDataDir();
|
|
||||||
_log.debug("Directory Monitor loop over " + dir.getAbsolutePath());
|
|
||||||
try {
|
|
||||||
monitorTorrents(dir);
|
|
||||||
} catch (Exception e) {
|
|
||||||
_log.error("Error in the DirectoryMonitor", e);
|
|
||||||
}
|
|
||||||
try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** two listeners */
|
|
||||||
public void torrentComplete(Snark snark) {
|
|
||||||
File f = new File(snark.torrent);
|
|
||||||
long len = snark.meta.getTotalLength();
|
|
||||||
addMessage("Download complete of " + f.getName()
|
|
||||||
+ (len < 5*1024*1024 ? " (size: " + (len/1024) + "KB)" : " (size: " + (len/(1024*1024l)) + "MB)"));
|
|
||||||
updateStatus(snark);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateStatus(Snark snark) {
|
|
||||||
saveTorrentStatus(snark.meta, snark.storage.getBitField());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void monitorTorrents(File dir) {
|
|
||||||
String fileNames[] = dir.list(TorrentFilenameFilter.instance());
|
|
||||||
List foundNames = new ArrayList(0);
|
|
||||||
if (fileNames != null) {
|
|
||||||
for (int i = 0; i < fileNames.length; i++) {
|
|
||||||
try {
|
|
||||||
foundNames.add(new File(dir, fileNames[i]).getCanonicalPath());
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.error("Error resolving '" + fileNames[i] + "' in '" + dir, ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Set existingNames = listTorrentFiles();
|
|
||||||
// lets find new ones first...
|
|
||||||
for (int i = 0; i < foundNames.size(); i++) {
|
|
||||||
if (existingNames.contains(foundNames.get(i))) {
|
|
||||||
// already known. noop
|
|
||||||
} else {
|
|
||||||
if (shouldAutoStart() && !_util.connect())
|
|
||||||
addMessage("Unable to connect to I2P");
|
|
||||||
addTorrent((String)foundNames.get(i), !shouldAutoStart());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// now lets see which ones have been removed...
|
|
||||||
for (Iterator iter = existingNames.iterator(); iter.hasNext(); ) {
|
|
||||||
String name = (String)iter.next();
|
|
||||||
if (foundNames.contains(name)) {
|
|
||||||
// known and still there. noop
|
|
||||||
} else {
|
|
||||||
// known, but removed. drop it
|
|
||||||
stopTorrent(name, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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/"
|
|
||||||
// , "eBook", "http://E71FRom6PZNEqTN2Lr8P-sr23b7HJVC32KoGnVQjaX6zJiXwhJy2HsXob36Qmj81TYFZdewFZa9mSJ533UZgGyQkXo2ahctg82JKYZfDe5uDxAn1E9YPjxZCWJaFJh0S~UwSs~9AZ7UcauSJIoNtpxrtbmRNVFLqnkEDdLZi26TeucfOmiFmIWnVblLniWv3tG1boE9Abd-6j3FmYVrRucYuepAILYt6katmVNOk6sXmno1Eynrp~~MBuFq0Ko6~jsc2E2CRVYXDhGHEMdt-j6JUz5D7S2RIVzDRqQyAZLKJ7OdQDmI31przzmne1vOqqqLC~1xUumZVIvF~yOeJUGNjJ1Vx0J8i2BQIusn1pQJ6UCB~ZtZZLQtEb8EPVCfpeRi2ri1M5CyOuxN0V5ekmPHrYIBNevuTCRC26NP7ZS5VDgx1~NaC3A-CzJAE6f1QXi0wMI9aywNG5KGzOPifcsih8eyGyytvgLtrZtV7ykzYpPCS-rDfITncpn5hliPUAAAA.i2p/pub/bt/announce.php=http://de-ebook-archiv.i2p/pub/bt/"
|
|
||||||
// , "Gaytorrents", "http://uxPWHbK1OIj9HxquaXuhMiIvi21iK0~ZiG9d8G0840ZXIg0r6CbiV71xlsqmdnU6wm0T2LySriM0doW2gUigo-5BNkUquHwOjLROiETnB3ZR0Ml4IGa6QBPn1aAq2d9~g1r1nVjLE~pcFnXB~cNNS7kIhX1d6nLgYVZf0C2cZopEow2iWVUggGGnAA9mHjE86zLEnTvAyhbAMTqDQJhEuLa0ZYSORqzJDMkQt90MV4YMjX1ICY6RfUSFmxEqu0yWTrkHsTtRw48l~dz9wpIgc0a0T9C~eeWvmBFTqlJPtQZwntpNeH~jF7nlYzB58olgV2HHFYpVYD87DYNzTnmNWxCJ5AfDorm6AIUCV2qaE7tZtI1h6fbmGpGlPyW~Kw5GXrRfJwNvr6ajwAVi~bPVnrBwDZezHkfW4slOO8FACPR28EQvaTu9nwhAbqESxV2hCTq6vQSGjuxHeOuzBOEvRWkLKOHWTC09t2DbJ94FSqETmZopTB1ukEmaxRWbKSIaAAAA.i2p/announce.php=http://gaytorrents.i2p/"
|
|
||||||
// , "NickyB", "http://9On6d3cZ27JjwYCtyJJbowe054d5tFnfMjv4PHsYs-EQn4Y4mk2zRixatvuAyXz2MmRfXG-NAUfhKr0KCxRNZbvHmlckYfT-WBzwwpiMAl0wDFY~Pl8cqXuhfikSG5WrqdPfDNNIBuuznS0dqaczf~OyVaoEOpvuP3qV6wKqbSSLpjOwwAaQPHjlRtNIW8-EtUZp-I0LT45HSoowp~6b7zYmpIyoATvIP~sT0g0MTrczWhbVTUZnEkZeLhOR0Duw1-IRXI2KHPbA24wLO9LdpKKUXed05RTz0QklW5ROgR6TYv7aXFufX8kC0-DaKvQ5JKG~h8lcoHvm1RCzNqVE-2aiZnO2xH08H-iCWoLNJE-Td2kT-Tsc~3QdQcnEUcL5BF-VT~QYRld2--9r0gfGl-yDrJZrlrihHGr5J7ImahelNn9PpkVp6eIyABRmJHf2iicrk3CtjeG1j9OgTSwaNmEpUpn4aN7Kx0zNLdH7z6uTgCGD9Kmh1MFYrsoNlTp4AAAA.i2p/bittorrent/announce.php=http://nickyb.i2p/bittorrent/"
|
|
||||||
// , "Orion", "http://gKik1lMlRmuroXVGTZ~7v4Vez3L3ZSpddrGZBrxVriosCQf7iHu6CIk8t15BKsj~P0JJpxrofeuxtm7SCUAJEr0AIYSYw8XOmp35UfcRPQWyb1LsxUkMT4WqxAT3s1ClIICWlBu5An~q-Mm0VFlrYLIPBWlUFnfPR7jZ9uP5ZMSzTKSMYUWao3ejiykr~mtEmyls6g-ZbgKZawa9II4zjOy-hdxHgP-eXMDseFsrym4Gpxvy~3Fv9TuiSqhpgm~UeTo5YBfxn6~TahKtE~~sdCiSydqmKBhxAQ7uT9lda7xt96SS09OYMsIWxLeQUWhns-C~FjJPp1D~IuTrUpAFcVEGVL-BRMmdWbfOJEcWPZ~CBCQSO~VkuN1ebvIOr9JBerFMZSxZtFl8JwcrjCIBxeKPBmfh~xYh16BJm1BBBmN1fp2DKmZ2jBNkAmnUbjQOqWvUcehrykWk5lZbE7bjJMDFH48v3SXwRuDBiHZmSbsTY6zhGY~GkMQHNGxPMMSIAAAA.i2p/bt/announce.php=http://orion.i2p/bt/"
|
|
||||||
// , "anonymity", "http://8EoJZIKrWgGuDrxA3nRJs1jsPfiGwmFWL91hBrf0HA7oKhEvAna4Ocx47VLUR9retVEYBAyWFK-eZTPcvhnz9XffBEiJQQ~kFSCqb1fV6IfPiV3HySqi9U5Caf6~hC46fRd~vYnxmaBLICT3N160cxBETqH3v2rdxdJpvYt8q4nMk9LUeVXq7zqCTFLLG5ig1uKgNzBGe58iNcsvTEYlnbYcE930ABmrzj8G1qQSgSwJ6wx3tUQNl1z~4wSOUMan~raZQD60lRK70GISjoX0-D0Po9WmPveN3ES3g72TIET3zc3WPdK2~lgmKGIs8GgNLES1cXTolvbPhdZK1gxddRMbJl6Y6IPFyQ9o4-6Rt3Lp-RMRWZ2TG7j2OMcNSiOmATUhKEFBDfv-~SODDyopGBmfeLw16F4NnYednvn4qP10dyMHcUASU6Zag4mfc2-WivrOqeWhD16fVAh8MoDpIIT~0r9XmwdaVFyLcjbXObabJczxCAW3fodQUnvuSkwzAAAA.i2p/anonymityTracker/announce.php=http://anonymityweb.i2p/anonymityTracker/"
|
|
||||||
// , "The freak's tracker", "http://mHKva9x24E5Ygfey2llR1KyQHv5f8hhMpDMwJDg1U-hABpJ2NrQJd6azirdfaR0OKt4jDlmP2o4Qx0H598~AteyD~RJU~xcWYdcOE0dmJ2e9Y8-HY51ie0B1yD9FtIV72ZI-V3TzFDcs6nkdX9b81DwrAwwFzx0EfNvK1GLVWl59Ow85muoRTBA1q8SsZImxdyZ-TApTVlMYIQbdI4iQRwU9OmmtefrCe~ZOf4UBS9-KvNIqUL0XeBSqm0OU1jq-D10Ykg6KfqvuPnBYT1BYHFDQJXW5DdPKwcaQE4MtAdSGmj1epDoaEBUa9btQlFsM2l9Cyn1hzxqNWXELmx8dRlomQLlV4b586dRzW~fLlOPIGC13ntPXogvYvHVyEyptXkv890jC7DZNHyxZd5cyrKC36r9huKvhQAmNABT2Y~pOGwVrb~RpPwT0tBuPZ3lHYhBFYmD8y~AOhhNHKMLzea1rfwTvovBMByDdFps54gMN1mX4MbCGT4w70vIopS9yAAAA.i2p/bytemonsoon/announce.php"
|
|
||||||
, "welterde", "http://BGKmlDOoH3RzFbPRfRpZV2FjpVj8~3moFftw5-dZfDf2070TOe8Tf2~DAVeaM6ZRLdmFEt~9wyFL8YMLMoLoiwGEH6IGW6rc45tstN68KsBDWZqkTohV1q9XFgK9JnCwE~Oi89xLBHsLMTHOabowWM6dkC8nI6QqJC2JODqLPIRfOVrDdkjLwtCrsckzLybNdFmgfoqF05UITDyczPsFVaHtpF1sRggOVmdvCM66otyonlzNcJbn59PA-R808vUrCPMGU~O9Wys0i-NoqtIbtWfOKnjCRFMNw5ex4n9m5Sxm9e20UkpKG6qzEuvKZWi8vTLe1NW~CBrj~vG7I3Ok4wybUFflBFOaBabxYJLlx4xTE1zJIVxlsekmAjckB4v-cQwulFeikR4LxPQ6mCQknW2HZ4JQIq6hL9AMabxjOlYnzh7kjOfRGkck8YgeozcyTvcDUcUsOuSTk06L4kdrv8h2Cozjbloi5zl6KTbj5ZTciKCxi73Pn9grICn-HQqEAAAA.i2p/a=http://tracker.welterde.i2p/stats?mode=top5"
|
|
||||||
// , "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/"
|
|
||||||
, "crstrack", "http://b4G9sCdtfvccMAXh~SaZrPqVQNyGQbhbYMbw6supq2XGzbjU4NcOmjFI0vxQ8w1L05twmkOvg5QERcX6Mi8NQrWnR0stLExu2LucUXg1aYjnggxIR8TIOGygZVIMV3STKH4UQXD--wz0BUrqaLxPhrm2Eh9Hwc8TdB6Na4ShQUq5Xm8D4elzNUVdpM~RtChEyJWuQvoGAHY3ppX-EJJLkiSr1t77neS4Lc-KofMVmgI9a2tSSpNAagBiNI6Ak9L1T0F9uxeDfEG9bBSQPNMOSUbAoEcNxtt7xOW~cNOAyMyGydwPMnrQ5kIYPY8Pd3XudEko970vE0D6gO19yoBMJpKx6Dh50DGgybLQ9CpRaynh2zPULTHxm8rneOGRcQo8D3mE7FQ92m54~SvfjXjD2TwAVGI~ae~n9HDxt8uxOecAAvjjJ3TD4XM63Q9TmB38RmGNzNLDBQMEmJFpqQU8YeuhnS54IVdUoVQFqui5SfDeLXlSkh4vYoMU66pvBfWbAAAA.i2p/tracker/announce.php=http://crstrack.i2p/tracker/"
|
|
||||||
};
|
|
||||||
|
|
||||||
/** comma delimited list of name=announceURL=baseURL for the trackers to be displayed */
|
|
||||||
public static final String PROP_TRACKERS = "i2psnark.trackers";
|
|
||||||
private static Map trackerMap = null;
|
|
||||||
/** sorted map of name to announceURL=baseURL */
|
|
||||||
public Map getTrackers() {
|
|
||||||
if (trackerMap != null) // only do this once, can't be updated while running
|
|
||||||
return trackerMap;
|
|
||||||
Map rv = new TreeMap();
|
|
||||||
String trackers = _config.getProperty(PROP_TRACKERS);
|
|
||||||
if ( (trackers == null) || (trackers.trim().length() <= 0) )
|
|
||||||
trackers = _context.getProperty(PROP_TRACKERS);
|
|
||||||
if ( (trackers == null) || (trackers.trim().length() <= 0) ) {
|
|
||||||
for (int i = 0; i < DEFAULT_TRACKERS.length; i += 2)
|
|
||||||
rv.put(DEFAULT_TRACKERS[i], DEFAULT_TRACKERS[i+1]);
|
|
||||||
} else {
|
|
||||||
StringTokenizer tok = new StringTokenizer(trackers, ",");
|
|
||||||
while (tok.hasMoreTokens()) {
|
|
||||||
String pair = tok.nextToken();
|
|
||||||
int split = pair.indexOf('=');
|
|
||||||
if (split <= 0)
|
|
||||||
continue;
|
|
||||||
String name = pair.substring(0, split).trim();
|
|
||||||
String url = pair.substring(split+1).trim();
|
|
||||||
if ( (name.length() > 0) && (url.length() > 0) )
|
|
||||||
rv.put(name, url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trackerMap = rv;
|
|
||||||
return trackerMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TorrentFilenameFilter implements FilenameFilter {
|
|
||||||
private static final TorrentFilenameFilter _filter = new TorrentFilenameFilter();
|
|
||||||
public static TorrentFilenameFilter instance() { return _filter; }
|
|
||||||
public boolean accept(File dir, String name) {
|
|
||||||
return (name != null) && (name.endsWith(".torrent"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SnarkManagerShutdown extends I2PAppThread {
|
|
||||||
public void run() {
|
|
||||||
Set names = listTorrentFiles();
|
|
||||||
for (Iterator iter = names.iterator(); iter.hasNext(); ) {
|
|
||||||
Snark snark = getTorrent((String)iter.next());
|
|
||||||
if ( (snark != null) && (!snark.stopped) )
|
|
||||||
snark.stopTorrent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
/* TrackerShutdown - Makes sure everything ends correctly when shutting down.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import net.i2p.util.I2PAppThread;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes sure everything ends correctly when shutting down.
|
|
||||||
*/
|
|
||||||
public class SnarkShutdown extends I2PAppThread
|
|
||||||
{
|
|
||||||
private final Storage storage;
|
|
||||||
private final PeerCoordinator coordinator;
|
|
||||||
private final ConnectionAcceptor acceptor;
|
|
||||||
private final TrackerClient trackerclient;
|
|
||||||
|
|
||||||
private final ShutdownListener listener;
|
|
||||||
|
|
||||||
public SnarkShutdown(Storage storage,
|
|
||||||
PeerCoordinator coordinator,
|
|
||||||
ConnectionAcceptor acceptor,
|
|
||||||
TrackerClient trackerclient,
|
|
||||||
ShutdownListener listener)
|
|
||||||
{
|
|
||||||
this.storage = storage;
|
|
||||||
this.coordinator = coordinator;
|
|
||||||
this.acceptor = acceptor;
|
|
||||||
this.trackerclient = trackerclient;
|
|
||||||
this.listener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
//Snark.debug("Shutting down...", Snark.NOTICE);
|
|
||||||
|
|
||||||
//Snark.debug("Halting ConnectionAcceptor...", Snark.INFO);
|
|
||||||
if (acceptor != null)
|
|
||||||
acceptor.halt();
|
|
||||||
|
|
||||||
//Snark.debug("Halting TrackerClient...", Snark.INFO);
|
|
||||||
if (trackerclient != null)
|
|
||||||
trackerclient.halt();
|
|
||||||
|
|
||||||
//Snark.debug("Halting PeerCoordinator...", Snark.INFO);
|
|
||||||
if (coordinator != null)
|
|
||||||
coordinator.halt();
|
|
||||||
|
|
||||||
//Snark.debug("Closing Storage...", Snark.INFO);
|
|
||||||
if (storage != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
storage.close();
|
|
||||||
}
|
|
||||||
catch(IOException ioe)
|
|
||||||
{
|
|
||||||
//I2PSnarkUtil.instance().debug("Couldn't properly close storage", Snark.ERROR, ioe);
|
|
||||||
throw new RuntimeException("b0rking");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX - Should actually wait till done...
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//Snark.debug("Waiting 5 seconds...", Snark.INFO);
|
|
||||||
Thread.sleep(5*1000);
|
|
||||||
}
|
|
||||||
catch (InterruptedException ie) { /* ignored */ }
|
|
||||||
|
|
||||||
listener.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
/* StaticSnark - Main snark startup class for staticly linking with gcj.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main snark startup class for staticly linking with gcj.
|
|
||||||
* It references somee necessary classes that are normally loaded through
|
|
||||||
* reflection.
|
|
||||||
*
|
|
||||||
* @author Mark Wielaard (mark@klomp.org)
|
|
||||||
*/
|
|
||||||
public class StaticSnark
|
|
||||||
{
|
|
||||||
public static void main(String[] args)
|
|
||||||
{
|
|
||||||
// The GNU security provider is needed for SHA-1 MessageDigest checking.
|
|
||||||
// So make sure it is available as a security provider.
|
|
||||||
//Provider gnu = new gnu.java.security.provider.Gnu();
|
|
||||||
//Security.addProvider(gnu);
|
|
||||||
|
|
||||||
// And finally call the normal starting point.
|
|
||||||
Snark.main(args);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,805 +0,0 @@
|
|||||||
/* Storage - Class used to store and retrieve pieces.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
|
|
||||||
import net.i2p.crypto.SHA1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maintains pieces on disk. Can be used to store and retrieve pieces.
|
|
||||||
*/
|
|
||||||
public class Storage
|
|
||||||
{
|
|
||||||
private MetaInfo metainfo;
|
|
||||||
private long[] lengths;
|
|
||||||
private RandomAccessFile[] rafs;
|
|
||||||
private String[] names;
|
|
||||||
private Object[] RAFlock; // lock on RAF access
|
|
||||||
private long[] RAFtime; // when was RAF last accessed, or 0 if closed
|
|
||||||
private File[] RAFfile; // File to make it easier to reopen
|
|
||||||
|
|
||||||
private final StorageListener listener;
|
|
||||||
private I2PSnarkUtil _util;
|
|
||||||
|
|
||||||
private BitField bitfield; // BitField to represent the pieces
|
|
||||||
private int needed; // Number of pieces needed
|
|
||||||
private boolean _probablyComplete; // use this to decide whether to open files RO
|
|
||||||
|
|
||||||
// XXX - Not always set correctly
|
|
||||||
int piece_size;
|
|
||||||
int pieces;
|
|
||||||
boolean changed;
|
|
||||||
|
|
||||||
/** The default piece size. */
|
|
||||||
private static int MIN_PIECE_SIZE = 256*1024;
|
|
||||||
private static int MAX_PIECE_SIZE = 1024*1024;
|
|
||||||
/** The maximum number of pieces in a torrent. */
|
|
||||||
private static long MAX_PIECES = 100*1024/20;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new storage based on the supplied MetaInfo. This will
|
|
||||||
* try to create and/or check all needed files in the MetaInfo.
|
|
||||||
*
|
|
||||||
* @exception IOException when creating and/or checking files fails.
|
|
||||||
*/
|
|
||||||
public Storage(I2PSnarkUtil util, MetaInfo metainfo, StorageListener listener)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
_util = util;
|
|
||||||
this.metainfo = metainfo;
|
|
||||||
this.listener = listener;
|
|
||||||
needed = metainfo.getPieces();
|
|
||||||
_probablyComplete = false;
|
|
||||||
bitfield = new BitField(needed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a storage from the existing file or directory together
|
|
||||||
* with an appropriate MetaInfo file as can be announced on the
|
|
||||||
* given announce String location.
|
|
||||||
*/
|
|
||||||
public Storage(I2PSnarkUtil util, File baseFile, String announce, StorageListener listener)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
_util = util;
|
|
||||||
this.listener = listener;
|
|
||||||
// Create names, rafs and lengths arrays.
|
|
||||||
getFiles(baseFile);
|
|
||||||
|
|
||||||
long total = 0;
|
|
||||||
ArrayList lengthsList = new ArrayList();
|
|
||||||
for (int i = 0; i < lengths.length; i++)
|
|
||||||
{
|
|
||||||
long length = lengths[i];
|
|
||||||
total += length;
|
|
||||||
lengthsList.add(new Long(length));
|
|
||||||
}
|
|
||||||
|
|
||||||
piece_size = MIN_PIECE_SIZE;
|
|
||||||
pieces = (int) ((total - 1)/piece_size) + 1;
|
|
||||||
while (pieces > MAX_PIECES && piece_size < MAX_PIECE_SIZE)
|
|
||||||
{
|
|
||||||
piece_size = piece_size*2;
|
|
||||||
pieces = (int) ((total - 1)/piece_size) +1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that piece_hashes and the bitfield will be filled after
|
|
||||||
// the MetaInfo is created.
|
|
||||||
byte[] piece_hashes = new byte[20*pieces];
|
|
||||||
bitfield = new BitField(pieces);
|
|
||||||
needed = 0;
|
|
||||||
|
|
||||||
List files = new ArrayList();
|
|
||||||
for (int i = 0; i < names.length; i++)
|
|
||||||
{
|
|
||||||
List file = new ArrayList();
|
|
||||||
StringTokenizer st = new StringTokenizer(names[i], File.separator);
|
|
||||||
while (st.hasMoreTokens())
|
|
||||||
{
|
|
||||||
String part = st.nextToken();
|
|
||||||
file.add(part);
|
|
||||||
}
|
|
||||||
files.add(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.size() == 1) // FIXME: ...and if base file not a directory or should this be the only check?
|
|
||||||
// this makes a bad metainfo if the directory has only one file in it
|
|
||||||
{
|
|
||||||
files = null;
|
|
||||||
lengthsList = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that the piece_hashes are not correctly setup yet.
|
|
||||||
metainfo = new MetaInfo(announce, baseFile.getName(), null, files,
|
|
||||||
lengthsList, piece_size, piece_hashes, total);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates piece hashes for a new storage.
|
|
||||||
// This does NOT create the files, just the hashes
|
|
||||||
public void create() throws IOException
|
|
||||||
{
|
|
||||||
// if (true) {
|
|
||||||
fast_digestCreate();
|
|
||||||
// } else {
|
|
||||||
// orig_digestCreate();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
private void orig_digestCreate() throws IOException {
|
|
||||||
// Calculate piece_hashes
|
|
||||||
MessageDigest digest = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
digest = MessageDigest.getInstance("SHA");
|
|
||||||
}
|
|
||||||
catch(NoSuchAlgorithmException nsa)
|
|
||||||
{
|
|
||||||
throw new InternalError(nsa.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] piece_hashes = metainfo.getPieceHashes();
|
|
||||||
|
|
||||||
byte[] piece = new byte[piece_size];
|
|
||||||
for (int i = 0; i < pieces; i++)
|
|
||||||
{
|
|
||||||
int length = getUncheckedPiece(i, piece);
|
|
||||||
digest.update(piece, 0, length);
|
|
||||||
byte[] hash = digest.digest();
|
|
||||||
for (int j = 0; j < 20; j++)
|
|
||||||
piece_hashes[20 * i + j] = hash[j];
|
|
||||||
|
|
||||||
bitfield.set(i);
|
|
||||||
|
|
||||||
if (listener != null)
|
|
||||||
listener.storageChecked(this, i, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listener != null)
|
|
||||||
listener.storageAllChecked(this);
|
|
||||||
|
|
||||||
// Reannounce to force recalculating the info_hash.
|
|
||||||
metainfo = metainfo.reannounce(metainfo.getAnnounce());
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
private void fast_digestCreate() throws IOException {
|
|
||||||
// Calculate piece_hashes
|
|
||||||
SHA1 digest = new SHA1();
|
|
||||||
|
|
||||||
byte[] piece_hashes = metainfo.getPieceHashes();
|
|
||||||
|
|
||||||
byte[] piece = new byte[piece_size];
|
|
||||||
for (int i = 0; i < pieces; i++)
|
|
||||||
{
|
|
||||||
int length = getUncheckedPiece(i, piece);
|
|
||||||
digest.update(piece, 0, length);
|
|
||||||
byte[] hash = digest.digest();
|
|
||||||
for (int j = 0; j < 20; j++)
|
|
||||||
piece_hashes[20 * i + j] = hash[j];
|
|
||||||
|
|
||||||
bitfield.set(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reannounce to force recalculating the info_hash.
|
|
||||||
metainfo = metainfo.reannounce(metainfo.getAnnounce());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getFiles(File base) throws IOException
|
|
||||||
{
|
|
||||||
ArrayList files = new ArrayList();
|
|
||||||
addFiles(files, base);
|
|
||||||
|
|
||||||
int size = files.size();
|
|
||||||
names = new String[size];
|
|
||||||
lengths = new long[size];
|
|
||||||
rafs = new RandomAccessFile[size];
|
|
||||||
RAFlock = new Object[size];
|
|
||||||
RAFtime = new long[size];
|
|
||||||
RAFfile = new File[size];
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
Iterator it = files.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
File f = (File)it.next();
|
|
||||||
names[i] = f.getPath();
|
|
||||||
if (base.isDirectory() && names[i].startsWith(base.getPath()))
|
|
||||||
names[i] = names[i].substring(base.getPath().length() + 1);
|
|
||||||
lengths[i] = f.length();
|
|
||||||
RAFlock[i] = new Object();
|
|
||||||
RAFfile[i] = f;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addFiles(List l, File f)
|
|
||||||
{
|
|
||||||
if (!f.isDirectory())
|
|
||||||
l.add(f);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
File[] files = f.listFiles();
|
|
||||||
if (files == null)
|
|
||||||
{
|
|
||||||
_util.debug("WARNING: Skipping '" + f
|
|
||||||
+ "' not a normal file.", Snark.WARNING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < files.length; i++)
|
|
||||||
addFiles(l, files[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the MetaInfo associated with this Storage.
|
|
||||||
*/
|
|
||||||
public MetaInfo getMetaInfo()
|
|
||||||
{
|
|
||||||
return metainfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How many pieces are still missing from this storage.
|
|
||||||
*/
|
|
||||||
public int needed()
|
|
||||||
{
|
|
||||||
return needed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not this storage contains all pieces if the MetaInfo.
|
|
||||||
*/
|
|
||||||
public boolean complete()
|
|
||||||
{
|
|
||||||
return needed == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The BitField that tells which pieces this storage contains.
|
|
||||||
* Do not change this since this is the current state of the storage.
|
|
||||||
*/
|
|
||||||
public BitField getBitField()
|
|
||||||
{
|
|
||||||
return bitfield;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates (and/or checks) all files from the metainfo file list.
|
|
||||||
*/
|
|
||||||
public void check(String rootDir) throws IOException
|
|
||||||
{
|
|
||||||
check(rootDir, 0, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** use a saved bitfield and timestamp from a config file */
|
|
||||||
public void check(String rootDir, long savedTime, BitField savedBitField) throws IOException
|
|
||||||
{
|
|
||||||
File base = new File(rootDir, filterName(metainfo.getName()));
|
|
||||||
boolean useSavedBitField = savedTime > 0 && savedBitField != null;
|
|
||||||
|
|
||||||
List files = metainfo.getFiles();
|
|
||||||
if (files == null)
|
|
||||||
{
|
|
||||||
// Create base as file.
|
|
||||||
_util.debug("Creating/Checking file: " + base, Snark.NOTICE);
|
|
||||||
if (!base.createNewFile() && !base.exists())
|
|
||||||
throw new IOException("Could not create file " + base);
|
|
||||||
|
|
||||||
lengths = new long[1];
|
|
||||||
rafs = new RandomAccessFile[1];
|
|
||||||
names = new String[1];
|
|
||||||
RAFlock = new Object[1];
|
|
||||||
RAFtime = new long[1];
|
|
||||||
RAFfile = new File[1];
|
|
||||||
lengths[0] = metainfo.getTotalLength();
|
|
||||||
RAFlock[0] = new Object();
|
|
||||||
RAFfile[0] = base;
|
|
||||||
if (useSavedBitField) {
|
|
||||||
long lm = base.lastModified();
|
|
||||||
if (lm <= 0 || lm > savedTime)
|
|
||||||
useSavedBitField = false;
|
|
||||||
}
|
|
||||||
names[0] = base.getName();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Create base as dir.
|
|
||||||
_util.debug("Creating/Checking directory: " + base, Snark.NOTICE);
|
|
||||||
if (!base.mkdir() && !base.isDirectory())
|
|
||||||
throw new IOException("Could not create directory " + base);
|
|
||||||
|
|
||||||
List ls = metainfo.getLengths();
|
|
||||||
int size = files.size();
|
|
||||||
long total = 0;
|
|
||||||
lengths = new long[size];
|
|
||||||
rafs = new RandomAccessFile[size];
|
|
||||||
names = new String[size];
|
|
||||||
RAFlock = new Object[size];
|
|
||||||
RAFtime = new long[size];
|
|
||||||
RAFfile = new File[size];
|
|
||||||
for (int i = 0; i < size; i++)
|
|
||||||
{
|
|
||||||
File f = createFileFromNames(base, (List)files.get(i));
|
|
||||||
lengths[i] = ((Long)ls.get(i)).longValue();
|
|
||||||
RAFlock[i] = new Object();
|
|
||||||
RAFfile[i] = f;
|
|
||||||
total += lengths[i];
|
|
||||||
if (useSavedBitField) {
|
|
||||||
long lm = base.lastModified();
|
|
||||||
if (lm <= 0 || lm > savedTime)
|
|
||||||
useSavedBitField = false;
|
|
||||||
}
|
|
||||||
names[i] = f.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check for metainfo file.
|
|
||||||
long metalength = metainfo.getTotalLength();
|
|
||||||
if (total != metalength)
|
|
||||||
throw new IOException("File lengths do not add up "
|
|
||||||
+ total + " != " + metalength);
|
|
||||||
}
|
|
||||||
if (useSavedBitField) {
|
|
||||||
bitfield = savedBitField;
|
|
||||||
needed = metainfo.getPieces() - bitfield.count();
|
|
||||||
_probablyComplete = complete();
|
|
||||||
_util.debug("Found saved state and files unchanged, skipping check", Snark.NOTICE);
|
|
||||||
} else {
|
|
||||||
// the following sets the needed variable
|
|
||||||
changed = true;
|
|
||||||
checkCreateFiles();
|
|
||||||
}
|
|
||||||
if (complete())
|
|
||||||
_util.debug("Torrent is complete", Snark.NOTICE);
|
|
||||||
else
|
|
||||||
_util.debug("Still need " + needed + " out of " + metainfo.getPieces() + " pieces", Snark.NOTICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reopen the file descriptors for a restart
|
|
||||||
* Do existence check but no length check or data reverification
|
|
||||||
*/
|
|
||||||
public void reopen(String rootDir) throws IOException
|
|
||||||
{
|
|
||||||
File base = new File(rootDir, filterName(metainfo.getName()));
|
|
||||||
|
|
||||||
List files = metainfo.getFiles();
|
|
||||||
if (files == null)
|
|
||||||
{
|
|
||||||
// Reopen base as file.
|
|
||||||
_util.debug("Reopening file: " + base, Snark.NOTICE);
|
|
||||||
if (!base.exists())
|
|
||||||
throw new IOException("Could not reopen file " + base);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Reopen base as dir.
|
|
||||||
_util.debug("Reopening directory: " + base, Snark.NOTICE);
|
|
||||||
if (!base.isDirectory())
|
|
||||||
throw new IOException("Could not reopen directory " + base);
|
|
||||||
|
|
||||||
int size = files.size();
|
|
||||||
for (int i = 0; i < size; i++)
|
|
||||||
{
|
|
||||||
File f = getFileFromNames(base, (List)files.get(i));
|
|
||||||
if (!f.exists())
|
|
||||||
throw new IOException("Could not reopen file " + f);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes 'suspicious' characters from the give file name.
|
|
||||||
*/
|
|
||||||
private static String filterName(String name)
|
|
||||||
{
|
|
||||||
// XXX - Is this enough?
|
|
||||||
return name.replace(File.separatorChar, '_');
|
|
||||||
}
|
|
||||||
|
|
||||||
private File createFileFromNames(File base, List names) throws IOException
|
|
||||||
{
|
|
||||||
File f = null;
|
|
||||||
Iterator it = names.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
String name = filterName((String)it.next());
|
|
||||||
if (it.hasNext())
|
|
||||||
{
|
|
||||||
// Another dir in the hierarchy.
|
|
||||||
f = new File(base, name);
|
|
||||||
if (!f.mkdir() && !f.isDirectory())
|
|
||||||
throw new IOException("Could not create directory " + f);
|
|
||||||
base = f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The final element (file) in the hierarchy.
|
|
||||||
f = new File(base, name);
|
|
||||||
if (!f.createNewFile() && !f.exists())
|
|
||||||
throw new IOException("Could not create file " + f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File getFileFromNames(File base, List names)
|
|
||||||
{
|
|
||||||
Iterator it = names.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
String name = filterName((String)it.next());
|
|
||||||
base = new File(base, name);
|
|
||||||
}
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is called at the beginning, and at presumed completion,
|
|
||||||
* so we have to be careful about locking.
|
|
||||||
*/
|
|
||||||
private void checkCreateFiles() throws IOException
|
|
||||||
{
|
|
||||||
// Whether we are resuming or not,
|
|
||||||
// if any of the files already exists we assume we are resuming.
|
|
||||||
boolean resume = false;
|
|
||||||
|
|
||||||
_probablyComplete = true;
|
|
||||||
needed = metainfo.getPieces();
|
|
||||||
|
|
||||||
// Make sure all files are available and of correct length
|
|
||||||
for (int i = 0; i < rafs.length; i++)
|
|
||||||
{
|
|
||||||
long length = RAFfile[i].length();
|
|
||||||
if(RAFfile[i].exists() && length == lengths[i])
|
|
||||||
{
|
|
||||||
if (listener != null)
|
|
||||||
listener.storageAllocated(this, length);
|
|
||||||
resume = true; // XXX Could dynamicly check
|
|
||||||
}
|
|
||||||
else if (length == 0) {
|
|
||||||
changed = true;
|
|
||||||
synchronized(RAFlock[i]) {
|
|
||||||
allocateFile(i);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_util.debug("File '" + names[i] + "' exists, but has wrong length - repairing corruption", Snark.ERROR);
|
|
||||||
changed = true;
|
|
||||||
_probablyComplete = false; // to force RW
|
|
||||||
synchronized(RAFlock[i]) {
|
|
||||||
checkRAF(i);
|
|
||||||
rafs[i].setLength(lengths[i]);
|
|
||||||
}
|
|
||||||
// will be closed below
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check which pieces match and which don't
|
|
||||||
if (resume)
|
|
||||||
{
|
|
||||||
pieces = metainfo.getPieces();
|
|
||||||
byte[] piece = new byte[metainfo.getPieceLength(0)];
|
|
||||||
for (int i = 0; i < pieces; i++)
|
|
||||||
{
|
|
||||||
int length = getUncheckedPiece(i, piece);
|
|
||||||
boolean correctHash = metainfo.checkPiece(i, piece, 0, length);
|
|
||||||
if (correctHash)
|
|
||||||
{
|
|
||||||
bitfield.set(i);
|
|
||||||
needed--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listener != null)
|
|
||||||
listener.storageChecked(this, i, correctHash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_probablyComplete = complete();
|
|
||||||
// close all the files so we don't end up with a zillion open ones;
|
|
||||||
// we will reopen as needed
|
|
||||||
for (int i = 0; i < rafs.length; i++) {
|
|
||||||
synchronized(RAFlock[i]) {
|
|
||||||
try {
|
|
||||||
closeRAF(i);
|
|
||||||
} catch (IOException ioe) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listener != null) {
|
|
||||||
listener.storageAllChecked(this);
|
|
||||||
if (needed <= 0)
|
|
||||||
listener.storageCompleted(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void allocateFile(int nr) throws IOException
|
|
||||||
{
|
|
||||||
// caller synchronized
|
|
||||||
openRAF(nr, false); // RW
|
|
||||||
// XXX - Is this the best way to make sure we have enough space for
|
|
||||||
// the whole file?
|
|
||||||
listener.storageCreateFile(this, names[nr], lengths[nr]);
|
|
||||||
final int ZEROBLOCKSIZE = metainfo.getPieceLength(0);
|
|
||||||
byte[] zeros = new byte[ZEROBLOCKSIZE];
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < lengths[nr]/ZEROBLOCKSIZE; i++)
|
|
||||||
{
|
|
||||||
rafs[nr].write(zeros);
|
|
||||||
if (listener != null)
|
|
||||||
listener.storageAllocated(this, ZEROBLOCKSIZE);
|
|
||||||
}
|
|
||||||
int size = (int)(lengths[nr] - i*ZEROBLOCKSIZE);
|
|
||||||
rafs[nr].write(zeros, 0, size);
|
|
||||||
// caller will close rafs[nr]
|
|
||||||
if (listener != null)
|
|
||||||
listener.storageAllocated(this, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the Storage and makes sure that all RandomAccessFiles are
|
|
||||||
* closed. The Storage is unusable after this.
|
|
||||||
*/
|
|
||||||
public void close() throws IOException
|
|
||||||
{
|
|
||||||
if (rafs == null) return;
|
|
||||||
for (int i = 0; i < rafs.length; i++)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
synchronized(RAFlock[i]) {
|
|
||||||
closeRAF(i);
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_util.debug("Error closing " + RAFfile[i], Snark.ERROR, ioe);
|
|
||||||
// gobble gobble
|
|
||||||
}
|
|
||||||
}
|
|
||||||
changed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a byte array containing a portion of the requested piece or null if
|
|
||||||
* the storage doesn't contain the piece yet.
|
|
||||||
*/
|
|
||||||
public byte[] getPiece(int piece, int off, int len) throws IOException
|
|
||||||
{
|
|
||||||
if (!bitfield.get(piece))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
//Catch a common place for OOMs esp. on 1MB pieces
|
|
||||||
byte[] bs;
|
|
||||||
try {
|
|
||||||
bs = new byte[len];
|
|
||||||
} catch (OutOfMemoryError oom) {
|
|
||||||
_util.debug("Out of memory, can't honor request for piece " + piece, Snark.WARNING, oom);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
getUncheckedPiece(piece, bs, off, len);
|
|
||||||
return bs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Put the piece in the Storage if it is correct.
|
|
||||||
*
|
|
||||||
* @return true if the piece was correct (sha metainfo hash
|
|
||||||
* matches), otherwise false.
|
|
||||||
* @exception IOException when some storage related error occurs.
|
|
||||||
*/
|
|
||||||
public boolean putPiece(int piece, byte[] ba) throws IOException
|
|
||||||
{
|
|
||||||
// First check if the piece is correct.
|
|
||||||
// Copy the array first to be paranoid.
|
|
||||||
byte[] bs = (byte[]) ba.clone();
|
|
||||||
int length = bs.length;
|
|
||||||
boolean correctHash = metainfo.checkPiece(piece, bs, 0, length);
|
|
||||||
if (listener != null)
|
|
||||||
listener.storageChecked(this, piece, correctHash);
|
|
||||||
if (!correctHash)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
synchronized(bitfield)
|
|
||||||
{
|
|
||||||
if (bitfield.get(piece))
|
|
||||||
return true; // No need to store twice.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Early typecast, avoid possibly overflowing a temp integer
|
|
||||||
long start = (long) piece * (long) metainfo.getPieceLength(0);
|
|
||||||
int i = 0;
|
|
||||||
long raflen = lengths[i];
|
|
||||||
while (start > raflen)
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
start -= raflen;
|
|
||||||
raflen = lengths[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
int written = 0;
|
|
||||||
int off = 0;
|
|
||||||
while (written < length)
|
|
||||||
{
|
|
||||||
int need = length - written;
|
|
||||||
int len = (start + need < raflen) ? need : (int)(raflen - start);
|
|
||||||
synchronized(RAFlock[i])
|
|
||||||
{
|
|
||||||
checkRAF(i);
|
|
||||||
rafs[i].seek(start);
|
|
||||||
rafs[i].write(bs, off + written, len);
|
|
||||||
}
|
|
||||||
written += len;
|
|
||||||
if (need - len > 0)
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
raflen = lengths[i];
|
|
||||||
start = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
changed = true;
|
|
||||||
|
|
||||||
// do this after the write, so we know it succeeded, and we don't set the
|
|
||||||
// needed count to zero, which would cause checkRAF() to open the file readonly.
|
|
||||||
boolean complete = false;
|
|
||||||
synchronized(bitfield)
|
|
||||||
{
|
|
||||||
if (!bitfield.get(piece))
|
|
||||||
{
|
|
||||||
bitfield.set(piece);
|
|
||||||
needed--;
|
|
||||||
complete = needed == 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (complete) {
|
|
||||||
// do we also need to close all of the files and reopen
|
|
||||||
// them readonly?
|
|
||||||
|
|
||||||
// Do a complete check to be sure.
|
|
||||||
// Temporarily resets the 'needed' variable and 'bitfield', then call
|
|
||||||
// checkCreateFiles() which will set 'needed' and 'bitfield'
|
|
||||||
// and also call listener.storageCompleted() if the double-check
|
|
||||||
// was successful.
|
|
||||||
// Todo: set a listener variable so the web shows "checking" and don't
|
|
||||||
// have the user panic when completed amount goes to zero temporarily?
|
|
||||||
needed = metainfo.getPieces();
|
|
||||||
bitfield = new BitField(needed);
|
|
||||||
checkCreateFiles();
|
|
||||||
if (needed > 0) {
|
|
||||||
if (listener != null)
|
|
||||||
listener.setWantedPieces(this);
|
|
||||||
_util.debug("WARNING: Not really done, missing " + needed
|
|
||||||
+ " pieces", Snark.WARNING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getUncheckedPiece(int piece, byte[] bs)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
return getUncheckedPiece(piece, bs, 0, metainfo.getPieceLength(piece));
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getUncheckedPiece(int piece, byte[] bs, int off, int length)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
// XXX - copy/paste code from putPiece().
|
|
||||||
|
|
||||||
// Early typecast, avoid possibly overflowing a temp integer
|
|
||||||
long start = ((long) piece * (long) metainfo.getPieceLength(0)) + off;
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
long raflen = lengths[i];
|
|
||||||
while (start > raflen)
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
start -= raflen;
|
|
||||||
raflen = lengths[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
int read = 0;
|
|
||||||
while (read < length)
|
|
||||||
{
|
|
||||||
int need = length - read;
|
|
||||||
int len = (start + need < raflen) ? need : (int)(raflen - start);
|
|
||||||
synchronized(RAFlock[i])
|
|
||||||
{
|
|
||||||
checkRAF(i);
|
|
||||||
rafs[i].seek(start);
|
|
||||||
rafs[i].readFully(bs, read, len);
|
|
||||||
}
|
|
||||||
read += len;
|
|
||||||
if (need - len > 0)
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
raflen = lengths[i];
|
|
||||||
start = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close unused RAFs - call periodically
|
|
||||||
*/
|
|
||||||
private static final long RAFCloseDelay = 7*60*1000;
|
|
||||||
public void cleanRAFs() {
|
|
||||||
long cutoff = System.currentTimeMillis() - RAFCloseDelay;
|
|
||||||
for (int i = 0; i < RAFlock.length; i++) {
|
|
||||||
synchronized(RAFlock[i]) {
|
|
||||||
if (RAFtime[i] > 0 && RAFtime[i] < cutoff) {
|
|
||||||
try {
|
|
||||||
closeRAF(i);
|
|
||||||
} catch (IOException ioe) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For each of the following,
|
|
||||||
* caller must synchronize on RAFlock[i]
|
|
||||||
* ... except at the beginning if you're careful
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This must be called before using the RAF to ensure it is open
|
|
||||||
*/
|
|
||||||
private void checkRAF(int i) throws IOException {
|
|
||||||
if (RAFtime[i] > 0) {
|
|
||||||
RAFtime[i] = System.currentTimeMillis();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
openRAF(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openRAF(int i) throws IOException {
|
|
||||||
openRAF(i, _probablyComplete);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openRAF(int i, boolean readonly) throws IOException {
|
|
||||||
rafs[i] = new RandomAccessFile(RAFfile[i], (readonly || !RAFfile[i].canWrite()) ? "r" : "rw");
|
|
||||||
RAFtime[i] = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can be called even if not open
|
|
||||||
*/
|
|
||||||
private void closeRAF(int i) throws IOException {
|
|
||||||
RAFtime[i] = 0;
|
|
||||||
if (rafs[i] == null)
|
|
||||||
return;
|
|
||||||
rafs[i].close();
|
|
||||||
rafs[i] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
/* StorageListener.java - Interface used as callback when storage changes.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback used when Storage changes.
|
|
||||||
*/
|
|
||||||
public interface StorageListener
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Called when the storage creates a new file of a given length.
|
|
||||||
*/
|
|
||||||
void storageCreateFile(Storage storage, String name, long length);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called to indicate that length bytes have been allocated.
|
|
||||||
*/
|
|
||||||
void storageAllocated(Storage storage, long length);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when storage is being checked and the num piece of that
|
|
||||||
* total pieces has been checked. When the piece hash matches the
|
|
||||||
* expected piece hash checked will be true, otherwise it will be
|
|
||||||
* false.
|
|
||||||
*/
|
|
||||||
void storageChecked(Storage storage, int num, boolean checked);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when all pieces in the storage have been checked. Does not
|
|
||||||
* mean that the storage is complete, just that the state of the
|
|
||||||
* storage is known.
|
|
||||||
*/
|
|
||||||
void storageAllChecked(Storage storage);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called the one time when the data is completely received and checked.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void storageCompleted(Storage storage);
|
|
||||||
|
|
||||||
/** Reset the peer's wanted pieces table
|
|
||||||
* Call after the storage double-check fails
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void setWantedPieces(Storage storage);
|
|
||||||
}
|
|
@ -1,416 +0,0 @@
|
|||||||
/* TrackerClient - Class that informs a tracker and gets new peers.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import net.i2p.util.I2PAppThread;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Informs metainfo tracker of events and gets new peers for peer
|
|
||||||
* coordinator.
|
|
||||||
*
|
|
||||||
* @author Mark Wielaard (mark@klomp.org)
|
|
||||||
*/
|
|
||||||
public class TrackerClient extends I2PAppThread
|
|
||||||
{
|
|
||||||
private static final Log _log = new Log(TrackerClient.class);
|
|
||||||
private static final String NO_EVENT = "";
|
|
||||||
private static final String STARTED_EVENT = "started";
|
|
||||||
private static final String COMPLETED_EVENT = "completed";
|
|
||||||
private static final String STOPPED_EVENT = "stopped";
|
|
||||||
private static final String NOT_REGISTERED = "torrent not registered"; //bytemonsoon
|
|
||||||
|
|
||||||
private final static int SLEEP = 5; // 5 minutes.
|
|
||||||
private final static int DELAY_MIN = 2000; // 2 secs.
|
|
||||||
private final static int DELAY_MUL = 1500; // 1.5 secs.
|
|
||||||
private final static int MAX_REGISTER_FAILS = 10; // * INITIAL_SLEEP = 15m to register
|
|
||||||
private final static int INITIAL_SLEEP = 90*1000;
|
|
||||||
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 I2PSnarkUtil _util;
|
|
||||||
private final MetaInfo meta;
|
|
||||||
private final PeerCoordinator coordinator;
|
|
||||||
private final int port;
|
|
||||||
|
|
||||||
private boolean stop;
|
|
||||||
private boolean started;
|
|
||||||
|
|
||||||
private List trackers;
|
|
||||||
|
|
||||||
public TrackerClient(I2PSnarkUtil util, MetaInfo meta, PeerCoordinator coordinator)
|
|
||||||
{
|
|
||||||
// Set unique name.
|
|
||||||
super("TrackerClient-" + urlencode(coordinator.getID()));
|
|
||||||
_util = util;
|
|
||||||
this.meta = meta;
|
|
||||||
this.coordinator = coordinator;
|
|
||||||
|
|
||||||
this.port = 6881; //(port == -1) ? 9 : port;
|
|
||||||
|
|
||||||
stop = false;
|
|
||||||
started = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start() {
|
|
||||||
if (stop) throw new RuntimeException("Dont rerun me, create a copy");
|
|
||||||
super.start();
|
|
||||||
started = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean halted() { return stop; }
|
|
||||||
public boolean started() { return started; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interrupts this Thread to stop it.
|
|
||||||
*/
|
|
||||||
public void halt()
|
|
||||||
{
|
|
||||||
stop = true;
|
|
||||||
this.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean verifyConnected() {
|
|
||||||
while (!stop && !_util.connected()) {
|
|
||||||
boolean ok = _util.connect();
|
|
||||||
if (!ok) {
|
|
||||||
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !stop && _util.connected();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
String infoHash = urlencode(meta.getInfoHash());
|
|
||||||
String peerID = urlencode(coordinator.getID());
|
|
||||||
|
|
||||||
_log.debug("Announce: [" + meta.getAnnounce() + "] infoHash: " + infoHash);
|
|
||||||
|
|
||||||
// Construct the list of trackers for this torrent,
|
|
||||||
// starting with the primary one listed in the metainfo,
|
|
||||||
// followed by the secondary open trackers
|
|
||||||
// It's painful, but try to make sure if an open tracker is also
|
|
||||||
// the primary tracker, that we don't add it twice.
|
|
||||||
trackers = new ArrayList(2);
|
|
||||||
trackers.add(new Tracker(meta.getAnnounce(), true));
|
|
||||||
List tlist = _util.getOpenTrackers();
|
|
||||||
if (tlist != null) {
|
|
||||||
for (int i = 0; i < tlist.size(); i++) {
|
|
||||||
String url = (String)tlist.get(i);
|
|
||||||
if (!url.startsWith("http://")) {
|
|
||||||
_log.error("Bad announce URL: [" + url + "]");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int slash = url.indexOf('/', 7);
|
|
||||||
if (slash <= 7) {
|
|
||||||
_log.error("Bad announce URL: [" + url + "]");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (meta.getAnnounce().startsWith(url.substring(0, slash)))
|
|
||||||
continue;
|
|
||||||
String dest = _util.lookup(url.substring(7, slash));
|
|
||||||
if (dest == null) {
|
|
||||||
_log.error("Announce host unknown: [" + url + "]");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (meta.getAnnounce().startsWith("http://" + dest))
|
|
||||||
continue;
|
|
||||||
if (meta.getAnnounce().startsWith("http://i2p/" + dest))
|
|
||||||
continue;
|
|
||||||
trackers.add(new Tracker(url, false));
|
|
||||||
_log.debug("Additional announce: [" + url + "] for infoHash: " + infoHash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long uploaded = coordinator.getUploaded();
|
|
||||||
long downloaded = coordinator.getDownloaded();
|
|
||||||
long left = coordinator.getLeft();
|
|
||||||
|
|
||||||
boolean completed = (left == 0);
|
|
||||||
int sleptTime = 0;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!verifyConnected()) return;
|
|
||||||
boolean started = false;
|
|
||||||
boolean firstTime = true;
|
|
||||||
int consecutiveFails = 0;
|
|
||||||
Random r = new Random();
|
|
||||||
while(!stop)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Sleep some minutes...
|
|
||||||
// Sleep the minimum interval for all the trackers, but 60s minimum
|
|
||||||
// except for the first time...
|
|
||||||
int delay;
|
|
||||||
int random = r.nextInt(120*1000);
|
|
||||||
if (firstTime) {
|
|
||||||
delay = r.nextInt(30*1000);
|
|
||||||
firstTime = false;
|
|
||||||
} else if (completed && started)
|
|
||||||
delay = 3*SLEEP*60*1000 + random;
|
|
||||||
else if (coordinator.trackerProblems != null && ++consecutiveFails < MAX_CONSEC_FAILS)
|
|
||||||
delay = INITIAL_SLEEP;
|
|
||||||
else
|
|
||||||
// sleep a while, when we wake up we will contact only the trackers whose intervals have passed
|
|
||||||
delay = SLEEP*60*1000 + random;
|
|
||||||
|
|
||||||
if (delay > 0)
|
|
||||||
Thread.sleep(delay);
|
|
||||||
}
|
|
||||||
catch(InterruptedException interrupt)
|
|
||||||
{
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stop)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (!verifyConnected()) return;
|
|
||||||
|
|
||||||
uploaded = coordinator.getUploaded();
|
|
||||||
downloaded = coordinator.getDownloaded();
|
|
||||||
left = coordinator.getLeft();
|
|
||||||
|
|
||||||
// First time we got a complete download?
|
|
||||||
String event;
|
|
||||||
if (!completed && left == 0)
|
|
||||||
{
|
|
||||||
completed = true;
|
|
||||||
event = COMPLETED_EVENT;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
event = NO_EVENT;
|
|
||||||
|
|
||||||
// *** loop once for each tracker
|
|
||||||
// Only do a request when necessary.
|
|
||||||
sleptTime = 0;
|
|
||||||
int maxSeenPeers = 0;
|
|
||||||
for (Iterator iter = trackers.iterator(); iter.hasNext(); ) {
|
|
||||||
Tracker tr = (Tracker)iter.next();
|
|
||||||
if ((!stop) && (!tr.stop) &&
|
|
||||||
(completed || coordinator.needPeers()) &&
|
|
||||||
(event == COMPLETED_EVENT || System.currentTimeMillis() > tr.lastRequestTime + tr.interval))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!tr.started)
|
|
||||||
event = STARTED_EVENT;
|
|
||||||
TrackerInfo info = doRequest(tr, infoHash, peerID,
|
|
||||||
uploaded, downloaded, left,
|
|
||||||
event);
|
|
||||||
|
|
||||||
coordinator.trackerProblems = null;
|
|
||||||
tr.trackerProblems = null;
|
|
||||||
tr.registerFails = 0;
|
|
||||||
tr.consecutiveFails = 0;
|
|
||||||
if (tr.isPrimary)
|
|
||||||
consecutiveFails = 0;
|
|
||||||
started = true;
|
|
||||||
tr.started = true;
|
|
||||||
|
|
||||||
Set peers = info.getPeers();
|
|
||||||
tr.seenPeers = peers.size();
|
|
||||||
if (coordinator.trackerSeenPeers < tr.seenPeers) // update rising number quickly
|
|
||||||
coordinator.trackerSeenPeers = tr.seenPeers;
|
|
||||||
if ( (left > 0) && (!completed) ) {
|
|
||||||
// we only want to talk to new people if we need things
|
|
||||||
// from them (duh)
|
|
||||||
List ordered = new ArrayList(peers);
|
|
||||||
Collections.shuffle(ordered);
|
|
||||||
Iterator it = ordered.iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
Peer cur = (Peer)it.next();
|
|
||||||
// only delay if we actually make an attempt to add peer
|
|
||||||
if(coordinator.addPeer(cur)) {
|
|
||||||
int delay = DELAY_MUL;
|
|
||||||
delay *= ((int)cur.getPeerID().getAddress().calculateHash().toBase64().charAt(0)) % 10;
|
|
||||||
delay += DELAY_MIN;
|
|
||||||
sleptTime += delay;
|
|
||||||
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
// Probably not fatal (if it doesn't last to long...)
|
|
||||||
_util.debug
|
|
||||||
("WARNING: Could not contact tracker at '"
|
|
||||||
+ tr.announce + "': " + ioe, Snark.WARNING);
|
|
||||||
tr.trackerProblems = ioe.getMessage();
|
|
||||||
// don't show secondary tracker problems to the user
|
|
||||||
if (tr.isPrimary)
|
|
||||||
coordinator.trackerProblems = tr.trackerProblems;
|
|
||||||
if (tr.trackerProblems.toLowerCase().startsWith(NOT_REGISTERED)) {
|
|
||||||
// Give a guy some time to register it if using opentrackers too
|
|
||||||
if (trackers.size() == 1) {
|
|
||||||
stop = true;
|
|
||||||
coordinator.snark.stopTorrent();
|
|
||||||
} else { // hopefully each on the opentrackers list is really open
|
|
||||||
if (tr.registerFails++ > MAX_REGISTER_FAILS)
|
|
||||||
tr.stop = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (++tr.consecutiveFails == MAX_CONSEC_FAILS) {
|
|
||||||
tr.seenPeers = 0;
|
|
||||||
if (tr.interval < LONG_SLEEP)
|
|
||||||
tr.interval = LONG_SLEEP; // slow down
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((!tr.stop) && maxSeenPeers < tr.seenPeers)
|
|
||||||
maxSeenPeers = tr.seenPeers;
|
|
||||||
} // *** end of trackers loop here
|
|
||||||
|
|
||||||
// we could try and total the unique peers but that's too hard for now
|
|
||||||
coordinator.trackerSeenPeers = maxSeenPeers;
|
|
||||||
if (!started)
|
|
||||||
_util.debug(" Retrying in one minute...", Snark.DEBUG);
|
|
||||||
} // *** end of while loop
|
|
||||||
} // try
|
|
||||||
catch (Throwable t)
|
|
||||||
{
|
|
||||||
_util.debug("TrackerClient: " + t, Snark.ERROR, t);
|
|
||||||
if (t instanceof OutOfMemoryError)
|
|
||||||
throw (OutOfMemoryError)t;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// try to contact everybody we can
|
|
||||||
// We don't need I2CP connection for eepget
|
|
||||||
// if (!verifyConnected()) return;
|
|
||||||
for (Iterator iter = trackers.iterator(); iter.hasNext(); ) {
|
|
||||||
Tracker tr = (Tracker)iter.next();
|
|
||||||
if (tr.started && (!tr.stop) && tr.trackerProblems == null)
|
|
||||||
doRequest(tr, infoHash, peerID, uploaded,
|
|
||||||
downloaded, left, STOPPED_EVENT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(IOException ioe) { /* ignored */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private TrackerInfo doRequest(Tracker tr, String infoHash,
|
|
||||||
String peerID, long uploaded,
|
|
||||||
long downloaded, long left, String event)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
String s = tr.announce
|
|
||||||
+ "?info_hash=" + infoHash
|
|
||||||
+ "&peer_id=" + peerID
|
|
||||||
+ "&port=" + port
|
|
||||||
+ "&ip=" + _util.getOurIPString() + ".i2p"
|
|
||||||
+ "&uploaded=" + uploaded
|
|
||||||
+ "&downloaded=" + downloaded
|
|
||||||
+ "&left=" + left
|
|
||||||
+ ((event != NO_EVENT) ? ("&event=" + event) : "");
|
|
||||||
_util.debug("Sending TrackerClient request: " + s, Snark.INFO);
|
|
||||||
|
|
||||||
tr.lastRequestTime = System.currentTimeMillis();
|
|
||||||
File fetched = _util.get(s);
|
|
||||||
if (fetched == null) {
|
|
||||||
throw new IOException("Error fetching " + s);
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStream in = null;
|
|
||||||
try {
|
|
||||||
in = new FileInputStream(fetched);
|
|
||||||
|
|
||||||
TrackerInfo info = new TrackerInfo(in, coordinator.getID(),
|
|
||||||
coordinator.getMetaInfo());
|
|
||||||
_util.debug("TrackerClient response: " + info, Snark.INFO);
|
|
||||||
|
|
||||||
String failure = info.getFailureReason();
|
|
||||||
if (failure != null)
|
|
||||||
throw new IOException(failure);
|
|
||||||
|
|
||||||
tr.interval = info.getInterval() * 1000;
|
|
||||||
return info;
|
|
||||||
} finally {
|
|
||||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
|
||||||
fetched.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Very lazy byte[] to URL encoder. Just encodes everything, even
|
|
||||||
* "normal" chars.
|
|
||||||
*/
|
|
||||||
public static String urlencode(byte[] bs)
|
|
||||||
{
|
|
||||||
StringBuffer sb = new StringBuffer(bs.length*3);
|
|
||||||
for (int i = 0; i < bs.length; i++)
|
|
||||||
{
|
|
||||||
int c = bs[i] & 0xFF;
|
|
||||||
sb.append('%');
|
|
||||||
if (c < 16)
|
|
||||||
sb.append('0');
|
|
||||||
sb.append(Integer.toHexString(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Tracker
|
|
||||||
{
|
|
||||||
String announce;
|
|
||||||
boolean isPrimary;
|
|
||||||
long interval;
|
|
||||||
long lastRequestTime;
|
|
||||||
String trackerProblems;
|
|
||||||
boolean stop;
|
|
||||||
boolean started;
|
|
||||||
int registerFails;
|
|
||||||
int consecutiveFails;
|
|
||||||
int seenPeers;
|
|
||||||
|
|
||||||
public Tracker(String a, boolean p)
|
|
||||||
{
|
|
||||||
announce = a;
|
|
||||||
isPrimary = p;
|
|
||||||
interval = INITIAL_SLEEP;
|
|
||||||
lastRequestTime = 0;
|
|
||||||
trackerProblems = null;
|
|
||||||
stop = false;
|
|
||||||
started = false;
|
|
||||||
registerFails = 0;
|
|
||||||
consecutiveFails = 0;
|
|
||||||
seenPeers = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
/* TrackerInfo - Holds information returned by a tracker, mainly the peer list.
|
|
||||||
Copyright (C) 2003 Mark J. Wielaard
|
|
||||||
|
|
||||||
This file is part of Snark.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.klomp.snark;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.klomp.snark.bencode.BDecoder;
|
|
||||||
import org.klomp.snark.bencode.BEValue;
|
|
||||||
import org.klomp.snark.bencode.InvalidBEncodingException;
|
|
||||||
|
|
||||||
public class TrackerInfo
|
|
||||||
{
|
|
||||||
private final String failure_reason;
|
|
||||||
private final int interval;
|
|
||||||
private final Set peers;
|
|
||||||
|
|
||||||
public TrackerInfo(InputStream in, byte[] my_id, MetaInfo metainfo)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
this(new BDecoder(in), my_id, metainfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TrackerInfo(BDecoder be, byte[] my_id, MetaInfo metainfo)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
this(be.bdecodeMap().getMap(), my_id, metainfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TrackerInfo(Map m, byte[] my_id, MetaInfo metainfo)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
BEValue reason = (BEValue)m.get("failure reason");
|
|
||||||
if (reason != null)
|
|
||||||
{
|
|
||||||
failure_reason = reason.getString();
|
|
||||||
interval = -1;
|
|
||||||
peers = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
failure_reason = null;
|
|
||||||
BEValue beInterval = (BEValue)m.get("interval");
|
|
||||||
if (beInterval == null)
|
|
||||||
throw new InvalidBEncodingException("No interval given");
|
|
||||||
else
|
|
||||||
interval = beInterval.getInt();
|
|
||||||
BEValue bePeers = (BEValue)m.get("peers");
|
|
||||||
if (bePeers == null)
|
|
||||||
throw new InvalidBEncodingException("No peer list");
|
|
||||||
else
|
|
||||||
peers = getPeers(bePeers.getList(), my_id, metainfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Set getPeers(InputStream in, byte[] my_id, MetaInfo metainfo)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
return getPeers(new BDecoder(in), my_id, metainfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Set getPeers(BDecoder be, byte[] my_id, MetaInfo metainfo)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
return getPeers(be.bdecodeList().getList(), my_id, metainfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Set getPeers(List l, byte[] my_id, MetaInfo metainfo)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
Set peers = new HashSet(l.size());
|
|
||||||
|
|
||||||
Iterator it = l.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
PeerID peerID;
|
|
||||||
try {
|
|
||||||
peerID = new PeerID(((BEValue)it.next()).getMap());
|
|
||||||
} catch (InvalidBEncodingException ibe) {
|
|
||||||
// don't let one bad entry spoil the whole list
|
|
||||||
//Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
peers.add(new Peer(peerID, my_id, metainfo));
|
|
||||||
}
|
|
||||||
|
|
||||||
return peers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set getPeers()
|
|
||||||
{
|
|
||||||
return peers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFailureReason()
|
|
||||||
{
|
|
||||||
return failure_reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getInterval()
|
|
||||||
{
|
|
||||||
return interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
if (failure_reason != null)
|
|
||||||
return "TrackerInfo[FAILED: " + failure_reason + "]";
|
|
||||||
else
|
|
||||||
return "TrackerInfo[interval=" + interval
|
|
||||||
+ ", peers=" + peers + "]";
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user