forked from I2P_Developers/i2p.i2p
Compare commits
2235 Commits
i2p-0.9.32
...
i2p-0.9.46
| Author | SHA1 | Date | |
|---|---|---|---|
| e9cc4a4357 | |||
| 0ab4730002 | |||
| 31d7b6fb7d | |||
| 26e5f4c482 | |||
| 766649bd54 | |||
| bb6641ed96 | |||
| 6aa81f7ec6 | |||
|
|
a3fc8af1dd | ||
|
|
05082e3ffa | ||
|
|
4b655070d4 | ||
| f25037447d | |||
| df3a03afc5 | |||
| ded4bde905 | |||
|
|
e1d8e360a5 | ||
|
|
b373e253c9 | ||
| a9d84da3b3 | |||
| 4b7c62aa9c | |||
| e16c0a3271 | |||
| ef66457181 | |||
| 093c46c937 | |||
| d48d16d237 | |||
| 65cd84dd2e | |||
| c457ef68c7 | |||
| f00b86475d | |||
| 90bc00436c | |||
| 1af32bfe79 | |||
| 25338019ca | |||
| 878c11b36f | |||
| c6c9ba76d9 | |||
| a0261e8fd7 | |||
| d493028c1e | |||
| 3992ea235d | |||
| 31b527a4c4 | |||
| 09b647f283 | |||
| 40b312d7c1 | |||
| fad9d0e3da | |||
| 31a57b1a60 | |||
| 50e44ece54 | |||
| f233416bf6 | |||
| 1b3885720a | |||
| 82dd9435a3 | |||
| bc89d247ec | |||
| fe36482063 | |||
| a47f6d1b2e | |||
| 8aa23c7dc7 | |||
| 0eef0dd21b | |||
| a12a26f65c | |||
| 1ebed8b6aa | |||
| deb0fe338c | |||
| 005f79c95f | |||
| eeee8d7600 | |||
| 13cfdf0d5a | |||
| e77c3f23d3 | |||
| d157daea10 | |||
| 9aa36562b9 | |||
| 6a36b79a95 | |||
| 2cd2f25c56 | |||
| 8631db8769 | |||
| 7da2ac9ef3 | |||
| 4da58258f5 | |||
| 4f78040569 | |||
| 0738d1d1fb | |||
| e99e15209a | |||
|
|
96a8aa9d0c | ||
|
|
0bbd747f82 | ||
| e31b837a7f | |||
| ab7e428624 | |||
| 565e7f4d67 | |||
| 910de68495 | |||
| e904c9981e | |||
| ddc372f58c | |||
| 4ff234c110 | |||
| 69ef47e68c | |||
| 59901ec7c3 | |||
| 13e38f3d3e | |||
| 4b85f06951 | |||
| 08df2ba907 | |||
| 7a192b4961 | |||
| d18f622458 | |||
| 8a482659e0 | |||
| eeecede382 | |||
| d49741c2db | |||
| 2ac2850cd4 | |||
| 3d56545210 | |||
| 5de76252ea | |||
| 778d6a2809 | |||
|
|
f90cbaba5a | ||
| 6510e07b8b | |||
| 4996c05361 | |||
| 78effe14ad | |||
| 122b12fa31 | |||
| 438703a29b | |||
| 467a48cfe1 | |||
| 2b1c6a9d8f | |||
| f1307614c6 | |||
| ea8e340895 | |||
| 26af857dc7 | |||
| f04526d83a | |||
| a255a60944 | |||
| b4de0cfaf9 | |||
| 2188d6b222 | |||
| 0528e4109d | |||
| 47f09479ad | |||
| 80d24fa70d | |||
|
|
bef9ea11bf | ||
|
|
278090a772 | ||
| eab688b0bc | |||
| 788c72c331 | |||
| 90d9843acc | |||
| d7095b69b9 | |||
| dd96e29213 | |||
| fd3e181b7c | |||
| d7b5783674 | |||
| 634802c008 | |||
| 9e233b42a5 | |||
| 4cf8bfbe0d | |||
| e1beeb3653 | |||
| 82a918b37d | |||
| b6ecad3c0f | |||
| e9d56d85af | |||
| b874bb2ba0 | |||
| 7f179551b7 | |||
| 10be87bb59 | |||
| 5173d24c72 | |||
| f4a37ce75f | |||
|
|
ff4cfd3eb0 | ||
|
|
8b6d8507ec | ||
|
|
65356f09a6 | ||
|
|
b0c6c845bf | ||
|
|
6859baba9f | ||
|
|
c14541f4a3 | ||
|
|
1083d2939b | ||
| 2a8f23602a | |||
| da5c92a599 | |||
| baa26aec26 | |||
| 9da290831b | |||
| 882f853b1d | |||
| a7de9a7f24 | |||
| f2f6dfbf18 | |||
| 5e2db982dd | |||
| bc11e66429 | |||
| da8f49a712 | |||
| a97085ac67 | |||
| eb7211660f | |||
| b182562c80 | |||
| 25a80c78b5 | |||
| c69af8308f | |||
| 0cc1861649 | |||
| 57cd4c5843 | |||
| cd035e1247 | |||
| 73886d06e8 | |||
|
|
ae199581be | ||
| bf425d8ac9 | |||
|
|
823dc72eaa | ||
| 3716ccc3ef | |||
| 108b3a2363 | |||
| 35bb5896a1 | |||
| da9bfd48c0 | |||
|
|
0bd3df6d98 | ||
|
|
0656abd101 | ||
|
|
5299eb365f | ||
| 53eb0fe7f8 | |||
| 4cdaa11024 | |||
| e642d8e538 | |||
| cc5f13fc5e | |||
| 80f66113c4 | |||
| b3d5accca2 | |||
| 12ac06d5ba | |||
| 7b47d3f314 | |||
| 0d2dbcc8fc | |||
| e2cc62a21f | |||
| 689b26102b | |||
| c989addadc | |||
| 5e00bc6510 | |||
| 828311a24f | |||
| 5976d4952f | |||
| 80ae2ccea6 | |||
| 370b7f1124 | |||
| 26b90b9d17 | |||
| c8647fc3a5 | |||
| 3fa15824ee | |||
| 1752291ffd | |||
|
|
4edb9bbf7f | ||
|
|
e88a585703 | ||
| 928c0e9b91 | |||
| 83a4f5f2f0 | |||
| f54db66f77 | |||
| f16981d844 | |||
| f6b5a2d493 | |||
| 14b33a1e4c | |||
| c99adeeb61 | |||
| b979a97905 | |||
| 916b296ee0 | |||
| f9f64a441b | |||
| 2482df7121 | |||
| f9d7cfa7e1 | |||
| 5229c0e811 | |||
| 1ca9674f3f | |||
| c77e41c59e | |||
| 8c4410277d | |||
| 2ec9a4ab64 | |||
| 6b05acff8d | |||
| 471b53698a | |||
| 7404bdc4fd | |||
| 9307cc8a0c | |||
| 6dd0b23c61 | |||
| 97f002bfb5 | |||
| 22ca4d0e44 | |||
| 86fc6478f5 | |||
| dee92b5290 | |||
| b19b529afe | |||
| 0c4cf5d3cd | |||
| eeb7ea4cae | |||
| 23634afbc9 | |||
| acf3abb19b | |||
| c5435410ba | |||
| 786d68ce9a | |||
| 0170ff2337 | |||
| a5c0448053 | |||
| 7a1e2865fc | |||
| 0c19216c4c | |||
| f2787a8df6 | |||
| 7654a0af42 | |||
| 5c1700c2ab | |||
| 6487fb0516 | |||
| 42fb3bb2e9 | |||
|
|
f57876dc3b | ||
|
|
0a7027dd9b | ||
| 0e68df6ba4 | |||
| 7707c4bb94 | |||
|
|
56639fa7d2 | ||
| f0a15d084a | |||
| de6cee8f6a | |||
| 8b075f26bf | |||
| 5455820a74 | |||
| d38c660c36 | |||
| 3f629ce1af | |||
| 29f9986f40 | |||
| d505a2fe76 | |||
| 3eb573295b | |||
| 0b9babab42 | |||
| c190ddd6b0 | |||
| 53118fa9c6 | |||
| 8cf241f6da | |||
| 6774af6910 | |||
| eb73f0aae2 | |||
| 0bec84a3b1 | |||
|
|
75c2d24c45 | ||
|
|
53e77df77d | ||
| 326a85ea4f | |||
| e567f7b460 | |||
| ab28ee960e | |||
| 88a4261b03 | |||
| e7a66659e9 | |||
| 44a935d086 | |||
| 1e5414f74f | |||
| 357d400bc6 | |||
| 594f1c7f8b | |||
| 4a845f0b73 | |||
| bc791f91ab | |||
| f938090d6a | |||
| d9ea6b1f9c | |||
| 5bed4a0d7f | |||
| 4826bbd333 | |||
| 0540c76c51 | |||
| f081e88420 | |||
| 4030d0b427 | |||
| c5f6c9a498 | |||
| 403440bab8 | |||
| 8ed7a029d7 | |||
| ec6807cd2c | |||
| 226494028a | |||
| 4bcd896829 | |||
| 2524a7a69d | |||
| 2805388a4c | |||
| 631a082ebd | |||
| 24b98b86c2 | |||
| 5ee903765e | |||
| c0db50ed0f | |||
| 3403ea3b51 | |||
| daf595ab7e | |||
| fd958df118 | |||
| dd794ceee4 | |||
| c19a63c6a2 | |||
| 84bea6438f | |||
| d3bd2ce190 | |||
| e82f420ee6 | |||
| a035901f0a | |||
| e73ddb548a | |||
| fd87c609f2 | |||
| 17060c0100 | |||
| fe16ccb434 | |||
| a46100bde5 | |||
| 0478ac75e9 | |||
| d42235486e | |||
| dd3dbbf7f9 | |||
|
|
efe85cc30a | ||
|
|
0b3e6acb97 | ||
|
|
aa6c218122 | ||
|
|
033c37f4a0 | ||
| e7ddee5509 | |||
| f8283c04ca | |||
| 0f7bcbf4bb | |||
|
|
c4fce448c3 | ||
| c4c75c0ce5 | |||
| 2988e58cb2 | |||
|
|
21ae518102 | ||
| 8bebb884c3 | |||
| 4401265200 | |||
|
|
094613b8de | ||
|
|
6e38cce149 | ||
|
|
9c626a07c5 | ||
|
|
80db2faeb7 | ||
|
|
b31eb3f53b | ||
|
|
b572a330ed | ||
| 98f7f30864 | |||
| 23d24a48b5 | |||
| 064e4046a6 | |||
|
|
50c86147b0 | ||
| 3c9e78bd76 | |||
| f65cfbf92c | |||
|
|
4ad4e579d8 | ||
|
|
1f6b3c1107 | ||
| a2a646e1f5 | |||
| ea05af42fa | |||
|
|
520faf477d | ||
|
|
cf2a4ba053 | ||
| 1a0b25a6ef | |||
| 23598ab011 | |||
| 7ac189696b | |||
| ecdccac37e | |||
| e865f451ca | |||
| 3d80f0122f | |||
| 0a34f3d5dd | |||
|
|
b588297458 | ||
|
|
b524e91888 | ||
|
|
29f8fec912 | ||
| 09d31cb107 | |||
| fa9f60bcd9 | |||
| 63b48e30be | |||
| e714e7082e | |||
| 9966c40d28 | |||
| 9dbec9fbb5 | |||
| aa472feea9 | |||
| c7a6584481 | |||
| ae994e3230 | |||
| fcd8a3ae69 | |||
| ccaf4ce4b9 | |||
| 5772b7d9ae | |||
| 61ec10ff47 | |||
| c3138793fe | |||
| c5904b080b | |||
| 7d10ef7a19 | |||
| 938410d68b | |||
| dd0d84fece | |||
| b2aa649c2a | |||
| 74020ae5a3 | |||
| 6f3cfd83cd | |||
| 10b8c92fdb | |||
| 520da91735 | |||
| d73fc85c34 | |||
| 4eca698584 | |||
| 3fa3b69cbc | |||
| bf94460420 | |||
| d054652952 | |||
| ad3c978c7c | |||
| 3277ea4854 | |||
| 7b6e25db8b | |||
| 1974cc5559 | |||
| 9c534fda7b | |||
| a5efce883a | |||
| 6197454225 | |||
| 64e9e2aebe | |||
| faf130cf38 | |||
| 831c1945a5 | |||
| 26ecf364a6 | |||
| 74b61a7ba6 | |||
| d65208b917 | |||
| bf70a1520b | |||
| 3a3416d2a5 | |||
| 00667151da | |||
| a9abf3babf | |||
| cad3c46ea6 | |||
| 9289a6daa9 | |||
|
|
f2f29d6a6c | ||
|
|
3e888f8936 | ||
|
|
cbccba8dc2 | ||
| 7cfc16ca36 | |||
| f7a2d23f1e | |||
| 31e8ff8f45 | |||
| 3a7ee4f211 | |||
| cc3c2d4d1a | |||
| 11d89e248e | |||
| 2bb94bea80 | |||
| 2bfd421b19 | |||
| 4c9c83231b | |||
| 32044f1682 | |||
| 51f7348d8a | |||
|
|
ae345cfb85 | ||
|
|
52edc35275 | ||
|
|
d5bc948bb4 | ||
|
|
86ff3420b5 | ||
| 6b0e3750c9 | |||
| 600c681143 | |||
| e1189b9c9f | |||
| 9feeb76d6c | |||
| 9f02b27c4a | |||
| 345cdd3eb0 | |||
| def1e2ec68 | |||
| 62c9ae67d0 | |||
| 10b756b726 | |||
| bf9d136a2b | |||
|
|
9ffd71e0b8 | ||
| 6fa13313f0 | |||
| 27fa991783 | |||
|
|
09f6429b5f | ||
|
|
252a7972a1 | ||
|
|
131ebc4d5a | ||
| 4e81e48d64 | |||
| 7b391fa17a | |||
| 5e67f4232a | |||
| 398b4ceade | |||
| 03f4624f91 | |||
| 79334afcbc | |||
| c711d48835 | |||
| 394db0b307 | |||
| 8bb1347e6e | |||
| e22810fd93 | |||
| 8218d55874 | |||
| 652b75a749 | |||
| 620917cd70 | |||
| b8ed77da9a | |||
| 65b3cdbb12 | |||
| cecf255706 | |||
| a854ccee69 | |||
| 6e2ad50e16 | |||
| fdb3c68d3f | |||
| d2e2109799 | |||
| 9f0c3ee345 | |||
| 725509fe5c | |||
| 13318a12da | |||
| e8bf2ee30d | |||
|
|
eda91af7c7 | ||
| 9ddb655a88 | |||
| 9d46a5d838 | |||
|
|
f7471713da | ||
|
|
b5d7f3e460 | ||
|
|
7b53b0d3ad | ||
|
|
ff293d50a9 | ||
| 552100da17 | |||
| a2b23f96e8 | |||
| 14499c2993 | |||
| dfc533b6e8 | |||
| 2738b3d29c | |||
| 07b7ab4262 | |||
| b7f6cfbf46 | |||
| ebc8dbe947 | |||
| 01d6cea017 | |||
| 160bcd7da8 | |||
| bc40978297 | |||
| 656dd42276 | |||
| 4d1d11d1d4 | |||
| 3ae5b90c98 | |||
| 2846c33b40 | |||
| b5f6c58a0b | |||
| 3ba48fda86 | |||
| 7c4569816f | |||
| 0cd8073f39 | |||
| 71411be6d9 | |||
| a51ee8e745 | |||
| 3719081469 | |||
| 2216a58143 | |||
| dc29525e5c | |||
|
|
e5163c6ee5 | ||
|
|
c5db5f0de3 | ||
| 3d75b3dc31 | |||
| 591b994b75 | |||
| 135e9ad31f | |||
| eee9e47cac | |||
| df6465f802 | |||
| 0c256d30c7 | |||
| 43c93bceed | |||
| f021abcae0 | |||
| 7b28640e91 | |||
| 6a47319b66 | |||
| ee46678955 | |||
| 8c498069d8 | |||
| 7389216560 | |||
| 129e474ecb | |||
| 3ed8620e5a | |||
| 436a8b8720 | |||
| 5d8871c17c | |||
| d84fc4f0c8 | |||
| 2c2f90089b | |||
| 236354e5a8 | |||
| aa3d2f39b1 | |||
| fb67ebb38d | |||
| a9d4798bfe | |||
| 8484a22fc4 | |||
| 0f7ebf2f71 | |||
| db37745a1c | |||
| b12e7214c8 | |||
| 079d464629 | |||
| 6dd2e9bb6b | |||
| 413eb7d0eb | |||
| 68a03b835a | |||
| eef6c5cb33 | |||
| 652f9bb6a0 | |||
| ab7b85cc1a | |||
| 2335f547f7 | |||
| 701f777035 | |||
| 77259293a8 | |||
| 4e231b26c3 | |||
| 9889d1adcb | |||
|
|
2377b1adde | ||
| 47aa6101d4 | |||
| 7161785c5b | |||
| 2ca9fe2050 | |||
| 1095a140de | |||
|
|
0485e690c6 | ||
|
|
20103957a9 | ||
| 9670858095 | |||
|
|
2bc8d58476 | ||
| 70fd0c6f6a | |||
| 2b1a7015e4 | |||
| e8404a75fb | |||
| 05c30b4f1d | |||
| 29eabc4706 | |||
| 049b34f7cf | |||
| 31f2c51e7e | |||
| 71cc55fa7d | |||
|
|
68a65d5cac | ||
|
|
e4c5c4862f | ||
|
|
cb2544157f | ||
| 612bdda281 | |||
| db5dd6a626 | |||
|
|
16db73b092 | ||
|
|
c259abc82c | ||
|
|
99b33fabe6 | ||
|
|
81133a1a95 | ||
|
|
9b3380cef3 | ||
|
|
87ba8577e9 | ||
| aca5617935 | |||
|
|
e44a7c5054 | ||
| 142508c872 | |||
| 788e041939 | |||
| 3563fdf9e4 | |||
| ea8ac884fa | |||
| bf33e8432d | |||
| 18ed1a6bb3 | |||
| 830e08065b | |||
| c1722a9f82 | |||
| e5475bc229 | |||
|
|
d81bf6a417 | ||
| 3710ff9aa1 | |||
|
|
679aa1afac | ||
| 320569ef7f | |||
| 4f0d764540 | |||
| a768afe05b | |||
| d15e068123 | |||
| c1431565ed | |||
| 2b6cb2099a | |||
| f9a2193e2f | |||
| 0d325d5a28 | |||
| 520da24e0c | |||
| 69638caa7b | |||
| 62914d7678 | |||
| 94c96b09e9 | |||
| 0c2a8e9244 | |||
| 67cd6409a0 | |||
| 65d0ea3f0b | |||
| f1b725a320 | |||
| 9e39cbe502 | |||
| 8d104f7fea | |||
| e66d64d89b | |||
| 9d28b17a04 | |||
| 43f055ec28 | |||
| b4a5cc07c2 | |||
| 479461ab3b | |||
| c99a42f0b1 | |||
| 65698aa0da | |||
| 7f75d0254c | |||
| 331ecf0625 | |||
| 32d420e76f | |||
| ee5a22be24 | |||
| b51962aeac | |||
| e73640bb8d | |||
| 92515179e9 | |||
| c5f126c196 | |||
| bb88555a63 | |||
| e03a94647c | |||
| 8eda9abab7 | |||
| aed6d433c8 | |||
| 71376d53c5 | |||
| 9a84f77fab | |||
| 2590fd8253 | |||
| ea3bddbcd6 | |||
| d04f0dae37 | |||
| 3504bddea4 | |||
| b3a5f1f8c1 | |||
| 0f2f7e2454 | |||
| b119d0be43 | |||
| 566221b732 | |||
| 81ab35abe6 | |||
| b2e37243ab | |||
| a6e3621c06 | |||
| 96d8385f49 | |||
| 912d25b775 | |||
| 55cdd81017 | |||
| a1c18fd0aa | |||
| 5f3da69acc | |||
| e6dcfaee15 | |||
| f724b2208b | |||
| 89b70895d7 | |||
|
|
cff2ae3ac9 | ||
| ec0f8566b6 | |||
| 2ebe59436a | |||
| dcf6983607 | |||
| 1779202ff9 | |||
| 6483abe7cf | |||
| 6618561237 | |||
| d8c9b0942f | |||
| 9938e50528 | |||
| 232b7f30d5 | |||
| adba7e4c25 | |||
| 764f89f5c5 | |||
| 87180e0e2d | |||
|
|
6789a735e3 | ||
|
|
65eb9b17db | ||
| 64ba43c007 | |||
| b2dec2f4b2 | |||
| 789f482373 | |||
| a667c36d2d | |||
| 65e2132a25 | |||
| 5e76118ffd | |||
| 131a70057e | |||
|
|
21ace66a46 | ||
|
|
459eb7fdcd | ||
|
|
8586c28b5f | ||
|
|
f32c863ba8 | ||
|
|
ee84e7e3e1 | ||
|
|
cf4298f759 | ||
|
|
13190931b9 | ||
| 08be6a4f4a | |||
| 7f015c4794 | |||
| 7b46d43492 | |||
| a4bcff093f | |||
| fb7b3c2793 | |||
| 397bf43147 | |||
| 49af26d958 | |||
| 0ce4811dec | |||
| d3e3ec4d35 | |||
| 8ed0dd2a5c | |||
| 8158753dac | |||
| e3481f6730 | |||
| 165beb3fbd | |||
| 5d367940d5 | |||
| 617294b7fa | |||
| 240d59393f | |||
| 6b94dc2dbd | |||
| 3a0873c991 | |||
| 72ef065ab7 | |||
| 66ecdb2f7a | |||
| e2980603b7 | |||
| 885e0468b2 | |||
| 3bfbb6aef6 | |||
| 365820172a | |||
| d73058fd65 | |||
| 7374484ccb | |||
|
|
7d4229acab | ||
|
|
41128f1457 | ||
|
|
61f45b3a3a | ||
|
|
aaacb2e558 | ||
|
|
9c2dc47a8a | ||
|
|
2db0a65425 | ||
| fd781f4894 | |||
|
|
4a099854f7 | ||
|
|
ff379c36cb | ||
|
|
d5970f17de | ||
|
|
595e43b8a4 | ||
| 6adc665fd3 | |||
| 1be569db7a | |||
| 63e75ed307 | |||
| 6b990689cc | |||
| 3e48bf80f1 | |||
| 5341166e95 | |||
| 685088ccca | |||
| 7a64505139 | |||
| df223af23d | |||
| 8d74a196cf | |||
| 26d74620a9 | |||
| e458361193 | |||
| fea35512a7 | |||
| d5dda73742 | |||
| 86fd8ce391 | |||
| e24333206e | |||
| 0231d5eec2 | |||
| 258aea136e | |||
|
|
44e1b6058f | ||
| 051e18f9c7 | |||
| a66fb815a6 | |||
| 3e8386382b | |||
| 87109c8fef | |||
| 7ecee9dfdb | |||
| 9dc24d5f27 | |||
| e6b5fc8fd4 | |||
| b52e8d6b5c | |||
| ce6551dacd | |||
| f7d785b6d2 | |||
| 27e060eee5 | |||
| cf1c1bb3f7 | |||
| 85a2c9026a | |||
| d084f93506 | |||
| 8433f658f8 | |||
| 2cdb86fa1e | |||
| d8e06a0d14 | |||
| c1418a1c2e | |||
| b99f239f3d | |||
| 1a030c3f92 | |||
| 41e9bfc6b7 | |||
| f40655fc2f | |||
| 845e5ed37c | |||
| 019c3171c4 | |||
| 93a3f09971 | |||
| 51457bd873 | |||
| ebe95fc575 | |||
| 6d8431a77e | |||
| a8f2745169 | |||
| 61cdc7c7d2 | |||
| 3aeadaa719 | |||
| a542b182ea | |||
| 1ddc651b11 | |||
| b0bca2f16c | |||
| d9775a5f10 | |||
| ea2ef7b127 | |||
| 6d9aa92ed0 | |||
| 0c54b6d9da | |||
| 5ede0a139c | |||
|
|
2918d6006f | ||
| 2c97dc2bcd | |||
| 7e6a92ab7b | |||
| 46ad48d5d1 | |||
| 2c24a68135 | |||
|
|
357feca7b1 | ||
|
|
920a83ddab | ||
|
|
42e3406529 | ||
|
|
7336f64c26 | ||
|
|
c1731f68fa | ||
| 5ed953e11e | |||
|
|
e4892c6d5c | ||
| 1a200a16cc | |||
| e50bf00fa8 | |||
| e75881d6cc | |||
| 75ace4266e | |||
| e82a547bce | |||
| 6d72aeed8c | |||
| c01bf47c8a | |||
| 4bd0f06cd6 | |||
| aab6529f62 | |||
| 11c8e8e794 | |||
| 0d1dbc7992 | |||
| 7cd60bb0e7 | |||
| 697b617c7a | |||
| fe6c7cd41f | |||
| eea0990b54 | |||
| aa0bafb8a8 | |||
| c4ebc7357d | |||
| 471c5d49d1 | |||
| eff2bdbf87 | |||
| b5de81582f | |||
| 500246ebed | |||
|
|
38f135ceb9 | ||
|
|
0a1a2ed82a | ||
| 0a774c6c58 | |||
| f44a8e49c5 | |||
| 1109331dfc | |||
| 8a4c4694ec | |||
| b4f331e621 | |||
| 67224858b7 | |||
| 447e29e381 | |||
| aae81a7b5f | |||
| f049319500 | |||
| 6b3896c1f8 | |||
| 652c3d5dc2 | |||
| 4d29bfefde | |||
| 16ac93c36b | |||
| 040e092153 | |||
|
|
098bde2a32 | ||
|
|
f94d2d5578 | ||
| 30a34acd41 | |||
| dca04f7f60 | |||
| 307a8239fb | |||
| e189236e3e | |||
| a3fe7467c3 | |||
| 973aab8f53 | |||
| 0c7c19451c | |||
| 73b00eb206 | |||
| 90e6458428 | |||
|
|
4fdcd25671 | ||
| c2e2cc2e57 | |||
| 2abd59e6d4 | |||
|
|
65481ad61e | ||
| 47c64c2eef | |||
| 7bdfd071ae | |||
| 7858dbe757 | |||
|
|
8466cdc675 | ||
| ea5ddfcf36 | |||
| 332898351c | |||
| 2ec34f4827 | |||
| 7489a64e6c | |||
| 06fa817bde | |||
| cb762356da | |||
| 62649a6343 | |||
| a6434fb71e | |||
| a8f11d1834 | |||
| 67e7e45779 | |||
| cafdca9a7b | |||
| 7efb290f13 | |||
| b3cb09481c | |||
| f543a45a72 | |||
| 099cacd3e3 | |||
| b89720e710 | |||
| 1ffc006b2e | |||
| f2f5df0db4 | |||
| 10354df425 | |||
| 03bd3e2db3 | |||
| 11b391bc35 | |||
| a6e6d29d33 | |||
| 6600f373da | |||
| efa72dbb56 | |||
| 4e267f690d | |||
| 05318013e2 | |||
| 8840532ed0 | |||
| 48a92ca1e7 | |||
| ff71540428 | |||
| f04b41c99a | |||
| 847ebda3e2 | |||
| e174a46801 | |||
| 04a985cd8c | |||
| adb1c6f58e | |||
| 9eec35713c | |||
|
|
f6efdceaca | ||
| c1adcfcc19 | |||
| 0a7330393d | |||
| 63b2f29633 | |||
| d8980d10a4 | |||
| c7d052646e | |||
| de8a079cb4 | |||
| fc8b55df27 | |||
| be6b200945 | |||
| cbeaca66a2 | |||
| 585778cddc | |||
| cb0235bb5d | |||
| 558592a87f | |||
| 41e8b6dfbb | |||
|
|
5aae625788 | ||
| 16d2bdc1db | |||
| edb352b9d0 | |||
| ffbf3d2023 | |||
| 0147f003c4 | |||
| 50d93b1993 | |||
| a39549a3d9 | |||
|
|
9adabadeb9 | ||
|
|
61303bfd0b | ||
|
|
e45963dbcb | ||
|
|
5f689ccbd4 | ||
|
|
17bb36deee | ||
|
|
0b50c36c8a | ||
|
|
feaa82181f | ||
|
|
803447b4c3 | ||
|
|
e93fb5c084 | ||
|
|
6a418ebcab | ||
|
|
67ca6e6552 | ||
|
|
540e7c37e0 | ||
|
|
7cb0c9bbb4 | ||
|
|
66deb5dc7e | ||
|
|
3e10745717 | ||
|
|
84419bbf0b | ||
|
|
d81f993f81 | ||
|
|
8453c5cce0 | ||
|
|
b2b047b4aa | ||
|
|
7aa68c0a2b | ||
|
|
811d1ccf9d | ||
|
|
2af1f68d84 | ||
|
|
56eb11bc17 | ||
|
|
214efb8ef9 | ||
|
|
d1631643a5 | ||
|
|
315d7728d8 | ||
|
|
647f9e728f | ||
|
|
fa2897d2f8 | ||
|
|
539f880f9b | ||
|
|
9caa7a61b0 | ||
| 52b14142bb | |||
| 7f60ee9f8b | |||
| b9726a0af8 | |||
| bb86c56e77 | |||
| 8cdeff74c7 | |||
| acf5c314de | |||
| 20413f00c0 | |||
| aa551acec4 | |||
| f088ea1263 | |||
| 46e31746b4 | |||
| 981737f8ed | |||
|
|
5f01796bae | ||
| b2575643a8 | |||
| baeaa65829 | |||
| 5afa32a393 | |||
| ca0f12782b | |||
| 148ed1e3a0 | |||
| 6f86522c2b | |||
| 5db67f13e1 | |||
| 21504f1539 | |||
|
|
fd311c7e1a | ||
|
|
7c71ff106b | ||
|
|
d13bf0b72a | ||
|
|
9e0934f958 | ||
|
|
1cc330ba66 | ||
|
|
967dde4395 | ||
|
|
278870606b | ||
|
|
e70a2c765b | ||
| 31856e8895 | |||
| 22aefa2042 | |||
|
|
aeded8c495 | ||
| 3248a15d59 | |||
| 5c81c00a18 | |||
| ea7ddaf6d5 | |||
| a8ad30b335 | |||
| 67570db664 | |||
| 87d8d69a20 | |||
|
|
e967b26f5a | ||
|
|
e5540d051f | ||
|
|
7ce81db9a8 | ||
| e42e04c0f0 | |||
| d32d5b5f29 | |||
| 8d00774b5e | |||
|
|
89b38f4fff | ||
| 0dbc809111 | |||
| 64c7625524 | |||
| 071e702e56 | |||
| 363317fc26 | |||
| f4d7a6d0d4 | |||
| b5a4f1626f | |||
| 73790e2353 | |||
| dd5f8b45ef | |||
| 2960156b33 | |||
| 9a72c4b2d1 | |||
| 7d4acb62d0 | |||
| cddace2a1d | |||
| 859584c2b3 | |||
| 6237fc89ad | |||
| cde53537af | |||
| 5490de1d61 | |||
|
|
488e89a0b4 | ||
|
|
4774cf6c37 | ||
| 7c7b0cb7fd | |||
|
|
6aeb89ccd0 | ||
| 6cc39a2672 | |||
|
|
cca68f9b79 | ||
|
|
904bf2a90b | ||
|
|
8a001adf59 | ||
|
|
2c602fa46b | ||
|
|
1c90985f93 | ||
|
|
3498ab05f4 | ||
|
|
d809b592c9 | ||
| c3aa459872 | |||
| d389b3b57c | |||
|
|
76ee5774c4 | ||
|
|
b7d980df06 | ||
|
|
491cd0aa46 | ||
|
|
e380b26798 | ||
|
|
4790a14542 | ||
|
|
194df9d88c | ||
| 567bccb51c | |||
| 0e8e3688f7 | |||
| d3170de74a | |||
| 908bf26151 | |||
|
|
e55702b219 | ||
|
|
c0c95827ef | ||
|
|
fc9ad32878 | ||
| 7d40dfe1e5 | |||
|
|
2304e9b558 | ||
| 944fe4794e | |||
| 7501e3feea | |||
| eb0920e2c7 | |||
|
|
956a714d6e | ||
|
|
3f990b0bc8 | ||
|
|
3dbe8f2003 | ||
|
|
d90fc421fd | ||
|
|
85db853d74 | ||
|
|
440d5571fa | ||
|
|
910a0d859d | ||
|
|
c5f9aea557 | ||
|
|
84ea533b11 | ||
|
|
fd2819c754 | ||
|
|
fd6cb07e5d | ||
|
|
6d2270a1ed | ||
|
|
94bde1d821 | ||
| ba801be24f | |||
|
|
e919271247 | ||
|
|
d2bdbcd27d | ||
|
|
97eb5a56ab | ||
|
|
473ced4d4a | ||
|
|
c7771095d3 | ||
| ea127d3fd4 | |||
|
|
b35762b4bb | ||
|
|
3f6fc7c0fb | ||
| 82eea0a8f9 | |||
|
|
670016e79c | ||
| 14492d7269 | |||
| de9d968b76 | |||
| 7bb7677604 | |||
|
|
841b16ef72 | ||
|
|
0d0dd1e241 | ||
|
|
62f7b2cece | ||
|
|
a5e568ffa1 | ||
| 64039ee3c2 | |||
| ce043943d9 | |||
| fea5bd4ada | |||
| 00d4525325 | |||
| f17776ec54 | |||
| 05845481d1 | |||
| bb5a89219a | |||
| c3ebc00a86 | |||
| a57c277af0 | |||
| 2b00bfa58b | |||
| 8962ea058f | |||
| 7511de68a4 | |||
| 0b5a36d5eb | |||
| 1f861c14a9 | |||
| d7d1dcb539 | |||
| 91c59dfb6b | |||
|
|
9d17066175 | ||
|
|
7db602d959 | ||
| 7e6fd01eef | |||
| 365f5a8c7b | |||
| 30dbe24777 | |||
|
|
e36a3b318a | ||
| 5d389c8855 | |||
| d6a53cc3a6 | |||
| 409207e02d | |||
| 95366c06ce | |||
| 5b1b4acd2c | |||
| 10bae6a07b | |||
| bfafdd34be | |||
| 0b2896516e | |||
| 268a3ee5f5 | |||
| ad1600eb51 | |||
| f1ed870a4a | |||
| 5d3b7c1c53 | |||
| cd97718682 | |||
| 54d9a29855 | |||
| 7cbb43ab75 | |||
|
|
335736b2b7 | ||
| f0b9986e67 | |||
| 82d187438f | |||
| d1617dd0b6 | |||
| 21c7a341fb | |||
| 302adc2d16 | |||
| c8b7e829db | |||
| cf1c0cb3ed | |||
| 566df1c275 | |||
| 16421fa0b6 | |||
| 3c911ee298 | |||
| a12058db3a | |||
| 7ce539a815 | |||
| b76b2ef206 | |||
| 32797dd415 | |||
| e1385a71e2 | |||
| 9cd90b0530 | |||
| 5440a3402f | |||
| 9fafc253b7 | |||
| b37160fa8d | |||
| 7fbe1ced5a | |||
| 17270b1502 | |||
| e34b646231 | |||
| 14ac8fe545 | |||
| b3c5974693 | |||
|
|
af46e48563 | ||
| 2aa093754d | |||
| ab7f61d220 | |||
| 2d67d11537 | |||
|
|
d244d17363 | ||
|
|
e662f09838 | ||
|
|
ad5301ae10 | ||
|
|
def939284c | ||
|
|
f9c13968a8 | ||
|
|
4f6b58e5af | ||
|
|
2556c7755b | ||
|
|
9731c203bd | ||
|
|
a1b67e3720 | ||
|
|
56c1739d54 | ||
|
|
1ae987927b | ||
| 51b27bbf0c | |||
| 5b78b53fe8 | |||
| 3ba0fcf6da | |||
| 7544d0a590 | |||
| 636016d107 | |||
| b310c60188 | |||
| 19cb85a74d | |||
| 490b81c1a1 | |||
| 3a30f07483 | |||
| 61c62424c2 | |||
| 16ccc1bea5 | |||
| b82702393f | |||
| 8a77db15d5 | |||
| beb0879f24 | |||
| 6f75680a7e | |||
| a756c12432 | |||
| ead49256c7 | |||
| 0e029f84b0 | |||
| bd6cf53d53 | |||
| e20a6a9685 | |||
| 0e710f8785 | |||
| 7d11fb269e | |||
| d7808cd16d | |||
| f288682436 | |||
| 19defbe05d | |||
| 175ea0f0c2 | |||
| 30015c1933 | |||
| 4f8455040e | |||
| 428fb269f0 | |||
| ec5e2dba94 | |||
| 316011e047 | |||
| 68567cb531 | |||
| 4eb9368830 | |||
| c555bb6c93 | |||
| 1bb57c4103 | |||
| a51a6d57f9 | |||
| 8465fe7717 | |||
| 0801d20fd1 | |||
| 9055982cf0 | |||
| 8c9ce56837 | |||
| 0a8ab44139 | |||
| 24fd48815a | |||
| 3fca0f6f99 | |||
| 3cd12ecefc | |||
| 0889a751db | |||
| efb1b7c24a | |||
| 3527f251c8 | |||
| 6cdc515cfd | |||
| 57de4b1805 | |||
|
|
609d5944bb | ||
| 88d9f1d509 | |||
| 5c0aab4190 | |||
| 33e96d7841 | |||
| d00e3c77ab | |||
| 318ebb3fc5 | |||
| f224a770f0 | |||
| ebcf187df6 | |||
| b3cfc89fa2 | |||
|
|
e57606a9f6 | ||
|
|
a36777882e | ||
|
|
605f9872cd | ||
|
|
52125917b4 | ||
|
|
ba853a8c97 | ||
|
|
6ad64d5b44 | ||
| 272588d820 | |||
| 344c812666 | |||
| b4e2619c3b | |||
| 6ca383071b | |||
| d851631494 | |||
| 16f4f04092 | |||
| ca86bbe4f7 | |||
|
|
cf27938983 | ||
| 4283d71b92 | |||
| 10f2d838c9 | |||
| f5ca17c844 | |||
| b62732b5b3 | |||
| 47beb8250e | |||
| 17228def91 | |||
| 5ca98022ab | |||
| 0c15936b5d | |||
| e02a0dfc5b | |||
| 0d330caf9a | |||
| 8cd7e7de65 | |||
| 671e9dd711 | |||
| 4ff9092bd9 | |||
| fb8665b9bd | |||
| 2c7033001e | |||
| 30ea6f3ffa | |||
| 5634055d4b | |||
| 3b7284c9cd | |||
| 74ed974145 | |||
| 4786081026 | |||
| 150cee9a08 | |||
| 941a994482 | |||
| d10d722763 | |||
| 9951e3467e | |||
| 62fd0497f8 | |||
| a0d2288ded | |||
| 149359fdc3 | |||
| 4ae57f4f01 | |||
| d95d81aea7 | |||
| 92ad4d1ce3 | |||
| 3c0ba5ce9c | |||
| f221e724b9 | |||
|
|
0fdede8657 | ||
| 47629bf2cf | |||
| 0474876b58 | |||
| edae8e3e0b | |||
| 1411d0cff3 | |||
| aad80eb2a3 | |||
| af49a90303 | |||
| fa6a4ca1d1 | |||
| e122393914 | |||
| 2d026d5ae7 | |||
| 367e5ac679 | |||
| f3a4b115f3 | |||
| 9f64cc7c60 | |||
| 03651292fb | |||
| 94fd60db10 | |||
| 18b7d97584 | |||
| 021375b5d5 | |||
|
|
8a0602732d | ||
| 20876ff307 | |||
| bf3a7d6ef7 | |||
| 35a771c764 | |||
| eb8178ea3b | |||
| 3867beb198 | |||
| cae5dcd69c | |||
| 2a805dddf5 | |||
| fa0d63f40f | |||
| 9437e2cb79 | |||
| 3054a240bb | |||
| 5d06de8608 | |||
| 468871f21e | |||
| 6c3c227c1b | |||
| 9738db7254 | |||
| 51bf23a34c | |||
| 5eda30644f | |||
| 2746ed5ce2 | |||
| fd23b23e56 | |||
| a63d2dccb2 | |||
| 0f6f8f90c9 | |||
| 87c97b2a0a | |||
| 6f4d76e871 | |||
| c1850cc4f3 | |||
| 399899e7e7 | |||
| bdc4d82eb2 | |||
| 2200cf6627 | |||
|
|
d382f1214e | ||
|
|
36a5790b87 | ||
| 5d100417c4 | |||
|
|
b946bb0679 | ||
|
|
c3b8317edb | ||
|
|
f90e2ee659 | ||
|
|
3bad8f33c4 | ||
|
|
dfcc616cb3 | ||
|
|
a0d356bc56 | ||
|
|
f4496a0c4c | ||
|
|
8841fed1f1 | ||
|
|
eca5805195 | ||
|
|
b8681ddf9d | ||
|
|
68ad4eec14 | ||
|
|
a192679638 | ||
|
|
7058bd85fa | ||
|
|
92c42db787 | ||
| 926bce78a7 | |||
| d054c6bc04 | |||
| 2876da2565 | |||
| 177f595f33 | |||
| 7c5162e155 | |||
| 5e7a277e98 | |||
| af2eea5916 | |||
| 633a75e286 | |||
| 6e053689b9 | |||
| ee722b7688 | |||
| e6912453e0 | |||
| 7c928f99ea | |||
| 9efa0eaa40 | |||
| 700d4d3b48 | |||
| 026ddb3278 | |||
| 79440f84eb | |||
| 922515dfe4 | |||
| f1689187a4 | |||
| 2487bca47c | |||
| cc4da1b4da | |||
| a8bacd8727 | |||
| b93be8bb4a | |||
| 9badfd07bf | |||
| dc8a822b0e | |||
| 43e0d4f910 | |||
| d4caafb592 | |||
| d6e350184c | |||
| 535f2daab0 | |||
| 5c0c69c654 | |||
| ef44c36f0c | |||
| f1297e7c62 | |||
| 21ca75da14 | |||
| 3923db0677 | |||
| 98de1ae404 | |||
| 76921b1e3e | |||
| 97e7a98aed | |||
| d263e42e1e | |||
| 54184f2889 | |||
| af9ce6d173 | |||
| a1baf856f9 | |||
| 13d80e604d | |||
| 09ea40ce8e | |||
| bbccb476c0 | |||
| 0526d6fd15 | |||
| c5bd0bd079 | |||
| 6963d1f746 | |||
| a5c38ba6e3 | |||
| bfb6eba90c | |||
| c07f68e622 | |||
| 9b9810ea1e | |||
| 9a254aec55 | |||
| aa11211e5e | |||
| f1e9bf8222 | |||
| 812baf8bd9 | |||
| 5fe2019897 | |||
| aebf7735fe | |||
| bdbadcd8af | |||
| 54a1609760 | |||
| 0369d321d3 | |||
| 62c712c462 | |||
| dc343b05a2 | |||
| 6462e2a292 | |||
| a35ad5fc57 | |||
| d95c284d3e | |||
| 84c0aa4072 | |||
| cec94e934a | |||
| b695242daf | |||
| ddfc7c05ef | |||
| 9377b57c9e | |||
| 2bfbcf1ae9 | |||
| f8f6375738 | |||
| c074467163 | |||
| 92787f8fcc | |||
| 71d8f0e4d4 | |||
| e20401373c | |||
| 6f7881c7ea | |||
|
|
e326011a93 | ||
| e9ec043bf4 | |||
| e391992251 | |||
| 02f5733eee | |||
| a9f957504e | |||
| f0b3815767 | |||
| ee57bd7363 | |||
| 4c970fa0aa | |||
| 0a99784221 | |||
|
|
eed8011725 | ||
|
|
f8fb4a66dd | ||
|
|
4f8af55378 | ||
|
|
530470972f | ||
|
|
a9bf9e0657 | ||
|
|
e2d22645e1 | ||
|
|
5f077891ae | ||
|
|
1a00f73191 | ||
|
|
d8cfe21e92 | ||
|
|
ee9976c288 | ||
|
|
10707c6d71 | ||
|
|
8e988c39e9 | ||
|
|
e3ab9f8e91 | ||
|
|
96d31995e1 | ||
|
|
3988a8645d | ||
|
|
b25dec12d1 | ||
|
|
f57f8c6083 | ||
|
|
2862be564e | ||
|
|
45b4f426a8 | ||
|
|
51cbd8ef87 | ||
|
|
8b2c3de795 | ||
|
|
969bc2dee3 | ||
| 77e539a73c | |||
| 2de36ee343 | |||
|
|
7dbf568212 | ||
|
|
7e9d1939b1 | ||
|
|
48877079ca | ||
| e68182a157 | |||
| 06914f9fd4 | |||
| 8332385f05 | |||
| 19b1b3cec4 | |||
| bdd7c35ab3 | |||
| 184b6179e5 | |||
| 6fccfc990a | |||
| 524c375944 | |||
| 5041bb8531 | |||
| 7dfee5f0ab | |||
| e3d456c733 | |||
| e2a41b7748 | |||
| 477fa9ef3a | |||
| 0cd67acd19 | |||
| aafccc0132 | |||
| ff1e53a0e3 | |||
| b8d33f7426 | |||
| 8dc3163c73 | |||
| 960636c6bf | |||
| e528775768 | |||
| 52739165a1 | |||
| eb1d848372 | |||
| 2a9c39ca6e | |||
| b84b298ec8 | |||
|
|
e59dd3bcec | ||
|
|
d6fc0adb56 | ||
|
|
5041883d5a | ||
|
|
5ed701dff3 | ||
|
|
410f9df4e4 | ||
|
|
b5497ef72e | ||
|
|
2233f7f47b | ||
|
|
36b758f2c0 | ||
|
|
ae0d420f5f | ||
|
|
b40b987c0f | ||
|
|
e8d371ed9b | ||
|
|
5a0017a8fe | ||
|
|
751f15c1b1 | ||
| d1b2a4e51c | |||
| 6ef6b3b705 | |||
|
|
c069f76324 | ||
|
|
9a11165bfc | ||
| 3a5f020dac | |||
| d67d501b28 | |||
| a69aa15acd | |||
| c391dacf7d | |||
| 69f380f4a1 | |||
|
|
22a0f396e6 | ||
| d27000ec07 | |||
| b82ace8a84 | |||
| b7ff82eb82 | |||
| f7ae675201 | |||
| 39aa2c597e | |||
| 34f0d7d7b3 | |||
| e59d7a821b | |||
|
|
7a72049e28 | ||
|
|
3b38f5a161 | ||
| 829eb665e9 | |||
| 4369b7d122 | |||
|
|
70fa2dae0a | ||
|
|
85ebb2f847 | ||
|
|
e8decff5cd | ||
|
|
3c0a8cf4ab | ||
|
|
eb81cadac0 | ||
|
|
0f8a61803f | ||
|
|
e046418f8f | ||
|
|
802115e813 | ||
|
|
9b958e4427 | ||
|
|
46ac008775 | ||
|
|
281c5f579f | ||
|
|
01a05f7600 | ||
|
|
f76874ac19 | ||
|
|
7615b9236b | ||
|
|
1bddf5527a | ||
|
|
beede9505e | ||
| b74abbe4de | |||
| 6b53a4fac4 | |||
|
|
be4c1afe13 | ||
|
|
4ead982831 | ||
|
|
4011680fad | ||
| 48c787ba8d | |||
|
|
c9002b327b | ||
|
|
873087441a | ||
|
|
f6c45355a4 | ||
|
|
92cc66ecb2 | ||
|
|
773676075c | ||
| e64ad7ce57 | |||
| 1f92232253 | |||
| db54edc36d | |||
| 3adbea9c76 | |||
| 483f043d3c | |||
| 819985f3b4 | |||
| 21fbcb8749 | |||
| 4a03b6fcb0 | |||
| 57ddc8ea4f | |||
| c3881a811b | |||
| ff551b19fb | |||
| a45f5374e4 | |||
| 1453527382 | |||
| cc2056d4cf | |||
| 6cf84ac5a3 | |||
| e243e0ddba | |||
| fddaa47ded | |||
| 658faf9dcf | |||
| 68feb080e7 | |||
| f4cab090cd | |||
| d69f078d8d | |||
| f2e00ca130 | |||
| ea3ce805e4 | |||
| 96f7c15e6e | |||
| a846271cd4 | |||
|
|
da476be75e | ||
|
|
a5830f0203 | ||
| a01bfff974 | |||
|
|
8b9e5de9a7 | ||
|
|
aef68eb93e | ||
|
|
4a5708ab78 | ||
|
|
7cf79a3a20 | ||
|
|
8bebda2e35 | ||
|
|
42fd4c645c | ||
|
|
211533f554 | ||
|
|
11945c823e | ||
|
|
26189704f3 | ||
|
|
5b114f4bf3 | ||
| c455f15b2a | |||
| 30fefa44ec | |||
|
|
e310a6ab31 | ||
|
|
a40ff5f528 | ||
|
|
a7e31f1f2b | ||
| 1c73be515a | |||
| 8dbc11566b | |||
|
|
8408e9fe22 | ||
| 62151ef525 | |||
| f855421901 | |||
|
|
61d2e3c208 | ||
| 9e237b5086 | |||
|
|
f97ec8820c | ||
|
|
f9d5d48a4d | ||
|
|
733d3496a9 | ||
|
|
5a6ee17f3c | ||
|
|
177ef573ee | ||
|
|
57fee62557 | ||
|
|
7084c86e76 | ||
|
|
68482cab43 | ||
|
|
fc22d0fcbc | ||
|
|
da5c922c40 | ||
| aaebbdedcc | |||
|
|
d2b32bc754 | ||
|
|
4aff615687 | ||
|
|
0b3abb3b63 | ||
| 775188a36c | |||
| 21fe962abd | |||
| 9b3eb8d2c3 | |||
| a5a5f7dbde | |||
| 30f25de49b | |||
| 2cc362ca7b | |||
| f1df49606d | |||
| c0be83fe08 | |||
| 0b2ef3da13 | |||
| 526aadb559 | |||
| 8d629de23d | |||
| 8fb7bd264f | |||
| 2e85c2387c | |||
| 1b6c002883 | |||
|
|
7121e3e52e | ||
| 726d2f4752 | |||
| 75d86727ec | |||
| 5c3e408772 | |||
| a51d260a78 | |||
| b5ed39f10d | |||
| f12dbba3d6 | |||
| 9b9f96daf8 | |||
| b8437cd247 | |||
| 63f0355680 | |||
| 2586db91c0 | |||
| fc817b0ec0 | |||
| 2f2ff4f181 | |||
| 68e8e597e8 | |||
| e7202e0ee6 | |||
| a700747595 | |||
| 0bcf9caf7a | |||
| 41e20ae707 | |||
| a6c506a176 | |||
| 7338282bbe | |||
|
|
f9fbc76dd0 | ||
|
|
2ee1a212c8 | ||
|
|
fe41dec699 | ||
|
|
2893cbb2e6 | ||
| 18e24edce5 | |||
| f554ca3493 | |||
|
|
03e10835fb | ||
| 1470ea2d72 | |||
| 292b7e6dc1 | |||
| 2800791f00 | |||
| c35d1583d4 | |||
| 412fcfb578 | |||
| 80973ca21a | |||
| 1b0102bd1c | |||
| 2a68a01a35 | |||
| 63f4e04624 | |||
| 6c3cf9bf77 | |||
| 4ff62c291e | |||
| d4992936b1 | |||
| 2a0b927022 | |||
|
|
17c2a2a580 | ||
|
|
a92993e05b | ||
|
|
3fcef90cc2 | ||
|
|
8c78d4f83e | ||
|
|
2daeb6d1a1 | ||
|
|
c8490a3140 | ||
|
|
818e70d3a7 | ||
|
|
6d0f80fa1e | ||
| 5fd1b69532 | |||
| e24ebf4bda | |||
| d34087f4d0 | |||
| b78870de54 | |||
| 9d2f684ac7 | |||
| 6ad1de8d85 | |||
| f6da5f43aa | |||
| 02669fafde | |||
| 30fc9544fe | |||
| 307a68aa5a | |||
|
|
2664c417e3 | ||
|
|
21b3864dfd | ||
|
|
7a0f384dbf | ||
|
|
27a0d4e51e | ||
|
|
dfa1470c17 | ||
| d992dbf92a | |||
| 1c28495fc3 | |||
| 950ca71a34 | |||
| fcf82ea580 | |||
|
|
381f390938 | ||
|
|
ab4a8ef16c | ||
|
|
998931ffde | ||
|
|
67b7febf72 | ||
| a916c1a22a | |||
| 1460bec8cf | |||
| 7aca1e4006 | |||
| 25e567a125 | |||
| 43c2018177 | |||
| 7d9207dc81 | |||
|
|
f9c3f82bb1 | ||
|
|
40b30979cd | ||
|
|
11e00ba9cb | ||
| 9ac31573b4 | |||
| a895bcc91e | |||
| eff0cac30b | |||
| c65ce1d3f9 | |||
| 201ef3b062 | |||
| 3b34581065 | |||
|
|
d4dd0ea982 | ||
|
|
0b01cc5070 | ||
|
|
3de1fa2295 | ||
| d8cc2a3560 | |||
|
|
d42a467c00 | ||
|
|
bc4ee0fc38 | ||
| 9b17b52b39 | |||
| c02f685307 | |||
| ef7b3e0c8b | |||
| baa11d8146 | |||
| 2d24f21327 | |||
| 96c565d486 | |||
| 3c53dfe628 | |||
| 761affc94d | |||
| f0f62527d7 | |||
| c826efd3a1 | |||
| ae8779e004 | |||
| 49221add97 | |||
| da810467b3 | |||
| 70f018eb87 | |||
| 55a8878a64 | |||
| 0f048a7aa6 | |||
| 435bf81945 | |||
| 1a56d5ab37 | |||
| 3d21ad0df9 | |||
| f6a4b0b709 | |||
| 199788495e | |||
| fecd0a9f5c | |||
| 1e24523df2 | |||
| 93c6df7e89 | |||
| e33bb2ac1e | |||
| 07f7f2be50 | |||
| 7ddeb0c945 | |||
| 00c1fc6ed4 | |||
| 4a211f1bf4 | |||
| 776c7aeb83 | |||
| c143a0e807 | |||
| 6ea0b05300 | |||
| 39dfdad0d2 | |||
| 4cd9fe725d | |||
| 03d430ba7c | |||
| 5592992b73 | |||
| d500f61738 | |||
| 8eb6dc0aef | |||
| dd5c4af6fb | |||
| 0756306e54 | |||
| 5e24daafc6 | |||
| 37bb09ffc7 | |||
| 89c9a6d0bb | |||
| 04bf5157bb | |||
| 790db3735f | |||
| 78b2d24001 | |||
| 14e4cba7cd | |||
| b1541dbf11 | |||
| da5de2fd4a | |||
| aa98e540e0 | |||
| 253c157ba8 | |||
| 9c35a55969 | |||
| a1bf2b8653 | |||
| 3361d14f3c | |||
|
|
e687e1d4d5 | ||
| 56bf844540 | |||
|
|
ee5012311e | ||
| 3f92d92ce4 | |||
| 20145333f4 | |||
| 7b3791627c | |||
|
|
98019bb998 | ||
| 5c3f264295 | |||
| 02855a0d97 | |||
| b5117eafc5 | |||
| 05ffa63dc0 | |||
| 06106c75dc | |||
| 363aaadb1a | |||
| 7713284e64 | |||
| cee45e3031 | |||
| 24a1cf713e | |||
| 481e28750b | |||
| 8b029dbae5 | |||
| 484d4e226c | |||
| a7db82a1fa | |||
| 3f2fe39515 | |||
| 04a9f5612b | |||
| 884a42b814 | |||
| 278a8142b8 | |||
| 277a1619e4 | |||
| d128bab2fa | |||
| 550ef2ae9c | |||
| 5feeffa550 | |||
| 36c279f23d | |||
| f3207464bb | |||
| 00acac4b61 | |||
| e67c750fab | |||
| 2d7f0d35ce | |||
| 3701f71fc6 | |||
| 3f64825436 | |||
| 5ca319e4bd | |||
| f11104e7bc | |||
| 39c0f558c1 | |||
| c1b9aa9665 | |||
| 9eb3cc5916 | |||
| 2fab97fb20 | |||
| 195a2d0473 | |||
| 288a6b0670 | |||
| df7c1c6104 | |||
|
|
598a177e5e | ||
|
|
0a1191aa3f | ||
|
|
5f81a8de59 | ||
| f6273a1662 | |||
| e14a4f0111 | |||
| 543e5bd218 | |||
| d3c28a6b44 | |||
| d851686272 | |||
| 87329bc32c | |||
| c734b84b28 | |||
| 6c88eb5211 | |||
| 7b2355a888 | |||
| b31ebfe368 | |||
| 9a7b58259f | |||
| 9c52d454f8 | |||
| a548c307bc | |||
| 2efd544713 | |||
| c9a6fab638 | |||
| 1c677cd99b | |||
|
|
70e994fcf3 | ||
|
|
c326bb7c6d | ||
|
|
f6c8e44329 | ||
|
|
345e7414e6 | ||
|
|
3488db38bc | ||
|
|
8957c85486 | ||
|
|
6b0a102c1b | ||
|
|
975d8a069a | ||
|
|
2d15f8e16e | ||
|
|
a3a8ee1329 | ||
| 4e5a2d00aa | |||
| ab039b8023 | |||
| 8ed1b96f3a | |||
|
|
c73b5b9edb | ||
|
|
e308804cb7 | ||
| cdd58f168a | |||
| 4d09e507fb | |||
| d600a2df12 | |||
| e310588273 | |||
| a0fabe5f92 | |||
| 780fdfe4f6 | |||
| 145730f5b8 | |||
| 624a672213 | |||
| 16c5252c43 | |||
| 50710f8066 | |||
| 10c6c428a3 | |||
| c34fd76a78 | |||
| ab9dac8827 | |||
|
|
49ba58cb8d | ||
|
|
4f47fab139 | ||
|
|
832e55ddf9 | ||
|
|
8adf55a568 | ||
|
|
9307b27655 | ||
|
|
345d078370 | ||
|
|
a4ef4e594a | ||
|
|
18b11d2a32 | ||
| 75d681a93c | |||
| 0500bbb186 | |||
|
|
a047b91deb | ||
|
|
e941e79830 | ||
|
|
b98254b2be | ||
|
|
e8ac24bedd | ||
|
|
38e109db2b | ||
|
|
8c10ec9db3 | ||
|
|
b9a2459315 | ||
|
|
86f8d63a69 | ||
| 1a27fe185c | |||
| 0573123460 | |||
| 1e33dac2d7 | |||
| 98663feaa3 | |||
| ceeacf6a07 | |||
| f040421848 | |||
| 9fcddbb6bf | |||
| 75c20c9b1a | |||
| ea9a3320a3 | |||
| d621e19349 | |||
| 1ba7fb9621 | |||
| d02b71a39e | |||
| f0e0c01518 | |||
| 76999ccb6f | |||
| d189c3912c | |||
| b9bc254fd3 | |||
| 8c0e82d4f3 | |||
| 844977cca3 | |||
| ffad52e48c | |||
| d12b531c54 | |||
| 941db3aeeb | |||
| 98aafee53a | |||
| abec6ad64c | |||
| 2f53f25580 | |||
| b374b1136e | |||
| 57e21e906f | |||
|
|
a474bd46d6 | ||
|
|
7ae8381465 | ||
| f00ac6ad14 | |||
|
|
5bef86f456 | ||
|
|
4d23e1d38f | ||
| c0308526a9 | |||
| 3c7898160e | |||
|
|
4ac505959f | ||
|
|
683e0f42bc | ||
|
|
d67cee5e45 | ||
| db72f87b34 | |||
| eb175f5542 | |||
| 43e6bb7569 | |||
| 4fbfc5d83d | |||
| 8ea3aa8592 | |||
| 94c7d840c2 | |||
| d11c01cc82 | |||
| 1a7e71c5ca | |||
| 4ca12d141c | |||
| 9c2994cf3e | |||
| 2550b2c557 | |||
|
|
5435a6584e | ||
|
|
9f11188e8f | ||
| 241ee71745 | |||
| a1ccd5a209 | |||
| 2a30bfedca | |||
| 129188f3cc | |||
| bdff474d94 | |||
| 6908616a74 | |||
| 841bb75554 | |||
| ca312f5bbc | |||
| ef692efc83 | |||
| e7e5ee1abe | |||
| f124404dc3 | |||
| 9332d1347d | |||
| afe34c6b75 | |||
| 24cb0934c7 | |||
| e139710352 | |||
| 144147d854 | |||
| cfd84bdcdd | |||
| 1de82a6801 | |||
| 1050fc7786 | |||
| 00dc1ed213 | |||
| 2aceca5f93 | |||
| b9d5cdf2be | |||
| 948bbae661 | |||
| 6eb09bd0c8 | |||
| 66ee7b563a | |||
| 9b3082be06 | |||
| 74ea45901a | |||
| 030beacf11 | |||
| b4f3d961e6 | |||
| a077245b3a | |||
| 5a13070a58 | |||
| 4f5e3226de | |||
| df075ca4f5 | |||
| ffc0bcc3a0 | |||
| 81808d4a60 | |||
| 63a8b4668c | |||
| 50268de894 | |||
| 96d7c73644 | |||
| 2deed13484 | |||
| eb321445d9 | |||
| 39039b89a4 | |||
| e7c2162874 | |||
| fe808a8800 | |||
| fa85c0c50b | |||
| 4bcb1d27e4 | |||
| a2dbb21853 | |||
| 44c38dffad | |||
| a845d4f225 | |||
| 9dcf48ea81 | |||
| 33b2d834c7 | |||
| fac4f6c28f | |||
| 69aadaa46a | |||
| 1412dbd16b | |||
| c626bb64bd | |||
| c5b6a4ee17 | |||
| 7433eeb5c0 | |||
| ceac733b66 | |||
| 665f58d62a | |||
| 0eb04facec | |||
| 28923825d9 | |||
| 195d7dc653 | |||
| 471fc8ee12 | |||
| 419d411b1b | |||
| c6e401a64f | |||
| 3bc9053a86 | |||
| 5a639260cd | |||
| 67e07d21ef | |||
| f0b9894e48 | |||
| 74dc427320 | |||
| d167b5eafb | |||
| 5cd1419897 | |||
| f129426eed | |||
| f3ec5f7956 | |||
| 2c17fef161 | |||
| ab182d08f3 | |||
| f869b8a133 | |||
| 7035db2bcd | |||
| 27042f9930 | |||
| 4c02c1f58b | |||
| 79baf70f9a | |||
| 622c6801ae | |||
| 005f73729e | |||
| 5b0680b29e | |||
| 9d989c6a67 | |||
| fa740e8271 | |||
| 0ba0f1bded | |||
| 4321f13453 | |||
| e71d1d38ad | |||
| 6fe25cda9e | |||
| 01962754b0 | |||
| 237447180c | |||
| 4b1a357ee7 | |||
| 56c17300d5 | |||
| 61ae0a597a | |||
| 3159c519b1 | |||
| f00bf7d2cb | |||
| 33ea4cf571 | |||
| 86b3b13d6f | |||
| b77524cfc7 | |||
| 12c7871640 | |||
| 88c7abedde | |||
| ad4eef9757 | |||
| 935ba77da7 | |||
| 673d765ed6 | |||
| c719410dfa | |||
| ff81b4246a | |||
| 646fe20726 | |||
| a5ca9364ef | |||
| 826d8ca07f | |||
|
|
acebd2ea68 | ||
|
|
fb6eea2484 | ||
| cd3515923e | |||
| 509e39b592 | |||
| dd575316bb | |||
| cf7d492e8d | |||
| 48027fe6ff | |||
| cabf1aa3c5 | |||
| a1ed4cf3b0 | |||
| 624c382652 | |||
| a62c6a8b96 | |||
| 167839b267 | |||
| c48796c0f4 | |||
| 507c1d55da | |||
| 6193e487c8 | |||
| 1826fcee0c | |||
| 2999638255 | |||
| 012fb4cacf | |||
| f13f4fcb6e | |||
| 31719d30cf | |||
| afad22a8cb | |||
| daf7e86b2f | |||
| 768921b361 | |||
| ddf7fba039 | |||
| 3d25a9fd66 | |||
| 8161f099d2 | |||
| 7da3de20aa | |||
| c5ca226ece | |||
| 96185d015a | |||
| 65484510c6 | |||
| b013173c86 | |||
| 172f0c9d95 | |||
| a3d3eec709 | |||
| 8250a8e7f7 | |||
| e01234c6a4 | |||
| ce484f2eb8 | |||
| 38da5cd4e8 | |||
| 42fa23017f | |||
| a021e0d31f | |||
| 81713a0cab | |||
| cccf76cc78 | |||
| e2afb72c16 | |||
| 40e12b4fb1 | |||
| 4a47bea5f1 | |||
| 23aaefa986 | |||
| 99b028a4e7 | |||
| fbcaad51d7 | |||
| 64fe2f1b05 | |||
| 582bab7c0d | |||
| 5f903cc7ca | |||
| 276d1c907e | |||
| c098e5602d | |||
| 42f8b98b03 | |||
| d364bba4c7 | |||
| 2df32aa8e5 | |||
| 709657e2c6 | |||
| 6176aaeb44 | |||
| d8c3f617aa | |||
| 08dec0e00d | |||
| 1b25eaca8f | |||
| 2f4ea9c588 | |||
| ae505995ac | |||
| 109ac5b61c | |||
| 79d8a0ed11 | |||
| 7e4ff0ba85 | |||
| c4c45f708c | |||
| 953381ff5e | |||
| cae1fe14f9 | |||
| efe3696aa2 | |||
| 889b5ae280 | |||
| c2bfb80233 | |||
| 8ef042af6a | |||
| fcb835da34 | |||
| 7493134f25 | |||
| c6c61c9e57 | |||
| 8d6d1bc751 | |||
| 541dae36d4 | |||
| 50eb93deec | |||
| ecb8480b5c | |||
| cfbcd54ba9 | |||
| 1c3fc2bbdb | |||
| d55a0c9c39 | |||
| 57e2bb7bc9 | |||
| eeb2835ca1 | |||
| 823526ae9b | |||
| ce49c00454 | |||
| 9f7ec398c1 | |||
| 25d16b13f5 | |||
| f32d3aaef5 | |||
| b8f17c7ac4 | |||
| d82499f07c | |||
| 62210c8165 | |||
| a827b2f964 | |||
| 1e30efdb0d | |||
| bf193b3218 | |||
| 003d865cc3 | |||
| 4b95824644 | |||
| 70b2dbec56 | |||
| 4ee6ae96a2 | |||
| 7379645869 | |||
| 8b8d32e496 | |||
| be004cd350 | |||
| 71640590bc | |||
| 0430323d2a | |||
| c2a1d7956c | |||
| aca2f5780b | |||
| 0ec39e5ea1 | |||
| 52f736a06e | |||
| 0722fd8b19 | |||
| b33551d4b8 | |||
| e6251e7cb5 | |||
| 5f413efc08 | |||
| bd341d4be3 | |||
| 34d879a066 | |||
| 789f15f1e4 | |||
| b04924d2b1 | |||
| ed4dc33105 | |||
| 8e51ca7f39 | |||
| 2e00cec408 | |||
| 2a57f62e6b | |||
| cd51c9b3c4 | |||
|
|
46a342180d | ||
|
|
578656021e | ||
|
|
7dae4eb303 | ||
|
|
4512a940fb | ||
|
|
a67ea4b2f2 | ||
| fe5e4a2c7a | |||
| 5aef5e24e3 | |||
| 4c18e2f057 | |||
| a3e42c435c | |||
| 2c4ecb7b7c | |||
|
|
28f31ae4ec | ||
| f34b4678ba | |||
| 1f569b7359 | |||
|
|
7f673bb254 | ||
|
|
a02cccaa50 | ||
|
|
bcb07ff3c6 | ||
| 479120118d | |||
| 0c4f945408 | |||
| 3291b761e8 | |||
|
|
b8d78ac779 | ||
|
|
e6d97308bc | ||
|
|
71dbc0abe6 | ||
|
|
ff911e37c4 | ||
|
|
f9eaf412b7 | ||
|
|
19016edcfc | ||
| 75e6539f81 | |||
| 956c9607ff | |||
| a28ddedce7 | |||
|
|
e13ce467d8 | ||
|
|
34b204c44c | ||
| f5dffb0726 | |||
|
|
d4bafaeee8 | ||
|
|
2f8249bed5 | ||
|
|
5a458822ce | ||
| 692790c4ed | |||
| 7f5f764aba | |||
| c299976165 | |||
| 14941d0dda | |||
|
|
676e9c9320 | ||
|
|
bb38e07e2a | ||
| 16282ec5c5 | |||
| 5912f7c259 | |||
| 89d7a594dd | |||
| 100d307037 | |||
| 3d0e15aaed | |||
| e9de0a14b9 | |||
|
|
5a3f0163b9 | ||
|
|
dd89c4a469 | ||
|
|
a65a498c86 | ||
|
|
10d5a17422 | ||
|
|
b6298dc091 | ||
|
|
11da21dd67 | ||
|
|
a574fcb68d | ||
|
|
0f757489ff | ||
|
|
b8eeb72044 | ||
|
|
60efd0b426 | ||
|
|
c36905a309 | ||
|
|
1ff9e6eff1 | ||
| 2e888501d6 | |||
| e6f17ec1ab | |||
|
|
2becaaa593 | ||
| f8e871cea9 | |||
| 80e3c96f2d | |||
| dc13ed6c19 | |||
| 17b72dd549 | |||
| 9b2a85df38 | |||
| 6c56d679ab | |||
|
|
5c5f5bc682 | ||
|
|
881ff77171 | ||
|
|
9be53ecb43 | ||
|
|
646dbffe69 | ||
|
|
95f5244fd9 | ||
|
|
694aab503d | ||
|
|
36a313cc30 | ||
|
|
e011d60987 | ||
|
|
d76d68ea8d | ||
|
|
404fe4f127 | ||
|
|
27d8b0ee3d | ||
|
|
89f2a76409 | ||
|
|
3acb7a5c28 | ||
| 03588e7648 | |||
| 6a48910935 | |||
| fb4fb47ee3 | |||
| 7a36b07cf7 | |||
| 4a59c19ac9 | |||
| 51a6f298e2 | |||
| 752c17ca6c | |||
| a3a17a4f32 | |||
|
|
7f6ba8cd15 | ||
| 6cd5f1d83b | |||
| 2a2795827a | |||
| 700c843b11 | |||
| d28220cd04 | |||
| ad056bcef5 | |||
| b2dcf2069f | |||
| 8c7898de1e | |||
| 07a83bf310 | |||
| 173823695f | |||
| 5000a266cb | |||
| 14e9315262 | |||
| 3abc777062 | |||
| 3ba46f16d1 | |||
| 85bb495754 | |||
| 0ba207cb01 | |||
| 33aac1b125 | |||
| d5cc1d83f3 | |||
| 92a26f6f7b | |||
| d04050e6b0 | |||
| 80cb62b777 | |||
| 570dea85f1 | |||
| 86a2e16ffb | |||
| ed39ea408f | |||
| e9ff0c2882 | |||
| e78081f33b | |||
|
|
eb04971d5a | ||
| 43d496a93e | |||
| 8212d68c41 | |||
| f060287df8 | |||
| 1fbf188d61 | |||
| c1591983f9 | |||
| b726424a34 | |||
| e4814a7d06 | |||
| 2b8f14f709 | |||
|
|
3f4daede8f | ||
|
|
49d970c5bc | ||
|
|
e34ab0b7a8 | ||
|
|
6cd560ff1b | ||
|
|
d3cdf3c7d7 | ||
|
|
ab69fab3e4 | ||
|
|
bf639478c7 | ||
|
|
69bd0571d4 | ||
|
|
a3df4a399c | ||
|
|
76629741f1 | ||
|
|
3eef793bda | ||
|
|
db17aa048d | ||
|
|
bf4cb419a1 | ||
|
|
a0b316e56d | ||
|
|
62d34f51cb | ||
|
|
113f2c4d14 | ||
|
|
2ca658174a | ||
|
|
c78af828ac | ||
|
|
041f5478a4 | ||
|
|
72081a8647 | ||
|
|
7b221fa239 | ||
|
|
d95e730d94 | ||
|
|
12ced03aa5 | ||
|
|
6d55f5bacf | ||
|
|
6468e9e317 | ||
|
|
2270c4744c | ||
|
|
0bfe45bebe | ||
|
|
3c383ca874 | ||
|
|
fccd0dad78 | ||
|
|
bb00e91672 | ||
|
|
2ef203246b | ||
|
|
367ab28706 | ||
|
|
8de0c0bd88 | ||
|
|
aa3ac75470 | ||
|
|
f6108b51a2 | ||
|
|
3c8862763b | ||
|
|
bab02a62e9 | ||
|
|
d97c9876e8 | ||
|
|
61285435e8 | ||
|
|
8ea4d893f6 | ||
|
|
593904cc44 | ||
|
|
957eb1f7ab | ||
|
|
7ca735bfb2 | ||
|
|
0919fa06f6 | ||
|
|
da777c1d7d | ||
|
|
0305274a13 |
67
.editorconfig
Normal file
67
.editorconfig
Normal file
@@ -0,0 +1,67 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[{*.bat,**/*.bat,installer/resources/proxy/*.ht}]
|
||||
end_of_line = crlf
|
||||
|
||||
[**/*.css]
|
||||
indent_size = 5
|
||||
|
||||
[{history.txt,LICENSE.txt}]
|
||||
indent_size = 3
|
||||
|
||||
[**/.project]
|
||||
indent_style = tab
|
||||
|
||||
[README.txt]
|
||||
indent_style = tab
|
||||
|
||||
[apps/BOB/**/*.java]
|
||||
indent_style = tab
|
||||
|
||||
[apps/routerconsole/java/src/com/vuze/plugins/mlab/tools/ndt/swingemu/*.java]
|
||||
indent_style = tab
|
||||
|
||||
[apps/routerconsole/java/src/edu/internet2/ndt/*.java]
|
||||
indent_style = tab
|
||||
|
||||
[apps/routerconsole/jsp/{createreseed.jsp,exportfamily.jsp,flags.jsp,index.jsp,viewhistory.jsp,viewrouterlog.jsp,viewstat.jsp,viewtheme.jsp,viewwrapperlog.jsp}]
|
||||
insert_final_newline = false
|
||||
|
||||
[apps/susimail/**/{*.java,*.js}]
|
||||
indent_style = tab
|
||||
|
||||
[apps/susimail/**/{Attachments.java,MailPart.java,WebMail.java}]
|
||||
end_of_line = crlf
|
||||
|
||||
[core/java/src/com/nettgryppa/**/*.java]
|
||||
indent_size = 2
|
||||
|
||||
[core/java/src/gnu/**/*.java]
|
||||
indent_size = 2
|
||||
|
||||
[core/java/src/gnu/getopt/*.properties]
|
||||
charset = iso-8859-1
|
||||
|
||||
[core/java/src/org/json/simple/**/*.java]
|
||||
end_of_line = crlf
|
||||
indent_style = tab
|
||||
|
||||
[core/java/src/com/southernstorm/**/*.java]
|
||||
indent_style = tab
|
||||
|
||||
[router/java/src/org/cybergarage/**/*.java]
|
||||
indent_style = tab
|
||||
|
||||
[router/java/src/org/freenetproject/*.java]
|
||||
indent_style = tab
|
||||
|
||||
[router/java/src/com/southernstorm/**/*.java]
|
||||
indent_style = tab
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -46,3 +46,12 @@ sloccount.sc
|
||||
|
||||
# but allow debian/patches
|
||||
!/debian/patches/*.(patch|diff)
|
||||
|
||||
# IDE project files
|
||||
# Eclipse
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
# IDEA
|
||||
*.iml
|
||||
|
||||
|
||||
53
.idea/compiler.xml
generated
53
.idea/compiler.xml
generated
@@ -1,22 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<resourceExtensions />
|
||||
<wildcardResourcePatterns>
|
||||
<entry name="!?*.java" />
|
||||
<entry name="!?*.form" />
|
||||
<entry name="!?*.class" />
|
||||
<entry name="!?*.groovy" />
|
||||
<entry name="!?*.scala" />
|
||||
<entry name="!?*.flex" />
|
||||
<entry name="!?*.kt" />
|
||||
<entry name="!?*.clj" />
|
||||
<entry name="!?*.aj" />
|
||||
</wildcardResourcePatterns>
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="false">
|
||||
<processorPath useClasspath="true" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
<bytecodeTargetLevel>
|
||||
<module name="addressbook_main" target="1.7" />
|
||||
<module name="addressbook_test" target="1.7" />
|
||||
<module name="BOB_main" target="1.7" />
|
||||
<module name="BOB_test" target="1.7" />
|
||||
<module name="core_main" target="1.7" />
|
||||
<module name="core_test" target="1.7" />
|
||||
<module name="desktopgui_main" target="1.7" />
|
||||
<module name="desktopgui_test" target="1.7" />
|
||||
<module name="i2psnark_main" target="1.7" />
|
||||
<module name="i2psnark_test" target="1.7" />
|
||||
<module name="i2ptunnel_main" target="1.7" />
|
||||
<module name="i2ptunnel_test" target="1.7" />
|
||||
<module name="installer_main" target="1.7" />
|
||||
<module name="installer_test" target="1.7" />
|
||||
<module name="jetty_main" target="1.7" />
|
||||
<module name="jetty_test" target="1.7" />
|
||||
<module name="jrobin_main" target="1.7" />
|
||||
<module name="jrobin_test" target="1.7" />
|
||||
<module name="ministreaming_main" target="1.7" />
|
||||
<module name="ministreaming_test" target="1.7" />
|
||||
<module name="router_main" target="1.7" />
|
||||
<module name="router_test" target="1.7" />
|
||||
<module name="routerconsole_main" target="1.7" />
|
||||
<module name="routerconsole_test" target="1.7" />
|
||||
<module name="sam_main" target="1.7" />
|
||||
<module name="sam_test" target="1.7" />
|
||||
<module name="streaming_main" target="1.7" />
|
||||
<module name="streaming_test" target="1.7" />
|
||||
<module name="susidns_main" target="1.7" />
|
||||
<module name="susidns_test" target="1.7" />
|
||||
<module name="susimail_main" target="1.7" />
|
||||
<module name="susimail_test" target="1.7" />
|
||||
<module name="systray_main" target="1.7" />
|
||||
<module name="systray_test" target="1.7" />
|
||||
</bytecodeTargetLevel>
|
||||
</component>
|
||||
</project>
|
||||
53
.idea/modules.xml
generated
53
.idea/modules.xml
generated
@@ -2,25 +2,78 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/BOB/BOB.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/BOB/BOB.iml" group="apps/BOB" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/BOB/BOB.iml" filepath="$PROJECT_DIR$/apps/BOB/BOB.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/BOB/BOB_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/BOB/BOB_main.iml" group="apps/BOB" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/BOB/BOB_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/BOB/BOB_test.iml" group="apps/BOB" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/addressbook/addressbook.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/addressbook/addressbook.iml" group="apps/addressbook" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/addressbook/addressbook.iml" filepath="$PROJECT_DIR$/apps/addressbook/addressbook.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/addressbook/addressbook_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/addressbook/addressbook_main.iml" group="apps/addressbook" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/addressbook/addressbook_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/addressbook/addressbook_test.iml" group="apps/addressbook" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/admin/admin.iml" filepath="$PROJECT_DIR$/apps/admin/admin.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/apps.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/apps.iml" group="apps" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/core/core.iml" filepath="$PROJECT_DIR$/.idea/modules/core/core.iml" group="core" />
|
||||
<module fileurl="file://$PROJECT_DIR$/core/core.iml" filepath="$PROJECT_DIR$/core/core.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/core/core_main.iml" filepath="$PROJECT_DIR$/.idea/modules/core/core_main.iml" group="core" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/core/core_test.iml" filepath="$PROJECT_DIR$/.idea/modules/core/core_test.iml" group="core" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/desktopgui/desktopgui.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/desktopgui/desktopgui.iml" group="apps/desktopgui" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/desktopgui/desktopgui.iml" filepath="$PROJECT_DIR$/apps/desktopgui/desktopgui.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/desktopgui/desktopgui_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/desktopgui/desktopgui_main.iml" group="apps/desktopgui" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/desktopgui/desktopgui_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/desktopgui/desktopgui_test.iml" group="apps/desktopgui" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/i2p.i2p.sl.iml" filepath="$PROJECT_DIR$/.idea/modules/i2p.i2p.sl.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/i2psnark/i2psnark.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/i2psnark/i2psnark.iml" group="apps/i2psnark" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/i2psnark/i2psnark.iml" filepath="$PROJECT_DIR$/apps/i2psnark/i2psnark.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/i2psnark/i2psnark_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/i2psnark/i2psnark_main.iml" group="apps/i2psnark" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/i2psnark/i2psnark_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/i2psnark/i2psnark_test.iml" group="apps/i2psnark" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/i2ptunnel/i2ptunnel.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/i2ptunnel/i2ptunnel.iml" group="apps/i2ptunnel" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/i2ptunnel/i2ptunnel.iml" filepath="$PROJECT_DIR$/apps/i2ptunnel/i2ptunnel.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/i2ptunnel/i2ptunnel_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/i2ptunnel/i2ptunnel_main.iml" group="apps/i2ptunnel" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/i2ptunnel/i2ptunnel_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/i2ptunnel/i2ptunnel_test.iml" group="apps/i2ptunnel" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/imagegen/identicon/identicon.iml" filepath="$PROJECT_DIR$/apps/imagegen/identicon/identicon.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/imagegen/imagegen/imagegen.iml" filepath="$PROJECT_DIR$/apps/imagegen/imagegen/imagegen.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/installer/installer.iml" filepath="$PROJECT_DIR$/.idea/modules/installer/installer.iml" group="installer" />
|
||||
<module fileurl="file://$PROJECT_DIR$/installer/installer.iml" filepath="$PROJECT_DIR$/installer/installer.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/installer/installer_main.iml" filepath="$PROJECT_DIR$/.idea/modules/installer/installer_main.iml" group="installer" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/installer/installer_test.iml" filepath="$PROJECT_DIR$/.idea/modules/installer/installer_test.iml" group="installer" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/jetty/jetty.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/jetty/jetty.iml" group="apps/jetty" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/jetty/jetty.iml" filepath="$PROJECT_DIR$/apps/jetty/jetty.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/jetty/jetty_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/jetty/jetty_main.iml" group="apps/jetty" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/jetty/jetty_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/jetty/jetty_test.iml" group="apps/jetty" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/jrobin/jrobin.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/jrobin/jrobin.iml" group="apps/jrobin" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/jrobin/jrobin_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/jrobin/jrobin_main.iml" group="apps/jrobin" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/jrobin/jrobin_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/jrobin/jrobin_test.iml" group="apps/jrobin" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/ministreaming/ministreaming.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/ministreaming/ministreaming.iml" group="apps/ministreaming" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/ministreaming/ministreaming.iml" filepath="$PROJECT_DIR$/apps/ministreaming/ministreaming.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/ministreaming/ministreaming_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/ministreaming/ministreaming_main.iml" group="apps/ministreaming" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/ministreaming/ministreaming_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/ministreaming/ministreaming_test.iml" group="apps/ministreaming" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/router/router.iml" filepath="$PROJECT_DIR$/.idea/modules/router/router.iml" group="router" />
|
||||
<module fileurl="file://$PROJECT_DIR$/router/router.iml" filepath="$PROJECT_DIR$/router/router.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/router/router_main.iml" filepath="$PROJECT_DIR$/.idea/modules/router/router_main.iml" group="router" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/router/router_test.iml" filepath="$PROJECT_DIR$/.idea/modules/router/router_test.iml" group="router" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/routerconsole/routerconsole.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/routerconsole/routerconsole.iml" group="apps/routerconsole" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/routerconsole/routerconsole.iml" filepath="$PROJECT_DIR$/apps/routerconsole/routerconsole.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/routerconsole/routerconsole_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/routerconsole/routerconsole_main.iml" group="apps/routerconsole" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/routerconsole/routerconsole_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/routerconsole/routerconsole_test.iml" group="apps/routerconsole" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/sam/sam.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/sam/sam.iml" group="apps/sam" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/sam/sam.iml" filepath="$PROJECT_DIR$/apps/sam/sam.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/sam/sam_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/sam/sam_main.iml" group="apps/sam" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/sam/sam_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/sam/sam_test.iml" group="apps/sam" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/streaming/streaming.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/streaming/streaming.iml" group="apps/streaming" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/streaming/streaming.iml" filepath="$PROJECT_DIR$/apps/streaming/streaming.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/streaming/streaming_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/streaming/streaming_main.iml" group="apps/streaming" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/streaming/streaming_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/streaming/streaming_test.iml" group="apps/streaming" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/susidns/susidns.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/susidns/susidns.iml" group="apps/susidns" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/susidns/susidns.iml" filepath="$PROJECT_DIR$/apps/susidns/susidns.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/susidns/susidns_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/susidns/susidns_main.iml" group="apps/susidns" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/susidns/susidns_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/susidns/susidns_test.iml" group="apps/susidns" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/susimail/susimail.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/susimail/susimail.iml" group="apps/susimail" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/susimail/susimail.iml" filepath="$PROJECT_DIR$/apps/susimail/susimail.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/susimail/susimail_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/susimail/susimail_main.iml" group="apps/susimail" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/susimail/susimail_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/susimail/susimail_test.iml" group="apps/susimail" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/systray/systray.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/systray/systray.iml" group="apps/systray" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/systray/systray.iml" filepath="$PROJECT_DIR$/apps/systray/systray.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/systray/systray_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/systray/systray_main.iml" group="apps/systray" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/systray/systray_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/systray/systray_test.iml" group="apps/systray" />
|
||||
<module fileurl="file://$PROJECT_DIR$/apps/imagegen/zxing/zxing.iml" filepath="$PROJECT_DIR$/apps/imagegen/zxing/zxing.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
|
||||
35
.mtn-ignore
35
.mtn-ignore
@@ -50,6 +50,41 @@ core/c/jcpuid/msvc/*.user
|
||||
# Build property overrides
|
||||
override.properties
|
||||
|
||||
# Launcher build paths
|
||||
launchers/macosx/target
|
||||
launchers/browserbundle/target
|
||||
launchers/macosx/project/target
|
||||
launchers/browserbundle/project/target
|
||||
launchers/target
|
||||
launchers/project/target
|
||||
launchers/common/target
|
||||
launchers/output
|
||||
launchers/project/project
|
||||
|
||||
# Reporting
|
||||
sloccount.sc
|
||||
^reports/
|
||||
|
||||
# IDE project files
|
||||
# Eclipse
|
||||
/.classpath$
|
||||
/.project$
|
||||
/.settings
|
||||
# IDEA
|
||||
\.iml$
|
||||
|
||||
|
||||
# Meeh stuff
|
||||
target/scala-2.12
|
||||
target/streams
|
||||
project/target
|
||||
launchers/macosx/version.h
|
||||
launchers/macosx/project/project/target
|
||||
launchers/macosx/I2PLauncher.xcodeproj/project.xcworkspace/xcuserdata
|
||||
launchers/macosx/I2PLauncher.xcodeproj/xcuserdata
|
||||
launchers/macosx/I2PLauncher.xcworkspace/xcuserdata
|
||||
launchers/macosx/Sparkle.framework
|
||||
launchers/macosx/I2PLauncher.app
|
||||
launchers/macosx/releases.done
|
||||
launchers/macosx/target
|
||||
|
||||
|
||||
64
.travis.yml
Normal file
64
.travis.yml
Normal file
@@ -0,0 +1,64 @@
|
||||
language: java
|
||||
dist: trusty
|
||||
|
||||
jdk:
|
||||
- oraclejdk11
|
||||
- oraclejdk9
|
||||
- openjdk12
|
||||
- openjdk11
|
||||
- openjdk10
|
||||
- openjdk9
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- jdk: oraclejdk8
|
||||
addons:
|
||||
sonarcloud:
|
||||
organization: "i2p"
|
||||
before_install:
|
||||
- export JAVA7_HOME=$(jdk_switcher home openjdk7)
|
||||
- sed -i "1iplugins {\n id 'org.sonarqube' version '2.6.1'\n}\n" build.gradle
|
||||
- jdk: openjdk8
|
||||
before_install:
|
||||
- export JAVA7_HOME=$(jdk_switcher home openjdk7)
|
||||
- jdk: openjdk7
|
||||
sudo: required
|
||||
before_install: # Work around missing crypto in openjdk7
|
||||
- export JAVA7_HOME=$(jdk_switcher home openjdk7)
|
||||
- sudo wget "https://bouncycastle.org/download/bcprov-ext-jdk15on-158.jar" -O "${JAVA_HOME}/jre/lib/ext/bcprov-ext-jdk15on-158.jar"
|
||||
- sudo perl -pi.bak -e 's/^(security\.provider\.)([0-9]+)/$1.($2+1)/ge' /etc/java-7-openjdk/security/java.security
|
||||
- echo "security.provider.1=org.bouncycastle.jce.provider.BouncyCastleProvider" | sudo tee -a /etc/java-7-openjdk/security/java.security
|
||||
install:
|
||||
- export TARGET_JAVA_HOME=$JAVA_HOME
|
||||
- jdk_switcher use oraclejdk8
|
||||
- ./gradlew assemble
|
||||
allow_failures:
|
||||
- jdk: openjdk12
|
||||
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
- $HOME/.sonar/cache/
|
||||
- .gradle
|
||||
|
||||
script:
|
||||
- |
|
||||
if [ "$TRAVIS_JDK_VERSION" == "oraclejdk8" ]; then
|
||||
./gradlew sonarqube codeCoverageReport
|
||||
else
|
||||
./gradlew check codeCoverageReport
|
||||
fi
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
- "chat.freenode.net#i2p-dev"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
220
.tx/config
220
.tx/config
@@ -1,3 +1,6 @@
|
||||
;; warning - conversions for all Java bundles:
|
||||
;; id->in, he->iw, iy, yi->ji
|
||||
|
||||
[I2P.i2ptunnel]
|
||||
source_file = apps/i2ptunnel/locale/messages_en.po
|
||||
source_lang = en
|
||||
@@ -6,6 +9,7 @@ trans.cs = apps/i2ptunnel/locale/messages_cs.po
|
||||
trans.da = apps/i2ptunnel/locale/messages_da.po
|
||||
trans.de = apps/i2ptunnel/locale/messages_de.po
|
||||
trans.es = apps/i2ptunnel/locale/messages_es.po
|
||||
trans.fa = apps/i2ptunnel/locale/messages_fa.po
|
||||
trans.fi = apps/i2ptunnel/locale/messages_fi.po
|
||||
trans.fr = apps/i2ptunnel/locale/messages_fr.po
|
||||
trans.hu = apps/i2ptunnel/locale/messages_hu.po
|
||||
@@ -23,6 +27,7 @@ trans.pt_BR = apps/i2ptunnel/locale/messages_pt_BR.po
|
||||
trans.ro = apps/i2ptunnel/locale/messages_ro.po
|
||||
trans.ru_RU = apps/i2ptunnel/locale/messages_ru.po
|
||||
trans.sk = apps/i2ptunnel/locale/messages_sk.po
|
||||
trans.sq = apps/i2ptunnel/locale/messages_sq.po
|
||||
trans.sv_SE = apps/i2ptunnel/locale/messages_sv.po
|
||||
trans.tr_TR = apps/i2ptunnel/locale/messages_tr.po
|
||||
trans.uk_UA = apps/i2ptunnel/locale/messages_uk.po
|
||||
@@ -36,7 +41,9 @@ source_lang = en
|
||||
trans.ar = apps/i2ptunnel/locale-proxy/messages_ar.po
|
||||
trans.cs = apps/i2ptunnel/locale-proxy/messages_cs.po
|
||||
trans.de = apps/i2ptunnel/locale-proxy/messages_de.po
|
||||
trans.el = apps/i2ptunnel/locale-proxy/messages_el.po
|
||||
trans.es = apps/i2ptunnel/locale-proxy/messages_es.po
|
||||
trans.fa = apps/i2ptunnel/locale-proxy/messages_fa.po
|
||||
trans.fi = apps/i2ptunnel/locale-proxy/messages_fi.po
|
||||
trans.fr = apps/i2ptunnel/locale-proxy/messages_fr.po
|
||||
trans.hu = apps/i2ptunnel/locale-proxy/messages_hu.po
|
||||
@@ -46,6 +53,7 @@ trans.it = apps/i2ptunnel/locale-proxy/messages_it.po
|
||||
trans.ko = apps/i2ptunnel/locale-proxy/messages_ko.po
|
||||
trans.nb = apps/i2ptunnel/locale-proxy/messages_nb.po
|
||||
trans.nl = apps/i2ptunnel/locale-proxy/messages_nl.po
|
||||
trans.nn = apps/i2ptunnel/locale-proxy/messages_nn.po
|
||||
trans.pl = apps/i2ptunnel/locale-proxy/messages_pl.po
|
||||
trans.pt = apps/i2ptunnel/locale-proxy/messages_pt.po
|
||||
trans.pt_BR = apps/i2ptunnel/locale-proxy/messages_pt_BR.po
|
||||
@@ -58,16 +66,88 @@ trans.uk_UA = apps/i2ptunnel/locale-proxy/messages_uk.po
|
||||
trans.vi = apps/i2ptunnel/locale-proxy/messages_vi.po
|
||||
trans.zh_CN = apps/i2ptunnel/locale-proxy/messages_zh.po
|
||||
|
||||
[I2P.core]
|
||||
type = PO
|
||||
source_file = core/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = core/locale/messages_ar.po
|
||||
trans.az = core/locale/messages_az.po
|
||||
trans.cs = core/locale/messages_cs.po
|
||||
trans.da = core/locale/messages_da.po
|
||||
trans.de = core/locale/messages_de.po
|
||||
trans.el = core/locale/messages_el.po
|
||||
trans.es = core/locale/messages_es.po
|
||||
trans.et_EE = core/locale/messages_et.po
|
||||
trans.fa = core/locale/messages_fa.po
|
||||
trans.fi = core/locale/messages_fi.po
|
||||
trans.fr = core/locale/messages_fr.po
|
||||
trans.hu = core/locale/messages_hu.po
|
||||
;; Java converts id to in
|
||||
trans.id = core/locale/messages_in.po
|
||||
trans.it = core/locale/messages_it.po
|
||||
trans.ja = core/locale/messages_ja.po
|
||||
trans.ko = core/locale/messages_ko.po
|
||||
trans.nb = core/locale/messages_nb.po
|
||||
trans.nl = core/locale/messages_nl.po
|
||||
trans.pl = core/locale/messages_pl.po
|
||||
trans.pt = core/locale/messages_pt.po
|
||||
trans.pt_BR = core/locale/messages_pt_BR.po
|
||||
trans.ro = core/locale/messages_ro.po
|
||||
trans.ru_RU = core/locale/messages_ru.po
|
||||
trans.sv_SE = core/locale/messages_sv.po
|
||||
trans.tr_TR = core/locale/messages_tr.po
|
||||
trans.uk_UA = core/locale/messages_uk.po
|
||||
trans.vi = core/locale/messages_vi.po
|
||||
trans.zh_CN = core/locale/messages_zh.po
|
||||
trans.zh_TW = core/locale/messages_zh_TW.po
|
||||
|
||||
[I2P.router]
|
||||
type = PO
|
||||
source_file = router/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = router/locale/messages_ar.po
|
||||
trans.az = router/locale/messages_az.po
|
||||
trans.cs = router/locale/messages_cs.po
|
||||
trans.da = router/locale/messages_da.po
|
||||
trans.de = router/locale/messages_de.po
|
||||
trans.el = router/locale/messages_el.po
|
||||
trans.es = router/locale/messages_es.po
|
||||
trans.et_EE = router/locale/messages_et.po
|
||||
trans.fa = router/locale/messages_fa.po
|
||||
trans.fi = router/locale/messages_fi.po
|
||||
trans.fr = router/locale/messages_fr.po
|
||||
trans.hu = router/locale/messages_hu.po
|
||||
;; Java converts id to in
|
||||
trans.id = router/locale/messages_in.po
|
||||
trans.it = router/locale/messages_it.po
|
||||
trans.ja = router/locale/messages_ja.po
|
||||
trans.ko = router/locale/messages_ko.po
|
||||
trans.nb = router/locale/messages_nb.po
|
||||
trans.nl = router/locale/messages_nl.po
|
||||
trans.pl = router/locale/messages_pl.po
|
||||
trans.pt = router/locale/messages_pt.po
|
||||
trans.pt_BR = router/locale/messages_pt_BR.po
|
||||
trans.ro = router/locale/messages_ro.po
|
||||
trans.ru_RU = router/locale/messages_ru.po
|
||||
trans.sv_SE = router/locale/messages_sv.po
|
||||
trans.tr_TR = router/locale/messages_tr.po
|
||||
trans.uk_UA = router/locale/messages_uk.po
|
||||
trans.vi = router/locale/messages_vi.po
|
||||
trans.zh_CN = router/locale/messages_zh.po
|
||||
trans.zh_TW = router/locale/messages_zh_TW.po
|
||||
|
||||
[I2P.routerconsole]
|
||||
source_file = apps/routerconsole/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/routerconsole/locale/messages_ar.po
|
||||
trans.az = apps/routerconsole/locale/messages_az.po
|
||||
trans.cs = apps/routerconsole/locale/messages_cs.po
|
||||
trans.da = apps/routerconsole/locale/messages_da.po
|
||||
trans.de = apps/routerconsole/locale/messages_de.po
|
||||
trans.el = apps/routerconsole/locale/messages_el.po
|
||||
trans.es = apps/routerconsole/locale/messages_es.po
|
||||
trans.et_EE = apps/routerconsole/locale/messages_et.po
|
||||
trans.fa = apps/routerconsole/locale/messages_fa.po
|
||||
trans.fi = apps/routerconsole/locale/messages_fi.po
|
||||
trans.fr = apps/routerconsole/locale/messages_fr.po
|
||||
trans.hu = apps/routerconsole/locale/messages_hu.po
|
||||
@@ -94,14 +174,17 @@ trans.zh_TW = apps/routerconsole/locale/messages_zh_TW.po
|
||||
source_file = apps/routerconsole/locale-news/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/routerconsole/locale-news/messages_ar.po
|
||||
trans.az = apps/routerconsole/locale-news/messages_az.po
|
||||
trans.cs = apps/routerconsole/locale-news/messages_cs.po
|
||||
trans.de = apps/routerconsole/locale-news/messages_de.po
|
||||
trans.el = apps/routerconsole/locale-news/messages_el.po
|
||||
trans.es = apps/routerconsole/locale-news/messages_es.po
|
||||
trans.fa = apps/routerconsole/locale-news/messages_fa.po
|
||||
trans.fi = apps/routerconsole/locale-news/messages_fi.po
|
||||
trans.fr = apps/routerconsole/locale-news/messages_fr.po
|
||||
trans.gl = apps/routerconsole/locale-news/messages_gl.po
|
||||
trans.he = apps/routerconsole/locale-news/messages_he.po
|
||||
;; Java converts he to iw
|
||||
trans.he = apps/routerconsole/locale-news/messages_iw.po
|
||||
;; Java converts id to in
|
||||
trans.id = apps/routerconsole/locale-news/messages_in.po
|
||||
trans.it = apps/routerconsole/locale-news/messages_it.po
|
||||
@@ -128,7 +211,11 @@ trans.zh_TW = apps/routerconsole/locale-news/messages_zh_TW.po
|
||||
type = PO
|
||||
source_file = apps/routerconsole/locale-countries/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/routerconsole/locale-countries/messages_ar.po
|
||||
trans.az = apps/routerconsole/locale-countries/messages_az.po
|
||||
trans.bg = apps/routerconsole/locale-countries/messages_bg.po
|
||||
trans.ca = apps/routerconsole/locale-countries/messages_ca.po
|
||||
trans.cs = apps/routerconsole/locale-countries/messages_cs.po
|
||||
trans.da = apps/routerconsole/locale-countries/messages_da.po
|
||||
trans.de = apps/routerconsole/locale-countries/messages_de.po
|
||||
trans.el = apps/routerconsole/locale-countries/messages_el.po
|
||||
@@ -138,7 +225,11 @@ trans.fa = apps/routerconsole/locale-countries/messages_fa.po
|
||||
trans.fi = apps/routerconsole/locale-countries/messages_fi.po
|
||||
trans.fr = apps/routerconsole/locale-countries/messages_fr.po
|
||||
trans.gl = apps/routerconsole/locale-countries/messages_gl.po
|
||||
trans.hi = apps/routerconsole/locale-countries/messages_hi.po
|
||||
trans.hr = apps/routerconsole/locale-countries/messages_hr.po
|
||||
trans.hu = apps/routerconsole/locale-countries/messages_hu.po
|
||||
;; Java converts he to iw
|
||||
trans.he = apps/routerconsole/locale-countries/messages_iw.po
|
||||
;; Java converts id to in
|
||||
trans.id = apps/routerconsole/locale-countries/messages_in.po
|
||||
trans.it = apps/routerconsole/locale-countries/messages_it.po
|
||||
@@ -154,6 +245,7 @@ trans.ro = apps/routerconsole/locale-countries/messages_ro.po
|
||||
trans.ru_RU = apps/routerconsole/locale-countries/messages_ru.po
|
||||
trans.sk = apps/routerconsole/locale-countries/messages_sk.po
|
||||
trans.sq = apps/routerconsole/locale-countries/messages_sq.po
|
||||
trans.sr = apps/routerconsole/locale-countries/messages_sr.po
|
||||
trans.sv_SE = apps/routerconsole/locale-countries/messages_sv.po
|
||||
trans.tr_TR = apps/routerconsole/locale-countries/messages_tr.po
|
||||
trans.uk_UA = apps/routerconsole/locale-countries/messages_uk.po
|
||||
@@ -167,7 +259,9 @@ source_lang = en
|
||||
trans.ar = apps/i2psnark/locale/messages_ar.po
|
||||
trans.cs = apps/i2psnark/locale/messages_cs.po
|
||||
trans.de = apps/i2psnark/locale/messages_de.po
|
||||
trans.el = apps/i2psnark/locale/messages_el.po
|
||||
trans.es = apps/i2psnark/locale/messages_es.po
|
||||
trans.fa = apps/i2psnark/locale/messages_fa.po
|
||||
trans.fi = apps/i2psnark/locale/messages_fi.po
|
||||
trans.fr = apps/i2psnark/locale/messages_fr.po
|
||||
trans.hu = apps/i2psnark/locale/messages_hu.po
|
||||
@@ -185,10 +279,12 @@ trans.ro = apps/i2psnark/locale/messages_ro.po
|
||||
trans.ru_RU = apps/i2psnark/locale/messages_ru.po
|
||||
trans.sk = apps/i2psnark/locale/messages_sk.po
|
||||
trans.sv_SE = apps/i2psnark/locale/messages_sv.po
|
||||
trans.sq = apps/i2psnark/locale/messages_sq.po
|
||||
trans.tr_TR = apps/i2psnark/locale/messages_tr.po
|
||||
trans.uk_UA = apps/i2psnark/locale/messages_uk.po
|
||||
trans.vi = apps/i2psnark/locale/messages_vi.po
|
||||
trans.zh_CN = apps/i2psnark/locale/messages_zh.po
|
||||
trans.zh_TW = apps/i2psnark/locale/messages_zh_TW.po
|
||||
|
||||
[I2P.susidns]
|
||||
source_file = apps/susidns/locale/messages_en.po
|
||||
@@ -199,6 +295,7 @@ trans.da = apps/susidns/locale/messages_da.po
|
||||
trans.de = apps/susidns/locale/messages_de.po
|
||||
trans.el = apps/susidns/locale/messages_el.po
|
||||
trans.es = apps/susidns/locale/messages_es.po
|
||||
trans.fa = apps/susidns/locale/messages_fa.po
|
||||
trans.fi = apps/susidns/locale/messages_fi.po
|
||||
trans.fr = apps/susidns/locale/messages_fr.po
|
||||
trans.gl = apps/susidns/locale/messages_gl.po
|
||||
@@ -225,6 +322,7 @@ trans.zh_TW = apps/susidns/locale/messages_zh_TW.po
|
||||
source_file = apps/desktopgui/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/desktopgui/locale/messages_ar.po
|
||||
trans.az = apps/desktopgui/locale/messages_az.po
|
||||
trans.bg = apps/desktopgui/locale/messages_bg.po
|
||||
trans.ca = apps/desktopgui/locale/messages_ca.po
|
||||
trans.cs = apps/desktopgui/locale/messages_cs.po
|
||||
@@ -251,26 +349,35 @@ trans.pt_BR = apps/desktopgui/locale/messages_pt_BR.po
|
||||
trans.ro = apps/desktopgui/locale/messages_ro.po
|
||||
trans.ru_RU = apps/desktopgui/locale/messages_ru.po
|
||||
trans.sk = apps/desktopgui/locale/messages_sk.po
|
||||
trans.sr = apps/desktopgui/locale/messages_sr.po
|
||||
trans.sv_SE = apps/desktopgui/locale/messages_sv.po
|
||||
trans.sq = apps/desktopgui/locale/messages_sq.po
|
||||
trans.uk_UA = apps/desktopgui/locale/messages_uk.po
|
||||
trans.tr_TR = apps/desktopgui/locale/messages_tr.po
|
||||
trans.vi = apps/desktopgui/locale/messages_vi.po
|
||||
trans.zh_CN = apps/desktopgui/locale/messages_zh.po
|
||||
trans.zh_TW = apps/desktopgui/locale/messages_zh_TW.po
|
||||
|
||||
[I2P.susimail]
|
||||
source_file = apps/susimail/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/susimail/locale/messages_ar.po
|
||||
trans.az = apps/susimail/locale/messages_az.po
|
||||
trans.bg = apps/susimail/locale/messages_bg.po
|
||||
trans.ca = apps/susimail/locale/messages_ca.po
|
||||
trans.cs = apps/susimail/locale/messages_cs.po
|
||||
trans.da = apps/susimail/locale/messages_da.po
|
||||
trans.de = apps/susimail/locale/messages_de.po
|
||||
trans.el = apps/susimail/locale/messages_el.po
|
||||
trans.es = apps/susimail/locale/messages_es.po
|
||||
trans.fa = apps/susimail/locale/messages_fa.po
|
||||
trans.fi = apps/susimail/locale/messages_fi.po
|
||||
trans.fr = apps/susimail/locale/messages_fr.po
|
||||
trans.gl = apps/susimail/locale/messages_gl.po
|
||||
trans.hr = apps/susimail/locale/messages_hr.po
|
||||
trans.hu = apps/susimail/locale/messages_hu.po
|
||||
;; Java converts he to iw
|
||||
trans.he = apps/susimail/locale/messages_iw.po
|
||||
;; Java converts id to in
|
||||
trans.id = apps/susimail/locale/messages_in.po
|
||||
trans.it = apps/susimail/locale/messages_it.po
|
||||
@@ -286,6 +393,7 @@ trans.ro = apps/susimail/locale/messages_ro.po
|
||||
trans.ru_RU = apps/susimail/locale/messages_ru.po
|
||||
trans.sk = apps/susimail/locale/messages_sk.po
|
||||
trans.sq = apps/susimail/locale/messages_sq.po
|
||||
trans.sr = apps/susimail/locale/messages_sr.po
|
||||
trans.sv_SE = apps/susimail/locale/messages_sv.po
|
||||
trans.tr_TR = apps/susimail/locale/messages_tr.po
|
||||
trans.uk_UA = apps/susimail/locale/messages_uk.po
|
||||
@@ -296,6 +404,8 @@ trans.zh_TW = apps/susimail/locale/messages_zh_TW.po
|
||||
[I2P.debconf]
|
||||
source_file = debian/po/templates.pot
|
||||
source_lang = en
|
||||
trans.ar = debian/po/ar.po
|
||||
trans.az = debian/po/az.po
|
||||
trans.cs = debian/po/cs.po
|
||||
trans.de = debian/po/de.po
|
||||
trans.el = debian/po/el.po
|
||||
@@ -326,6 +436,8 @@ trans.zh_TW = debian/po/zh_TW.po
|
||||
[I2P.i2prouter-script]
|
||||
source_file = installer/resources/locale/po/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = installer/resources/locale/po/messages_ar.po
|
||||
trans.az = installer/resources/locale/po/messages_az.po
|
||||
trans.ca = installer/resources/locale/po/messages_ca.po
|
||||
trans.de = installer/resources/locale/po/messages_de.po
|
||||
trans.es = installer/resources/locale/po/messages_es.po
|
||||
@@ -335,6 +447,7 @@ trans.id = installer/resources/locale/po/messages_id.po
|
||||
trans.it = installer/resources/locale/po/messages_it.po
|
||||
trans.ja = installer/resources/locale/po/messages_ja.po
|
||||
trans.ko = installer/resources/locale/po/messages_ko.po
|
||||
trans.nb = installer/resources/locale/po/messages_nb.po
|
||||
trans.nl = installer/resources/locale/po/messages_nl.po
|
||||
trans.pl = installer/resources/locale/po/messages_pl.po
|
||||
trans.pt = installer/resources/locale/po/messages_pt.po
|
||||
@@ -348,49 +461,69 @@ trans.uk_UA = installer/resources/locale/po/messages_uk.po
|
||||
trans.zh_CN = installer/resources/locale/po/messages_zh.po
|
||||
|
||||
[I2P.getopt]
|
||||
;;
|
||||
;; Java properties files (when not read with our DataHelper methods) must be ISO-8859-1 encoded.
|
||||
;; See https://docs.oracle.com/javase/6/docs/api/java/util/Properties.html
|
||||
;; Several getopt translations from Transifex are ISO-8859-1 encoded.
|
||||
;; So that all our source files are consistently UTF-8 encoded,
|
||||
;; you may optionally convert them to ASCII with Unicode escapes after downloading.
|
||||
;; Convert files with:
|
||||
;; native2ascii -encoding iso-8859-1 foo.properties foo.properties.new
|
||||
;; Then review that it looks right:
|
||||
;; native2ascii -reverse -encoding UTF-8 foo.properties.new
|
||||
;; Then copy it over:
|
||||
;; mv foo.properties.new foo.properties
|
||||
;; The following files from Transifex are known to be ISO-8859-1 encoded,
|
||||
;; and should be escaped using native2ascii after downloading:
|
||||
;; gl, pt, pt_BR, sq, sv, tr
|
||||
;;
|
||||
source_file = core/java/src/gnu/getopt/MessagesBundle.properties
|
||||
source_lang = en
|
||||
type = PROPERTIES
|
||||
trans.ar = core/java/src/gnu/getopt/MessagesBundle_ar.properties
|
||||
trans.az = core/java/src/gnu/getopt/MessagesBundle_az.properties
|
||||
trans.cs = core/java/src/gnu/getopt/MessagesBundle_cs.properties
|
||||
trans.de = core/java/src/gnu/getopt/MessagesBundle_de.properties
|
||||
trans.es = core/java/src/gnu/getopt/MessagesBundle_es.properties
|
||||
trans.fi = core/java/src/gnu/getopt/MessagesBundle_fi.properties
|
||||
trans.fr = core/java/src/gnu/getopt/MessagesBundle_fr.properties
|
||||
;; currently corrupt, non-UTF-8
|
||||
;;trans.gl = core/java/src/gnu/getopt/MessagesBundle_gl.properties
|
||||
trans.gl = core/java/src/gnu/getopt/MessagesBundle_gl.properties
|
||||
trans.hu = core/java/src/gnu/getopt/MessagesBundle_hu.properties
|
||||
;; Java converts id to in
|
||||
trans.id = core/java/src/gnu/getopt/MessagesBundle_in.properties
|
||||
trans.it = core/java/src/gnu/getopt/MessagesBundle_it.properties
|
||||
trans.ja = core/java/src/gnu/getopt/MessagesBundle_ja.properties
|
||||
trans.ko = core/java/src/gnu/getopt/MessagesBundle_ko.properties
|
||||
trans.nl = core/java/src/gnu/getopt/MessagesBundle_nl.properties
|
||||
trans.nb = core/java/src/gnu/getopt/MessagesBundle_nb.properties
|
||||
trans.nl = core/java/src/gnu/getopt/MessagesBundle_nl.properties
|
||||
trans.pl = core/java/src/gnu/getopt/MessagesBundle_pl.properties
|
||||
;; currently corrupt, non-UTF-8
|
||||
;;trans.pt = core/java/src/gnu/getopt/MessagesBundle_pt.properties
|
||||
;; currently corrupt, non-UTF-8
|
||||
;;trans.pt_BR = core/java/src/gnu/getopt/MessagesBundle_pt_BR.properties
|
||||
trans.pt = core/java/src/gnu/getopt/MessagesBundle_pt.properties
|
||||
trans.pt_BR = core/java/src/gnu/getopt/MessagesBundle_pt_BR.properties
|
||||
trans.ro = core/java/src/gnu/getopt/MessagesBundle_ro.properties
|
||||
trans.ru_RU = core/java/src/gnu/getopt/MessagesBundle_ru.properties
|
||||
trans.sk = core/java/src/gnu/getopt/MessagesBundle_sk.properties
|
||||
;; currently corrupt, non-UTF-8
|
||||
;;trans.sq = core/java/src/gnu/getopt/MessagesBundle_sq.properties
|
||||
;; currently corrupt, non-UTF-8
|
||||
;;trans.tr_TR = core/java/src/gnu/getopt/MessagesBundle_tr.properties
|
||||
trans.sq = core/java/src/gnu/getopt/MessagesBundle_sq.properties
|
||||
trans.sr = core/java/src/gnu/getopt/MessagesBundle_sr.properties
|
||||
trans.sv_SE = core/java/src/gnu/getopt/MessagesBundle_sv.properties
|
||||
trans.tr_TR = core/java/src/gnu/getopt/MessagesBundle_tr.properties
|
||||
trans.uk_UA = core/java/src/gnu/getopt/MessagesBundle_uk.properties
|
||||
trans.zh_CN = core/java/src/gnu/getopt/MessagesBundle_zh.properties
|
||||
trans.zh_TW = core/java/src/gnu/getopt/MessagesBundle_zh_TW.properties
|
||||
|
||||
[I2P.streaming]
|
||||
source_file = apps/ministreaming/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/ministreaming/locale/messages_ar.po
|
||||
trans.az = apps/ministreaming/locale/messages_az.po
|
||||
trans.ca = apps/ministreaming/locale/messages_ca.po
|
||||
trans.cs = apps/ministreaming/locale/messages_cs.po
|
||||
trans.de = apps/ministreaming/locale/messages_de.po
|
||||
trans.es = apps/ministreaming/locale/messages_es.po
|
||||
trans.fa = apps/ministreaming/locale/messages_fa.po
|
||||
trans.fi = apps/ministreaming/locale/messages_fi.po
|
||||
trans.fr = apps/ministreaming/locale/messages_fr.po
|
||||
trans.gl = apps/ministreaming/locale/messages_gl.po
|
||||
trans.hu = apps/ministreaming/locale/messages_hu.po
|
||||
;; Java converts id to in
|
||||
trans.id = apps/ministreaming/locale/messages_in.po
|
||||
trans.it = apps/ministreaming/locale/messages_it.po
|
||||
@@ -408,10 +541,15 @@ trans.uk_UA = apps/ministreaming/locale/messages_uk.po
|
||||
trans.zh_CN = apps/ministreaming/locale/messages_zh.po
|
||||
|
||||
[I2P.manpages]
|
||||
;;
|
||||
;; after adding languages here, add to debian/*.manpages also
|
||||
;; You must run installer/resources/poupdate-man.sh first.
|
||||
;;
|
||||
type = PO
|
||||
source_file = installer/resources/locale-man/man.pot
|
||||
source_lang = en
|
||||
; after adding languages here, add to debian/*.manpages also
|
||||
trans.ar = installer/resources/locale-man/man_ar.po
|
||||
trans.az = installer/resources/locale-man/man_az.po
|
||||
trans.de = installer/resources/locale-man/man_de.po
|
||||
trans.es = installer/resources/locale-man/man_es.po
|
||||
trans.fi = installer/resources/locale-man/man_fi.po
|
||||
@@ -423,10 +561,66 @@ trans.nl = installer/resources/locale-man/man_nl.po
|
||||
trans.pl = installer/resources/locale-man/man_pl.po
|
||||
trans.pt = installer/resources/locale-man/man_pt.po
|
||||
trans.pt_BR = installer/resources/locale-man/man_pt_BR.po
|
||||
trans.ro = installer/resources/locale-man/man_ro.po
|
||||
trans.ru_RU = installer/resources/locale-man/man_ru.po
|
||||
trans.sv_SE = installer/resources/locale-man/man_sv.po
|
||||
trans.tr_TR = installer/resources/locale-man/man_tr.po
|
||||
trans.zh_CN = installer/resources/locale-man/man_zh.po
|
||||
|
||||
[I2P.eepsite]
|
||||
;;
|
||||
;; For any new translations, add links in installer/resources/eepsite/docroot/help/index.html
|
||||
;; and copy new flags in build.xml copyflags-unlesspkg target,
|
||||
;; and add to debian/i2p-router.links and debian-alt/*/i2p-router.links
|
||||
;;
|
||||
type = HTML
|
||||
source_file = installer/resources/eepsite/docroot/help/index.html
|
||||
source_lang = en
|
||||
;; File contains local changes to fix RTL issues.
|
||||
;; Merge locally or make changes on TX side before re-enabling
|
||||
;;trans.ar = installer/resources/eepsite/docroot/help/index_ar.html
|
||||
trans.az = installer/resources/eepsite/docroot/help/index_az.html
|
||||
trans.de = installer/resources/eepsite/docroot/help/index_de.html
|
||||
;; not yet translated on TX, use old page
|
||||
;;trans.es = installer/resources/eepsite/docroot/help/index_es.html
|
||||
trans.fr = installer/resources/eepsite/docroot/help/index_fr.html
|
||||
trans.hu = installer/resources/eepsite/docroot/help/index_hu.html
|
||||
;; Java converts id to in
|
||||
trans.id = installer/resources/eepsite/docroot/help/index_in.html
|
||||
trans.it = installer/resources/eepsite/docroot/help/index_it.html
|
||||
;; not yet translated on TX, use old page
|
||||
;;trans.nl = installer/resources/eepsite/docroot/help/index_nl.html
|
||||
trans.pl = installer/resources/eepsite/docroot/help/index_pl.html
|
||||
trans.pt = installer/resources/eepsite/docroot/help/index_pt.html
|
||||
trans.ro = installer/resources/eepsite/docroot/help/index_ro.html
|
||||
trans.ru_RU = installer/resources/eepsite/docroot/help/index_ru.html
|
||||
;; not yet translated on TX, use old page
|
||||
;;trans.sv_SE = installer/resources/eepsite/docroot/help/index_sv.html
|
||||
trans.tr_TR = installer/resources/eepsite/docroot/help/index_tr.html
|
||||
;; not yet translated on TX, use old page
|
||||
;;trans.zh_CN = installer/resources/eepsite/docroot/help/index_zh.html
|
||||
|
||||
[I2P.readme]
|
||||
;;
|
||||
;; Text on /console
|
||||
;;
|
||||
type = HTML
|
||||
source_file = installer/resources/readme/readme.html
|
||||
source_lang = en
|
||||
trans.ar = installer/resources/readme/readme_ar.html
|
||||
trans.de = installer/resources/readme/readme_de.html
|
||||
trans.fr = installer/resources/readme/readme_fr.html
|
||||
;; Java converts id to in
|
||||
trans.id = installer/resources/readme/readme_in.html
|
||||
trans.it = installer/resources/readme/readme_it.html
|
||||
trans.ja = installer/resources/readme/readme_ja.html
|
||||
trans.pl = installer/resources/readme/readme_pl.html
|
||||
trans.pt = installer/resources/readme/readme_pt.html
|
||||
trans.ro = installer/resources/readme/readme_ro.html
|
||||
trans.ru_RU = installer/resources/readme/readme_ru.html
|
||||
trans.tr_TR = installer/resources/readme/readme_tr.html
|
||||
trans.zh_CN = installer/resources/readme/readme_zh.html
|
||||
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ If you're having trouble, check the
|
||||
website at https://geti2p.net/, or get on irc://irc.freenode.net/#i2p
|
||||
|
||||
I2P will create and store files and configuration data in the user directory
|
||||
~/.i2p/ on Linux and %APPDATA%\I2P\ on Windows. This directory is created
|
||||
~/.i2p/ on Linux and %LOCALAPPDATA%\I2P\ on Windows. This directory is created
|
||||
when I2P is run for the first time. It also creates files in the system
|
||||
temporary directory specified by the Java Virtual Machine.
|
||||
To change the location of these directories, or to configure I2P to
|
||||
|
||||
175
LICENSE.txt
175
LICENSE.txt
@@ -29,7 +29,7 @@ POSSIBILITY OF SUCH DAMAGES.
|
||||
LICENSES
|
||||
--------
|
||||
|
||||
Core (i2p.jar):
|
||||
Core API (i2p.jar):
|
||||
Public domain except as listed below:
|
||||
|
||||
ElGamal and DSA code:
|
||||
@@ -65,6 +65,7 @@ Public domain except as listed below:
|
||||
See licenses/LICENSE-HashCash.txt
|
||||
|
||||
GettextResource from gettext v0.19.8:
|
||||
(not included in most distribution packages)
|
||||
Copyright (C) 2001, 2007 Free Software Foundation, Inc.
|
||||
See licenses/LICENSE-LGPLv2.1.txt
|
||||
|
||||
@@ -72,22 +73,30 @@ Public domain except as listed below:
|
||||
Contains some code Copyright 2006 Sun Microsystems, Inc.
|
||||
See licenses/LICENSE-InstallCert.txt
|
||||
|
||||
BlockFile:
|
||||
Copyright (c) 2006, Matthew Estes
|
||||
See licenses/LICENSE-BlockFile.txt
|
||||
|
||||
SipHashInline:
|
||||
Copyright 2012 Hiroshi Nakamura <nahi@ruby-lang.org>
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
Getopt:
|
||||
(not included in most distribution packages)
|
||||
Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com)
|
||||
See licenses/LICENSE-LGPLv2.1.txt
|
||||
|
||||
EdDSA-Java:
|
||||
See licenses/LICENSE-CC0-1.0-Universal.txt
|
||||
|
||||
HostnameVerifier:
|
||||
From Apache HttpClient 4.4.1 and HttpCore 4.4.1
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
json-simple 1.1.1
|
||||
(not included in most distribution packages)
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
Noise library:
|
||||
Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||
See licenses/LICENSE-Noise.txt
|
||||
|
||||
|
||||
Router (router.jar):
|
||||
Public domain except as listed below:
|
||||
@@ -99,15 +108,26 @@ Public domain except as listed below:
|
||||
Copyright (C) 2003-2010 Satoshi Konno
|
||||
See licenses/LICENSE-UPnP.txt
|
||||
|
||||
GeoIP: GeoLite databases are licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
|
||||
http://creativecommons.org/licenses/by-sa/3.0/
|
||||
This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com/
|
||||
|
||||
GeoIP API 1.3.1:
|
||||
See licenses/LICENSE-LGPLv2.1.txt
|
||||
|
||||
GeoIP2-java 2.12.0:
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
Maxmind-DB-Reader-java 1.2.2:
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
Noise library:
|
||||
Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||
See licenses/LICENSE-Noise.txt
|
||||
|
||||
SparseArray:
|
||||
Copyright (C) 2006 The Android Open Source Project
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
|
||||
Installer:
|
||||
(not included in distribution packages)
|
||||
Launch4j 3.0.1:
|
||||
(Launch4j is only included in the upstream source package and Windows binaries.
|
||||
Not applicable for non-Windows binaries or Debian/Launchpad packages.)
|
||||
@@ -123,7 +143,7 @@ Installer:
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
See licenses/NOTICE-Commons-Logging.txt
|
||||
|
||||
XStream (http://xstream.codehaus.org/)
|
||||
XStream (http://xstream.github.io/)
|
||||
Copyright (c) 2003-2004, Joe Walnes
|
||||
See licenses/LICENSE-XStream.txt
|
||||
|
||||
@@ -149,59 +169,84 @@ Installer:
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
|
||||
GeoIP Data:
|
||||
(not included in most distribution packages)
|
||||
This product includes GeoLite2 data created by MaxMind, available from http://www.maxmind.com/
|
||||
See licenses/LICENSE-GeoIP.txt
|
||||
|
||||
Java Service Wrapper Community Edition 32-bit 3.5.30:
|
||||
Copyright (C) 1999-2016 Tanuki Software, Ltd. All Rights Reserved.
|
||||
Launchers:
|
||||
(not included in distribution packages)
|
||||
Copyright (c) 2002-2018 EPFL, Lausanne / Lightbend, Inc. , unless otherwise specified.
|
||||
See licenses/LICENSE-Scala.md
|
||||
|
||||
Java Service Wrapper Community Edition 3.5.39:
|
||||
(Windows: 3.5.25)
|
||||
(not included in most distribution packages)
|
||||
Copyright (C) 1999-2019 Tanuki Software, Ltd. All Rights Reserved.
|
||||
See licenses/LICENSE-Wrapper.txt
|
||||
|
||||
|
||||
Jbigi Libraries (jbigi.jar):
|
||||
JNI code public domain.
|
||||
JNI code: public domain.
|
||||
|
||||
GMP 4.3.2 / 5.0.2:
|
||||
Copyright 1991, 1996, 1999, 2000, 2007 Free Software Foundation, Inc.
|
||||
See licenses/LICENSE-LGPLv3.txt
|
||||
GMP 6.0.0:
|
||||
GMP 6.0.0 / 6.1.2:
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
|
||||
Applications:
|
||||
|
||||
Addressbook:
|
||||
Addressbook (addressbook.jar):
|
||||
Copyright (c) 2004 Ragnarok
|
||||
See licenses/LICENSE-Addressbook.txt
|
||||
BlockFile:
|
||||
Copyright (c) 2006, Matthew Estes
|
||||
See licenses/LICENSE-BlockFile.txt
|
||||
|
||||
BOB:
|
||||
BOB (BOB.jar):
|
||||
Copyright (C) sponge
|
||||
See licenses/COPYING-BOB.txt
|
||||
|
||||
Desktopgui
|
||||
Desktopgui (desktopgui.jar):
|
||||
Copyright (c) Mathias De Maré
|
||||
See licenses/LICENSE-DesktopGUI.txt
|
||||
|
||||
I2PSnark:
|
||||
Glassfish Standard Tag Library (JSTL) 1.2 (commons-el.jar):
|
||||
(not included in most distribution packages)
|
||||
Common Development and Distribution License (CDDL) version 1.0 + GNU General Public License (GPL) version 2
|
||||
See https://glassfish.dev.java.net/public/CDDL+GPL.html
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
I2PControl
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
Includes jBCrypt:
|
||||
Copyright (c) 2006 Damien Miller <djm@mindrot.org>
|
||||
See licenses/LICENSE-jBCrypt.txt
|
||||
Includes jsonrpc2 1.38.1 (base) 1.11 (server)
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
I2PSnark (i2psnark.jar, i2psnark.war):
|
||||
Copyright (C) 2003 Mark J. Wielaard
|
||||
GPLv2 (or any later version)
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
Silk icons: See licenses/LICENSE-SilkIcons.txt
|
||||
|
||||
I2PTunnel:
|
||||
I2PTunnel (i2ptunnel.jar, i2ptunnel.war):
|
||||
(c) 2003 - 2004 mihi
|
||||
GPLv2 (or any later version) with exception.
|
||||
See licenses/LICENSE-I2PTunnel.txt
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
I2PTunnel SOCKS Proxy:
|
||||
Copyright (c) 2004 by human
|
||||
GPLv2 (or any later version) with exception.
|
||||
See licenses/LICENSE-I2PTunnel.txt
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
I2PTunnel UDP and Streamr (i2ptunnel.jar):
|
||||
By welterde.
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
I2PTunnel SOCKS Proxy:
|
||||
Copyright (c) 2004 by human
|
||||
GPLv2 (or any later version) with exception.
|
||||
See licenses/LICENSE-I2PTunnel.txt
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
I2PTunnel UDP and Streamr:
|
||||
By welterde.
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
Imagegen:
|
||||
Imagegen (imagegen.war):
|
||||
Identicon:
|
||||
Copyright (c) 2007-2014 Don Park <donpark@docuverse.com>
|
||||
See licenses/LICENSE-Identicon.txt
|
||||
@@ -212,79 +257,83 @@ Applications:
|
||||
Zxing 3.3.0:
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
Jetty 9.2.21.v20170120:
|
||||
Jetty 9.2.29.v20191105 (jetty-*.jar, org.mortbay.*.jar):
|
||||
(not included in most distribution packages, except for jetty-i2p.jar)
|
||||
See licenses/ABOUT-Jetty.html
|
||||
See licenses/NOTICE-Jetty.html
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
See licenses/LICENSE-ECLIPSE-1.0.html
|
||||
|
||||
JRobin 1.6.0-1:
|
||||
RRD4J 3.5 (jrobin.jar):
|
||||
Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
Copyright 2011 The RRD4J Authors.
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
See licenses/LICENSE-LGPLv2.1.txt
|
||||
DeallocationHelper:
|
||||
Copyright (c) 2006-2016 Julien Gouesse
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
Ministreaming Lib:
|
||||
Ministreaming Lib (mstreaming.jar):
|
||||
By mihi.
|
||||
See licenses/LICENSE-BSD.txt
|
||||
|
||||
Proxyscript:
|
||||
Proxyscript (i2pProxy.pac):
|
||||
(not included in distribution packages)
|
||||
By Cervantes.
|
||||
Public domain.
|
||||
|
||||
Router console:
|
||||
Router console (routerconsole.jar, routerconsole.war):
|
||||
Public domain.
|
||||
|
||||
Router Console themes:
|
||||
Flag icons:
|
||||
- Jersey and EU flag icons: public domain, courtesy Xrmap flag
|
||||
collection http://www.arvernes.com/wiki/index.php/Xrmap
|
||||
- Guernsey and Isle of Man flags from the Open Clip Art Library, released into the public domain
|
||||
- Curaçao, courtesy of David Benbennick, released into the public domain
|
||||
- All other flag icons: public domain, courtesy mjames@gmail.com http://www.famfamfam.com/
|
||||
Silk icons: See licenses/LICENSE-SilkIcons.txt
|
||||
FatCow icons: See licenses/LICENSE-FatCowIcons.txt
|
||||
Fugue Icons: See licenses/LICENSE-FugueIcons.txt
|
||||
Feather icons: Copyright (c) 2013-2017 Cole Bemis; see licenses/LICENSE-Feather.txt
|
||||
|
||||
GeoIP Data:
|
||||
This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com/
|
||||
See licenses/LICENSE-GeoIP.txt
|
||||
Router Console unthemed, light, and dark theme logos:
|
||||
Copyright (c) 2019 The Invisible Internet Project. All rights reserved.
|
||||
Creative Commons Attribution-ShareAlike 4.0 International License
|
||||
See https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
Router Console and I2PSnark themes:
|
||||
"Man with hat over face" & related images licensed under a Creative Commons 2.0 license.
|
||||
Original photos by Florian Kuhlmann. http://www.flickr.com/photos/floriankuhlmann/3117758155
|
||||
Some images licensed under a Creative Commons 2.0 license.
|
||||
Silk icons: See licenses/LICENSE-SilkIcons.txt
|
||||
|
||||
I2PSnark light theme:
|
||||
"Creative Commons Cat" licensed under a Creative Commons Attribution 3.0 Unported License.
|
||||
Original photo by Boaz Arad. http://www.luxphile.com/2011/01/creative-commons-cat.html
|
||||
Router Console M-Lab subsystem:
|
||||
Copyright 2010 Vuze, Inc. All rights reserved.
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
SAM:
|
||||
Router Console NDT subsystem:
|
||||
Copyright (c) 2003 University of Chicago. All rights reserved.
|
||||
See licenses/LICENSE-NDT.txt
|
||||
Notice: I2P has changed specified portions of the Software, including the package edu.internet2.ndt.
|
||||
|
||||
SAM (sam.jar):
|
||||
Public domain.
|
||||
|
||||
Streaming Lib:
|
||||
Streaming Lib (streaming.jar):
|
||||
Public domain.
|
||||
|
||||
SusiDNS:
|
||||
SusiDNS (susidns.war):
|
||||
Copyright (C) 2005 <susi23@mail.i2p>
|
||||
GPLv2 (or any later version)
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
Uses Glassfish Standard Tag Library (JSTL) 1.2:
|
||||
Common Development and Distribution License (CDDL) version 1.0 + GNU General Public License (GPL) version 2
|
||||
See https://glassfish.dev.java.net/public/CDDL+GPL.html
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
SusiMail:
|
||||
SusiMail (susimail.war):
|
||||
Copyright (C) 2004-2005 <susi23@mail.i2p>
|
||||
GPLv2 (or any later version)
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
Systray:
|
||||
Systray (systray.jar):
|
||||
Public domain.
|
||||
Bundles systray4j-2.4.1:
|
||||
See licenses/LICENSE-LGPLv2.1.txt
|
||||
|
||||
Tomcat 8.0.33:
|
||||
Copyright 1999-2016 The Apache Software Foundation
|
||||
Tomcat 8.5.50 (jasper-runtime.jar):
|
||||
(not included in most distribution packages)
|
||||
Copyright 1999-2019 The Apache Software Foundation
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
See licenses/NOTICE-Tomcat.txt
|
||||
|
||||
@@ -302,6 +351,10 @@ distributions. See the source package for the additional license information.
|
||||
Copyright (C) sponge
|
||||
DWTFYWTPL
|
||||
|
||||
Gradle wrapper:
|
||||
Copyright (c) 2017 the original author or authors.
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
||||
SAM C Library:
|
||||
Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
|
||||
See apps/sam/c/doc/license.txt
|
||||
|
||||
20
README.md
20
README.md
@@ -17,6 +17,10 @@ FAQ: https://geti2p.net/faq
|
||||
API: http://docs.i2p-projekt.de/javadoc/
|
||||
or run 'ant javadoc' then start at build/javadoc/index.html
|
||||
|
||||
## How to contribute / Hack on I2P
|
||||
|
||||
Please check out [HACKING.md](docs/HACKING.md) and other documents in the docs directory.
|
||||
|
||||
## Building packages from source
|
||||
|
||||
To get development branch from source control: https://geti2p.net/newdevelopers
|
||||
@@ -32,7 +36,7 @@ To get development branch from source control: https://geti2p.net/newdevelopers
|
||||
http://www.gnu.org/software/gettext/
|
||||
- Build environment must use a UTF-8 locale.
|
||||
|
||||
### Build process
|
||||
### Ant build process
|
||||
|
||||
On x86 systems do:
|
||||
|
||||
@@ -46,6 +50,20 @@ On non-x86, use one of the following instead:
|
||||
|
||||
Run 'ant' with no arguments to see other build options.
|
||||
|
||||
### Gradle build process
|
||||
|
||||
Full builds of installers or updates are not yet possible, but the code can be
|
||||
compiled with:
|
||||
|
||||
./gradlew assemble
|
||||
|
||||
This will download dependencies over the clearnet by default, including Gradle
|
||||
itself. To download through a SOCKS proxy (e.g. Tor), add the following lines to
|
||||
your `~/.gradle/gradle.properties`:
|
||||
|
||||
systemProp.socksProxyHost=localhost
|
||||
systemProp.socksProxyPort=9150
|
||||
|
||||
## Contact info
|
||||
|
||||
Need help? See the IRC channel #i2p on irc.freenode.net
|
||||
|
||||
10
README.txt
10
README.txt
@@ -19,6 +19,16 @@ To build:
|
||||
Run 'ant' with no arguments to see other build options.
|
||||
See INSTALL.txt or https://geti2p.net/download for installation instructions.
|
||||
|
||||
Gradle build system:
|
||||
Full builds are not yet possible, but the command is:
|
||||
./gradlew assemble
|
||||
|
||||
gradlew will download dependencies over the clearnet by default, including
|
||||
Gradle itself. To download over Tor, create a gradle.properties file
|
||||
containing:
|
||||
systemProp.socksProxyHost=localhost
|
||||
systemProp.socksProxyPort=9150
|
||||
|
||||
Documentation:
|
||||
https://geti2p.net/how
|
||||
API: http://docs.i2p-projekt.de/javadoc/
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/i2p_sdk"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/ministreaming"/>
|
||||
<classpathentry kind="output" path="build"/>
|
||||
</classpath>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>BOB</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="core" />
|
||||
<orderEntry type="module" module-name="ministreaming" />
|
||||
</component>
|
||||
</module>
|
||||
12
apps/BOB/build.gradle
Normal file
12
apps/BOB/build.gradle
Normal file
@@ -0,0 +1,12 @@
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir 'src'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':core')
|
||||
compile project(':apps:ministreaming')
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.*;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.PortMapper;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
@@ -131,6 +132,7 @@ public class BOB implements Runnable, ClientApp {
|
||||
// no longer used.
|
||||
// private static int maxConnections = 0;
|
||||
|
||||
private final I2PAppContext _context;
|
||||
private final Logger _log;
|
||||
private final ClientAppManager _mgr;
|
||||
private final String[] _args;
|
||||
@@ -158,6 +160,7 @@ public class BOB implements Runnable, ClientApp {
|
||||
* @since 0.9.10
|
||||
*/
|
||||
public BOB(I2PAppContext context, ClientAppManager mgr, String[] args) {
|
||||
_context = context;
|
||||
// If we were run from command line, log to stdout
|
||||
boolean logToStdout = false;
|
||||
URL classResource = BOB.class.getResource("BOB.class");
|
||||
@@ -218,7 +221,7 @@ public class BOB implements Runnable, ClientApp {
|
||||
{
|
||||
File cfg = new File(configLocation);
|
||||
if (!cfg.isAbsolute()) {
|
||||
cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), configLocation);
|
||||
cfg = new File(_context.getConfigDir(), configLocation);
|
||||
}
|
||||
FileInputStream fi = null;
|
||||
try {
|
||||
@@ -239,7 +242,7 @@ public class BOB implements Runnable, ClientApp {
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(I2PClient.PROP_TCP_PORT)) {
|
||||
props.setProperty(I2PClient.PROP_TCP_PORT, "7654");
|
||||
props.setProperty(I2PClient.PROP_TCP_PORT, Integer.toString(I2PClient.DEFAULT_LISTEN_PORT));
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(PROP_BOB_PORT)) {
|
||||
@@ -275,7 +278,7 @@ public class BOB implements Runnable, ClientApp {
|
||||
if (save) {
|
||||
File cfg = new File(configLocation);
|
||||
if (!cfg.isAbsolute()) {
|
||||
cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), configLocation);
|
||||
cfg = new File(_context.getConfigDir(), configLocation);
|
||||
}
|
||||
FileOutputStream fo = null;
|
||||
try {
|
||||
@@ -316,6 +319,8 @@ public class BOB implements Runnable, ClientApp {
|
||||
_log.info("BOB is now running.");
|
||||
if (_mgr != null)
|
||||
_mgr.register(this);
|
||||
_context.portMapper().register(PortMapper.SVC_BOB, props.getProperty(PROP_BOB_HOST),
|
||||
Integer.parseInt(props.getProperty(PROP_BOB_PORT)));
|
||||
|
||||
int i = 0;
|
||||
boolean g = false;
|
||||
@@ -353,6 +358,7 @@ public class BOB implements Runnable, ClientApp {
|
||||
changeState(STOPPING, e);
|
||||
} finally {
|
||||
_log.info("BOB is now shutting down...");
|
||||
_context.portMapper().unregister(PortMapper.SVC_BOB);
|
||||
// Clean up everything.
|
||||
try {
|
||||
listener.close();
|
||||
|
||||
@@ -96,12 +96,14 @@ public class MUXlisten implements Runnable {
|
||||
}
|
||||
|
||||
String i2cpHost = Q.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
|
||||
int i2cpPort = 7654;
|
||||
String i2cpPortStr = Q.getProperty(I2PClient.PROP_TCP_PORT, "7654");
|
||||
try {
|
||||
i2cpPort = Integer.parseInt(i2cpPortStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new IllegalArgumentException("Invalid I2CP port specified [" + i2cpPortStr + "]");
|
||||
int i2cpPort = I2PClient.DEFAULT_LISTEN_PORT;
|
||||
String i2cpPortStr = Q.getProperty(I2PClient.PROP_TCP_PORT);
|
||||
if (i2cpPortStr != null) {
|
||||
try {
|
||||
i2cpPort = Integer.parseInt(i2cpPortStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new IllegalArgumentException("Invalid I2CP port specified [" + i2cpPortStr + "]");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.come_in) {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="java/src"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/i2p_sdk"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="lib" path="/jetty/jettylib/javax.servlet.jar"/>
|
||||
<classpathentry kind="output" path="build"/>
|
||||
</classpath>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>addressbook</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="web" name="Web">
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<deploymentDescriptor name="web.xml" url="file://$MODULE_DIR$/web.xml" />
|
||||
</descriptors>
|
||||
<webroots>
|
||||
<root url="file://$MODULE_DIR$" relative="/WEB-INF" />
|
||||
</webroots>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/java/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="jettylib" level="project" />
|
||||
<orderEntry type="module" module-name="core" />
|
||||
</component>
|
||||
</module>
|
||||
12
apps/addressbook/build.gradle
Normal file
12
apps/addressbook/build.gradle
Normal file
@@ -0,0 +1,12 @@
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir 'java/src'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':core')
|
||||
compile project(':apps:jetty')
|
||||
}
|
||||
@@ -41,6 +41,7 @@
|
||||
<target name="compile" depends="init, depend, warUpToDate">
|
||||
<javac debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
|
||||
includeAntRuntime="false"
|
||||
encoding="UTF-8"
|
||||
srcdir="${src}" destdir="${build}">
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
<classpath>
|
||||
|
||||
@@ -133,8 +133,9 @@ class AddressBook implements Iterable<Map.Entry<String, HostTxtEntry>> {
|
||||
public AddressBook(Subscription subscription, String proxyHost, int proxyPort) {
|
||||
Map<String, HostTxtEntry> a = null;
|
||||
File subf = null;
|
||||
File tmp = null;
|
||||
try {
|
||||
File tmp = SecureFile.createTempFile("addressbook", null, I2PAppContext.getGlobalContext().getTempDir());
|
||||
tmp = SecureFile.createTempFile("addressbook", null, I2PAppContext.getGlobalContext().getTempDir());
|
||||
EepGet get = new EepGet(I2PAppContext.getGlobalContext(), true,
|
||||
proxyHost, proxyPort, 0, -1l, MAX_SUB_SIZE, tmp.getAbsolutePath(), null,
|
||||
subscription.getLocation(), true, subscription.getEtag(), subscription.getLastModified(), null);
|
||||
@@ -148,6 +149,8 @@ class AddressBook implements Iterable<Map.Entry<String, HostTxtEntry>> {
|
||||
tmp.delete();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
if (tmp != null)
|
||||
tmp.delete();
|
||||
a = Collections.emptyMap();
|
||||
}
|
||||
this.addresses = a;
|
||||
@@ -306,7 +309,17 @@ class AddressBook implements Iterable<Map.Entry<String, HostTxtEntry>> {
|
||||
public void merge(AddressBook other, boolean overwrite, Log log) {
|
||||
if (this.addresses == null)
|
||||
throw new IllegalStateException();
|
||||
for (Iterator<Map.Entry<String, HostTxtEntry>> iter = other.iterator(); iter.hasNext(); ) {
|
||||
Iterator<Map.Entry<String, HostTxtEntry>> iter = other.iterator();
|
||||
try {
|
||||
merge2(other, iter, overwrite, log);
|
||||
} finally {
|
||||
if (iter instanceof HostTxtIterator)
|
||||
((HostTxtIterator) iter).close();
|
||||
}
|
||||
}
|
||||
|
||||
private void merge2(AddressBook other, Iterator<Map.Entry<String, HostTxtEntry>> iter, boolean overwrite, Log log) {
|
||||
while(iter.hasNext()) {
|
||||
Map.Entry<String, HostTxtEntry> entry = iter.next();
|
||||
String otherKey = entry.getKey();
|
||||
HostTxtEntry otherValue = entry.getValue();
|
||||
@@ -367,11 +380,6 @@ class AddressBook implements Iterable<Map.Entry<String, HostTxtEntry>> {
|
||||
this.write(new File(this.location));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
delete();
|
||||
}
|
||||
|
||||
/****
|
||||
public static void main(String[] args) {
|
||||
String[] tests = { "foo.i2p",
|
||||
|
||||
@@ -130,9 +130,22 @@ class Daemon {
|
||||
// This also has the advantage of not flushing the NamingService's LRU cache.
|
||||
String nsClass = router.getClass().getSimpleName();
|
||||
boolean isTextFile = nsClass.equals("HostsTxtNamingService") || nsClass.equals("SingleFileNamingService");
|
||||
Set<String> knownNames = null;
|
||||
Set<String> knownNames;
|
||||
if (isTextFile) {
|
||||
// load the hostname set
|
||||
Properties opts = new Properties();
|
||||
opts.setProperty("file", "hosts.txt");
|
||||
knownNames = router.getNames(opts);
|
||||
} else {
|
||||
knownNames = null;
|
||||
}
|
||||
NamingService publishedNS;
|
||||
if (published != null) {
|
||||
publishedNS = new SingleFileNamingService(I2PAppContext.getGlobalContext(), published.getAbsolutePath());
|
||||
} else {
|
||||
publishedNS = null;
|
||||
}
|
||||
|
||||
NamingService publishedNS = null;
|
||||
Iterator<AddressBook> iter = subscriptions.iterator();
|
||||
while (iter.hasNext()) {
|
||||
// yes, the EepGet fetch() is done in next()
|
||||
@@ -142,26 +155,41 @@ class Daemon {
|
||||
if (DEBUG && log != null && addressbook.getLocation() != null) {
|
||||
long end = System.currentTimeMillis();
|
||||
log.append("Fetch of " + addressbook.getLocation() + " took " + (end - start));
|
||||
start = end;
|
||||
}
|
||||
Iterator<Map.Entry<String, HostTxtEntry>> iter2 = addressbook.iterator();
|
||||
try {
|
||||
update(router, knownNames, publishedNS, addressbook, iter2, log);
|
||||
} finally {
|
||||
if (iter2 instanceof HostTxtIterator)
|
||||
((HostTxtIterator) iter2).close();
|
||||
addressbook.delete();
|
||||
}
|
||||
} // subscriptions
|
||||
subscriptions.write();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param knownNames only non-null if router book is a text file
|
||||
* @param publishedNS only non-null if we have a published address book
|
||||
* @since 0.9.33 split out from above
|
||||
*/
|
||||
private static void update(NamingService router, Set<String> knownNames,
|
||||
NamingService publishedNS, AddressBook addressbook,
|
||||
Iterator<Map.Entry<String, HostTxtEntry>> iter, Log log) {
|
||||
long start = DEBUG ? System.currentTimeMillis() : 0;
|
||||
int old = 0, nnew = 0, invalid = 0, conflict = 0, total = 0;
|
||||
int deleted = 0;
|
||||
for (Map.Entry<String, HostTxtEntry> entry : addressbook) {
|
||||
while(iter.hasNext()) {
|
||||
Map.Entry<String, HostTxtEntry> entry = iter.next();
|
||||
total++;
|
||||
// may be null for 'remove' entries
|
||||
String key = entry.getKey();
|
||||
boolean isKnown;
|
||||
// NOT set for text file NamingService
|
||||
Destination oldDest;
|
||||
if (isTextFile) {
|
||||
if (knownNames == null) {
|
||||
// load the hostname set
|
||||
Properties opts = new Properties();
|
||||
opts.setProperty("file", "hosts.txt");
|
||||
knownNames = router.getNames(opts);
|
||||
}
|
||||
if (knownNames != null) {
|
||||
oldDest = null;
|
||||
isKnown = key != null ? knownNames.contains(key) : null;
|
||||
isKnown = key != null ? knownNames.contains(key) : false;
|
||||
} else {
|
||||
oldDest = key != null ? router.lookup(key) : null;
|
||||
isKnown = oldDest != null;
|
||||
@@ -243,13 +271,11 @@ class Daemon {
|
||||
}
|
||||
// now update the published addressbook
|
||||
// ditto
|
||||
if (published != null) {
|
||||
if (publishedNS == null)
|
||||
publishedNS = new SingleFileNamingService(I2PAppContext.getGlobalContext(), published.getAbsolutePath());
|
||||
if (publishedNS != null) {
|
||||
// FIXME this fails, no support in SFNS
|
||||
success = publishedNS.addDestination(key, dest, props);
|
||||
if (log != null && !success)
|
||||
log.append("Add to published address book " + published.getAbsolutePath() + " failed for " + key);
|
||||
log.append("Add to published address book " + publishedNS.getName() + " failed for " + key);
|
||||
}
|
||||
nnew++;
|
||||
continue;
|
||||
@@ -416,12 +442,10 @@ class Daemon {
|
||||
". From: " + addressbook.getLocation());
|
||||
}
|
||||
// now update the published addressbook
|
||||
if (published != null) {
|
||||
if (publishedNS == null)
|
||||
publishedNS = new SingleFileNamingService(I2PAppContext.getGlobalContext(), published.getAbsolutePath());
|
||||
if (publishedNS != null) {
|
||||
success = publishedNS.remove(poldname, dest);
|
||||
if (log != null && !success)
|
||||
log.append("Remove from published address book " + published.getAbsolutePath() + " failed for " + poldname);
|
||||
log.append("Remove from published address book " + publishedNS.getName() + " failed for " + poldname);
|
||||
}
|
||||
} else {
|
||||
// mismatch, disallow
|
||||
@@ -463,20 +487,19 @@ class Daemon {
|
||||
log.append("Save to naming service " + router + " failed for new key " + key);
|
||||
}
|
||||
// now update the published addressbook
|
||||
if (published != null) {
|
||||
if (publishedNS == null)
|
||||
publishedNS = new SingleFileNamingService(I2PAppContext.getGlobalContext(), published.getAbsolutePath());
|
||||
if (publishedNS != null) {
|
||||
if (allowExistingKeyInPublished)
|
||||
success = publishedNS.put(key, dest, props);
|
||||
else
|
||||
success = publishedNS.putIfAbsent(key, dest, props);
|
||||
if (log != null && !success) {
|
||||
log.append("Save to published address book " + published.getAbsolutePath() + " failed for new key " + key);
|
||||
log.append("Save to published address book " + publishedNS.getName() + " failed for new key " + key);
|
||||
}
|
||||
}
|
||||
if (isTextFile)
|
||||
if (knownNames != null) {
|
||||
// keep track for later dup check
|
||||
knownNames.add(key);
|
||||
}
|
||||
nnew++;
|
||||
} else if (key == null) {
|
||||
// 'remove' actions
|
||||
@@ -507,12 +530,10 @@ class Daemon {
|
||||
". From: " + addressbook.getLocation());
|
||||
}
|
||||
// now update the published addressbook
|
||||
if (published != null) {
|
||||
if (publishedNS == null)
|
||||
publishedNS = new SingleFileNamingService(I2PAppContext.getGlobalContext(), published.getAbsolutePath());
|
||||
if (publishedNS != null) {
|
||||
success = publishedNS.remove(poldname, pod);
|
||||
if (log != null && !success)
|
||||
log.append("Remove from published address book " + published.getAbsolutePath() + " failed for " + poldname);
|
||||
log.append("Remove from published address book " + publishedNS.getName() + " failed for " + poldname);
|
||||
}
|
||||
} else if (pod2 != null) {
|
||||
// mismatch, disallow
|
||||
@@ -551,12 +572,10 @@ class Daemon {
|
||||
". From: " + addressbook.getLocation());
|
||||
}
|
||||
// now update the published addressbook
|
||||
if (published != null) {
|
||||
if (publishedNS == null)
|
||||
publishedNS = new SingleFileNamingService(I2PAppContext.getGlobalContext(), published.getAbsolutePath());
|
||||
if (publishedNS != null) {
|
||||
success = publishedNS.remove(poldname, pod);
|
||||
if (log != null && !success)
|
||||
log.append("Remove from published address book " + published.getAbsolutePath() + " failed for " + poldname);
|
||||
log.append("Remove from published address book " + publishedNS.getName() + " failed for " + poldname);
|
||||
}
|
||||
} else if (pod2 != null) {
|
||||
// mismatch, disallow
|
||||
@@ -586,12 +605,10 @@ class Daemon {
|
||||
". From: " + addressbook.getLocation());
|
||||
}
|
||||
// now update the published addressbook
|
||||
if (published != null) {
|
||||
if (publishedNS == null)
|
||||
publishedNS = new SingleFileNamingService(I2PAppContext.getGlobalContext(), published.getAbsolutePath());
|
||||
if (publishedNS != null) {
|
||||
success = publishedNS.remove(rev, pod);
|
||||
if (log != null && !success)
|
||||
log.append("Remove from published address book " + published.getAbsolutePath() + " failed for " + rev);
|
||||
log.append("Remove from published address book " + publishedNS.getName() + " failed for " + rev);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -641,7 +658,7 @@ class Daemon {
|
||||
log.append("Invalid b64 for " + key + " From: " + addressbook.getLocation());
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
} // entries
|
||||
if (DEBUG && log != null && total > 0) {
|
||||
log.append("Merge of " + addressbook.getLocation() + " into " + router +
|
||||
" took " + (System.currentTimeMillis() - start) + " ms with " +
|
||||
@@ -651,10 +668,7 @@ class Daemon {
|
||||
deleted + " deleted, " +
|
||||
invalid + " invalid, " +
|
||||
conflict + " conflicts");
|
||||
} // entries
|
||||
addressbook.delete();
|
||||
} // subscriptions
|
||||
subscriptions.write();
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 0.9.26 */
|
||||
@@ -789,7 +803,7 @@ class Daemon {
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Daemon daemon = new Daemon();
|
||||
if (args != null && args.length > 0 && args[0].equals("test"))
|
||||
if (args.length > 0 && args[0].equals("test"))
|
||||
daemon.test(args);
|
||||
else
|
||||
daemon.run(args);
|
||||
@@ -809,11 +823,14 @@ class Daemon {
|
||||
ctx.logManager().flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args may be null
|
||||
*/
|
||||
public void run(String[] args) {
|
||||
_running = true;
|
||||
String settingsLocation = "config.txt";
|
||||
File homeFile;
|
||||
if (args.length > 0) {
|
||||
if (args != null && args.length > 0) {
|
||||
homeFile = new SecureDirectory(args[0]);
|
||||
if (!homeFile.isAbsolute())
|
||||
homeFile = new SecureDirectory(I2PAppContext.getGlobalContext().getRouterDir(), args[0]);
|
||||
@@ -839,10 +856,10 @@ class Daemon {
|
||||
|
||||
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");
|
||||
if (!created)
|
||||
System.out.println("ERROR: Addressbook directory " + homeFile.getAbsolutePath() + " could not be created");
|
||||
//else
|
||||
// System.out.println("INFO: Addressbook directory " + homeFile.getAbsolutePath() + " created");
|
||||
}
|
||||
|
||||
File settingsFile = new File(homeFile, settingsLocation);
|
||||
|
||||
@@ -110,11 +110,6 @@ class HostTxtIterator implements Iterator<Map.Entry<String, HostTxtEntry>>, Clos
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* The object returned by the iterator.
|
||||
*/
|
||||
|
||||
@@ -138,9 +138,11 @@ public class BlockfileNamingService extends DummyNamingService {
|
||||
private static final String VERSION = "4";
|
||||
|
||||
private static final String PROP_ADDED = "a";
|
||||
private static final String PROP_MODDED = "m";
|
||||
// See susidns
|
||||
//private static final String PROP_MODDED = "m";
|
||||
private static final String PROP_SOURCE = "s";
|
||||
private static final String PROP_VALIDATED = "v";
|
||||
// See susidns
|
||||
//private static final String PROP_VALIDATED = "v";
|
||||
|
||||
private static final String DUMMY = "";
|
||||
private static final int NEGATIVE_CACHE_SIZE = 32;
|
||||
@@ -249,6 +251,7 @@ public class BlockfileNamingService extends DummyNamingService {
|
||||
|
||||
int total = 0;
|
||||
for (String hostsfile : getFilenames(list)) {
|
||||
_lists.add(hostsfile);
|
||||
File file = new File(_context.getRouterDir(), hostsfile);
|
||||
if ((!file.exists()) || !(file.canRead()))
|
||||
continue;
|
||||
@@ -288,7 +291,6 @@ public class BlockfileNamingService extends DummyNamingService {
|
||||
}
|
||||
total += count;
|
||||
_log.logAlways(Log.INFO, "Migrating " + count + " hosts from " + file + " to new hosts database");
|
||||
_lists.add(hostsfile);
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("DB init took " + DataHelper.formatDuration(_context.clock().now() - start));
|
||||
@@ -1395,6 +1397,11 @@ public class BlockfileNamingService extends DummyNamingService {
|
||||
p = de.propsList.get(i);
|
||||
d = de.destList.get(i);
|
||||
}
|
||||
out.write("# ");
|
||||
out.write(key);
|
||||
out.write(": ");
|
||||
out.write(d.toBase32());
|
||||
out.write(nl);
|
||||
out.write(key);
|
||||
out.write('=');
|
||||
out.write(d.toBase64());
|
||||
|
||||
@@ -144,14 +144,17 @@ public class BlockFile implements Closeable {
|
||||
return;
|
||||
}
|
||||
boolean init = !(new File(args[0])).exists();
|
||||
RAIFile raif = null;
|
||||
BlockFile bf = null;
|
||||
try {
|
||||
RAIFile raif = new RAIFile(new File(args[0]), true, true);
|
||||
BlockFile bf = new BlockFile(raif, init);
|
||||
raif = new RAIFile(new File(args[0]), true, true);
|
||||
bf = new BlockFile(raif, init);
|
||||
bf.bfck(true);
|
||||
bf.close();
|
||||
raif.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (bf != null) try { bf.close(); } catch (IOException ioe) {}
|
||||
if (raif != null) try { raif.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,12 +590,7 @@ public class BlockFile implements Closeable {
|
||||
_isClosed = true;
|
||||
metaIndex.close();
|
||||
|
||||
Set<String> oi = openIndices.keySet();
|
||||
Iterator<String> i = oi.iterator();
|
||||
Object k;
|
||||
while(i.hasNext()) {
|
||||
k = i.next();
|
||||
BSkipList bsl = openIndices.get(k);
|
||||
for (BSkipList bsl : openIndices.values()) {
|
||||
bsl.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ the API may be subject to change.
|
||||
Contact I2P developers if you are considering use in another application.
|
||||
Following is the original documentation copied from metanotion website.
|
||||
</p>
|
||||
<h1>Metanotion BlockFile Database</h1>
|
||||
<h2>Metanotion BlockFile Database</h2>
|
||||
<p>A 100% Java 1.3, BSD Licensed, embeddable single file database engine in 32KB. This database was designed for PDA based and J2ME applications.</p>
|
||||
|
||||
<h2>Table of Contents</h2>
|
||||
@@ -32,18 +32,18 @@ Following is the original documentation copied from metanotion website.
|
||||
<li><a href="#download">Download</a></li>
|
||||
</ul>
|
||||
|
||||
<a name="features"><h2>Features</h2></a>
|
||||
<h2 id="features">Features</h2>
|
||||
<ul>
|
||||
<li>100% Java 1.3. No JNI.</li>
|
||||
<li>Will work with any "file" as long as you can approximate something like <a href="http://java.sun.com/j2se/1.3/docs/api/java/io/RandomAccessFile.html">java.io.RandomAccessFile</a>, you can use this.</li>
|
||||
<li>BSD Licensed. Yes, this means you can use it for free in a commercial project. However, if you base some really cool mobile technology startup on this code we'll gladly accept stock options...</p>
|
||||
<li>BSD Licensed. Yes, this means you can use it for free in a commercial project. However, if you base some really cool mobile technology startup on this code we'll gladly accept stock options...</li>
|
||||
<li>No dependence on file API's(useful for mobile apps)</li>
|
||||
<li>Small. 32KB in a JAR file. <2000 lines of code.</li>
|
||||
<li>Reasonably fast. This is used in an app running on a sub 200MHz StrongARM PocketPC, and quite handily deals with 70,000 records. The load time is a little slow, but its been tested with a <a href="http://java.sun.com/javame/reference/apis.jsp">CDC 1.0/Personal Profile</a> device.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<a name="unfeatures"><h2>Unfeatures</h2></a>
|
||||
<h2 id="unfeatures">Unfeatures</h2>
|
||||
<p>A good, ACID database is a nice thing to work with. Unfortunately, in the goal to make this small, fast, and work with minimal dependencies, something had to give. So I list things which this database will likely never have. Of course, since it is BSD Licensed, patches welcome...</p>
|
||||
|
||||
<ul>
|
||||
@@ -53,11 +53,11 @@ Following is the original documentation copied from metanotion website.
|
||||
<li>No use of reflection or automagical serialization tricks.</li>
|
||||
</ul>
|
||||
|
||||
<a name="future"><h2>Future Plans</h2></a>
|
||||
<h2 id="future">Future Plans</h2>
|
||||
<p>There are still bugs(none known...). The app that this was written for is still in testing, but we should most of the issues sorted by the time we deploy it in a few weeks(early November, 2006). Some loading speed issues on large record sets, and memory usage could still be improved. All this and feedback from other uses will direct this products evolution.</p>
|
||||
<p>What is currently up here is not "1.0" code, but we will release a labeled "1.0" version once we feel happy with the state of the codebase.</p>
|
||||
|
||||
<a name="design"><h2>What KIND of database is this?</h2></a>
|
||||
<h2 id="design">What KIND of database is this?</h2>
|
||||
<p>You probably store at least part of your application data in memory in a class from the <a href="http://java.sun.com/j2se/1.4.2/docs/guide/collections/">Java Collections Framework</a>. The BlockFile database stores data in a <a href="http://en.wikipedia.org/wiki/Skip_list">Skip</a> <a href="http://eternallyconfuzzled.com/tuts/skip.html">List</a> that almost implements <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/SortedMap.html">java.util.SortedMap</a>. You can create and store as many named(with a string) SkipList in the database as you want.</p>
|
||||
<p>To serialize your data, you have to either extend our SerialStreams class or implement our Serializer interface. We could have done something cool and fancy with reflection(and other cool stuff with Java 1.5), but that would probably not do the Right Thing™ most of the time. As you can see, there's not a lot to it anyway:</p>
|
||||
<h3>net.metanotion.io.SerialStreams</h3>
|
||||
@@ -91,9 +91,8 @@ public class SkipList {
|
||||
public ListIterator find(Comparable key) ...
|
||||
}
|
||||
</pre>
|
||||
</p>
|
||||
|
||||
<a name="examples"><h2>Examples</h2></a>
|
||||
<h2 id="examples">Examples</h2>
|
||||
<p>Better documentation is forthcoming, but there really isn't much to know. The entire public interface to the library is on this page. Where possible, it sticks to idiomatic Java and standard interfaces.</p>
|
||||
<ul>
|
||||
<li>Open a database:
|
||||
@@ -146,8 +145,8 @@ public class BlockFile implements Closeable {
|
||||
|
||||
<p>So, in other words, if you can provide an implementation of this interface, you can use the BlockFile database. This frees it from dependence on the RandomAccessFile class. If you don't see why this is useful and you're going to be using "files" on PDA's and phone's, well, you'll understand soon enough...</p>
|
||||
|
||||
<a name="download"><h2>Download</h2></a>
|
||||
<h3>Bugfix and cleanup Release 10/6/2006</h2>
|
||||
<h2 id="download">Download</h2>
|
||||
<h3>Bugfix and cleanup Release 10/6/2006</h3>
|
||||
<p>An unnecessary class was removed, some junk methods removed, and a couple of JDK compatability issues were fixed. The StringBytes class was switched to ASCII(from UTF-8) for better compatibility.</p>
|
||||
<ul>
|
||||
<li><a href="http://www.metanotion.net/software/sandbox/BlockFile.2006.10.06.jar">BlockFile binary JAR, version 0.1.1</a></li>
|
||||
@@ -160,7 +159,7 @@ public class BlockFile implements Closeable {
|
||||
<li><a href="http://www.metanotion.net/software/sandbox/BlockFile.src.2006.09.28.zip">BlockFile source code</a></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<center>© 2006 <a href="http://www.metanotion.net/">Metanotion Software</a></center>
|
||||
<hr>
|
||||
<p>© 2006 <a href="http://www.metanotion.net/">Metanotion Software</a></p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
#Last Modified: Sun Dec 06 12:30:32 2015
|
||||
# vim:syntax=apparmor et ts=8 sw=4
|
||||
|
||||
#include <tunables/global>
|
||||
|
||||
$INSTALL_PATH/{i2prouter,runplain.sh} flags=(complain) {
|
||||
#include <abstractions/base>
|
||||
#include <abstractions/fonts>
|
||||
#include <abstractions/nameservice>
|
||||
#include <abstractions/ssl_certs>
|
||||
|
||||
capability sys_ptrace,
|
||||
network inet stream,
|
||||
network inet6 stream,
|
||||
|
||||
$INSTALL_PATH/ r,
|
||||
$INSTALL_PATH/{i2psvc,wrapper} rmix,
|
||||
owner $INSTALL_PATH/** rwkm,
|
||||
|
||||
# Needed for Java
|
||||
owner @{PROC} r,
|
||||
owner @{PROC}/[0-9]*/ r,
|
||||
owner @{PROC}/[0-9]*/status r,
|
||||
owner @{PROC}/[0-9]*/stat r,
|
||||
owner @{PROC}/[0-9]*/cmdline r,
|
||||
@{PROC}/uptime r,
|
||||
@{PROC}/sys/kernel/pid_max r,
|
||||
/sys/devices/system/cpu/ r,
|
||||
/sys/devices/system/cpu/** r,
|
||||
|
||||
/dev/random r,
|
||||
/dev/urandom r,
|
||||
|
||||
@{PROC}/1/comm r,
|
||||
|
||||
/etc/ssl/certs/java/** r,
|
||||
/etc/timezone r,
|
||||
/usr/share/javazi/** r,
|
||||
|
||||
# Debian
|
||||
/etc/java-{6,7,8}-openjdk/** r,
|
||||
/usr/lib/jvm/default-java/jre/bin/java rix,
|
||||
|
||||
# Debian, Ubuntu, openSUSE
|
||||
/usr/lib{,32,64}/jvm/java-*-openjdk-*/jre/bin/java rix,
|
||||
/usr/lib{,32,64}/jvm/java-*-openjdk-*/jre/bin/keytool rix,
|
||||
|
||||
# Raspbian
|
||||
/usr/lib/jvm/jdk-*-oracle-*/jre/bin/java rix,
|
||||
/usr/lib/jvm/jdk-*-oracle-*/jre/bin/keytool rix,
|
||||
|
||||
|
||||
# Fonts are needed for I2P's graphs
|
||||
/usr/share/java/java-atk-wrapper.jar r,
|
||||
|
||||
# Used by some plugins
|
||||
/usr/share/java/eclipse-ecj-*.jar r,
|
||||
|
||||
/{,var/}tmp/ rwm,
|
||||
owner /{,var/}tmp/** rwkm,
|
||||
|
||||
/{,usr/}bin/{,b,d}ash rix,
|
||||
/{,usr/}bin/cat rix,
|
||||
/{,usr/}bin/cut rix,
|
||||
/{,usr/}bin/dirname rix,
|
||||
/{,usr/}bin/expr rix,
|
||||
/{,usr/}bin/{,g,m}awk rix,
|
||||
/{,usr/}bin/grep rix,
|
||||
/{,usr/}bin/id rix,
|
||||
/{,usr/}bin/ldd rix,
|
||||
/{,usr/}bin/ls rix,
|
||||
/{,usr/}bin/mkdir rix,
|
||||
/{,usr/}bin/nohup rix,
|
||||
/{,usr/}bin/ps rix,
|
||||
/{,usr/}bin/rm rix,
|
||||
/{,usr/}bin/sed rix,
|
||||
/{,usr/}bin/sleep rix,
|
||||
/{,usr/}bin/tail rix,
|
||||
/{,usr/}bin/tr rix,
|
||||
/{,usr/}bin/uname rix,
|
||||
/{,usr/}bin/which rix,
|
||||
|
||||
@{HOME}/.java/fonts/** r,
|
||||
owner @{HOME}/.i2p/ rw,
|
||||
owner @{HOME}/.i2p/** rwk,
|
||||
|
||||
# Prevent spamming the logs
|
||||
deny owner @{HOME}/.java/ wk,
|
||||
deny @{HOME}/.fontconfig/ wk,
|
||||
deny @{HOME}/.java/fonts/** w,
|
||||
deny /dev/tty rw,
|
||||
deny /dev/pts/[0-9]* rw,
|
||||
deny @{PROC}/[0-9]*/fd/ r,
|
||||
deny /usr/local/share/fonts/ r,
|
||||
deny /var/cache/fontconfig/ wk,
|
||||
# Used by some versions of the Tanuki wrapper but never used by I2P
|
||||
deny /usr/share/java/hamcrest*.jar r,
|
||||
deny /usr/share/java/junit*.jar r,
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/i2p_router"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/i2p_sdk"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/installer"/>
|
||||
<classpathentry kind="output" path="build"/>
|
||||
</classpath>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>desktopgui</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
14
apps/desktopgui/build.gradle
Normal file
14
apps/desktopgui/build.gradle
Normal file
@@ -0,0 +1,14 @@
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir 'src'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':core')
|
||||
compile project(':router')
|
||||
compile project(':installer')
|
||||
compile project(':apps:systray')
|
||||
}
|
||||
@@ -28,12 +28,14 @@
|
||||
<target name="compile" depends="init">
|
||||
<javac debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
|
||||
includeAntRuntime="false"
|
||||
encoding="UTF-8"
|
||||
srcdir="${src}" destdir="${build}">
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
<classpath>
|
||||
<pathelement location="../../core/java/build/i2p.jar" />
|
||||
<pathelement location="../../installer/lib/wrapper/all/wrapper.jar" />
|
||||
<pathelement location="../../router/java/build/router.jar" />
|
||||
<pathelement location="../systray/java/build/systray.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
@@ -51,6 +53,7 @@
|
||||
</exec>
|
||||
<javac source="${javac.version}" target="${javac.version}"
|
||||
includeAntRuntime="false"
|
||||
encoding="UTF-8"
|
||||
srcdir="${build}/messages-src" destdir="${build}">
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
</javac>
|
||||
@@ -120,15 +123,20 @@
|
||||
</target>
|
||||
|
||||
<target name="poupdate">
|
||||
<!-- set if unset -->
|
||||
<property name="lg2" value="" />
|
||||
<exec executable="sh" osfamily="unix" failifexecutionfails="true" failonerror="true" >
|
||||
<env key="LG2" value="${lg2}" />
|
||||
<arg value="./bundle-messages.sh" />
|
||||
<arg value="-p" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="mac" failifexecutionfails="true" failonerror="true" >
|
||||
<env key="LG2" value="${lg2}" />
|
||||
<arg value="./bundle-messages.sh" />
|
||||
<arg value="-p" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="windows" failifexecutionfails="true" failonerror="true" >
|
||||
<env key="LG2" value="${lg2}" />
|
||||
<arg value="./bundle-messages.sh" />
|
||||
<arg value="-p" />
|
||||
</exec>
|
||||
|
||||
@@ -105,12 +105,12 @@ do
|
||||
# only generate for non-source language
|
||||
echo "Generating ${CLASS}_$LG ResourceBundle..."
|
||||
|
||||
msgfmt -V | grep -q '0\.19'
|
||||
msgfmt -V | grep -q -E ' 0\.((19)|[2-9])'
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
# slow way
|
||||
# convert to class files in build
|
||||
msgfmt --java --statistics -r $CLASS -l $LG -d build $i
|
||||
msgfmt --java2 --statistics -r $CLASS -l $LG -d build $i
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "ERROR - msgfmt failed on ${i}, not updating translations"
|
||||
@@ -128,7 +128,7 @@ do
|
||||
TDY=$TD2/net/i2p/desktopgui
|
||||
rm -rf $TD
|
||||
mkdir -p $TD $TDY
|
||||
msgfmt --java --statistics --source -r $CLASS -l $LG -d $TD $i
|
||||
msgfmt --java2 --statistics --source -r $CLASS -l $LG -d $TD $i
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "ERROR - msgfmt failed on ${i}, not updating translations"
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="core" />
|
||||
<orderEntry type="module" module-name="router" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -4,6 +4,7 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Ali <aboshanab_@hotmail.com>, 2018
|
||||
# ducki2p <ducki2p@gmail.com>, 2011
|
||||
# foo <foo@bar>, 2009
|
||||
msgid ""
|
||||
@@ -11,8 +12,8 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"PO-Revision-Date: 2018-05-14 15:25+0000\n"
|
||||
"Last-Translator: Ali <aboshanab_@hotmail.com>\n"
|
||||
"Language-Team: Arabic (http://www.transifex.com/otf/I2P/language/ar/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -43,7 +44,7 @@ msgstr " تشغيل متصفح I2P"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr ""
|
||||
msgstr "تكوين أيقونة I2P المصغرة "
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
@@ -63,17 +64,17 @@ msgstr "توقيف I2P"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "إعادة تشغيل I2P على الفور"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "إيقاف تشغيل I2P على الفور"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr ""
|
||||
msgstr "إلغاء إيقاف تشغيل I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
@@ -92,4 +93,4 @@ msgstr "الشبكة"
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr ""
|
||||
msgstr "I2P: أنقر بالزر الأيمن للقائمة"
|
||||
|
||||
94
apps/desktopgui/locale/messages_az.po
Normal file
94
apps/desktopgui/locale/messages_az.po
Normal file
@@ -0,0 +1,94 @@
|
||||
# I2P
|
||||
# Copyright (C) 2009 The I2P Project
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Nikafn <appone6@gmail.com>, 2018
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2018-02-25 16:35+0000\n"
|
||||
"Last-Translator: Nikafn <appone6@gmail.com>\n"
|
||||
"Language-Team: Azerbaijani (http://www.transifex.com/otf/I2P/language/az/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: az\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
msgid "Start I2P"
|
||||
msgstr "I2P Başla"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P başlayır!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "Starting"
|
||||
msgstr "Başlanğıc"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "I2P Brauzerini başla"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "I2P Sistem Zolağını quraşdır"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Söndür"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "I2P yenidən başlat"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "I2P-ni durdur"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr " I2P-ni dərhal yenidən başlat"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "I2P-ni dərhal durdur"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "I2P-ni söndürməyi ləğv et"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "{0}-da sönmə"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Sönmə yaxınlaşır"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Şəbəkə"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Menyu üçün sağ düyməni klikləyin"
|
||||
@@ -6,19 +6,20 @@
|
||||
# Translators:
|
||||
# Amir H. Firouzian, 2017
|
||||
# NoProfile, 2016
|
||||
# Reza Ghasemi, 2019
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Amir H. Firouzian\n"
|
||||
"PO-Revision-Date: 2019-07-02 14:46+0000\n"
|
||||
"Last-Translator: Reza Ghasemi\n"
|
||||
"Language-Team: Persian (http://www.transifex.com/otf/I2P/language/fa/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fa\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
@@ -48,12 +49,12 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "غیرفعال کن"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr ""
|
||||
msgstr "راه اندازی مجدد I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
@@ -63,17 +64,17 @@ msgstr "توقف I2P"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "راه اندازی مجدد I2P بلافاصله"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "توقف I2P بلافاصله"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr ""
|
||||
msgstr "لغو خاموش کردن I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
@@ -87,7 +88,7 @@ msgstr ""
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "شبکه"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# AO <ao@localizationlab.org>, 2019
|
||||
# blabla <blabla@trash-mail.com>, 2011
|
||||
# Boxoa590, 2013
|
||||
# ducki2p <ducki2p@gmail.com>, 2011
|
||||
# foo <foo@bar>, 2009
|
||||
# French language coordinator <french.coordinator@rbox.me>, 2017
|
||||
# French language coordinator <french.coordinator@rbox.me>, 2017
|
||||
# AO <ao@localizationlab.org>, 2017
|
||||
# AO <ao@localizationlab.org>, 2017
|
||||
# Boxoa590, 2013
|
||||
# Towinet, 2016
|
||||
msgid ""
|
||||
@@ -17,8 +18,8 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-10-25 17:51+0000\n"
|
||||
"Last-Translator: French language coordinator <french.coordinator@rbox.me>\n"
|
||||
"PO-Revision-Date: 2019-12-03 16:10+0000\n"
|
||||
"Last-Translator: AO <ao@localizationlab.org>\n"
|
||||
"Language-Team: French (http://www.transifex.com/otf/I2P/language/fr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -34,7 +35,7 @@ msgstr "Démarrer I2P"
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P démarre !"
|
||||
msgstr "I2P démarre !"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
@@ -98,4 +99,4 @@ msgstr "Réseau"
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P : clic droit pour obtenir le menu"
|
||||
msgstr "I2P : clic droit pour obtenir le menu"
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
#
|
||||
# Translators:
|
||||
# タカハシ <indexial@outlook.jp>, 2013
|
||||
# Masayuki Hatta <mhatta@mhatta.org>, 2018
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"PO-Revision-Date: 2018-08-17 22:08+0000\n"
|
||||
"Last-Translator: Masayuki Hatta <mhatta@mhatta.org>\n"
|
||||
"Language-Team: Japanese (http://www.transifex.com/otf/I2P/language/ja/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -27,7 +28,7 @@ msgstr "I2P を開始"
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P 起動中!"
|
||||
msgstr "I2P を起動中!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
@@ -42,7 +43,7 @@ msgstr "I2P ブラウザを起動"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr ""
|
||||
msgstr "I2P システムトレイを設定"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
@@ -62,17 +63,17 @@ msgstr "I2P を停止"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "すぐに I2P を再起動"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr ""
|
||||
msgstr "すぐに I2P を停止"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr ""
|
||||
msgstr "I2P のシャットダウンを中止"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
@@ -91,4 +92,4 @@ msgstr "ネットワーク"
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr ""
|
||||
msgstr "I2P: 右クリックでメニュー"
|
||||
|
||||
@@ -10,8 +10,8 @@ msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"PO-Revision-Date: 2019-05-01 07:33+0000\n"
|
||||
"Last-Translator: Giovanni Pellerano <giovanni.pellerano@evilaliv3.org>\n"
|
||||
"Language-Team: Malagasy (http://www.transifex.com/otf/I2P/language/mg/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -47,7 +47,7 @@ msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
msgstr "Désactiver"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
# ducki2p <ducki2p@gmail.com>, 2011
|
||||
# foo <foo@bar>, 2009
|
||||
# Martijn de Boer, 2016
|
||||
# Nathan Follens, 2018
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Martijn de Boer\n"
|
||||
"PO-Revision-Date: 2018-01-26 09:36+0000\n"
|
||||
"Last-Translator: Nathan Follens\n"
|
||||
"Language-Team: Dutch (http://www.transifex.com/otf/I2P/language/nl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -39,12 +40,12 @@ msgstr "Bezig met starten"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Start I2P Browser"
|
||||
msgstr "I2P-browser starten"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Configureer I2P systeembalk"
|
||||
msgstr "I2P-systeemvak configureren"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
@@ -64,26 +65,26 @@ msgstr "I2P stoppen"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Herstart I2P direct"
|
||||
msgstr "I2P onmiddellijk herstarten"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Stop I2P direct"
|
||||
msgstr "I2P onmiddellijk stoppen"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Annuleer afsluiten I2P"
|
||||
msgstr "Afsluiten I2P annuleren"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Afsluiten in {0}"
|
||||
msgstr "Afsluiten over {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Afsluiten op handen"
|
||||
msgstr "Wordt zo meteen afgesloten"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
@@ -93,4 +94,4 @@ msgstr "Netwerk"
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Rechts klikken voor menu"
|
||||
msgstr "I2P: rechterklik voor menu"
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Besnik <besnik@programeshqip.org>, 2016
|
||||
# Besnik <besnik@programeshqip.org>, 2016,2019
|
||||
# Shpetim <shpetim@privacysolutions.no>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"PO-Revision-Date: 2019-01-10 14:28+0000\n"
|
||||
"Last-Translator: Besnik <besnik@programeshqip.org>\n"
|
||||
"Language-Team: Albanian (http://www.transifex.com/otf/I2P/language/sq/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -92,4 +92,4 @@ msgstr "Rrjet"
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Djathtas-klikoni për menu"
|
||||
msgstr "I2P: Djathtasklikoni për menu"
|
||||
|
||||
94
apps/desktopgui/locale/messages_sr.po
Normal file
94
apps/desktopgui/locale/messages_sr.po
Normal file
@@ -0,0 +1,94 @@
|
||||
# I2P
|
||||
# Copyright (C) 2009 The I2P Project
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Slobodan Simić <slsimic@gmail.com>, 2017
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2019-07-11 00:36+0000\n"
|
||||
"Last-Translator: Aleksa Ristić\n"
|
||||
"Language-Team: Serbian (http://www.transifex.com/otf/I2P/language/sr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: sr\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
msgid "Start I2P"
|
||||
msgstr "Покрени И2П"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "I2P is starting!"
|
||||
msgstr "И2П се покреће!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "Starting"
|
||||
msgstr "Покрећем"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Покрени И2П прегледач"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Подеси И2П системску касету"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Искључи"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "Поново покрени И2П"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "Заустави И2П"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Рестартуј И2П одмах"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Заустави И2П одмах"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Откажи И2П гашење"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Гашење за {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Гашење неизбежно"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Мрежа"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "И2П: десни клик за мени"
|
||||
@@ -6,14 +6,16 @@
|
||||
# Translators:
|
||||
# 123hund123 <M8R-ra4r1r@mailinator.com>, 2011
|
||||
# Anders Nilsson <anders@devode.se>, 2016
|
||||
# Jony, 2016-2017
|
||||
# Jonatan Nyberg, 2016-2017
|
||||
# Jonatan Nyberg, 2018
|
||||
# Jonatan Nyberg, 2017
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Jony\n"
|
||||
"PO-Revision-Date: 2018-06-17 14:17+0000\n"
|
||||
"Last-Translator: Jonatan Nyberg\n"
|
||||
"Language-Team: Swedish (Sweden) (http://www.transifex.com/otf/I2P/language/sv_SE/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -74,7 +76,7 @@ msgstr "Stoppa I2P omedelbart"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Avbryt I2P-avstängning"
|
||||
msgstr "Avbryt I2P-stängning"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
@@ -83,7 +85,7 @@ msgstr "Stänger av om {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Avstängning nära"
|
||||
msgstr "Stängning nära"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
# Translators:
|
||||
# ducki2p <ducki2p@gmail.com>, 2011
|
||||
# walking <walking@i2pmail.org>, 2011
|
||||
# YF <yfdyh000@gmail.com>, 2016
|
||||
# YFdyh000 <yfdyh000@gmail.com>, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: YF <yfdyh000@gmail.com>\n"
|
||||
"PO-Revision-Date: 2019-06-20 03:18+0000\n"
|
||||
"Last-Translator: Beta Ba <linsui555@gmail.com>\n"
|
||||
"Language-Team: Chinese (China) (http://www.transifex.com/otf/I2P/language/zh_CN/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -39,7 +39,7 @@ msgstr "正在启动"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "正在启动I2P浏览器"
|
||||
msgstr "启动 I2P 浏览器"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
|
||||
94
apps/desktopgui/locale/messages_zh_TW.po
Normal file
94
apps/desktopgui/locale/messages_zh_TW.po
Normal file
@@ -0,0 +1,94 @@
|
||||
# I2P
|
||||
# Copyright (C) 2009 The I2P Project
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# 黃彥儒 <r1235613@gmail.com>, 2017
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2018-10-04 00:43+0000\n"
|
||||
"Last-Translator: erinm\n"
|
||||
"Language-Team: Chinese (Taiwan) (http://www.transifex.com/otf/I2P/language/zh_TW/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: zh_TW\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
msgid "Start I2P"
|
||||
msgstr "啟動I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P已啟動"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "Starting"
|
||||
msgstr "啟動中"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "開啟I2P瀏覽器"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "設定I2P系統文件夾"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "停用"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "重啟I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "停止I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "強制重啟I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "強制終止I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "取消停止I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "關閉於 {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "強制關閉"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "網路"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P:右鍵開啟選單"
|
||||
@@ -6,16 +6,15 @@ import java.awt.MenuItem;
|
||||
import java.awt.PopupMenu;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.SwingWorker;
|
||||
|
||||
import net.i2p.apps.systray.UrlLauncher;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.desktopgui.router.RouterManager;
|
||||
import net.i2p.desktopgui.util.BrowseException;
|
||||
import net.i2p.desktopgui.util.I2PDesktop;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.PortMapper;
|
||||
@@ -433,29 +432,16 @@ class InternalTrayManager extends TrayManager {
|
||||
* and launch the browser at it.
|
||||
*
|
||||
* Modified from I2PTunnelHTTPClientBase.
|
||||
* TODO perhaps move this to a new PortMapper method.
|
||||
*
|
||||
* @since 0.9.26
|
||||
*/
|
||||
private void launchBrowser() {
|
||||
String unset = "*unset*";
|
||||
PortMapper pm = _context.portMapper();
|
||||
String httpHost = pm.getActualHost(PortMapper.SVC_CONSOLE, unset);
|
||||
String httpsHost = pm.getActualHost(PortMapper.SVC_HTTPS_CONSOLE, unset);
|
||||
int httpPort = pm.getPort(PortMapper.SVC_CONSOLE, 7657);
|
||||
int httpsPort = pm.getPort(PortMapper.SVC_HTTPS_CONSOLE, -1);
|
||||
boolean httpsOnly = httpsPort > 0 && httpHost.equals(unset) && !httpsHost.equals(unset);
|
||||
String url;
|
||||
if (httpsOnly) {
|
||||
url = "https://" + httpsHost + ':' + httpsPort + '/';
|
||||
} else {
|
||||
if (httpHost.equals(unset))
|
||||
httpHost = "127.0.0.1";
|
||||
url = "http://" + httpHost + ':' + httpPort + '/';
|
||||
}
|
||||
// null args ok
|
||||
UrlLauncher launcher = new UrlLauncher(_context, null, null);
|
||||
String url = _context.portMapper().getConsoleURL();
|
||||
try {
|
||||
I2PDesktop.browse(url);
|
||||
} catch (BrowseException e1) {
|
||||
launcher.openUrl(url);
|
||||
} catch (IOException e1) {
|
||||
log.log(Log.WARN, "Failed to open browser!", e1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,12 @@ package net.i2p.desktopgui;
|
||||
* Main.java
|
||||
*/
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@@ -11,7 +17,6 @@ import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.app.ClientAppState;
|
||||
import static net.i2p.app.ClientAppState.*;
|
||||
import net.i2p.desktopgui.router.RouterManager;
|
||||
import net.i2p.desktopgui.util.*;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.app.RouterApp;
|
||||
import net.i2p.util.Log;
|
||||
@@ -24,7 +29,9 @@ import net.i2p.util.I2PProperties.I2PPropertyCallback;
|
||||
*/
|
||||
public class Main implements RouterApp {
|
||||
|
||||
// non-null
|
||||
private final I2PAppContext _appContext;
|
||||
// warning, null in app context
|
||||
private final RouterContext _context;
|
||||
private final ClientAppManager _mgr;
|
||||
private final Log log;
|
||||
@@ -106,6 +113,8 @@ public class Main implements RouterApp {
|
||||
changeState(START_FAILED, "Headless environment: not starting desktopgui!", null);
|
||||
return;
|
||||
}
|
||||
if (SystemVersion.isMac())
|
||||
setMacTrayIcon();
|
||||
|
||||
// TODO process args with getopt if needed
|
||||
|
||||
@@ -129,6 +138,45 @@ public class Main implements RouterApp {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unless we do this, when we start DesktopGUI we get a Java coffee cup
|
||||
* in the tray.
|
||||
*
|
||||
* Based on code from https://gist.github.com/bchapuis/1562406 , no apparent license.
|
||||
* See also https://stackoverflow.com/questions/6006173/how-do-you-change-the-dock-icon-of-a-java-program
|
||||
*
|
||||
* TODO, if we wanted to add our own menu, see
|
||||
* https://stackoverflow.com/questions/1319805/java-os-x-dock-menu
|
||||
*
|
||||
* TODO, if we want to make it bounce, see
|
||||
* https://stackoverflow.com/questions/15079783/how-to-make-my-app-icon-bounce-in-the-mac-dock
|
||||
*
|
||||
* TODO, if we want to handle Quit, see
|
||||
* https://nakkaya.com/2009/04/19/java-osx-integration/
|
||||
*
|
||||
* @since 0.9.33
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void setMacTrayIcon() {
|
||||
File f = new File(_appContext.getBaseDir(), "docs/themes/console/images/itoopie_sm.png");
|
||||
if (!f.exists())
|
||||
return;
|
||||
try {
|
||||
Class util = Class.forName("com.apple.eawt.Application");
|
||||
Method getApplication = util.getMethod("getApplication", new Class[0]);
|
||||
Object application = getApplication.invoke(util);
|
||||
Class params[] = new Class[1];
|
||||
params[0] = Image.class;
|
||||
Method setDockIconImage = util.getMethod("setDockIconImage", params);
|
||||
URL url = f.toURI().toURL();
|
||||
Image image = Toolkit.getDefaultToolkit().getImage(url);
|
||||
setDockIconImage.invoke(application, image);
|
||||
} catch (Exception e) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("Can't set OSX Dock icon", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoids the app terminating because no Window is opened anymore.
|
||||
* More info: http://java.sun.com/javase/6/docs/api/java/awt/doc-files/AWTThreadIssues.html#Autoshutdown
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package net.i2p.desktopgui.util;
|
||||
|
||||
public class BrowseException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public BrowseException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BrowseException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public BrowseException(String s, Throwable t) {
|
||||
super(s, t);
|
||||
}
|
||||
|
||||
public BrowseException(Throwable t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package net.i2p.desktopgui.util;
|
||||
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Desktop.Action;
|
||||
import java.net.URI;
|
||||
|
||||
public class I2PDesktop {
|
||||
|
||||
public static void browse(String url) throws BrowseException {
|
||||
if(Desktop.isDesktopSupported()) {
|
||||
Desktop desktop = Desktop.getDesktop();
|
||||
if(desktop.isSupported(Action.BROWSE)) {
|
||||
try {
|
||||
desktop.browse(new URI(url));
|
||||
} catch (Exception e) {
|
||||
throw new BrowseException();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new BrowseException();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new BrowseException();
|
||||
}
|
||||
}
|
||||
}
|
||||
144
apps/i2pcontrol/build.xml
Normal file
144
apps/i2pcontrol/build.xml
Normal file
@@ -0,0 +1,144 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="all" name="source">
|
||||
<property name="i2pbase" value="../.."/>
|
||||
<property name="i2plib" value="${i2pbase}/build"/>
|
||||
<property name="jettylib" value="${i2pbase}/apps/jetty/jettylib"/>
|
||||
<property name="wrapperlib" value="${i2pbase}/installer/lib/wrapper/all"/>
|
||||
|
||||
<!--
|
||||
Supports four build options:
|
||||
1) war (jsonrpc.war) for running under the console
|
||||
2) jar (i2pcontrol.jar) for starting and running on its own Jetty instance,
|
||||
no console dependency
|
||||
3) socketJar (i2pcontrol.jar) for running on a ServerSocket (JSON splitting),
|
||||
no Jetty dependency (Work in progress)
|
||||
4) TODO xxxjar with bundled, small non-Jetty server, similar to I2PTunnelHTTPClient?
|
||||
-->
|
||||
|
||||
<path id="cp">
|
||||
<pathelement location="${i2plib}/i2p.jar" />
|
||||
<pathelement location="${i2plib}/router.jar" />
|
||||
<pathelement location="${jettylib}/org.mortbay.jetty.jar" />
|
||||
<pathelement location="${jettylib}/javax.servlet.jar" />
|
||||
<pathelement location="${jettylib}/jetty-servlet.jar" />
|
||||
<pathelement location="${wrapperlib}/wrapper.jar" />
|
||||
<!-- following jar only present for debian builds -->
|
||||
<pathelement location="../../core/java/build/json-simple.jar" />
|
||||
</path>
|
||||
|
||||
<path id="cpSocket">
|
||||
<pathelement location="${i2plib}/i2p.jar" />
|
||||
<pathelement location="${i2plib}/router.jar" />
|
||||
<!-- TODO get rid of wraper dependency also -->
|
||||
<pathelement location="${wrapperlib}/wrapper.jar" />
|
||||
<!-- following jar only present for debian builds -->
|
||||
<pathelement location="../../core/java/build/json-simple.jar" />
|
||||
</path>
|
||||
|
||||
<target name="all" depends="clean, build" />
|
||||
<target name="build" depends="jar" />
|
||||
<condition property="depend.available">
|
||||
<typefound name="depend" />
|
||||
</condition>
|
||||
<target name="builddep" if="depend.available">
|
||||
<depend
|
||||
cache="${i2pbase}/build"
|
||||
srcdir="./java"
|
||||
classpath="${cp}"
|
||||
destdir="./build/obj" >
|
||||
</depend>
|
||||
</target>
|
||||
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="javac.version" value="1.7" />
|
||||
|
||||
<target name="compile" depends="builddep" >
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/obj" />
|
||||
<javac
|
||||
srcdir="./java"
|
||||
debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
|
||||
includeAntRuntime="false"
|
||||
encoding="UTF-8"
|
||||
destdir="./build/obj"
|
||||
classpath="${cp}">
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
<classpath refid="cp"/>
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<!--
|
||||
More TODO here. jsonrpc2 lib uses MessageContext which depends on servlet
|
||||
-->
|
||||
<target name="compileSocketJar" depends="builddep" >
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/obj" />
|
||||
<javac
|
||||
sourcepath=""
|
||||
srcdir="./java"
|
||||
debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
|
||||
includeAntRuntime="false"
|
||||
encoding="UTF-8"
|
||||
destdir="./build/obj"
|
||||
classpath="${cpSocket}">
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
<classpath refid="cpSocket" />
|
||||
<exclude name="**/I2PControlController.java" />
|
||||
<exclude name="**/HostCheckHandler.java" />
|
||||
<exclude name="**/JSONRPC2Servlet.java" />
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="compile">
|
||||
<jar destfile="build/i2pcontrol.jar" basedir="./build/obj" includes="**/*.class" >
|
||||
<manifest>
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
<attribute name="Build-Date" value="${build.timestamp}" />
|
||||
<attribute name="Base-Revision" value="${workspace.version}" />
|
||||
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" />
|
||||
<attribute name="X-Compile-Source-JDK" value="${javac.version}" />
|
||||
<attribute name="X-Compile-Target-JDK" value="${javac.version}" />
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="socketJar" depends="compileSocketJar">
|
||||
<jar destfile="build/i2pcontrol.jar" basedir="./build/obj" includes="**/*.class" >
|
||||
<manifest>
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
<attribute name="Build-Date" value="${build.timestamp}" />
|
||||
<attribute name="Base-Revision" value="${workspace.version}" />
|
||||
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" />
|
||||
<attribute name="X-Compile-Source-JDK" value="${javac.version}" />
|
||||
<attribute name="X-Compile-Target-JDK" value="${javac.version}" />
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="war" depends="compile" >
|
||||
<war destfile="build/jsonrpc.war" webxml="web.xml" >
|
||||
<classes dir="./build/obj" excludes="net/i2p/i2pcontrol/I2PControlController.class net/i2p/i2pcontrol/HostCheckHandler.class net/i2p/i2pcontrol/SocketController*.class" />
|
||||
<manifest>
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
<attribute name="Build-Date" value="${build.timestamp}" />
|
||||
<attribute name="Base-Revision" value="${workspace.version}" />
|
||||
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" />
|
||||
<attribute name="X-Compile-Source-JDK" value="${javac.version}" />
|
||||
<attribute name="X-Compile-Target-JDK" value="${javac.version}" />
|
||||
</manifest>
|
||||
</war>
|
||||
</target>
|
||||
|
||||
<target name="clean">
|
||||
<delete dir="./build" />
|
||||
</target>
|
||||
|
||||
<target name="cleandep" depends="clean">
|
||||
</target>
|
||||
|
||||
<target name="distclean" depends="clean">
|
||||
</target>
|
||||
</project>
|
||||
@@ -0,0 +1,274 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a JSON-RPC 2.0 error that occurred during the processing of a
|
||||
* request. This class is immutable.
|
||||
*
|
||||
* <p>The protocol expects error objects to be structured like this:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code code} An integer that indicates the error type.
|
||||
* <li>{@code message} A string providing a short description of the
|
||||
* error. The message should be limited to a concise single sentence.
|
||||
* <li>{@code data} Additional information, which may be omitted. Its
|
||||
* contents is entirely defined by the application.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note that the "Error" word in the class name was put there solely to
|
||||
* comply with the parlance of the JSON-RPC spec. This class doesn't inherit
|
||||
* from {@code java.lang.Error}. It's a regular subclass of
|
||||
* {@code java.lang.Exception} and, if thrown, it's to indicate a condition
|
||||
* that a reasonable application might want to catch.
|
||||
*
|
||||
* <p>This class also includes convenient final static instances for all
|
||||
* standard JSON-RPC 2.0 errors:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #PARSE_ERROR} JSON parse error (-32700)
|
||||
* <li>{@link #INVALID_REQUEST} Invalid JSON-RPC 2.0 Request (-32600)
|
||||
* <li>{@link #METHOD_NOT_FOUND} Method not found (-32601)
|
||||
* <li>{@link #INVALID_PARAMS} Invalid parameters (-32602)
|
||||
* <li>{@link #INTERNAL_ERROR} Internal error (-32603)
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note that the range -32099..-32000 is reserved for additional server
|
||||
* errors.
|
||||
*
|
||||
* <p id="map">The mapping between JSON and Java entities (as defined by the
|
||||
* underlying JSON Smart library):
|
||||
* <pre>
|
||||
* true|false <---> java.lang.Boolean
|
||||
* number <---> java.lang.Number
|
||||
* string <---> java.lang.String
|
||||
* array <---> java.util.List
|
||||
* object <---> java.util.Map
|
||||
* null <---> null
|
||||
* </pre>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class JSONRPC2Error extends Exception {
|
||||
|
||||
|
||||
/**
|
||||
* Serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 4682571044532698806L;
|
||||
|
||||
|
||||
/**
|
||||
* JSON parse error (-32700).
|
||||
*/
|
||||
public static final JSONRPC2Error PARSE_ERROR = new JSONRPC2Error(-32700, "JSON parse error");
|
||||
|
||||
|
||||
/**
|
||||
* Invalid JSON-RPC 2.0 request error (-32600).
|
||||
*/
|
||||
public static final JSONRPC2Error INVALID_REQUEST = new JSONRPC2Error(-32600, "Invalid request");
|
||||
|
||||
|
||||
/**
|
||||
* Method not found error (-32601).
|
||||
*/
|
||||
public static final JSONRPC2Error METHOD_NOT_FOUND = new JSONRPC2Error(-32601, "Method not found");
|
||||
|
||||
|
||||
/**
|
||||
* Invalid parameters error (-32602).
|
||||
*/
|
||||
public static final JSONRPC2Error INVALID_PARAMS = new JSONRPC2Error(-32602, "Invalid parameters");
|
||||
|
||||
|
||||
/**
|
||||
* Internal JSON-RPC 2.0 error (-32603).
|
||||
*/
|
||||
public static final JSONRPC2Error INTERNAL_ERROR = new JSONRPC2Error(-32603, "Internal error");
|
||||
|
||||
|
||||
/**
|
||||
* The error code.
|
||||
*/
|
||||
private final int code;
|
||||
|
||||
|
||||
/**
|
||||
* The optional error data.
|
||||
*/
|
||||
private final Object data;
|
||||
|
||||
|
||||
/**
|
||||
* Appends the specified string to the message of a JSON-RPC 2.0 error.
|
||||
*
|
||||
* @param err The JSON-RPC 2.0 error. Must not be {@code null}.
|
||||
* @param apx The string to append to the original error message.
|
||||
*
|
||||
* @return A new JSON-RPC 2.0 error with the appended message.
|
||||
*/
|
||||
@Deprecated
|
||||
public static JSONRPC2Error appendMessage(final JSONRPC2Error err, final String apx) {
|
||||
|
||||
return new JSONRPC2Error(err.getCode(), err.getMessage() + apx, err.getData());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the specified data to a JSON-RPC 2.0 error.
|
||||
*
|
||||
* @param err The JSON-RPC 2.0 error to have its data field set. Must
|
||||
* not be {@code null}.
|
||||
* @param data Optional error data, must <a href="#map">map</a> to a
|
||||
* valid JSON type.
|
||||
*
|
||||
* @return A new JSON-RPC 2.0 error with the set data.
|
||||
*/
|
||||
@Deprecated
|
||||
public static JSONRPC2Error setData(final JSONRPC2Error err, final Object data) {
|
||||
|
||||
return new JSONRPC2Error(err.getCode(), err.getMessage(), data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 error with the specified code and
|
||||
* message. The optional data is omitted.
|
||||
*
|
||||
* @param code The error code (standard pre-defined or
|
||||
* application-specific).
|
||||
* @param message The error message.
|
||||
*/
|
||||
public JSONRPC2Error(int code, String message) {
|
||||
|
||||
this(code, message, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 error with the specified code,
|
||||
* message and data.
|
||||
*
|
||||
* @param code The error code (standard pre-defined or
|
||||
* application-specific).
|
||||
* @param message The error message.
|
||||
* @param data Optional error data, must <a href="#map">map</a>
|
||||
* to a valid JSON type.
|
||||
*/
|
||||
public JSONRPC2Error(int code, String message, Object data) {
|
||||
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the JSON-RPC 2.0 error code.
|
||||
*
|
||||
* @return The error code.
|
||||
*/
|
||||
public int getCode() {
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the JSON-RPC 2.0 error data.
|
||||
*
|
||||
* @return The error data, {@code null} if none was specified.
|
||||
*/
|
||||
public Object getData() {
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the specified data to a JSON-RPC 2.0 error.
|
||||
*
|
||||
* @param data Optional error data, must <a href="#map">map</a> to a
|
||||
* valid JSON type.
|
||||
*
|
||||
* @return A new JSON-RPC 2.0 error with the set data.
|
||||
*/
|
||||
public JSONRPC2Error setData(final Object data) {
|
||||
|
||||
return new JSONRPC2Error(code, getMessage(), data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Appends the specified string to the message of this JSON-RPC 2.0
|
||||
* error.
|
||||
*
|
||||
* @param apx The string to append to the original error message.
|
||||
*
|
||||
* @return A new JSON-RPC 2.0 error with the appended message.
|
||||
*/
|
||||
public JSONRPC2Error appendMessage(final String apx) {
|
||||
|
||||
return new JSONRPC2Error(code, getMessage() + apx, data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see #toJSONObject
|
||||
*/
|
||||
@Deprecated
|
||||
public JSONObject toJSON() {
|
||||
|
||||
return toJSONObject();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a JSON object representation of this JSON-RPC 2.0 error.
|
||||
*
|
||||
* @return A JSON object representing this error object.
|
||||
*/
|
||||
public JSONObject toJSONObject() {
|
||||
|
||||
JSONObject out = new JSONObject();
|
||||
|
||||
out.put("code", code);
|
||||
out.put("message", super.getMessage());
|
||||
if (data != null)
|
||||
out.put("data", data);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serialises the error object to a JSON string.
|
||||
*
|
||||
* @return A JSON-encoded string representing this error object.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return toJSON().toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Overrides {@code Object.equals()}.
|
||||
*
|
||||
* @param object The object to compare to.
|
||||
*
|
||||
* @return {@code true} if both objects are instances if this class and
|
||||
* their error codes are identical, {@code false} if not.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
|
||||
return object != null &&
|
||||
object instanceof JSONRPC2Error &&
|
||||
code == ((JSONRPC2Error)object).getCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.JSONAware;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
|
||||
/**
|
||||
* The base abstract class for JSON-RPC 2.0 requests, notifications and
|
||||
* responses. Provides common methods for parsing (from JSON string) and
|
||||
* serialisation (to JSON string) of these three message types.
|
||||
*
|
||||
* <p>Example parsing and serialisation back to JSON:
|
||||
*
|
||||
* <pre>
|
||||
* String jsonString = "{\"method\":\"progressNotify\",\"params\":[\"75%\"],\"jsonrpc\":\"2.0\"}";
|
||||
*
|
||||
* JSONRPC2Message message = null;
|
||||
*
|
||||
* // parse
|
||||
* try {
|
||||
* message = JSONRPC2Message.parse(jsonString);
|
||||
* } catch (JSONRPC2ParseException e) {
|
||||
* // handle parse exception
|
||||
* }
|
||||
*
|
||||
* if (message instanceof JSONRPC2Request)
|
||||
* System.out.println("The message is a request");
|
||||
* else if (message instanceof JSONRPC2Notification)
|
||||
* System.out.println("The message is a notification");
|
||||
* else if (message instanceof JSONRPC2Response)
|
||||
* System.out.println("The message is a response");
|
||||
*
|
||||
* // serialise back to JSON string
|
||||
* System.out.println(message);
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* <p id="map">The mapping between JSON and Java entities (as defined by the
|
||||
* underlying JSON Smart library):
|
||||
*
|
||||
* <pre>
|
||||
* true|false <---> java.lang.Boolean
|
||||
* number <---> java.lang.Number
|
||||
* string <---> java.lang.String
|
||||
* array <---> java.util.List
|
||||
* object <---> java.util.Map
|
||||
* null <---> null
|
||||
* </pre>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public abstract class JSONRPC2Message implements JSONAware {
|
||||
|
||||
|
||||
/**
|
||||
* Map of non-standard JSON-RPC 2.0 message attributes, {@code null} if
|
||||
* none.
|
||||
*/
|
||||
private Map <String,Object> nonStdAttributes = null;
|
||||
|
||||
|
||||
/**
|
||||
* Provides common parsing of JSON-RPC 2.0 requests, notifications
|
||||
* and responses. Use this method if you don't know which type of
|
||||
* JSON-RPC message the input JSON string represents.
|
||||
*
|
||||
* <p>Batched requests / notifications are not supported.
|
||||
*
|
||||
* <p>This method is thread-safe.
|
||||
*
|
||||
* <p>If you are certain about the message type use the dedicated
|
||||
* {@link JSONRPC2Request#parse}, {@link JSONRPC2Notification#parse}
|
||||
* or {@link JSONRPC2Response#parse} methods. They are more efficient
|
||||
* and provide a more detailed parse error reporting.
|
||||
*
|
||||
* <p>The member order of parsed JSON objects will not be preserved
|
||||
* (for efficiency reasons) and the JSON-RPC 2.0 version field must be
|
||||
* set to "2.0". To change this behaviour check the optional {@link
|
||||
* #parse(String,boolean,boolean)} method.
|
||||
*
|
||||
* @param jsonString A JSON string representing a JSON-RPC 2.0 request,
|
||||
* notification or response, UTF-8 encoded. Must not
|
||||
* be {@code null}.
|
||||
*
|
||||
* @return An instance of {@link JSONRPC2Request},
|
||||
* {@link JSONRPC2Notification} or {@link JSONRPC2Response}.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Message parse(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides common parsing of JSON-RPC 2.0 requests, notifications
|
||||
* and responses. Use this method if you don't know which type of
|
||||
* JSON-RPC message the input string represents.
|
||||
*
|
||||
* <p>Batched requests / notifications are not supported.
|
||||
*
|
||||
* <p>This method is thread-safe.
|
||||
*
|
||||
* <p>If you are certain about the message type use the dedicated
|
||||
* {@link JSONRPC2Request#parse}, {@link JSONRPC2Notification#parse}
|
||||
* or {@link JSONRPC2Response#parse} methods. They are more efficient
|
||||
* and provide a more detailed parse error reporting.
|
||||
*
|
||||
* @param jsonString A JSON string representing a JSON-RPC 2.0
|
||||
* request, notification or response, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
* @param preserveOrder If {@code true} the member order of JSON objects
|
||||
* in parameters and results must be preserved.
|
||||
* @param ignoreVersion If {@code true} the {@code "jsonrpc":"2.0"}
|
||||
* version field in the JSON-RPC 2.0 message will
|
||||
* not be checked.
|
||||
*
|
||||
* @return An instance of {@link JSONRPC2Request},
|
||||
* {@link JSONRPC2Notification} or {@link JSONRPC2Response}.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Message parse(final String jsonString, final boolean preserveOrder, final boolean ignoreVersion)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder, ignoreVersion);
|
||||
|
||||
return parser.parseJSONRPC2Message(jsonString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Appends a non-standard attribute to this JSON-RPC 2.0 message. This is
|
||||
* done by adding a new member (key / value pair) to the top level JSON
|
||||
* object representing the message.
|
||||
*
|
||||
* <p>You may use this method to add meta and debugging attributes,
|
||||
* such as the request processing time, to a JSON-RPC 2.0 message.
|
||||
*
|
||||
* @param name The attribute name. Must not conflict with the existing
|
||||
* "method", "id", "params", "result", "error" and "jsonrpc"
|
||||
* attributes reserved by the JSON-RPC 2.0 protocol, else
|
||||
* an {@code IllegalArgumentException} will be thrown. Must
|
||||
* not be {@code null} either.
|
||||
* @param value The attribute value. Must be of type String, boolean,
|
||||
* number, List, Map or null, else an
|
||||
* {@code IllegalArgumentException} will be thrown.
|
||||
*/
|
||||
public void appendNonStdAttribute(final String name, final Object value) {
|
||||
|
||||
// Name check
|
||||
if (name == null ||
|
||||
name.equals("method") ||
|
||||
name.equals("id") ||
|
||||
name.equals("params") ||
|
||||
name.equals("result") ||
|
||||
name.equals("error") ||
|
||||
name.equals("jsonrpc") )
|
||||
|
||||
throw new IllegalArgumentException("Non-standard attribute name violation");
|
||||
|
||||
// Value check
|
||||
if ( value != null &&
|
||||
! (value instanceof Boolean) &&
|
||||
! (value instanceof Number) &&
|
||||
! (value instanceof String) &&
|
||||
! (value instanceof List) &&
|
||||
! (value instanceof Map) )
|
||||
|
||||
throw new IllegalArgumentException("Illegal non-standard attribute value, must map to a valid JSON type");
|
||||
|
||||
|
||||
if (nonStdAttributes == null)
|
||||
nonStdAttributes = new HashMap<String,Object>();
|
||||
|
||||
nonStdAttributes.put(name, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves a non-standard JSON-RPC 2.0 message attribute.
|
||||
*
|
||||
* @param name The name of the non-standard attribute to retrieve. Must
|
||||
* not be {@code null}.
|
||||
*
|
||||
* @return The value of the non-standard attribute (may also be
|
||||
* {@code null}, {@code null} if not found.
|
||||
*/
|
||||
public Object getNonStdAttribute(final String name) {
|
||||
|
||||
if (nonStdAttributes == null)
|
||||
return null;
|
||||
|
||||
return nonStdAttributes.get(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the non-standard JSON-RPC 2.0 message attributes.
|
||||
*
|
||||
* @return The non-standard attributes as a map, {@code null} if none.
|
||||
*/
|
||||
public Map<String,Object> getNonStdAttributes() {
|
||||
|
||||
return nonStdAttributes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a JSON object representing this JSON-RPC 2.0 message.
|
||||
*
|
||||
* @return The JSON object.
|
||||
*/
|
||||
public abstract JSONObject toJSONObject();
|
||||
|
||||
|
||||
/**
|
||||
* Returns a JSON string representation of this JSON-RPC 2.0 message.
|
||||
*
|
||||
* @see #toString
|
||||
*
|
||||
* @return The JSON object string representing this JSON-RPC 2.0
|
||||
* message.
|
||||
*/
|
||||
public String toJSONString() {
|
||||
|
||||
return toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serialises this JSON-RPC 2.0 message to a JSON object string.
|
||||
*
|
||||
* @return The JSON object string representing this JSON-RPC 2.0
|
||||
* message.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return toJSONObject().toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a JSON-RPC 2.0 notification.
|
||||
*
|
||||
* <p>Notifications provide a mean for calling a remote procedure without
|
||||
* generating a response. Note that notifications are inherently unreliable
|
||||
* as no confirmation is sent back to the caller.
|
||||
*
|
||||
* <p>Notifications have the same JSON structure as requests, except that they
|
||||
* lack an identifier:
|
||||
* <ul>
|
||||
* <li>{@code method} The name of the remote method to call.
|
||||
* <li>{@code params} The required method parameters (if any), which can
|
||||
* be packed into a JSON array or object.
|
||||
* <li>{@code jsonrpc} A string indicating the JSON-RPC protocol version
|
||||
* set to "2.0".
|
||||
* </ul>
|
||||
*
|
||||
* <p>Here is a sample JSON-RPC 2.0 notification string:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "method" : "progressNotify",
|
||||
* "params" : ["75%"],
|
||||
* "jsonrpc" : "2.0"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>This class provides two methods to obtain a request object:
|
||||
* <ul>
|
||||
* <li>Pass a JSON-RPC 2.0 notification string to the static
|
||||
* {@link #parse} method, or
|
||||
* <li>Invoke one of the constructors with the appropriate arguments.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Example 1: Parsing a notification string:
|
||||
*
|
||||
* <pre>
|
||||
* String jsonString = "{\"method\":\"progressNotify\",\"params\":[\"75%\"],\"jsonrpc\":\"2.0\"}";
|
||||
*
|
||||
* JSONRPC2Notification notification = null;
|
||||
*
|
||||
* try {
|
||||
* notification = JSONRPC2Notification.parse(jsonString);
|
||||
*
|
||||
* } catch (JSONRPC2ParseException e) {
|
||||
* // handle exception
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>Example 2: Recreating the above request:
|
||||
*
|
||||
* <pre>
|
||||
* String method = "progressNotify";
|
||||
* List<Object> params = new Vector<Object>();
|
||||
* params.add("75%");
|
||||
*
|
||||
* JSONRPC2Notification notification = new JSONRPC2Notification(method, params);
|
||||
*
|
||||
* System.out.println(notification);
|
||||
* </pre>
|
||||
*
|
||||
* <p id="map">The mapping between JSON and Java entities (as defined by the
|
||||
* underlying JSON Smart library):
|
||||
*
|
||||
* <pre>
|
||||
* true|false <---> java.lang.Boolean
|
||||
* number <---> java.lang.Number
|
||||
* string <---> java.lang.String
|
||||
* array <---> java.util.List
|
||||
* object <---> java.util.Map
|
||||
* null <---> null
|
||||
* </pre>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class JSONRPC2Notification extends JSONRPC2Message {
|
||||
|
||||
|
||||
/**
|
||||
* The requested method name.
|
||||
*/
|
||||
private String method;
|
||||
|
||||
|
||||
/**
|
||||
* The positional parameters, {@code null} if none.
|
||||
*/
|
||||
private List<Object> positionalParams;
|
||||
|
||||
|
||||
/**
|
||||
* The named parameters, {@code null} if none.
|
||||
*/
|
||||
private Map<String,Object> namedParams;
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 notification string. This method is
|
||||
* thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 notification string, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 notification object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Notification parse(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, false, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 notification string. This method is
|
||||
* thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 notification string, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members in parameters.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 notification object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Notification parse(final String jsonString,
|
||||
final boolean preserveOrder)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, preserveOrder, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 notification string. This method is
|
||||
* thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 notification string, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members in parameters.
|
||||
* @param ignoreVersion {@code true} to skip a check of the
|
||||
* {@code "jsonrpc":"2.0"} version attribute in the
|
||||
* JSON-RPC 2.0 message.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 notification object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Notification parse(final String jsonString,
|
||||
final boolean preserveOrder,
|
||||
final boolean ignoreVersion)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, preserveOrder, ignoreVersion, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 notification string. This method is
|
||||
* thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 notification string,
|
||||
* UTF-8 encoded. Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of
|
||||
* JSON object members in parameters.
|
||||
* @param ignoreVersion {@code true} to skip a check of the
|
||||
* {@code "jsonrpc":"2.0"} version
|
||||
* attribute in the JSON-RPC 2.0 message.
|
||||
* @param parseNonStdAttributes {@code true} to parse non-standard
|
||||
* attributes found in the JSON-RPC 2.0
|
||||
* message.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 notification object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Notification parse(final String jsonString,
|
||||
final boolean preserveOrder,
|
||||
final boolean ignoreVersion,
|
||||
final boolean parseNonStdAttributes)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder, ignoreVersion, parseNonStdAttributes);
|
||||
|
||||
return parser.parseJSONRPC2Notification(jsonString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new JSON-RPC 2.0 notification with no parameters.
|
||||
*
|
||||
* @param method The name of the requested method. Must not be
|
||||
* {@code null}.
|
||||
*/
|
||||
public JSONRPC2Notification(final String method) {
|
||||
|
||||
setMethod(method);
|
||||
setParams(null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new JSON-RPC 2.0 notification with positional (JSON
|
||||
* array) parameters.
|
||||
*
|
||||
* @param method The name of the requested method. Must not
|
||||
* be {@code null}.
|
||||
* @param positionalParams The positional (JSON array) parameters,
|
||||
* {@code null} if none.
|
||||
*/
|
||||
public JSONRPC2Notification(final String method,
|
||||
final List<Object> positionalParams) {
|
||||
|
||||
setMethod(method);
|
||||
setPositionalParams(positionalParams);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new JSON-RPC 2.0 notification with named (JSON object)
|
||||
* parameters.
|
||||
*
|
||||
* @param method The name of the requested method.
|
||||
* @param namedParams The named (JSON object) parameters, {@code null}
|
||||
* if none.
|
||||
*/
|
||||
public JSONRPC2Notification(final String method,
|
||||
final Map <String,Object> namedParams) {
|
||||
|
||||
setMethod(method);
|
||||
setNamedParams(namedParams);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the name of the requested method.
|
||||
*
|
||||
* @return The method name.
|
||||
*/
|
||||
public String getMethod() {
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the name of the requested method.
|
||||
*
|
||||
* @param method The method name. Must not be {@code null}.
|
||||
*/
|
||||
public void setMethod(final String method) {
|
||||
|
||||
// The method name is mandatory
|
||||
if (method == null)
|
||||
throw new IllegalArgumentException("The method name must not be null");
|
||||
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the parameters type ({@link JSONRPC2ParamsType#ARRAY positional},
|
||||
* {@link JSONRPC2ParamsType#OBJECT named} or
|
||||
* {@link JSONRPC2ParamsType#NO_PARAMS none}).
|
||||
*
|
||||
* @return The parameters type.
|
||||
*/
|
||||
public JSONRPC2ParamsType getParamsType() {
|
||||
|
||||
if (positionalParams == null && namedParams == null)
|
||||
return JSONRPC2ParamsType.NO_PARAMS;
|
||||
|
||||
if (positionalParams != null)
|
||||
return JSONRPC2ParamsType.ARRAY;
|
||||
|
||||
if (namedParams != null)
|
||||
return JSONRPC2ParamsType.OBJECT;
|
||||
|
||||
else
|
||||
return JSONRPC2ParamsType.NO_PARAMS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the notification parameters.
|
||||
*
|
||||
* <p>This method was deprecated in version 1.30. Use
|
||||
* {@link #getPositionalParams} or {@link #getNamedParams} instead.
|
||||
*
|
||||
* @return The parameters as {@code List<Object>} for positional
|
||||
* (JSON array), {@code Map<String,Object>} for named
|
||||
* (JSON object), or {@code null} if none.
|
||||
*/
|
||||
@Deprecated
|
||||
public Object getParams() {
|
||||
|
||||
switch (getParamsType()) {
|
||||
|
||||
case ARRAY:
|
||||
return positionalParams;
|
||||
|
||||
case OBJECT:
|
||||
return namedParams;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the positional (JSON array) parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @return The positional (JSON array) parameters, {@code null} if none
|
||||
* or named.
|
||||
*/
|
||||
public List<Object> getPositionalParams() {
|
||||
|
||||
return positionalParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the named parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @return The named (JSON object) parameters, {@code null} if none or
|
||||
* positional.
|
||||
*/
|
||||
public Map<String,Object> getNamedParams() {
|
||||
|
||||
return namedParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the notification parameters.
|
||||
*
|
||||
* <p>This method was deprecated in version 1.30. Use
|
||||
* {@link #setPositionalParams} or {@link #setNamedParams} instead.
|
||||
*
|
||||
* @param params The parameters. For positional (JSON array) pass a
|
||||
* {@code List<Object>}. For named (JSON object)
|
||||
* pass a {@code Map<String,Object>}. If there are
|
||||
* no parameters pass {@code null}.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setParams(final Object params) {
|
||||
|
||||
if (params == null) {
|
||||
positionalParams = null;
|
||||
namedParams = null;
|
||||
} else if (params instanceof List) {
|
||||
positionalParams = (List<Object>) params;
|
||||
} else if (params instanceof Map) {
|
||||
namedParams = (Map<String, Object>) params;
|
||||
} else {
|
||||
throw new IllegalArgumentException("The notification parameters must be of type List, Map or null");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the positional (JSON array) request parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @param positionalParams The positional (JSON array) request
|
||||
* parameters, {@code null} if none.
|
||||
*/
|
||||
public void setPositionalParams(final List<Object> positionalParams) {
|
||||
|
||||
if (positionalParams == null)
|
||||
return;
|
||||
|
||||
this.positionalParams = positionalParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the named (JSON object) request parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @param namedParams The named (JSON object) request parameters,
|
||||
* {@code null} if none.
|
||||
*/
|
||||
public void setNamedParams(final Map<String,Object> namedParams) {
|
||||
|
||||
if (namedParams == null)
|
||||
return;
|
||||
|
||||
this.namedParams = namedParams;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JSONObject toJSONObject() {
|
||||
|
||||
JSONObject notf = new JSONObject();
|
||||
|
||||
notf.put("method", method);
|
||||
|
||||
// The params can be omitted if none
|
||||
switch (getParamsType()) {
|
||||
|
||||
case ARRAY:
|
||||
notf.put("params", positionalParams);
|
||||
break;
|
||||
|
||||
case OBJECT:
|
||||
notf.put("params", namedParams);
|
||||
break;
|
||||
}
|
||||
|
||||
notf.put("jsonrpc", "2.0");
|
||||
|
||||
|
||||
Map <String,Object> nonStdAttributes = getNonStdAttributes();
|
||||
|
||||
if (nonStdAttributes != null) {
|
||||
|
||||
for (final Map.Entry<String,Object> attr: nonStdAttributes.entrySet())
|
||||
notf.put(attr.getKey(), attr.getValue());
|
||||
}
|
||||
|
||||
return notf;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
/**
|
||||
* Enumeration of the three parameter types in JSON-RPC 2.0 requests and
|
||||
* notifications.
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #NO_PARAMS} The method takes no parameters.
|
||||
* <li>{@link #ARRAY} The method takes positional parameters, packed as a
|
||||
* JSON array, e.g. {@code ["val1", "val2", ...]}.
|
||||
* <li>{@link #OBJECT} The method takes named parameters, packed as a JSON
|
||||
* object, e.g. {@code {"param1":"val1", "param2":"val2", ...}}.
|
||||
* </ul>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public enum JSONRPC2ParamsType {
|
||||
|
||||
|
||||
/**
|
||||
* No parameters.
|
||||
*/
|
||||
NO_PARAMS,
|
||||
|
||||
|
||||
/**
|
||||
* Positional parameters, packed as a JSON array.
|
||||
*/
|
||||
ARRAY,
|
||||
|
||||
|
||||
/**
|
||||
* Named parameters, packed as a JSON object.
|
||||
*/
|
||||
OBJECT
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
/**
|
||||
* Thrown to indicate an exception during the parsing of a JSON-RPC 2.0
|
||||
* message string.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class JSONRPC2ParseException extends Exception {
|
||||
|
||||
|
||||
/**
|
||||
* Serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 3376608778436136410L;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates a parse exception caused by a JSON message not conforming
|
||||
* to the JSON-RPC 2.0 protocol.
|
||||
*/
|
||||
public static final int PROTOCOL = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates a parse exception caused by invalid JSON.
|
||||
*/
|
||||
public static final int JSON = 1;
|
||||
|
||||
|
||||
/**
|
||||
* The parse exception cause type. Default is {@link #PROTOCOL}.
|
||||
*/
|
||||
private int causeType = PROTOCOL;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The string that could't be parsed.
|
||||
*/
|
||||
private String unparsableString = null;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new parse exception with the specified message. The cause
|
||||
* type is set to {@link #PROTOCOL}.
|
||||
*
|
||||
* @param message The exception message.
|
||||
*/
|
||||
public JSONRPC2ParseException(final String message) {
|
||||
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new parse exception with the specified message and the
|
||||
* original string that didn't parse. The cause type is set to
|
||||
* {@link #PROTOCOL}.
|
||||
*
|
||||
* @param message The exception message.
|
||||
* @param unparsableString The unparsable string.
|
||||
*/
|
||||
public JSONRPC2ParseException(final String message, final String unparsableString) {
|
||||
|
||||
super(message);
|
||||
this.unparsableString = unparsableString;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new parse exception with the specified message, cause type
|
||||
* and the original string that didn't parse.
|
||||
*
|
||||
* @param message The exception message.
|
||||
* @param causeType The exception cause type, either
|
||||
* {@link #PROTOCOL} or {@link #JSON}.
|
||||
* @param unparsableString The unparsable string.
|
||||
*/
|
||||
public JSONRPC2ParseException(final String message, final int causeType, final String unparsableString) {
|
||||
|
||||
super(message);
|
||||
|
||||
if (causeType != PROTOCOL && causeType != JSON)
|
||||
throw new IllegalArgumentException("Cause type must be either PROTOCOL or JSON");
|
||||
|
||||
this.causeType = causeType;
|
||||
this.unparsableString = unparsableString;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the parse exception cause type.
|
||||
*
|
||||
* @return The cause type, either {@link #PROTOCOL} or {@link #JSON}.
|
||||
*/
|
||||
public int getCauseType() {
|
||||
|
||||
return causeType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets original string that caused the parse exception (if specified).
|
||||
*
|
||||
* @return The string that didn't parse, {@code null} if none.
|
||||
*/
|
||||
public String getUnparsableString() {
|
||||
|
||||
return unparsableString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,654 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.parser.ContainerFactory;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
|
||||
/**
|
||||
* Parses JSON-RPC 2.0 request, notification and response messages.
|
||||
*
|
||||
* <p>Parsing of batched requests / notifications is not supported.
|
||||
*
|
||||
* <p>This class is not thread-safe. A parser instance should not be used by
|
||||
* more than one thread unless properly synchronised. Alternatively, you may
|
||||
* use the thread-safe {@link JSONRPC2Message#parse} and its sister methods.
|
||||
*
|
||||
* <p>Example:
|
||||
*
|
||||
* <pre>
|
||||
* String jsonString = "{\"method\":\"makePayment\"," +
|
||||
* "\"params\":{\"recipient\":\"Penny Adams\",\"amount\":175.05}," +
|
||||
* "\"id\":\"0001\","+
|
||||
* "\"jsonrpc\":\"2.0\"}";
|
||||
*
|
||||
* JSONRPC2Request req = null;
|
||||
*
|
||||
* JSONRPC2Parser parser = new JSONRPC2Parser();
|
||||
*
|
||||
* try {
|
||||
* req = parser.parseJSONRPC2Request(jsonString);
|
||||
*
|
||||
* } catch (JSONRPC2ParseException e) {
|
||||
* // handle exception
|
||||
* }
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* <p id="map">The mapping between JSON and Java entities (as defined by the
|
||||
* underlying JSON Smart library):
|
||||
*
|
||||
* <pre>
|
||||
* true|false <---> java.lang.Boolean
|
||||
* number <---> java.lang.Number
|
||||
* string <---> java.lang.String
|
||||
* array <---> java.util.List
|
||||
* object <---> java.util.Map
|
||||
* null <---> null
|
||||
* </pre>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class JSONRPC2Parser {
|
||||
|
||||
|
||||
/**
|
||||
* Reusable JSON parser. Not thread-safe!
|
||||
*/
|
||||
private final JSONParser parser;
|
||||
|
||||
|
||||
/**
|
||||
* If {@code true} the order of the parsed JSON object members must be
|
||||
* preserved.
|
||||
*/
|
||||
private boolean preserveOrder;
|
||||
|
||||
|
||||
/**
|
||||
* If {@code true} the {@code "jsonrpc":"2.0"} version attribute in the
|
||||
* JSON-RPC 2.0 message must be ignored during parsing.
|
||||
*/
|
||||
private boolean ignoreVersion;
|
||||
|
||||
|
||||
/**
|
||||
* If {@code true} non-standard JSON-RPC 2.0 message attributes must be
|
||||
* parsed too.
|
||||
*/
|
||||
private boolean parseNonStdAttributes;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 message parser.
|
||||
*
|
||||
* <p>The member order of parsed JSON objects in parameters and results
|
||||
* will not be preserved; strict checking of the 2.0 JSON-RPC version
|
||||
* attribute will be enforced; non-standard message attributes will be
|
||||
* ignored. Check the other constructors if you want to specify
|
||||
* different behaviour.
|
||||
*/
|
||||
public JSONRPC2Parser() {
|
||||
|
||||
this(false, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 message parser.
|
||||
*
|
||||
* <p>Strict checking of the 2.0 JSON-RPC version attribute will be
|
||||
* enforced; non-standard message attributes will be ignored. Check the
|
||||
* other constructors if you want to specify different behaviour.
|
||||
*
|
||||
* @param preserveOrder If {@code true} the member order of JSON objects
|
||||
* in parameters and results will be preserved.
|
||||
*/
|
||||
public JSONRPC2Parser(final boolean preserveOrder) {
|
||||
|
||||
this(preserveOrder, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 message parser.
|
||||
*
|
||||
* <p>Non-standard message attributes will be ignored. Check the other
|
||||
* constructors if you want to specify different behaviour.
|
||||
*
|
||||
* @param preserveOrder If {@code true} the member order of JSON objects
|
||||
* in parameters and results will be preserved.
|
||||
* @param ignoreVersion If {@code true} the {@code "jsonrpc":"2.0"}
|
||||
* version attribute in the JSON-RPC 2.0 message
|
||||
* will not be checked.
|
||||
*/
|
||||
public JSONRPC2Parser(final boolean preserveOrder,
|
||||
final boolean ignoreVersion) {
|
||||
|
||||
this(preserveOrder, ignoreVersion, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 message parser.
|
||||
*
|
||||
* <p>This constructor allows full specification of the available
|
||||
* JSON-RPC message parsing properties.
|
||||
*
|
||||
* @param preserveOrder If {@code true} the member order of JSON
|
||||
* objects in parameters and results will
|
||||
* be preserved.
|
||||
* @param ignoreVersion If {@code true} the
|
||||
* {@code "jsonrpc":"2.0"} version
|
||||
* attribute in the JSON-RPC 2.0 message
|
||||
* will not be checked.
|
||||
* @param parseNonStdAttributes If {@code true} non-standard attributes
|
||||
* found in the JSON-RPC 2.0 messages will
|
||||
* be parsed too.
|
||||
*/
|
||||
public JSONRPC2Parser(final boolean preserveOrder,
|
||||
final boolean ignoreVersion,
|
||||
final boolean parseNonStdAttributes) {
|
||||
|
||||
// Numbers parsed as long/double, requires JSON Smart 1.0.9+
|
||||
parser = new JSONParser();
|
||||
|
||||
this.preserveOrder = preserveOrder;
|
||||
this.ignoreVersion = ignoreVersion;
|
||||
this.parseNonStdAttributes = parseNonStdAttributes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON object string. Provides the initial parsing of
|
||||
* JSON-RPC 2.0 messages. The member order of JSON objects will be
|
||||
* preserved if {@link #preserveOrder} is set to {@code true}.
|
||||
*
|
||||
* @param jsonString The JSON string to parse. Must not be
|
||||
* {@code null}.
|
||||
*
|
||||
* @return The parsed JSON object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String,Object> parseJSONObject(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
if (jsonString.trim().length()==0)
|
||||
throw new JSONRPC2ParseException("Invalid JSON: Empty string",
|
||||
JSONRPC2ParseException.JSON,
|
||||
jsonString);
|
||||
|
||||
Object json;
|
||||
|
||||
// Parse the JSON string
|
||||
try {
|
||||
//if (preserveOrder)
|
||||
// json = parser.parse(jsonString, ContainerFactory.FACTORY_ORDERED);
|
||||
|
||||
//else
|
||||
json = parser.parse(jsonString);
|
||||
|
||||
} catch (ParseException e) {
|
||||
|
||||
// Terse message, do not include full parse exception message
|
||||
throw new JSONRPC2ParseException("Invalid JSON",
|
||||
JSONRPC2ParseException.JSON,
|
||||
jsonString);
|
||||
}
|
||||
|
||||
if (json instanceof List)
|
||||
throw new JSONRPC2ParseException("JSON-RPC 2.0 batch requests/notifications not supported", jsonString);
|
||||
|
||||
if (! (json instanceof Map))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 message: Message must be a JSON object", jsonString);
|
||||
|
||||
return (Map<String,Object>)json;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ensures the specified parameter is a {@code String} object set to
|
||||
* "2.0". This method is intended to check the "jsonrpc" attribute
|
||||
* during parsing of JSON-RPC messages.
|
||||
*
|
||||
* @param version The version parameter. Must not be {@code null}.
|
||||
* @param jsonString The original JSON string.
|
||||
*
|
||||
* @throws JSONRPC2ParseException If the parameter is not a string that
|
||||
* equals "2.0".
|
||||
*/
|
||||
private static void ensureVersion2(final Object version, final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
if (version == null)
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0: Version string missing", jsonString);
|
||||
|
||||
else if (! (version instanceof String))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0: Version not a JSON string", jsonString);
|
||||
|
||||
else if (! version.equals("2.0"))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0: Version must be \"2.0\"", jsonString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides common parsing of JSON-RPC 2.0 requests, notifications
|
||||
* and responses. Use this method if you don't know which type of
|
||||
* JSON-RPC message the input string represents.
|
||||
*
|
||||
* <p>If a particular message type is expected use the dedicated
|
||||
* {@link #parseJSONRPC2Request}, {@link #parseJSONRPC2Notification}
|
||||
* and {@link #parseJSONRPC2Response} methods. They are more efficient
|
||||
* and would provide you with more detailed parse error reporting.
|
||||
*
|
||||
* @param jsonString A JSON string representing a JSON-RPC 2.0 request,
|
||||
* notification or response, UTF-8 encoded. Must not
|
||||
* be {@code null}.
|
||||
*
|
||||
* @return An instance of {@link JSONRPC2Request},
|
||||
* {@link JSONRPC2Notification} or {@link JSONRPC2Response}.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if the parsing
|
||||
* failed.
|
||||
*/
|
||||
public JSONRPC2Message parseJSONRPC2Message(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
// Try each of the parsers until one succeeds (or all fail)
|
||||
|
||||
try {
|
||||
return parseJSONRPC2Request(jsonString);
|
||||
|
||||
} catch (JSONRPC2ParseException e) {
|
||||
|
||||
// throw on JSON error, ignore on protocol error
|
||||
if (e.getCauseType() == JSONRPC2ParseException.JSON)
|
||||
throw e;
|
||||
}
|
||||
|
||||
try {
|
||||
return parseJSONRPC2Notification(jsonString);
|
||||
|
||||
} catch (JSONRPC2ParseException e) {
|
||||
|
||||
// throw on JSON error, ignore on protocol error
|
||||
if (e.getCauseType() == JSONRPC2ParseException.JSON)
|
||||
throw e;
|
||||
}
|
||||
|
||||
try {
|
||||
return parseJSONRPC2Response(jsonString);
|
||||
|
||||
} catch (JSONRPC2ParseException e) {
|
||||
|
||||
// throw on JSON error, ignore on protocol error
|
||||
if (e.getCauseType() == JSONRPC2ParseException.JSON)
|
||||
throw e;
|
||||
}
|
||||
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 message",
|
||||
JSONRPC2ParseException.PROTOCOL,
|
||||
jsonString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 request string.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 request object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public JSONRPC2Request parseJSONRPC2Request(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
// Initial JSON object parsing
|
||||
Map<String,Object> jsonObject = parseJSONObject(jsonString);
|
||||
|
||||
|
||||
// Check for JSON-RPC version "2.0"
|
||||
Object version = jsonObject.remove("jsonrpc");
|
||||
|
||||
if (! ignoreVersion)
|
||||
ensureVersion2(version, jsonString);
|
||||
|
||||
|
||||
// Extract method name
|
||||
Object method = jsonObject.remove("method");
|
||||
|
||||
if (method == null)
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 request: Method name missing", jsonString);
|
||||
|
||||
else if (! (method instanceof String))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 request: Method name not a JSON string", jsonString);
|
||||
|
||||
else if (((String)method).length() == 0)
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 request: Method name is an empty string", jsonString);
|
||||
|
||||
|
||||
// Extract ID
|
||||
if (! jsonObject.containsKey("id"))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 request: Missing identifier", jsonString);
|
||||
|
||||
Object id = jsonObject.remove("id");
|
||||
|
||||
if ( id != null &&
|
||||
!(id instanceof Number ) &&
|
||||
!(id instanceof Boolean) &&
|
||||
!(id instanceof String ) )
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 request: Identifier not a JSON scalar", jsonString);
|
||||
|
||||
|
||||
// Extract params
|
||||
Object params = jsonObject.remove("params");
|
||||
|
||||
|
||||
JSONRPC2Request request;
|
||||
|
||||
if (params == null)
|
||||
request = new JSONRPC2Request((String)method, id);
|
||||
|
||||
else if (params instanceof List)
|
||||
request = new JSONRPC2Request((String)method, (List<Object>)params, id);
|
||||
|
||||
else if (params instanceof Map)
|
||||
request = new JSONRPC2Request((String)method, (Map<String,Object>)params, id);
|
||||
|
||||
else
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 request: Method parameters have unexpected JSON type", jsonString);
|
||||
|
||||
|
||||
// Extract remaining non-std params?
|
||||
if (parseNonStdAttributes) {
|
||||
|
||||
for (Map.Entry<String,Object> entry: jsonObject.entrySet()) {
|
||||
|
||||
request.appendNonStdAttribute(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 notification string.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 notification string, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 notification object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public JSONRPC2Notification parseJSONRPC2Notification(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
// Initial JSON object parsing
|
||||
Map<String,Object> jsonObject = parseJSONObject(jsonString);
|
||||
|
||||
|
||||
// Check for JSON-RPC version "2.0"
|
||||
Object version = jsonObject.remove("jsonrpc");
|
||||
|
||||
if (! ignoreVersion)
|
||||
ensureVersion2(version, jsonString);
|
||||
|
||||
|
||||
// Extract method name
|
||||
Object method = jsonObject.remove("method");
|
||||
|
||||
if (method == null)
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 notification: Method name missing", jsonString);
|
||||
|
||||
else if (! (method instanceof String))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 notification: Method name not a JSON string", jsonString);
|
||||
|
||||
else if (((String)method).length() == 0)
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 notification: Method name is an empty string", jsonString);
|
||||
|
||||
|
||||
// Extract params
|
||||
Object params = jsonObject.get("params");
|
||||
|
||||
JSONRPC2Notification notification;
|
||||
|
||||
if (params == null)
|
||||
notification = new JSONRPC2Notification((String)method);
|
||||
|
||||
else if (params instanceof List)
|
||||
notification = new JSONRPC2Notification((String)method, (List<Object>)params);
|
||||
|
||||
else if (params instanceof Map)
|
||||
notification = new JSONRPC2Notification((String)method, (Map<String,Object>)params);
|
||||
else
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 notification: Method parameters have unexpected JSON type", jsonString);
|
||||
|
||||
// Extract remaining non-std params?
|
||||
if (parseNonStdAttributes) {
|
||||
|
||||
for (Map.Entry<String,Object> entry: jsonObject.entrySet()) {
|
||||
|
||||
notification.appendNonStdAttribute(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 response string.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 response string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 response object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public JSONRPC2Response parseJSONRPC2Response(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
// Initial JSON object parsing
|
||||
Map<String,Object> jsonObject = parseJSONObject(jsonString);
|
||||
|
||||
// Check for JSON-RPC version "2.0"
|
||||
Object version = jsonObject.remove("jsonrpc");
|
||||
|
||||
if (! ignoreVersion)
|
||||
ensureVersion2(version, jsonString);
|
||||
|
||||
|
||||
// Extract request ID
|
||||
Object id = jsonObject.remove("id");
|
||||
|
||||
if ( id != null &&
|
||||
! (id instanceof Boolean) &&
|
||||
! (id instanceof Number ) &&
|
||||
! (id instanceof String ) )
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: Identifier not a JSON scalar", jsonString);
|
||||
|
||||
|
||||
// Extract result/error and create response object
|
||||
// Note: result and error are mutually exclusive
|
||||
|
||||
JSONRPC2Response response;
|
||||
|
||||
if (jsonObject.containsKey("result") && ! jsonObject.containsKey("error")) {
|
||||
|
||||
// Success
|
||||
Object res = jsonObject.remove("result");
|
||||
|
||||
response = new JSONRPC2Response(res, id);
|
||||
|
||||
}
|
||||
else if (! jsonObject.containsKey("result") && jsonObject.containsKey("error")) {
|
||||
|
||||
// Error JSON object
|
||||
Object errorJSON = jsonObject.remove("error");
|
||||
|
||||
if (errorJSON == null)
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: Missing error object", jsonString);
|
||||
|
||||
|
||||
if (! (errorJSON instanceof Map))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: Error object not a JSON object");
|
||||
|
||||
|
||||
Map<String,Object> error = (Map<String,Object>)errorJSON;
|
||||
|
||||
|
||||
int errorCode;
|
||||
|
||||
try {
|
||||
errorCode = ((Number)error.get("code")).intValue();
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: Error code missing or not an integer", jsonString);
|
||||
}
|
||||
|
||||
String errorMessage;
|
||||
|
||||
try {
|
||||
errorMessage = (String)error.get("message");
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: Error message missing or not a string", jsonString);
|
||||
}
|
||||
|
||||
Object errorData = error.get("data");
|
||||
|
||||
response = new JSONRPC2Response(new JSONRPC2Error(errorCode, errorMessage, errorData), id);
|
||||
|
||||
}
|
||||
else if (jsonObject.containsKey("result") && jsonObject.containsKey("error")) {
|
||||
|
||||
// Invalid response
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: You cannot have result and error at the same time", jsonString);
|
||||
}
|
||||
else if (! jsonObject.containsKey("result") && ! jsonObject.containsKey("error")){
|
||||
|
||||
// Invalid response
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: Neither result nor error specified", jsonString);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
|
||||
// Extract remaining non-std params?
|
||||
if (parseNonStdAttributes) {
|
||||
|
||||
for (Map.Entry<String,Object> entry: jsonObject.entrySet()) {
|
||||
|
||||
response.appendNonStdAttribute(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Controls the preservation of JSON object member order in parsed
|
||||
* JSON-RPC 2.0 messages.
|
||||
*
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members, else {@code false}.
|
||||
*/
|
||||
public void preserveOrder(final boolean preserveOrder) {
|
||||
|
||||
this.preserveOrder = preserveOrder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the order of JSON object members in parsed
|
||||
* JSON-RPC 2.0 messages is preserved, else {@code false}.
|
||||
*
|
||||
* @return {@code true} if order is preserved, else {@code false}.
|
||||
*/
|
||||
public boolean preservesOrder() {
|
||||
|
||||
return preserveOrder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specifies whether to ignore the {@code "jsonrpc":"2.0"} version
|
||||
* attribute during parsing of JSON-RPC 2.0 messages.
|
||||
*
|
||||
* <p>You may with to disable strict 2.0 version checking if the parsed
|
||||
* JSON-RPC 2.0 messages don't include a version attribute or if you
|
||||
* wish to achieve limited compatibility with older JSON-RPC protocol
|
||||
* versions.
|
||||
*
|
||||
* @param ignore {@code true} to skip checks of the
|
||||
* {@code "jsonrpc":"2.0"} version attribute in parsed
|
||||
* JSON-RPC 2.0 messages, else {@code false}.
|
||||
*/
|
||||
public void ignoreVersion(final boolean ignore) {
|
||||
|
||||
ignoreVersion = ignore;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the {@code "jsonrpc":"2.0"} version
|
||||
* attribute in parsed JSON-RPC 2.0 messages is ignored, else
|
||||
* {@code false}.
|
||||
*
|
||||
* @return {@code true} if the {@code "jsonrpc":"2.0"} version
|
||||
* attribute in parsed JSON-RPC 2.0 messages is ignored, else
|
||||
* {@code false}.
|
||||
*/
|
||||
public boolean ignoresVersion() {
|
||||
|
||||
return ignoreVersion;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specifies whether to parse non-standard attributes found in JSON-RPC
|
||||
* 2.0 messages.
|
||||
*
|
||||
* @param enable {@code true} to parse non-standard attributes, else
|
||||
* {@code false}.
|
||||
*/
|
||||
public void parseNonStdAttributes(final boolean enable) {
|
||||
|
||||
parseNonStdAttributes = enable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if non-standard attributes in JSON-RPC 2.0
|
||||
* messages are parsed.
|
||||
*
|
||||
* @return {@code true} if non-standard attributes are parsed, else
|
||||
* {@code false}.
|
||||
*/
|
||||
public boolean parsesNonStdAttributes() {
|
||||
|
||||
return parseNonStdAttributes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a JSON-RPC 2.0 request.
|
||||
*
|
||||
* <p>A request carries four pieces of data:
|
||||
* <ul>
|
||||
* <li>{@code method} The name of the remote method to call.
|
||||
* <li>{@code params} The required method parameters (if any), which can
|
||||
* be packed into a JSON array or object.
|
||||
* <li>{@code id} An identifier which is echoed back to the client with
|
||||
* the response.
|
||||
* <li>{@code jsonrpc} A string indicating the JSON-RPC protocol version
|
||||
* set to "2.0".
|
||||
* </ul>
|
||||
*
|
||||
* <p>Here is a sample JSON-RPC 2.0 request string:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "method" : "makePayment",
|
||||
* "params" : { "recipient" : "Penny Adams", "amount":175.05 },
|
||||
* "id" : "0001",
|
||||
* "jsonrpc" : "2.0"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>This class provides two methods to obtain a request object:
|
||||
* <ul>
|
||||
* <li>Pass a JSON-RPC 2.0 request string to the static
|
||||
* {@link #parse} method, or
|
||||
* <li>Invoke one of the constructors with the appropriate arguments.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Example 1: Parsing a request string:
|
||||
*
|
||||
* <pre>
|
||||
* String jsonString = "{\"method\":\"makePayment\"," +
|
||||
* "\"params\":{\"recipient\":\"Penny Adams\",\"amount\":175.05}," +
|
||||
* "\"id\":\"0001\","+
|
||||
* "\"jsonrpc\":\"2.0\"}";
|
||||
*
|
||||
* JSONRPC2Request req = null;
|
||||
*
|
||||
* try {
|
||||
* req = JSONRPC2Request.parse(jsonString);
|
||||
*
|
||||
* } catch (JSONRPC2ParseException e) {
|
||||
* // handle exception
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>Example 2: Recreating the above request:
|
||||
*
|
||||
* <pre>
|
||||
* String method = "makePayment";
|
||||
* Map<String,Object> params = new HashMap<String,Object>();
|
||||
* params.put("recipient", "Penny Adams");
|
||||
* params.put("amount", 175.05);
|
||||
* String id = "0001";
|
||||
*
|
||||
* JSONRPC2Request req = new JSONRPC2Request(method, params, id);
|
||||
*
|
||||
* System.out.println(req);
|
||||
* </pre>
|
||||
*
|
||||
* <p id="map">The mapping between JSON and Java entities (as defined by the
|
||||
* underlying JSON Smart library):
|
||||
*
|
||||
* <pre>
|
||||
* true|false <---> java.lang.Boolean
|
||||
* number <---> java.lang.Number
|
||||
* string <---> java.lang.String
|
||||
* array <---> java.util.List
|
||||
* object <---> java.util.Map
|
||||
* null <---> null
|
||||
* </pre>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class JSONRPC2Request extends JSONRPC2Message {
|
||||
|
||||
|
||||
/**
|
||||
* The method name.
|
||||
*/
|
||||
private String method;
|
||||
|
||||
|
||||
/**
|
||||
* The positional parameters, {@code null} if none.
|
||||
*/
|
||||
private List<Object> positionalParams;
|
||||
|
||||
|
||||
/**
|
||||
* The named parameters, {@code null} if none.
|
||||
*/
|
||||
private Map<String,Object> namedParams;
|
||||
|
||||
|
||||
/**
|
||||
* The request identifier.
|
||||
*/
|
||||
private Object id;
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 request string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 request object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Request parse(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, false, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 request string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members in parameters.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 request object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Request parse(final String jsonString,
|
||||
final boolean preserveOrder)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, preserveOrder, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 request string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members in parameters.
|
||||
* @param ignoreVersion {@code true} to skip a check of the
|
||||
* {@code "jsonrpc":"2.0"} version attribute in the
|
||||
* JSON-RPC 2.0 message.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 request object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Request parse(final String jsonString,
|
||||
final boolean preserveOrder,
|
||||
final boolean ignoreVersion)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, preserveOrder, ignoreVersion, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 request string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 request string, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of
|
||||
* JSON object members in parameters.
|
||||
* @param ignoreVersion {@code true} to skip a check of the
|
||||
* {@code "jsonrpc":"2.0"} version
|
||||
* attribute in the JSON-RPC 2.0 message.
|
||||
* @param parseNonStdAttributes {@code true} to parse non-standard
|
||||
* attributes found in the JSON-RPC 2.0
|
||||
* message.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 request object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Request parse(final String jsonString,
|
||||
final boolean preserveOrder,
|
||||
final boolean ignoreVersion,
|
||||
final boolean parseNonStdAttributes)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder,
|
||||
ignoreVersion,
|
||||
parseNonStdAttributes);
|
||||
|
||||
return parser.parseJSONRPC2Request(jsonString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new JSON-RPC 2.0 request with no parameters.
|
||||
*
|
||||
* @param method The name of the requested method. Must not be
|
||||
* {@code null}.
|
||||
* @param id The request identifier echoed back to the caller.
|
||||
* The value must <a href="#map">map</a> to a JSON
|
||||
* scalar ({@code null} and fractions, however, should
|
||||
* be avoided).
|
||||
*/
|
||||
public JSONRPC2Request(final String method, final Object id) {
|
||||
|
||||
setMethod(method);
|
||||
setID(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new JSON-RPC 2.0 request with positional (JSON array)
|
||||
* parameters.
|
||||
*
|
||||
* @param method The name of the requested method. Must not
|
||||
* be {@code null}.
|
||||
* @param positionalParams The positional (JSON array) parameters,
|
||||
* {@code null} if none.
|
||||
* @param id The request identifier echoed back to the
|
||||
* caller. The value must <a href="#map">map</a>
|
||||
* to a JSON scalar ({@code null} and
|
||||
* fractions, however, should be avoided).
|
||||
*/
|
||||
public JSONRPC2Request(final String method,
|
||||
final List<Object> positionalParams,
|
||||
final Object id) {
|
||||
|
||||
setMethod(method);
|
||||
setPositionalParams(positionalParams);
|
||||
setID(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new JSON-RPC 2.0 request with named (JSON object)
|
||||
* parameters.
|
||||
*
|
||||
* @param method The name of the requested method.
|
||||
* @param namedParams The named (JSON object) parameters, {@code null}
|
||||
* if none.
|
||||
* @param id The request identifier echoed back to the caller.
|
||||
* The value must <a href="#map">map</a> to a JSON
|
||||
* scalar ({@code null} and fractions, however,
|
||||
* should be avoided).
|
||||
*/
|
||||
public JSONRPC2Request(final String method,
|
||||
final Map <String,Object> namedParams,
|
||||
final Object id) {
|
||||
|
||||
setMethod(method);
|
||||
setNamedParams(namedParams);
|
||||
setID(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the name of the requested method.
|
||||
*
|
||||
* @return The method name.
|
||||
*/
|
||||
public String getMethod() {
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the name of the requested method.
|
||||
*
|
||||
* @param method The method name. Must not be {@code null}.
|
||||
*/
|
||||
public void setMethod(final String method) {
|
||||
|
||||
// The method name is mandatory
|
||||
if (method == null)
|
||||
throw new IllegalArgumentException("The method name must not be null");
|
||||
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the parameters type ({@link JSONRPC2ParamsType#ARRAY positional},
|
||||
* {@link JSONRPC2ParamsType#OBJECT named} or
|
||||
* {@link JSONRPC2ParamsType#NO_PARAMS none}).
|
||||
*
|
||||
* @return The parameters type.
|
||||
*/
|
||||
public JSONRPC2ParamsType getParamsType() {
|
||||
|
||||
if (positionalParams == null && namedParams == null)
|
||||
return JSONRPC2ParamsType.NO_PARAMS;
|
||||
|
||||
if (positionalParams != null)
|
||||
return JSONRPC2ParamsType.ARRAY;
|
||||
|
||||
if (namedParams != null)
|
||||
return JSONRPC2ParamsType.OBJECT;
|
||||
|
||||
else
|
||||
return JSONRPC2ParamsType.NO_PARAMS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the request parameters.
|
||||
*
|
||||
* <p>This method was deprecated in version 1.30. Use
|
||||
* {@link #getPositionalParams} or {@link #getNamedParams} instead.
|
||||
*
|
||||
* @return The parameters as {@code List<Object>} for positional
|
||||
* (JSON array), {@code Map<String,Object>} for named
|
||||
* (JSON object), or {@code null} if none.
|
||||
*/
|
||||
@Deprecated
|
||||
public Object getParams() {
|
||||
|
||||
switch (getParamsType()) {
|
||||
|
||||
case ARRAY:
|
||||
return positionalParams;
|
||||
|
||||
case OBJECT:
|
||||
return namedParams;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the positional (JSON array) parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @return The positional (JSON array) parameters, {@code null} if none
|
||||
* or named.
|
||||
*/
|
||||
public List<Object> getPositionalParams() {
|
||||
|
||||
return positionalParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the named parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @return The named (JSON object) parameters, {@code null} if none or
|
||||
* positional.
|
||||
*/
|
||||
public Map<String,Object> getNamedParams() {
|
||||
|
||||
return namedParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the request parameters.
|
||||
*
|
||||
* <p>This method was deprecated in version 1.30. Use
|
||||
* {@link #setPositionalParams} or {@link #setNamedParams} instead.
|
||||
*
|
||||
* @param params The parameters. For positional (JSON array) pass a
|
||||
* {@code List<Object>}. For named (JSON object)
|
||||
* pass a {@code Map<String,Object>}. If there are
|
||||
* no parameters pass {@code null}.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setParams(final Object params) {
|
||||
|
||||
if (params == null) {
|
||||
positionalParams = null;
|
||||
namedParams = null;
|
||||
} else if (params instanceof List) {
|
||||
positionalParams = (List<Object>) params;
|
||||
} else if (params instanceof Map) {
|
||||
namedParams = (Map<String, Object>) params;
|
||||
} else {
|
||||
throw new IllegalArgumentException("The request parameters must be of type List, Map or null");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the positional (JSON array) request parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @param positionalParams The positional (JSON array) request
|
||||
* parameters, {@code null} if none.
|
||||
*/
|
||||
public void setPositionalParams(final List<Object> positionalParams) {
|
||||
|
||||
if (positionalParams == null)
|
||||
return;
|
||||
|
||||
this.positionalParams = positionalParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the named (JSON object) request parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @param namedParams The named (JSON object) request parameters,
|
||||
* {@code null} if none.
|
||||
*/
|
||||
public void setNamedParams(final Map<String,Object> namedParams) {
|
||||
|
||||
if (namedParams == null)
|
||||
return;
|
||||
|
||||
this.namedParams = namedParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the request identifier.
|
||||
*
|
||||
* @return The request identifier ({@code Number}, {@code Boolean},
|
||||
* {@code String}) or {@code null}.
|
||||
*/
|
||||
public Object getID() {
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the request identifier (ID).
|
||||
*
|
||||
* @param id The request identifier echoed back to the caller.
|
||||
* The value must <a href="#map">map</a> to a JSON
|
||||
* scalar ({@code null} and fractions, however, should
|
||||
* be avoided).
|
||||
*/
|
||||
public void setID(final Object id) {
|
||||
|
||||
if (id == null ||
|
||||
id instanceof Boolean ||
|
||||
id instanceof Number ||
|
||||
id instanceof String
|
||||
) {
|
||||
this.id = id;
|
||||
} else {
|
||||
this.id = id.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JSONObject toJSONObject() {
|
||||
|
||||
JSONObject req = new JSONObject();
|
||||
|
||||
req.put("method", method);
|
||||
|
||||
// The params can be omitted if none
|
||||
switch (getParamsType()) {
|
||||
|
||||
case ARRAY:
|
||||
req.put("params", positionalParams);
|
||||
break;
|
||||
|
||||
case OBJECT:
|
||||
req.put("params", namedParams);
|
||||
break;
|
||||
}
|
||||
|
||||
req.put("id", id);
|
||||
|
||||
req.put("jsonrpc", "2.0");
|
||||
|
||||
Map <String,Object> nonStdAttributes = getNonStdAttributes();
|
||||
|
||||
if (nonStdAttributes != null) {
|
||||
|
||||
for (final Map.Entry<String,Object> attr: nonStdAttributes.entrySet())
|
||||
req.put(attr.getKey(), attr.getValue());
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,414 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a JSON-RPC 2.0 response.
|
||||
*
|
||||
* <p>A response is returned to the caller after a JSON-RPC 2.0 request has
|
||||
* been processed (notifications, however, don't produce a response). The
|
||||
* response can take two different forms depending on the outcome:
|
||||
*
|
||||
* <ul>
|
||||
* <li>The request was successful. The corresponding response returns
|
||||
* a JSON object with the following information:
|
||||
* <ul>
|
||||
* <li>{@code result} The result, which can be of any JSON type
|
||||
* - a number, a boolean value, a string, an array, an object
|
||||
* or null.
|
||||
* <li>{@code id} The request identifier which is echoed back back
|
||||
* to the caller.
|
||||
* <li>{@code jsonrpc} A string indicating the JSON-RPC protocol
|
||||
* version set to "2.0".
|
||||
* </ul>
|
||||
* <li>The request failed. The returned JSON object contains:
|
||||
* <ul>
|
||||
* <li>{@code error} An object with:
|
||||
* <ul>
|
||||
* <li>{@code code} An integer indicating the error type.
|
||||
* <li>{@code message} A brief error messsage.
|
||||
* <li>{@code data} Optional error data.
|
||||
* </ul>
|
||||
* <li>{@code id} The request identifier. If it couldn't be
|
||||
* determined, e.g. due to a request parse error, the ID is
|
||||
* set to {@code null}.
|
||||
* <li>{@code jsonrpc} A string indicating the JSON-RPC protocol
|
||||
* version set to "2.0".
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Here is an example JSON-RPC 2.0 response string where the request
|
||||
* has succeeded:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "result" : true,
|
||||
* "id" : "req-002",
|
||||
* "jsonrpc" : "2.0"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>And here is an example JSON-RPC 2.0 response string indicating a failure:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "error" : { "code" : -32601, "message" : "Method not found" },
|
||||
* "id" : "req-003",
|
||||
* "jsonrpc" : "2.0"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>A response object is obtained either by passing a valid JSON-RPC 2.0
|
||||
* response string to the static {@link #parse} method or by invoking the
|
||||
* appropriate constructor.
|
||||
*
|
||||
* <p>Here is how parsing is done:
|
||||
*
|
||||
* <pre>
|
||||
* String jsonString = "{\"result\":true,\"id\":\"req-002\",\"jsonrpc\":\"2.0\"}";
|
||||
*
|
||||
* JSONRPC2Response response = null;
|
||||
*
|
||||
* try {
|
||||
* response = JSONRPC2Response.parse(jsonString);
|
||||
*
|
||||
* } catch (JSONRPC2Exception e) {
|
||||
* // handle exception
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>And here is how you can replicate the above example response strings:
|
||||
*
|
||||
* <pre>
|
||||
* // success example
|
||||
* JSONRPC2Response resp = new JSONRPC2Response(true, "req-002");
|
||||
* System.out.println(resp);
|
||||
*
|
||||
* // failure example
|
||||
* JSONRPC2Error err = new JSONRPC2Error(-32601, "Method not found");
|
||||
* resp = new JSONRPC2Response(err, "req-003");
|
||||
* System.out.println(resp);
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* <p id="map">The mapping between JSON and Java entities (as defined by the
|
||||
* underlying JSON Smart library):
|
||||
*
|
||||
* <pre>
|
||||
* true|false <---> java.lang.Boolean
|
||||
* number <---> java.lang.Number
|
||||
* string <---> java.lang.String
|
||||
* array <---> java.util.List
|
||||
* object <---> java.util.Map
|
||||
* null <---> null
|
||||
* </pre>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class JSONRPC2Response extends JSONRPC2Message {
|
||||
|
||||
|
||||
/**
|
||||
* The result.
|
||||
*/
|
||||
private Object result = null;
|
||||
|
||||
|
||||
/**
|
||||
* The error object.
|
||||
*/
|
||||
private JSONRPC2Error error = null;
|
||||
|
||||
|
||||
/**
|
||||
* The echoed request identifier.
|
||||
*/
|
||||
private Object id = null;
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 response string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 response string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 response object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Response parse(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, false, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 response string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 response string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members in results.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 response object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Response parse(final String jsonString,
|
||||
final boolean preserveOrder)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, preserveOrder, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 response string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 response string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members in results.
|
||||
* @param ignoreVersion {@code true} to skip a check of the
|
||||
* {@code "jsonrpc":"2.0"} version attribute in the
|
||||
* JSON-RPC 2.0 message.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 response object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if the parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Response parse(final String jsonString,
|
||||
final boolean preserveOrder,
|
||||
final boolean ignoreVersion)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, preserveOrder, ignoreVersion, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 response string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 response string, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of
|
||||
* JSON object members in results.
|
||||
* @param ignoreVersion {@code true} to skip a check of the
|
||||
* {@code "jsonrpc":"2.0"} version
|
||||
* attribute in the JSON-RPC 2.0 message.
|
||||
* @param parseNonStdAttributes {@code true} to parse non-standard
|
||||
* attributes found in the JSON-RPC 2.0
|
||||
* message.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 response object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if the parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Response parse(final String jsonString,
|
||||
final boolean preserveOrder,
|
||||
final boolean ignoreVersion,
|
||||
final boolean parseNonStdAttributes)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder, ignoreVersion, parseNonStdAttributes);
|
||||
|
||||
return parser.parseJSONRPC2Response(jsonString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 response to a successful request.
|
||||
*
|
||||
* @param result The result. The value can <a href="#map">map</a>
|
||||
* to any JSON type. May be {@code null}.
|
||||
* @param id The request identifier echoed back to the caller. May
|
||||
* be {@code null} though not recommended.
|
||||
*/
|
||||
public JSONRPC2Response(final Object result, final Object id) {
|
||||
|
||||
setResult(result);
|
||||
setID(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 response to a successful request which
|
||||
* result is {@code null}.
|
||||
*
|
||||
* @param id The request identifier echoed back to the caller. May be
|
||||
* {@code null} though not recommended.
|
||||
*/
|
||||
public JSONRPC2Response(final Object id) {
|
||||
|
||||
setResult(null);
|
||||
setID(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 response to a failed request.
|
||||
*
|
||||
* @param error A JSON-RPC 2.0 error instance indicating the
|
||||
* cause of the failure. Must not be {@code null}.
|
||||
* @param id The request identifier echoed back to the caller.
|
||||
* Pass a {@code null} if the request identifier couldn't
|
||||
* be determined (e.g. due to a parse error).
|
||||
*/
|
||||
public JSONRPC2Response(final JSONRPC2Error error, final Object id) {
|
||||
|
||||
setError(error);
|
||||
setID(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicates a successful JSON-RPC 2.0 request and sets the result.
|
||||
* Note that if the response was previously indicating failure this
|
||||
* will turn it into a response indicating success. Any previously set
|
||||
* error data will be invalidated.
|
||||
*
|
||||
* @param result The result. The value can <a href="#map">map</a> to
|
||||
* any JSON type. May be {@code null}.
|
||||
*/
|
||||
public void setResult(final Object result) {
|
||||
|
||||
// result and error are mutually exclusive
|
||||
this.result = result;
|
||||
this.error = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the result of the request. The returned value has meaning
|
||||
* only if the request was successful. Use the
|
||||
* {@link #getError getError} method to check this.
|
||||
*
|
||||
* @return The result.
|
||||
*/
|
||||
public Object getResult() {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicates a failed JSON-RPC 2.0 request and sets the error details.
|
||||
* Note that if the response was previously indicating success this
|
||||
* will turn it into a response indicating failure. Any previously set
|
||||
* result data will be invalidated.
|
||||
*
|
||||
* @param error A JSON-RPC 2.0 error instance indicating the cause of
|
||||
* the failure. Must not be {@code null}.
|
||||
*/
|
||||
public void setError(final JSONRPC2Error error) {
|
||||
|
||||
if (error == null)
|
||||
throw new IllegalArgumentException("The error object cannot be null");
|
||||
|
||||
// result and error are mutually exclusive
|
||||
this.error = error;
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the error object indicating the cause of the request failure.
|
||||
* If a {@code null} is returned, the request succeeded and there was
|
||||
* no error.
|
||||
*
|
||||
* @return A JSON-RPC 2.0 error object, {@code null} if the response
|
||||
* indicates success.
|
||||
*/
|
||||
public JSONRPC2Error getError() {
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A convinience method to check if the response indicates success or
|
||||
* failure of the request. Alternatively, you can use the
|
||||
* {@code #getError} method for this purpose.
|
||||
*
|
||||
* @return {@code true} if the request succeeded, {@code false} if
|
||||
* there was an error.
|
||||
*/
|
||||
public boolean indicatesSuccess() {
|
||||
|
||||
return error == null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the request identifier echoed back to the caller.
|
||||
*
|
||||
* @param id The value must <a href="#map">map</a> to a JSON scalar.
|
||||
* Pass a {@code null} if the request identifier couldn't
|
||||
* be determined (e.g. due to a parse error).
|
||||
*/
|
||||
public void setID(final Object id) {
|
||||
|
||||
if (id == null ||
|
||||
id instanceof Boolean ||
|
||||
id instanceof Number ||
|
||||
id instanceof String
|
||||
) {
|
||||
this.id = id;
|
||||
} else {
|
||||
this.id = id.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the request identifier that is echoed back to the caller.
|
||||
*
|
||||
* @return The request identifier. If there was an error during the
|
||||
* the request retrieval (e.g. parse error) and the identifier
|
||||
* couldn't be determined, the value will be {@code null}.
|
||||
*/
|
||||
public Object getID() {
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JSONObject toJSONObject() {
|
||||
|
||||
JSONObject out = new JSONObject();
|
||||
|
||||
// Result and error are mutually exclusive
|
||||
if (error != null) {
|
||||
out.put("error", error.toJSONObject());
|
||||
}
|
||||
else {
|
||||
out.put("result", result);
|
||||
}
|
||||
|
||||
out.put("id", id);
|
||||
|
||||
out.put("jsonrpc", "2.0");
|
||||
|
||||
|
||||
Map <String,Object> nonStdAttributes = getNonStdAttributes();
|
||||
|
||||
if (nonStdAttributes != null) {
|
||||
|
||||
for (final Map.Entry<String,Object> attr: nonStdAttributes.entrySet())
|
||||
out.put(attr.getKey(), attr.getValue());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Classes to represent, parse and serialise JSON-RPC 2.0 requests,
|
||||
* notifications and responses.
|
||||
*
|
||||
* <p>JSON-RPC is a protocol for
|
||||
* <a href="http://en.wikipedia.org/wiki/Remote_procedure_call">remote
|
||||
* procedure calls</a> (RPC) using <a href="http://www.json.org" >JSON</a>
|
||||
* - encoded requests and responses. It can be easily relayed over HTTP
|
||||
* and is of JavaScript origin, making it ideal for use in dynamic web
|
||||
* applications in the spirit of Ajax and Web 2.0.
|
||||
*
|
||||
* <p>This package implements <b>version 2.0</b> of the protocol, with the
|
||||
* exception of <i>batching / multicall</i>. This feature is deliberately left
|
||||
* out as it tends to confuse users (judging by posts in the JSON-RPC forum).
|
||||
*
|
||||
* <p>See the <a href="http://www.jsonrpc.org/specification">JSON-RPC 2.0
|
||||
* specification</a> for more information or write to the
|
||||
* <a href="https://groups.google.com/forum/#!forum/json-rpc">user group</a> if
|
||||
* you have questions.
|
||||
*
|
||||
* <p><b>Package dependencies:</b> The classes in this package rely on the
|
||||
* {@code org.json.simple} and {@code org.json.simple.parser} packages
|
||||
* (version 1.1.1 and compabile) for JSON encoding and decoding. You can obtain
|
||||
* them from the <a href="http://code.google.com/p/json-smart/">JSON-Smart</a>
|
||||
* website.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
package com.thetransactioncompany.jsonrpc2.server;
|
||||
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Notification;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
|
||||
|
||||
|
||||
/**
|
||||
* Dispatcher for JSON-RPC 2.0 requests and notifications. This class is
|
||||
* tread-safe.
|
||||
*
|
||||
* <p>Use the {@code register()} methods to add a request or notification
|
||||
* handler for an RPC method.
|
||||
*
|
||||
* <p>Use the {@code process()} methods to have an incoming request or
|
||||
* notification processed by the matching handler.
|
||||
*
|
||||
* <p>The {@code reportProcTime()} method enables reporting of request
|
||||
* processing time (in microseconds) by appending a non-standard "xProcTime"
|
||||
* attribute to the resulting JSON-RPC 2.0 response message.
|
||||
*
|
||||
* <p>Example:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "result" : "xyz",
|
||||
* "id" : 1,
|
||||
* "jsonrpc" : "2.0",
|
||||
* "xProcTime" : "189 us"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>Note: The dispatch(...) methods were deprecated in version 1.7. Use
|
||||
* process(...) instead.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class Dispatcher implements RequestHandler, NotificationHandler {
|
||||
|
||||
|
||||
/**
|
||||
* Hashtable of request name / handler pairs.
|
||||
*/
|
||||
private final Hashtable<String,RequestHandler> requestHandlers;
|
||||
|
||||
|
||||
/**
|
||||
* Hashtable of notification name / handler pairs.
|
||||
*/
|
||||
private final Hashtable<String,NotificationHandler> notificationHandlers;
|
||||
|
||||
|
||||
/**
|
||||
* Controls reporting of request processing time by appending a
|
||||
* non-standard "xProcTime" attribute to the JSON-RPC 2.0 response.
|
||||
*/
|
||||
private boolean reportProcTime = false;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new dispatcher with no registered handlers.
|
||||
*/
|
||||
public Dispatcher() {
|
||||
|
||||
requestHandlers = new Hashtable<String,RequestHandler>();
|
||||
notificationHandlers = new Hashtable<String,NotificationHandler>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a new JSON-RPC 2.0 request handler.
|
||||
*
|
||||
* @param handler The request handler to register. Must not be
|
||||
* {@code null}.
|
||||
*
|
||||
* @throws IllegalArgumentException On attempting to register a handler
|
||||
* that duplicates an existing request
|
||||
* name.
|
||||
*/
|
||||
public void register(final RequestHandler handler) {
|
||||
|
||||
for (String name: handler.handledRequests()) {
|
||||
|
||||
if (requestHandlers.containsKey(name))
|
||||
throw new IllegalArgumentException("Cannot register a duplicate JSON-RPC 2.0 handler for request " + name);
|
||||
|
||||
requestHandlers.put(name, handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a new JSON-RPC 2.0 notification handler.
|
||||
*
|
||||
* @param handler The notification handler to register. Must not be
|
||||
* {@code null}.
|
||||
*
|
||||
* @throws IllegalArgumentException On attempting to register a handler
|
||||
* that duplicates an existing
|
||||
* notification name.
|
||||
*/
|
||||
public void register(final NotificationHandler handler) {
|
||||
|
||||
for (String name: handler.handledNotifications()) {
|
||||
|
||||
if (notificationHandlers.containsKey(name))
|
||||
throw new IllegalArgumentException("Cannot register a duplicate JSON-RPC 2.0 handler for notification " + name);
|
||||
|
||||
notificationHandlers.put(name, handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String[] handledRequests() {
|
||||
|
||||
java.util.Set<String> var = requestHandlers.keySet();
|
||||
return var.toArray(new String[var.size()]);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String[] handledNotifications() {
|
||||
|
||||
java.util.Set<String> var = notificationHandlers.keySet();
|
||||
return var.toArray(new String[var.size()]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the handler for the specified JSON-RPC 2.0 request name.
|
||||
*
|
||||
* @param requestName The request name to lookup.
|
||||
*
|
||||
* @return The corresponding request handler or {@code null} if none
|
||||
* was found.
|
||||
*/
|
||||
public RequestHandler getRequestHandler(final String requestName) {
|
||||
|
||||
return requestHandlers.get(requestName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the handler for the specified JSON-RPC 2.0 notification name.
|
||||
*
|
||||
* @param notificationName The notification name to lookup.
|
||||
*
|
||||
* @return The corresponding notification handler or {@code null} if
|
||||
* none was found.
|
||||
*/
|
||||
public NotificationHandler getNotificationHandler(final String notificationName) {
|
||||
|
||||
return notificationHandlers.get(notificationName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public JSONRPC2Response dispatch(final JSONRPC2Request request, final MessageContext requestCtx) {
|
||||
|
||||
return process(request, requestCtx);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JSONRPC2Response process(final JSONRPC2Request request, final MessageContext requestCtx) {
|
||||
|
||||
long startNanosec = 0;
|
||||
|
||||
// Measure request processing time?
|
||||
if (reportProcTime)
|
||||
startNanosec = System.nanoTime();
|
||||
|
||||
|
||||
final String method = request.getMethod();
|
||||
|
||||
RequestHandler handler = getRequestHandler(method);
|
||||
|
||||
if (handler == null) {
|
||||
|
||||
// We didn't find a handler for the requested RPC
|
||||
|
||||
Object id = request.getID();
|
||||
|
||||
return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, id);
|
||||
}
|
||||
|
||||
// Process the request
|
||||
|
||||
JSONRPC2Response response = handler.process(request, requestCtx);
|
||||
|
||||
if (reportProcTime) {
|
||||
|
||||
final long procTimeNanosec = System.nanoTime() - startNanosec;
|
||||
|
||||
response.appendNonStdAttribute("xProcTime", procTimeNanosec / 1000 + " us");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public void dispatch(final JSONRPC2Notification notification, final MessageContext notificationCtx) {
|
||||
|
||||
process(notification, notificationCtx);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void process(final JSONRPC2Notification notification, final MessageContext notificationCtx) {
|
||||
|
||||
final String method = notification.getMethod();
|
||||
|
||||
NotificationHandler handler = getNotificationHandler(method);
|
||||
|
||||
if (handler == null) {
|
||||
|
||||
// We didn't find a handler for the requested RPC
|
||||
return;
|
||||
}
|
||||
|
||||
// Process the notification
|
||||
|
||||
handler.process(notification, notificationCtx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Controls reporting of request processing time by appending a
|
||||
* non-standard "xProcTime" attribute to the JSON-RPC 2.0 response.
|
||||
* Reporting is disabled by default.
|
||||
*
|
||||
* @param enable {@code true} to enable proccessing time reporting,
|
||||
* {@code false} to disable it.
|
||||
*/
|
||||
public void reportProcTime(final boolean enable) {
|
||||
|
||||
reportProcTime = enable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if reporting of request processing time is
|
||||
* enabled. See the {@link #reportProcTime} description for more
|
||||
* information.
|
||||
*
|
||||
* @return {@code true} if reporting of request processing time is
|
||||
* enabled, else {@code false}.
|
||||
*/
|
||||
public boolean reportsProcTime() {
|
||||
|
||||
return reportProcTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
package com.thetransactioncompany.jsonrpc2.server;
|
||||
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.URLConnection;
|
||||
import java.security.Principal;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
|
||||
/**
|
||||
* Context information about JSON-RPC 2.0 request and notification messages.
|
||||
* This class is immutable.
|
||||
*
|
||||
* <ul>
|
||||
* <li>The client's host name.
|
||||
* <li>The client's IP address.
|
||||
* <li>Whether the request / notification was transmitted securely (e.g.
|
||||
* via HTTPS).
|
||||
* <li>The client principal(s) (user), if authenticated.
|
||||
* </ul>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class MessageContext {
|
||||
|
||||
|
||||
/**
|
||||
* The client hostname, {@code null} if none was specified.
|
||||
*/
|
||||
private String clientHostName = null;
|
||||
|
||||
|
||||
/**
|
||||
* The client IP address, {@code null} if none was specified.
|
||||
*/
|
||||
private String clientInetAddress = null;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates whether the request was received over a secure channel
|
||||
* (typically HTTPS).
|
||||
*/
|
||||
private boolean secure = false;
|
||||
|
||||
|
||||
/**
|
||||
* The authenticated client principals, {@code null} if none were
|
||||
* specified.
|
||||
*/
|
||||
private Principal[] principals = null;
|
||||
|
||||
|
||||
/**
|
||||
* Minimal implementation of the {@link java.security.Principal}
|
||||
* interface.
|
||||
*/
|
||||
public class BasicPrincipal implements Principal {
|
||||
|
||||
/**
|
||||
* The principal name.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new principal.
|
||||
*
|
||||
* @param name The principal name, must not be {@code null} or
|
||||
* empty string.
|
||||
*
|
||||
* @throws IllegalArgumentException On a {@code null} or empty
|
||||
* principal name.
|
||||
*/
|
||||
public BasicPrincipal(final String name) {
|
||||
|
||||
if (name == null || name.trim().isEmpty())
|
||||
throw new IllegalArgumentException("The principal name must be defined");
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks for equality.
|
||||
*
|
||||
* @param another The object to compare to.
|
||||
*/
|
||||
public boolean equals(final Object another) {
|
||||
|
||||
return another != null &&
|
||||
another instanceof Principal &&
|
||||
((Principal)another).getName().equals(this.getName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a hash code for this principal.
|
||||
*
|
||||
* @return The hash code.
|
||||
*/
|
||||
public int hashCode() {
|
||||
|
||||
return getName().hashCode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the principal name.
|
||||
*
|
||||
* @return The principal name.
|
||||
*/
|
||||
public String getName() {
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context.
|
||||
*
|
||||
* @param clientHostName The client host name, {@code null} if
|
||||
* unknown.
|
||||
* @param clientInetAddress The client IP address, {@code null} if
|
||||
* unknown.
|
||||
* @param secure Specifies a request received over HTTPS.
|
||||
* @param principalName Specifies the authenticated client principle
|
||||
* name, {@code null} if unknown. The name must
|
||||
* not be an empty or blank string.
|
||||
*/
|
||||
public MessageContext(final String clientHostName,
|
||||
final String clientInetAddress,
|
||||
final boolean secure,
|
||||
final String principalName) {
|
||||
|
||||
this.clientHostName = clientHostName;
|
||||
this.clientInetAddress = clientInetAddress;
|
||||
this.secure = secure;
|
||||
|
||||
if (principalName != null) {
|
||||
principals = new Principal[1];
|
||||
principals[0] = new BasicPrincipal(principalName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context.
|
||||
*
|
||||
* @param clientHostName The client host name, {@code null} if
|
||||
* unknown.
|
||||
* @param clientInetAddress The client IP address, {@code null} if
|
||||
* unknown.
|
||||
* @param secure Specifies a request received over HTTPS.
|
||||
* @param principalNames Specifies the authenticated client principle
|
||||
* names, {@code null} if unknown. The names
|
||||
* must not be an empty or blank string.
|
||||
*/
|
||||
public MessageContext(final String clientHostName,
|
||||
final String clientInetAddress,
|
||||
final boolean secure,
|
||||
final String[] principalNames) {
|
||||
|
||||
this.clientHostName = clientHostName;
|
||||
this.clientInetAddress = clientInetAddress;
|
||||
this.secure = secure;
|
||||
|
||||
if (principalNames != null) {
|
||||
principals = new Principal[principalNames.length];
|
||||
|
||||
for (int i=0; i < principals.length; i++)
|
||||
principals[0] = new BasicPrincipal(principalNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context. No
|
||||
* authenticated client principal is specified.
|
||||
*
|
||||
* @param clientHostName The client host name, {@code null} if
|
||||
* unknown.
|
||||
* @param clientInetAddress The client IP address, {@code null} if
|
||||
* unknown.
|
||||
* @param secure Specifies a request received over HTTPS.
|
||||
*/
|
||||
public MessageContext(final String clientHostName,
|
||||
final String clientInetAddress,
|
||||
final boolean secure) {
|
||||
|
||||
this.clientHostName = clientHostName;
|
||||
this.clientInetAddress = clientInetAddress;
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context. Indicates
|
||||
* an insecure transport (plain HTTP) and no authenticated client
|
||||
* principal.
|
||||
*
|
||||
* @param clientHostName The client host name, {@code null} if
|
||||
* unknown.
|
||||
* @param clientInetAddress The client IP address, {@code null} if
|
||||
* unknown.
|
||||
*/
|
||||
public MessageContext(final String clientHostName,
|
||||
final String clientInetAddress) {
|
||||
|
||||
this.clientHostName = clientHostName;
|
||||
this.clientInetAddress = clientInetAddress;
|
||||
this.secure = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context. Indicates
|
||||
* an insecure transport (plain HTTP) and no authenticated client
|
||||
* principal. Not client host name / IP is specified.
|
||||
*/
|
||||
public MessageContext() {
|
||||
|
||||
this.secure = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context from the
|
||||
* specified HTTP request.
|
||||
*
|
||||
* @param httpRequest The HTTP request.
|
||||
*/
|
||||
public MessageContext(final HttpServletRequest httpRequest) {
|
||||
|
||||
clientInetAddress = httpRequest.getRemoteAddr();
|
||||
|
||||
clientHostName = httpRequest.getRemoteHost();
|
||||
|
||||
if (clientHostName != null && clientHostName.equals(clientInetAddress))
|
||||
clientHostName = null; // not resolved actually
|
||||
|
||||
secure = httpRequest.isSecure();
|
||||
|
||||
X509Certificate[] certs = (X509Certificate[])httpRequest.getAttribute("javax.servlet.request.X509Certificate");
|
||||
|
||||
if (certs != null && certs.length > 0) {
|
||||
|
||||
principals = new Principal[certs.length];
|
||||
|
||||
for (int i=0; i < principals.length; i++)
|
||||
principals[i] = certs[i].getSubjectX500Principal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context from the
|
||||
* specified URL connection. Use this constructor in cases when the
|
||||
* HTTP server is the origin of the JSON-RPC 2.0 requests /
|
||||
* notifications. If the IP address of the HTTP server cannot be
|
||||
* resolved {@link #getClientInetAddress} will return {@code null}.
|
||||
*
|
||||
* @param connection The URL connection, must be established and not
|
||||
* {@code null}.
|
||||
*/
|
||||
public MessageContext(final URLConnection connection) {
|
||||
|
||||
clientHostName = connection.getURL().getHost();
|
||||
|
||||
InetAddress ip = null;
|
||||
|
||||
if (clientHostName != null) {
|
||||
|
||||
try {
|
||||
ip = InetAddress.getByName(clientHostName);
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
// UnknownHostException, SecurityException
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
if (ip != null)
|
||||
clientInetAddress = ip.getHostAddress();
|
||||
|
||||
|
||||
if (connection instanceof HttpsURLConnection) {
|
||||
|
||||
secure = true;
|
||||
|
||||
HttpsURLConnection httpsConnection = (HttpsURLConnection)connection;
|
||||
|
||||
Principal prn = null;
|
||||
|
||||
try {
|
||||
prn = httpsConnection.getPeerPrincipal();
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
// SSLPeerUnverifiedException, IllegalStateException
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (prn != null) {
|
||||
|
||||
principals = new Principal[1];
|
||||
principals[0] = prn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the host name of the client that sent the request /
|
||||
* notification.
|
||||
*
|
||||
* @return The client host name, {@code null} if unknown.
|
||||
*/
|
||||
public String getClientHostName() {
|
||||
|
||||
return clientHostName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the IP address of the client that sent the request /
|
||||
* notification.
|
||||
*
|
||||
* @return The client IP address, {@code null} if unknown.
|
||||
*/
|
||||
public String getClientInetAddress() {
|
||||
|
||||
return clientInetAddress;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicates whether the request / notification was received over a
|
||||
* secure HTTPS connection.
|
||||
*
|
||||
* @return {@code true} If the request was received over HTTPS,
|
||||
* {@code false} if it was received over plain HTTP.
|
||||
*/
|
||||
public boolean isSecure() {
|
||||
|
||||
return secure;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first authenticated client principal, {@code null} if
|
||||
* none.
|
||||
*
|
||||
* @return The first client principal, {@code null} if none.
|
||||
*/
|
||||
public Principal getPrincipal() {
|
||||
|
||||
if (principals != null)
|
||||
return principals[0];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the authenticated client principals, {@code null} if
|
||||
* none.
|
||||
*
|
||||
* @return The client principals, {@code null} if none.
|
||||
*/
|
||||
public Principal[] getPrincipals() {
|
||||
|
||||
return principals;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first authenticated client principal name, {@code null}
|
||||
* if none.
|
||||
*
|
||||
* @return The first client principal name, {@code null} if none.
|
||||
*/
|
||||
public String getPrincipalName() {
|
||||
|
||||
if (principals != null)
|
||||
return principals[0].getName();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the authenticated client principal names, {@code null}
|
||||
* if none.
|
||||
*
|
||||
* @return The client principal names, {@code null} if none.
|
||||
*/
|
||||
public String[] getPrincipalNames() {
|
||||
|
||||
String[] names = new String[principals.length];
|
||||
|
||||
for (int i=0; i < names.length; i++)
|
||||
names[i] = principals[i].getName();
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String s = "[host=" + clientHostName + " hostIP=" + clientInetAddress + " secure=" + secure;
|
||||
|
||||
if (principals != null) {
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (Principal p: principals)
|
||||
s += " principal[" + (i++) + "]=" + p;
|
||||
}
|
||||
|
||||
return s + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.thetransactioncompany.jsonrpc2.server;
|
||||
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Notification;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for handling JSON-RPC 2.0 notifications.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public interface NotificationHandler {
|
||||
|
||||
|
||||
/**
|
||||
* Gets the names of the handled JSON-RPC 2.0 notification methods.
|
||||
*
|
||||
* @return The names of the handled JSON-RPC 2.0 notification methods.
|
||||
*/
|
||||
public String[] handledNotifications();
|
||||
|
||||
|
||||
/**
|
||||
* Processes a JSON-RPC 2.0 notification.
|
||||
*
|
||||
* <p>Note that JSON-RPC 2.0 notifications don't produce a response!
|
||||
*
|
||||
* @param notification A valid JSON-RPC 2.0 notification instance.
|
||||
* Must not be {@code null}.
|
||||
* @param notificationCtx Context information about the notification
|
||||
* message, may be {@code null} if undefined.
|
||||
*/
|
||||
public void process(final JSONRPC2Notification notification, final MessageContext notificationCtx);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.thetransactioncompany.jsonrpc2.server;
|
||||
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for handling JSON-RPC 2.0 requests.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public interface RequestHandler {
|
||||
|
||||
|
||||
/**
|
||||
* Gets the names of the handled JSON-RPC 2.0 request methods.
|
||||
*
|
||||
* @return The names of the handled JSON-RPC 2.0 request methods.
|
||||
*/
|
||||
public String[] handledRequests();
|
||||
|
||||
|
||||
/**
|
||||
* Processes a JSON-RPC 2.0 request.
|
||||
*
|
||||
* @param request A valid JSON-RPC 2.0 request instance. Must not be
|
||||
* {@code null}.
|
||||
* @param requestCtx Context information about the request message, may
|
||||
* be {@code null} if undefined.
|
||||
*
|
||||
* @return The resulting JSON-RPC 2.0 response. It indicates success
|
||||
* or an error, such as METHOD_NOT_FOUND.
|
||||
*/
|
||||
public JSONRPC2Response process(final JSONRPC2Request request, final MessageContext requestCtx);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Simple server framework for processing JSON-RPC 2.0 requests and
|
||||
* notifications.
|
||||
*
|
||||
* <p>Usage:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Implement {@link com.thetransactioncompany.jsonrpc2.server.RequestHandler request}
|
||||
* and / or {@link com.thetransactioncompany.jsonrpc2.server.NotificationHandler notification}
|
||||
* handlers for the various expected JSON-RPC 2.0 messages. A handler
|
||||
* may process one or more request/notification methods (identified by
|
||||
* method name).
|
||||
* <li>Create a new {@link com.thetransactioncompany.jsonrpc2.server.Dispatcher}
|
||||
* and register the handlers with it.
|
||||
* <li>Pass the received JSON-RPC 2.0 requests and notifications to the
|
||||
* appropriate {@code Dispatcher.dispatch(...)} method, then, if the
|
||||
* message is a request, pass the resulting JSON-RPC 2.0 response back
|
||||
* to the client.
|
||||
* </ol>
|
||||
*
|
||||
* <p>Direct package dependencies:
|
||||
*
|
||||
* <ul>
|
||||
* <li><b><a href="http://software.dzhuvinov.com/json-rpc-2.0-base.html">JSON-RPC 2.0 Base</a></b>
|
||||
* [<i>com.thetransactioncompany.jsonrpc2</i>] to construct and represent
|
||||
* JSON-RPC 2.0 messages.
|
||||
* <li><b>Java Servlet API</b> [<i>javax.servlet.http</i>] for constructing
|
||||
* {@link com.thetransactioncompany.jsonrpc2.server.MessageContext}
|
||||
* objects from HTTP servlet requests.
|
||||
* </ul>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
package com.thetransactioncompany.jsonrpc2.server;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
package com.thetransactioncompany.jsonrpc2.util;
|
||||
|
||||
|
||||
/**
|
||||
* The base abstract class for the JSON-RPC 2.0 parameter retrievers.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public abstract class ParamsRetriever {
|
||||
|
||||
|
||||
/**
|
||||
* Returns the parameter count.
|
||||
*
|
||||
* @return The parameters count.
|
||||
*/
|
||||
public abstract int size();
|
||||
|
||||
|
||||
/**
|
||||
* Matches a string against an array of acceptable values.
|
||||
*
|
||||
* @param input The string to match.
|
||||
* @param enumStrings The acceptable string values. Must not be
|
||||
* {@code null}.
|
||||
* @param ignoreCase {@code true} for a case insensitive match.
|
||||
*
|
||||
* @return The matching string value, {@code null} if no match was
|
||||
* found.
|
||||
*/
|
||||
protected static String getEnumStringMatch(final String input,
|
||||
final String[] enumStrings,
|
||||
final boolean ignoreCase) {
|
||||
|
||||
for (final String en: enumStrings) {
|
||||
|
||||
if (ignoreCase) {
|
||||
if (en.equalsIgnoreCase(input))
|
||||
return en;
|
||||
}
|
||||
else {
|
||||
if (en.equals(input))
|
||||
return en;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Matches a string against an enumeration of acceptable values.
|
||||
*
|
||||
* @param input The string to match.
|
||||
* @param enumClass The enumeration class specifying the acceptable
|
||||
* string values. Must not be {@code null}.
|
||||
* @param ignoreCase {@code true} for a case insensitive match.
|
||||
*
|
||||
* @return The matching enumeration constant, {@code null} if no match
|
||||
* was found.
|
||||
*/
|
||||
protected static <T extends Enum<T>> T getEnumStringMatch(final String input,
|
||||
final Class<T> enumClass,
|
||||
final boolean ignoreCase) {
|
||||
|
||||
for (T en: enumClass.getEnumConstants()) {
|
||||
|
||||
if (ignoreCase) {
|
||||
if (en.toString().equalsIgnoreCase(input))
|
||||
return en;
|
||||
}
|
||||
else {
|
||||
if (en.toString().equals(input))
|
||||
return en;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Utility classes for typed retrieval of JSON-RPC 2.0 request parameters on the
|
||||
* server side.
|
||||
*
|
||||
* <p>The following parameter type conversion choices are available:
|
||||
*
|
||||
* <ul>
|
||||
* <li>JSON true/false to Java {@code boolean}
|
||||
* <li>JSON number to Java {@code int}, {@code long}, {@code float} or
|
||||
* {@code double}
|
||||
* <li>JSON string to {@code java.lang.String}
|
||||
* <li>Predefined (enumerated) JSON string to a Java {@code enum} constant
|
||||
* or {@code java.lang.String}
|
||||
* <li>JSON array to Java {@code boolean[]}, {@code int[]}, {@code long[]},
|
||||
* {@code float[]}, {@code double[]} or {@code string[]} array, or
|
||||
* to mixed type {@code java.util.List}
|
||||
* <li>JSON object to {@code java.util.Map}
|
||||
* </ul>
|
||||
*
|
||||
* <p>If a parameter cannot be retrieved, either because it's missing or
|
||||
* is of the wrong type, a standard
|
||||
* {@link com.thetransactioncompany.jsonrpc2.JSONRPC2Error#INVALID_PARAMS}
|
||||
* exception is thrown.
|
||||
*
|
||||
* <p>There are two concrete classes:
|
||||
*
|
||||
* <ul>
|
||||
* <li>The {@link com.thetransactioncompany.jsonrpc2.util.PositionalParamsRetriever}
|
||||
* class is for extracting <em>positional parameters</em> (packed in a
|
||||
* JSON array).
|
||||
* <li>The {@link com.thetransactioncompany.jsonrpc2.util.NamedParamsRetriever}
|
||||
* class is for extracting <em>named parameters</em> (packed in a JSON
|
||||
* object).
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* <p><b>Package dependencies:</b> The classes in this package depend on the
|
||||
* sister {@link com.thetransactioncompany.jsonrpc2} package.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
package com.thetransactioncompany.jsonrpc2.util;
|
||||
|
||||
|
||||
129
apps/i2pcontrol/java/net/i2p/i2pcontrol/HostCheckHandler.java
Normal file
129
apps/i2pcontrol/java/net/i2p/i2pcontrol/HostCheckHandler.java
Normal file
@@ -0,0 +1,129 @@
|
||||
package net.i2p.i2pcontrol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
|
||||
/**
|
||||
* Block certain Host headers to prevent DNS rebinding attacks.
|
||||
*
|
||||
* This Handler wraps the ContextHandlerCollection, which handles
|
||||
* all the webapps (not just routerconsole).
|
||||
* Therefore, this protects all the webapps.
|
||||
*
|
||||
* This class is NOT used for the webapp or the bare ServerSocket implementation.
|
||||
*
|
||||
* @since 0.12 copied from routerconsole
|
||||
*/
|
||||
public class HostCheckHandler extends HandlerWrapper
|
||||
{
|
||||
private final I2PAppContext _context;
|
||||
private final Set<String> _listenHosts;
|
||||
|
||||
/**
|
||||
* MUST call setListenHosts() afterwards.
|
||||
*/
|
||||
public HostCheckHandler(I2PAppContext ctx) {
|
||||
super();
|
||||
_context = ctx;
|
||||
_listenHosts = new HashSet<String>(8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the legal hosts.
|
||||
* Not synched. Call this BEFORE starting.
|
||||
* If empty, all are allowed.
|
||||
*
|
||||
* @param hosts contains hostnames or IPs. But we allow all IPs anyway.
|
||||
*/
|
||||
public void setListenHosts(Set<String> hosts) {
|
||||
_listenHosts.clear();
|
||||
_listenHosts.addAll(hosts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Block by Host header, pass everything else to the delegate.
|
||||
*/
|
||||
public void handle(String pathInContext,
|
||||
Request baseRequest,
|
||||
HttpServletRequest httpRequest,
|
||||
HttpServletResponse httpResponse)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
|
||||
String host = httpRequest.getHeader("Host");
|
||||
if (!allowHost(host)) {
|
||||
Log log = _context.logManager().getLog(HostCheckHandler.class);
|
||||
host = DataHelper.stripHTML(getHost(host));
|
||||
String s = "Console request denied.\n" +
|
||||
" To allow access using the hostname \"" + host + "\", add the line \"" +
|
||||
I2PControlController.PROP_ALLOWED_HOSTS + '=' + host +
|
||||
"\" to I2PControl.conf and restart.";
|
||||
log.logAlways(Log.WARN, s);
|
||||
httpResponse.sendError(403, s);
|
||||
return;
|
||||
}
|
||||
|
||||
super.handle(pathInContext, baseRequest, httpRequest, httpResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we allow a request with this Host header?
|
||||
*
|
||||
* ref: https://en.wikipedia.org/wiki/DNS_rebinding
|
||||
*
|
||||
* @param host the HTTP Host header, null ok
|
||||
* @return true if OK
|
||||
*/
|
||||
private boolean allowHost(String host) {
|
||||
if (host == null)
|
||||
return true;
|
||||
// common cases
|
||||
if (host.equals("127.0.0.1:7650") ||
|
||||
host.equals("localhost:7650"))
|
||||
return true;
|
||||
// all allowed?
|
||||
if (_listenHosts.isEmpty())
|
||||
return true;
|
||||
host = getHost(host);
|
||||
if (_listenHosts.contains(host))
|
||||
return true;
|
||||
// allow all IP addresses
|
||||
if (InetAddressUtils.isIPv4Address(host) || InetAddressUtils.isIPv6Address(host))
|
||||
return true;
|
||||
//System.out.println(host + " not found in " + s);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip [] and port from a host header
|
||||
*
|
||||
* @param host the HTTP Host header non-null
|
||||
*/
|
||||
private static String getHost(String host) {
|
||||
if (host.startsWith("[")) {
|
||||
host = host.substring(1);
|
||||
int brack = host.indexOf(']');
|
||||
if (brack >= 0)
|
||||
host = host.substring(0, brack);
|
||||
} else {
|
||||
int colon = host.indexOf(':');
|
||||
if (colon >= 0)
|
||||
host = host.substring(0, colon);
|
||||
}
|
||||
return host;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
package net.i2p.i2pcontrol;
|
||||
/*
|
||||
* Copyright 2010 hottuna (dev@robertfoss.se)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.app.ClientAppState;
|
||||
import static net.i2p.app.ClientAppState.*;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.app.RouterApp;
|
||||
import net.i2p.util.I2PSSLSocketFactory;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.PortMapper;
|
||||
|
||||
import net.i2p.i2pcontrol.security.KeyStoreProvider;
|
||||
import net.i2p.i2pcontrol.security.SecurityManager;
|
||||
import net.i2p.i2pcontrol.servlets.JSONRPC2Servlet;
|
||||
import net.i2p.i2pcontrol.servlets.configuration.ConfigurationManager;
|
||||
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.KeyStore;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
|
||||
/**
|
||||
* This handles the starting and stopping of Jetty
|
||||
* from a single static class so it can be called via clients.config.
|
||||
*
|
||||
* This makes installation of a new eepsite a turnkey operation.
|
||||
*
|
||||
* Usage: I2PControlController -d $PLUGIN [start|stop]
|
||||
*
|
||||
* This class is NOT used for the webapp or the bare ServerSocket implementation.
|
||||
*
|
||||
* @author hottuna
|
||||
*/
|
||||
public class I2PControlController implements RouterApp {
|
||||
// non-null
|
||||
private final I2PAppContext _appContext;
|
||||
// warning, null in app context
|
||||
private final RouterContext _context;
|
||||
private final ClientAppManager _mgr;
|
||||
private final Log _log;
|
||||
private final String _pluginDir;
|
||||
private final ConfigurationManager _conf;
|
||||
private final KeyStoreProvider _ksp;
|
||||
private final SecurityManager _secMan;
|
||||
private final Server _server;
|
||||
private ClientAppState _state = UNINITIALIZED;
|
||||
// only for main()
|
||||
private static I2PControlController _instance;
|
||||
static final String PROP_ALLOWED_HOSTS = "i2pcontrol.allowedhosts";
|
||||
private static final String SVC_HTTPS_I2PCONTROL = "https_i2pcontrol";
|
||||
private static final int DEFAULT_PORT = 7650;
|
||||
|
||||
/**
|
||||
* RouterApp (new way)
|
||||
*/
|
||||
public I2PControlController(RouterContext ctx, ClientAppManager mgr, String args[]) {
|
||||
_appContext = _context = ctx;
|
||||
_mgr = mgr;
|
||||
_log = _appContext.logManager().getLog(I2PControlController.class);
|
||||
File pluginDir = new File(_context.getAppDir(), "plugins/I2PControl");
|
||||
_pluginDir = pluginDir.getAbsolutePath();
|
||||
_conf = new ConfigurationManager(_appContext, pluginDir, true);
|
||||
_ksp = new KeyStoreProvider(_pluginDir);
|
||||
_secMan = new SecurityManager(_appContext, _ksp, _conf);
|
||||
_server = buildServer();
|
||||
_state = INITIALIZED;
|
||||
}
|
||||
|
||||
/**
|
||||
* From main() (old way)
|
||||
*/
|
||||
public I2PControlController(File pluginDir) {
|
||||
_appContext = I2PAppContext.getGlobalContext();
|
||||
if (_appContext instanceof RouterContext)
|
||||
_context = (RouterContext) _appContext;
|
||||
else
|
||||
_context = null;
|
||||
_mgr = null;
|
||||
_log = _appContext.logManager().getLog(I2PControlController.class);
|
||||
_pluginDir = pluginDir.getAbsolutePath();
|
||||
_conf = new ConfigurationManager(_appContext, pluginDir, true);
|
||||
_ksp = new KeyStoreProvider(_pluginDir);
|
||||
_secMan = new SecurityManager(_appContext, _ksp, _conf);
|
||||
_server = buildServer();
|
||||
_state = INITIALIZED;
|
||||
}
|
||||
|
||||
/////// ClientApp methods
|
||||
|
||||
public synchronized void startup() {
|
||||
changeState(STARTING);
|
||||
try {
|
||||
start(null);
|
||||
changeState(RUNNING);
|
||||
} catch (Exception e) {
|
||||
changeState(START_FAILED, "Failed to start", e);
|
||||
_log.error("Unable to start jetty server", e);
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void shutdown(String[] args) {
|
||||
if (_state == STOPPED)
|
||||
return;
|
||||
changeState(STOPPING);
|
||||
stop();
|
||||
changeState(STOPPED);
|
||||
}
|
||||
|
||||
public synchronized ClientAppState getState() {
|
||||
return _state;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "I2PControl";
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return "I2PControl";
|
||||
}
|
||||
|
||||
/////// end ClientApp methods
|
||||
|
||||
private void changeState(ClientAppState state) {
|
||||
changeState(state, null, null);
|
||||
}
|
||||
|
||||
private synchronized void changeState(ClientAppState state, String msg, Exception e) {
|
||||
_state = state;
|
||||
if (_mgr != null)
|
||||
_mgr.notify(this, state, msg, e);
|
||||
if (_context == null) {
|
||||
if (msg != null)
|
||||
System.out.println(state + ": " + msg);
|
||||
if (e != null)
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deprecated, use constructor
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
if (args.length != 3 || (!"-d".equals(args[0])))
|
||||
throw new IllegalArgumentException("Usage: PluginController -d $PLUGINDIR [start|stop]");
|
||||
|
||||
if ("start".equals(args[2])) {
|
||||
File pluginDir = new File(args[1]);
|
||||
if (!pluginDir.exists())
|
||||
throw new IllegalArgumentException("Plugin directory " + pluginDir.getAbsolutePath() + " does not exist");
|
||||
synchronized(I2PControlController.class) {
|
||||
if (_instance != null)
|
||||
throw new IllegalStateException();
|
||||
I2PControlController i2pcc = new I2PControlController(pluginDir);
|
||||
try {
|
||||
i2pcc.startup();
|
||||
_instance = i2pcc;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} else if ("stop".equals(args[2])) {
|
||||
synchronized(I2PControlController.class) {
|
||||
if (_instance != null) {
|
||||
_instance.shutdown(null);
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Usage: PluginController -d $PLUGINDIR [start|stop]");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private synchronized void start(String args[]) throws Exception {
|
||||
_appContext.logManager().getLog(JSONRPC2Servlet.class).setMinimumPriority(Log.DEBUG);
|
||||
_server.start();
|
||||
_context.portMapper().register(SVC_HTTPS_I2PCONTROL,
|
||||
_conf.getConf("i2pcontrol.listen.address", "127.0.0.1"),
|
||||
_conf.getConf("i2pcontrol.listen.port", DEFAULT_PORT));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Builds a new server. Used for changing ports during operation and such.
|
||||
* @return Server - A new server built from current configuration.
|
||||
*/
|
||||
private Connector buildDefaultListener(Server server) {
|
||||
Connector ssl = buildSslListener(server, _conf.getConf("i2pcontrol.listen.address", "127.0.0.1"),
|
||||
_conf.getConf("i2pcontrol.listen.port", DEFAULT_PORT));
|
||||
return ssl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds a new server. Used for changing ports during operation and such.
|
||||
*
|
||||
* Does NOT start the server. Must call start() on the returned server.
|
||||
*
|
||||
* @return Server - A new server built from current configuration.
|
||||
*/
|
||||
public Server buildServer() {
|
||||
Server server = new Server();
|
||||
Connector ssl = buildDefaultListener(server);
|
||||
server.addConnector(ssl);
|
||||
|
||||
ServletHandler sh = new ServletHandler();
|
||||
sh.addServletWithMapping(new ServletHolder(new JSONRPC2Servlet(_context, _secMan)), "/");
|
||||
HostCheckHandler hch = new HostCheckHandler(_appContext);
|
||||
Set<String> listenHosts = new HashSet<String>(8);
|
||||
// fix up the allowed hosts set (see HostCheckHandler)
|
||||
// empty set says all are valid
|
||||
String address = _conf.getConf("i2pcontrol.listen.address", "127.0.0.1");
|
||||
if (!(address.equals("0.0.0.0") ||
|
||||
address.equals("::") ||
|
||||
address.equals("0:0:0:0:0:0:0:0"))) {
|
||||
listenHosts.add("localhost");
|
||||
listenHosts.add("127.0.0.1");
|
||||
listenHosts.add("::1");
|
||||
listenHosts.add("0:0:0:0:0:0:0:1");
|
||||
String allowed = _conf.getConf(PROP_ALLOWED_HOSTS, "");
|
||||
if (!allowed.equals("")) {
|
||||
StringTokenizer tok = new StringTokenizer(allowed, " ,");
|
||||
while (tok.hasMoreTokens()) {
|
||||
listenHosts.add(tok.nextToken());
|
||||
}
|
||||
}
|
||||
}
|
||||
hch.setListenHosts(listenHosts);
|
||||
hch.setHandler(sh);
|
||||
server.getServer().setHandler(hch);
|
||||
|
||||
_conf.writeConfFile();
|
||||
return server;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a SSLListener with all the default options. The listener will use all the default options.
|
||||
* @param address - The address the listener will listen to.
|
||||
* @param port - The port the listener will listen to.
|
||||
* @return - Newly created listener
|
||||
*/
|
||||
private Connector buildSslListener(Server server, String address, int port) {
|
||||
int listeners = 0;
|
||||
if (server != null) {
|
||||
listeners = server.getConnectors().length;
|
||||
}
|
||||
|
||||
// the keystore path and password
|
||||
SslContextFactory sslFactory = new SslContextFactory(_ksp.getKeyStoreLocation());
|
||||
sslFactory.setKeyStorePassword(KeyStoreProvider.DEFAULT_KEYSTORE_PASSWORD);
|
||||
// the X.509 cert password (if not present, verifyKeyStore() returned false)
|
||||
sslFactory.setKeyManagerPassword(KeyStoreProvider.DEFAULT_CERTIFICATE_PASSWORD);
|
||||
sslFactory.addExcludeProtocols(I2PSSLSocketFactory.EXCLUDE_PROTOCOLS.toArray(
|
||||
new String[I2PSSLSocketFactory.EXCLUDE_PROTOCOLS.size()]));
|
||||
sslFactory.addExcludeCipherSuites(I2PSSLSocketFactory.EXCLUDE_CIPHERS.toArray(
|
||||
new String[I2PSSLSocketFactory.EXCLUDE_CIPHERS.size()]));
|
||||
|
||||
HttpConfiguration httpConfig = new HttpConfiguration();
|
||||
httpConfig.setSecureScheme("https");
|
||||
httpConfig.setSecurePort(port);
|
||||
httpConfig.addCustomizer(new SecureRequestCustomizer());
|
||||
// number of acceptors, (default) number of selectors
|
||||
ServerConnector ssl = new ServerConnector(server, 1, 0,
|
||||
new SslConnectionFactory(sslFactory, "http/1.1"),
|
||||
new HttpConnectionFactory(httpConfig));
|
||||
ssl.setHost(address);
|
||||
ssl.setPort(port);
|
||||
ssl.setIdleTimeout(90*1000); // default 10 sec
|
||||
// all with same name will use the same thread pool
|
||||
ssl.setName("I2PControl");
|
||||
|
||||
ssl.setName("SSL Listener-" + ++listeners);
|
||||
|
||||
return ssl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a listener to the server
|
||||
* If a listener listening to the same port as the provided listener
|
||||
* uses already exists within the server, replace the one already used by
|
||||
* the server with the provided listener.
|
||||
* @param listener
|
||||
* @throws Exception
|
||||
*/
|
||||
/****
|
||||
public synchronized void replaceListener(Connector listener) throws Exception {
|
||||
if (_server != null) {
|
||||
stopServer();
|
||||
}
|
||||
_server = buildServer(listener);
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* Get all listeners of the server.
|
||||
* @return
|
||||
*/
|
||||
/****
|
||||
public synchronized Connector[] getListeners() {
|
||||
if (_server != null) {
|
||||
return _server.getConnectors();
|
||||
}
|
||||
return new Connector[0];
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* Removes all listeners
|
||||
*/
|
||||
/****
|
||||
public synchronized void clearListeners() {
|
||||
if (_server != null) {
|
||||
for (Connector listen : getListeners()) {
|
||||
_server.removeConnector(listen);
|
||||
}
|
||||
}
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* Stop it
|
||||
*/
|
||||
private synchronized void stopServer()
|
||||
{
|
||||
try {
|
||||
if (_server != null) {
|
||||
_appContext.portMapper().unregister(SVC_HTTPS_I2PCONTROL);
|
||||
_server.stop();
|
||||
for (Connector listener : _server.getConnectors()) {
|
||||
listener.stop();
|
||||
}
|
||||
_server.destroy();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
_log.error("Stopping server", e);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void stop() {
|
||||
_conf.writeConfFile();
|
||||
_secMan.stopTimedEvents();
|
||||
stopServer();
|
||||
|
||||
/****
|
||||
// Get and stop all running threads
|
||||
ThreadGroup threadgroup = Thread.currentThread().getThreadGroup();
|
||||
Thread[] threads = new Thread[threadgroup.activeCount() + 3];
|
||||
threadgroup.enumerate(threads, true);
|
||||
for (Thread thread : threads) {
|
||||
if (thread != null) {//&& thread.isAlive()){
|
||||
thread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
for (Thread thread : threads) {
|
||||
if (thread != null) {
|
||||
System.out.println("Active thread: " + thread.getName());
|
||||
}
|
||||
}
|
||||
threadgroup.interrupt();
|
||||
|
||||
//Thread.currentThread().getThreadGroup().destroy();
|
||||
****/
|
||||
}
|
||||
|
||||
public String getPluginDir() {
|
||||
return _pluginDir;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package net.i2p.i2pcontrol;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class I2PControlVersion {
|
||||
/** The current version of I2PControl */
|
||||
public final static String VERSION = "0.12.0";
|
||||
|
||||
/** The current version of the I2PControl API being primarily being implemented */
|
||||
public final static int API_VERSION = 1;
|
||||
|
||||
/** The supported versions of the I2PControl API */
|
||||
public final static Set<Integer> SUPPORTED_API_VERSIONS;
|
||||
|
||||
static {
|
||||
SUPPORTED_API_VERSIONS = new HashSet<Integer>();
|
||||
SUPPORTED_API_VERSIONS.add(1);
|
||||
}
|
||||
}
|
||||
246
apps/i2pcontrol/java/net/i2p/i2pcontrol/SocketController.java
Normal file
246
apps/i2pcontrol/java/net/i2p/i2pcontrol/SocketController.java
Normal file
@@ -0,0 +1,246 @@
|
||||
package net.i2p.i2pcontrol;
|
||||
/*
|
||||
* Copyright 2010 hottuna (dev@robertfoss.se)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.security.KeyStore;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.app.ClientAppState;
|
||||
import static net.i2p.app.ClientAppState.*;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.app.RouterApp;
|
||||
import net.i2p.util.I2PSSLSocketFactory;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.PortMapper;
|
||||
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import net.i2p.i2pcontrol.security.KeyStoreProvider;
|
||||
import net.i2p.i2pcontrol.security.SecurityManager;
|
||||
import net.i2p.i2pcontrol.servlets.JSONRPC2Servlet;
|
||||
import net.i2p.i2pcontrol.servlets.configuration.ConfigurationManager;
|
||||
|
||||
|
||||
/**
|
||||
* This handles the starting and stopping of a ServerSocket
|
||||
* from a single static class so it can be called via clients.config.
|
||||
*
|
||||
* This class is NOT used for the webapp or the HTTP/HTTPS implementation.
|
||||
*
|
||||
* @author hottuna
|
||||
*/
|
||||
public class SocketController implements RouterApp {
|
||||
// non-null
|
||||
private final RouterContext _context;
|
||||
private final ClientAppManager _mgr;
|
||||
private final Log _log;
|
||||
private final String _pluginDir;
|
||||
private final ConfigurationManager _conf;
|
||||
private final KeyStoreProvider _ksp;
|
||||
private final SecurityManager _secMan;
|
||||
private ServerSocket _server;
|
||||
private final List<Socket> _listeners;
|
||||
private ClientAppState _state = UNINITIALIZED;
|
||||
// only for main()
|
||||
private static SocketController _instance;
|
||||
static final String PROP_ALLOWED_HOSTS = "i2pcontrol.allowedhosts";
|
||||
private static final String SVC_SKT_I2PCONTROL = "skt_i2pcontrol";
|
||||
private static final String SVC_SSL_I2PCONTROL = "skt_ssl_i2pcontrol";
|
||||
private static final int DEFAULT_PORT = 7640;
|
||||
|
||||
/**
|
||||
* RouterApp (new way)
|
||||
*/
|
||||
public SocketController(RouterContext ctx, ClientAppManager mgr, String args[]) throws IOException {
|
||||
_context = ctx;
|
||||
_mgr = mgr;
|
||||
_log = _context.logManager().getLog(SocketController.class);
|
||||
File pluginDir = new File(_context.getConfigDir(), "keystore/I2PControl");
|
||||
_pluginDir = pluginDir.getAbsolutePath();
|
||||
_conf = new ConfigurationManager(_context, pluginDir, true);
|
||||
_ksp = new KeyStoreProvider(_pluginDir);
|
||||
_secMan = new SecurityManager(_context, _ksp, _conf);
|
||||
_listeners = new ArrayList<Socket>(4);
|
||||
_state = INITIALIZED;
|
||||
}
|
||||
|
||||
/////// ClientApp methods
|
||||
|
||||
public synchronized void startup() {
|
||||
changeState(STARTING);
|
||||
try {
|
||||
start(null);
|
||||
changeState(RUNNING);
|
||||
} catch (Exception e) {
|
||||
changeState(START_FAILED, "Failed to start", e);
|
||||
_log.error("Unable to start socket server", e);
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void shutdown(String[] args) {
|
||||
if (_state == STOPPED)
|
||||
return;
|
||||
changeState(STOPPING);
|
||||
stop();
|
||||
changeState(STOPPED);
|
||||
}
|
||||
|
||||
public synchronized ClientAppState getState() {
|
||||
return _state;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "I2PControl-Socket";
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return "I2PControl-Socket";
|
||||
}
|
||||
|
||||
/////// end ClientApp methods
|
||||
|
||||
private void changeState(ClientAppState state) {
|
||||
changeState(state, null, null);
|
||||
}
|
||||
|
||||
private synchronized void changeState(ClientAppState state, String msg, Exception e) {
|
||||
_state = state;
|
||||
if (_mgr != null)
|
||||
_mgr.notify(this, state, msg, e);
|
||||
if (_context == null) {
|
||||
if (msg != null)
|
||||
System.out.println(state + ": " + msg);
|
||||
if (e != null)
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void start(String args[]) throws Exception {
|
||||
_context.logManager().getLog(JSONRPC2Servlet.class).setMinimumPriority(Log.DEBUG);
|
||||
_server = buildServer();
|
||||
_context.portMapper().register(SVC_SKT_I2PCONTROL,
|
||||
_conf.getConf("i2pcontrol.listen.address", "127.0.0.1"),
|
||||
_conf.getConf("i2pcontrol.listen.port", DEFAULT_PORT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new server. Used for changing ports during operation and such.
|
||||
*
|
||||
* Does NOT start the server. Must call start() on the returned server.
|
||||
*
|
||||
* @return Server - A new server built from current configuration.
|
||||
*/
|
||||
public ServerSocket buildServer() throws IOException {
|
||||
String address = _conf.getConf("i2pcontrol.listen.address", "127.0.0.1");
|
||||
int port = _conf.getConf("i2pcontrol.listen.port", DEFAULT_PORT);
|
||||
ServerSocket server = new ServerSocket(port, 0, InetAddress.getByName(address));
|
||||
_conf.writeConfFile();
|
||||
return server;
|
||||
}
|
||||
|
||||
private class Server implements Runnable {
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
Socket s = _server.accept();
|
||||
synchronized (SocketController.this) {
|
||||
_listeners.add(s);
|
||||
}
|
||||
new Handler(s);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.error("i2pcontrol server", ioe);
|
||||
} finally {
|
||||
synchronized (SocketController.this) {
|
||||
if (_server != null) {
|
||||
try { _server.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Handler implements Runnable {
|
||||
private final Socket s;
|
||||
|
||||
public Handler(Socket skt) { s = skt; }
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
|
||||
final JSONParser parser = new JSONParser();
|
||||
while (true) {
|
||||
Object o = parser.parse(reader);
|
||||
// TODO
|
||||
System.out.println("i2pcontrol got: " + o);
|
||||
}
|
||||
} catch (ParseException pe) {
|
||||
_log.error("i2pcontrol handler", pe);
|
||||
return;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("i2pcontrol handler", ioe);
|
||||
return;
|
||||
} finally {
|
||||
synchronized (SocketController.this) {
|
||||
_listeners.remove(s);
|
||||
}
|
||||
try { s.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop it
|
||||
*/
|
||||
private synchronized void stopServer()
|
||||
{
|
||||
try {
|
||||
if (_server != null) {
|
||||
_context.portMapper().unregister(SVC_SKT_I2PCONTROL);
|
||||
try {
|
||||
_server.close();
|
||||
} catch (IOException ioe) {}
|
||||
for (Socket listener : _listeners) {
|
||||
try {
|
||||
listener.close();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
_listeners.clear();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
_log.error("Stopping server", e);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void stop() {
|
||||
_conf.writeConfFile();
|
||||
_secMan.stopTimedEvents();
|
||||
stopServer();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package net.i2p.i2pcontrol.security;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
public class AuthToken {
|
||||
static final int VALIDITY_TIME = 1; // Measured in days
|
||||
private final SecurityManager _secMan;
|
||||
private final String id;
|
||||
private final Date expiry;
|
||||
|
||||
public AuthToken(SecurityManager secMan, String password) {
|
||||
_secMan = secMan;
|
||||
String hash = _secMan.getPasswdHash(password);
|
||||
this.id = _secMan.getHash(hash + Calendar.getInstance().getTimeInMillis());
|
||||
Calendar expiry = Calendar.getInstance();
|
||||
expiry.add(Calendar.DAY_OF_YEAR, VALIDITY_TIME);
|
||||
this.expiry = expiry.getTime();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the AuthToken has expired.
|
||||
* @return True if AuthToken hasn't expired. False in any other case.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return Calendar.getInstance().getTime().before(expiry);
|
||||
}
|
||||
|
||||
public String getExpiryTime() {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat();
|
||||
sdf.applyPattern("yyyy-MM-dd HH:mm:ss");
|
||||
return sdf.format(expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package net.i2p.i2pcontrol.security;
|
||||
|
||||
public class ExpiredAuthTokenException extends Exception {
|
||||
private static final long serialVersionUID = 2279019346592900289L;
|
||||
|
||||
private String expiryTime;
|
||||
|
||||
public ExpiredAuthTokenException(String str, String expiryTime) {
|
||||
super(str);
|
||||
this.expiryTime = expiryTime;
|
||||
}
|
||||
|
||||
public String getExpirytime() {
|
||||
return expiryTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.i2p.i2pcontrol.security;
|
||||
|
||||
public class InvalidAuthTokenException extends Exception {
|
||||
private static final long serialVersionUID = 7605321329341235577L;
|
||||
|
||||
public InvalidAuthTokenException(String str) {
|
||||
super(str);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package net.i2p.i2pcontrol.security;
|
||||
|
||||
import net.i2p.crypto.KeyStoreUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public class KeyStoreProvider {
|
||||
public static final String DEFAULT_CERTIFICATE_ALGORITHM_STRING = "RSA";
|
||||
public static final int DEFAULT_CERTIFICATE_KEY_LENGTH = 4096;
|
||||
public static final int DEFAULT_CERTIFICATE_VALIDITY = 365 * 10;
|
||||
public final static String DEFAULT_CERTIFICATE_DOMAIN = "localhost";
|
||||
public final static String DEFAULT_CERTIFICATE_ALIAS = "I2PControl CA";
|
||||
public static final String DEFAULT_KEYSTORE_NAME = "i2pcontrol.ks";
|
||||
public static final String DEFAULT_KEYSTORE_PASSWORD = KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD;
|
||||
public static final String DEFAULT_CERTIFICATE_PASSWORD = "nut'nfancy";
|
||||
private final String _pluginDir;
|
||||
private KeyStore _keystore;
|
||||
|
||||
public KeyStoreProvider(String pluginDir) {
|
||||
_pluginDir = pluginDir;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
KeyStoreUtil.createKeys(new File(getKeyStoreLocation()),
|
||||
DEFAULT_KEYSTORE_PASSWORD,
|
||||
DEFAULT_CERTIFICATE_ALIAS,
|
||||
DEFAULT_CERTIFICATE_DOMAIN,
|
||||
"i2pcontrol",
|
||||
DEFAULT_CERTIFICATE_VALIDITY,
|
||||
DEFAULT_CERTIFICATE_ALGORITHM_STRING,
|
||||
DEFAULT_CERTIFICATE_KEY_LENGTH,
|
||||
DEFAULT_CERTIFICATE_PASSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param password unused
|
||||
* @return null on failure
|
||||
*/
|
||||
public static X509Certificate readCert(KeyStore ks, String certAlias, String password) {
|
||||
try {
|
||||
X509Certificate cert = (X509Certificate) ks.getCertificate(certAlias);
|
||||
|
||||
if (cert == null) {
|
||||
throw new RuntimeException("Got null cert from keystore!");
|
||||
}
|
||||
|
||||
try {
|
||||
cert.verify(cert.getPublicKey());
|
||||
return cert;
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to verify caCert certificate against caCert");
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (KeyStoreException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param password for the keystore
|
||||
* @return null on failure
|
||||
*/
|
||||
/****
|
||||
public static X509Certificate readCert(File keyStoreFile, String certAlias, String password) {
|
||||
try {
|
||||
KeyStore ks = getDefaultKeyStore();
|
||||
ks.load(new FileInputStream(keyStoreFile), password.toCharArray());
|
||||
X509Certificate cert = (X509Certificate) ks.getCertificate(certAlias);
|
||||
|
||||
if (cert == null) {
|
||||
throw new RuntimeException("Got null cert from keystore!");
|
||||
}
|
||||
|
||||
try {
|
||||
cert.verify(cert.getPublicKey());
|
||||
return cert;
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to verify caCert certificate against caCert");
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("Couldn't read keystore from: " + keyStoreFile.toString());
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (CertificateException e) {
|
||||
e.printStackTrace();
|
||||
} catch (KeyStoreException e) {
|
||||
System.err.println("No certificate with alias: " + certAlias + " found.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* @param password for the key
|
||||
* @return null on failure, or throws RuntimeException...
|
||||
*/
|
||||
/****
|
||||
public static PrivateKey readPrivateKey(KeyStore ks, String alias, String password) {
|
||||
try {
|
||||
// load the key entry from the keystore
|
||||
Key key = ks.getKey(alias, password.toCharArray());
|
||||
|
||||
if (key == null) {
|
||||
throw new RuntimeException("Got null key from keystore!");
|
||||
}
|
||||
|
||||
PrivateKey privKey = (PrivateKey) key;
|
||||
return privKey;
|
||||
} catch (UnrecoverableKeyException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (KeyStoreException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* @return null on failure
|
||||
*/
|
||||
/****
|
||||
public static PrivateKey readPrivateKey(String alias, File keyStoreFile, String keyStorePassword, String keyPassword) {
|
||||
try {
|
||||
KeyStore ks = getDefaultKeyStore();
|
||||
ks.load(new FileInputStream(keyStoreFile), keyStorePassword.toCharArray());
|
||||
return readPrivateKey(ks, alias, keyStorePassword);
|
||||
} catch (CertificateException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Couldn't read keystore from: " + keyStoreFile.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* @return null on failure
|
||||
*/
|
||||
/****
|
||||
public static KeyStore writeCACertToKeyStore(KeyStore keyStore, String keyPassword, String alias, PrivateKey caPrivKey, X509Certificate caCert) {
|
||||
try {
|
||||
X509Certificate[] chain = new X509Certificate[1];
|
||||
chain[0] = caCert;
|
||||
|
||||
keyStore.setKeyEntry(alias, caPrivKey, keyPassword.toCharArray(), chain);
|
||||
File keyStoreFile = new File(I2PControlController.getPluginDir() + File.separator + DEFAULT_KEYSTORE_NAME);
|
||||
keyStore.store(new FileOutputStream(keyStoreFile), DEFAULT_KEYSTORE_PASSWORD.toCharArray());
|
||||
return keyStore;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (CertificateException e) {
|
||||
e.printStackTrace();
|
||||
} catch (KeyStoreException e) {
|
||||
e.printStackTrace();
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* @return null on failure
|
||||
*/
|
||||
public synchronized KeyStore getDefaultKeyStore() {
|
||||
if (_keystore == null) {
|
||||
File keyStoreFile = new File(getKeyStoreLocation());
|
||||
|
||||
try {
|
||||
_keystore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
if (keyStoreFile.exists()) {
|
||||
InputStream is = new FileInputStream(keyStoreFile);
|
||||
_keystore.load(is, DEFAULT_KEYSTORE_PASSWORD.toCharArray());
|
||||
return _keystore;
|
||||
}
|
||||
|
||||
initialize();
|
||||
if (keyStoreFile.exists()) {
|
||||
InputStream is = new FileInputStream(keyStoreFile);
|
||||
_keystore.load(is, DEFAULT_KEYSTORE_PASSWORD.toCharArray());
|
||||
return _keystore;
|
||||
} else {
|
||||
throw new IOException("KeyStore file " + keyStoreFile.getAbsolutePath() + " wasn't readable");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore. Not an issue. Let's just create a new keystore instead.
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return _keystore;
|
||||
}
|
||||
}
|
||||
|
||||
public String getKeyStoreLocation() {
|
||||
File keyStoreFile = new File(_pluginDir, DEFAULT_KEYSTORE_NAME);
|
||||
return keyStoreFile.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package net.i2p.i2pcontrol.security;
|
||||
/*
|
||||
* Copyright 2011 hottuna (dev@robertfoss.se)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
import org.mindrot.jbcrypt.BCrypt;
|
||||
|
||||
import net.i2p.i2pcontrol.servlets.configuration.ConfigurationManager;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Manage the password storing for I2PControl.
|
||||
*/
|
||||
public class SecurityManager {
|
||||
public final static String DEFAULT_AUTH_PASSWORD = "itoopie";
|
||||
private final HashMap<String, AuthToken> authTokens;
|
||||
private final SimpleTimer2.TimedEvent timer;
|
||||
private final KeyStore _ks;
|
||||
private final Log _log;
|
||||
private final ConfigurationManager _conf;
|
||||
private final I2PAppContext _context;
|
||||
|
||||
/**
|
||||
* @param ksp may be null (if webapp)
|
||||
*/
|
||||
public SecurityManager(I2PAppContext ctx, KeyStoreProvider ksp, ConfigurationManager conf) {
|
||||
_context = ctx;
|
||||
_conf = conf;
|
||||
_log = ctx.logManager().getLog(SecurityManager.class);
|
||||
authTokens = new HashMap<String, AuthToken>();
|
||||
|
||||
timer = new Sweeper();
|
||||
|
||||
_ks = ksp != null ? ksp.getDefaultKeyStore() : null;
|
||||
}
|
||||
|
||||
public void stopTimedEvents() {
|
||||
timer.cancel();
|
||||
synchronized (authTokens) {
|
||||
authTokens.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the X509Certificate of the server as a Base64 encoded string.
|
||||
* @return base64 encode of X509Certificate
|
||||
*/
|
||||
/**** unused and incorrectly uses I2P Base64. Switch to CertUtil.exportCert() if needed.
|
||||
public String getBase64Cert() {
|
||||
X509Certificate caCert = KeyStoreProvider.readCert(_ks,
|
||||
KeyStoreProvider.DEFAULT_CERTIFICATE_ALIAS,
|
||||
KeyStoreProvider.DEFAULT_KEYSTORE_PASSWORD);
|
||||
return getBase64FromCert(caCert);
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* Return the X509Certificate as a base64 encoded string.
|
||||
* @param cert
|
||||
* @return base64 encode of X509Certificate
|
||||
*/
|
||||
/**** unused and incorrectly uses I2P Base64. Switch to CertUtil.exportCert() if needed.
|
||||
private static String getBase64FromCert(X509Certificate cert) {
|
||||
try {
|
||||
return Base64.encode(cert.getEncoded());
|
||||
} catch (CertificateEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
****/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Hash pwd with using BCrypt with the default salt.
|
||||
* @param pwd
|
||||
* @return BCrypt hash of salt and input string
|
||||
*/
|
||||
public String getPasswdHash(String pwd) {
|
||||
String salt;
|
||||
synchronized(_conf) {
|
||||
salt = _conf.getConf("auth.salt", "");
|
||||
if (salt.equals("")) {
|
||||
salt = BCrypt.gensalt(10, _context.random());
|
||||
_conf.setConf("auth.salt", salt);
|
||||
_conf.writeConfFile();
|
||||
}
|
||||
}
|
||||
return BCrypt.hashpw(pwd, salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get saved password hash. Stores if not previously set.
|
||||
* @return BCrypt hash of salt and password
|
||||
* @since 0.12
|
||||
*/
|
||||
private String getSavedPasswdHash() {
|
||||
String pw;
|
||||
synchronized(_conf) {
|
||||
pw = _conf.getConf("auth.password", "");
|
||||
if (pw.equals("")) {
|
||||
pw = getPasswdHash(DEFAULT_AUTH_PASSWORD);
|
||||
_conf.setConf("auth.password", pw);
|
||||
_conf.writeConfFile();
|
||||
}
|
||||
}
|
||||
return pw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash input one time with SHA-256, return Base64 encdoded string.
|
||||
* @param string
|
||||
* @return Base64 encoded string
|
||||
*/
|
||||
public String getHash(String string) {
|
||||
SHA256Generator hashGen = _context.sha();
|
||||
byte[] bytes = string.getBytes();
|
||||
bytes = hashGen.calculateHash(bytes).toByteArray();
|
||||
return Base64.encode(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this password correct?
|
||||
* @return true if password is valid.
|
||||
* @since 0.12
|
||||
*/
|
||||
public boolean isValid(String pwd) {
|
||||
String storedPass = getSavedPasswdHash();
|
||||
byte[] p1 = DataHelper.getASCII(getPasswdHash(pwd));
|
||||
byte[] p2 = DataHelper.getASCII(storedPass);
|
||||
return p1.length == p2.length && DataHelper.eqCT(p1, 0, p2, 0, p1.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this password correct?
|
||||
* @return true if password is valid.
|
||||
* @since 0.12
|
||||
*/
|
||||
public boolean isDefaultPasswordValid() {
|
||||
return isValid(DEFAULT_AUTH_PASSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Authentication Token if the provided password is valid.
|
||||
* The token will be valid for one day.
|
||||
* @return AuthToken if password is valid. If password is invalid null will be returned.
|
||||
*/
|
||||
public AuthToken validatePasswd(String pwd) {
|
||||
if (isValid(pwd)) {
|
||||
AuthToken token = new AuthToken(this, pwd);
|
||||
synchronized (authTokens) {
|
||||
authTokens.put(token.getId(), token);
|
||||
}
|
||||
return token;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new password. Old tokens will NOT remain valid, to encourage the new password being tested.
|
||||
* @param newPasswd
|
||||
* @return Returns true if a new password was set.
|
||||
*/
|
||||
public boolean setPasswd(String newPasswd) {
|
||||
String newHash = getPasswdHash(newPasswd);
|
||||
String oldHash = getSavedPasswdHash();
|
||||
|
||||
if (!newHash.equals(oldHash)) {
|
||||
_conf.setConf("auth.password", newHash);
|
||||
_conf.writeConfFile();
|
||||
synchronized (authTokens) {
|
||||
authTokens.clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the AuthToken with the given ID exists and if it does whether is has expired.
|
||||
* @param tokenID - The token to validate
|
||||
* @throws InvalidAuthTokenException
|
||||
* @throws ExpiredAuthTokenException
|
||||
*/
|
||||
public void verifyToken(String tokenID) throws InvalidAuthTokenException, ExpiredAuthTokenException {
|
||||
synchronized (authTokens) {
|
||||
AuthToken token = authTokens.get(tokenID);
|
||||
if (token == null)
|
||||
throw new InvalidAuthTokenException("AuthToken with ID: " + tokenID + " couldn't be found.");
|
||||
if (!token.isValid()) {
|
||||
authTokens.remove(tokenID);
|
||||
throw new ExpiredAuthTokenException("AuthToken with ID: " + tokenID + " expired " + token.getExpiryTime(), token.getExpiryTime());
|
||||
}
|
||||
}
|
||||
// Everything is fine. :)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up old authorization tokens to keep the token store slim and fit.
|
||||
* @author hottuna
|
||||
*
|
||||
*/
|
||||
private class Sweeper extends SimpleTimer2.TimedEvent {
|
||||
// Start running periodic task after 1 day, run periodically every 30 minutes.
|
||||
public Sweeper() {
|
||||
super(_context.simpleTimer2(), AuthToken.VALIDITY_TIME * 24*60*60*1000L);
|
||||
}
|
||||
|
||||
public void timeReached() {
|
||||
_log.debug("Starting cleanup job..");
|
||||
synchronized (authTokens) {
|
||||
for (Iterator<AuthToken> iter = authTokens.values().iterator(); iter.hasNext(); ) {
|
||||
AuthToken token = iter.next();
|
||||
if (!token.isValid())
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
_log.debug("Cleanup job done.");
|
||||
schedule(30*60*1000L);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
package net.i2p.i2pcontrol.servlets;
|
||||
/*
|
||||
* Copyright 2011 hottuna (dev@robertfoss.se)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.*;
|
||||
import com.thetransactioncompany.jsonrpc2.server.Dispatcher;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.PortMapper;
|
||||
|
||||
import net.i2p.i2pcontrol.I2PControlVersion;
|
||||
import net.i2p.i2pcontrol.security.KeyStoreProvider;
|
||||
import net.i2p.i2pcontrol.security.SecurityManager;
|
||||
import net.i2p.i2pcontrol.servlets.jsonrpc2handlers.*;
|
||||
import net.i2p.i2pcontrol.servlets.configuration.ConfigurationManager;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
|
||||
/**
|
||||
* Provide an JSON-RPC 2.0 API for remote controlling of I2P
|
||||
*/
|
||||
public class JSONRPC2Servlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = -45075606818515212L;
|
||||
private static final int BUFFER_LENGTH = 2048;
|
||||
private static final String SVC_HTTP_I2PCONTROL = "http_i2pcontrol";
|
||||
private static final String SVC_HTTPS_I2PCONTROL = "https_i2pcontrol";
|
||||
private Dispatcher disp;
|
||||
private Log _log;
|
||||
private final SecurityManager _secMan;
|
||||
private final ConfigurationManager _conf;
|
||||
private final JSONRPC2Helper _helper;
|
||||
private final RouterContext _context;
|
||||
private final boolean _isWebapp;
|
||||
private boolean _isHTTP, _isHTTPS;
|
||||
|
||||
/**
|
||||
* Webapp
|
||||
*/
|
||||
public JSONRPC2Servlet() {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
if (!ctx.isRouterContext())
|
||||
throw new IllegalStateException();
|
||||
_context = (RouterContext) ctx;
|
||||
File appDir = ctx.getAppDir();
|
||||
_conf = new ConfigurationManager(ctx, appDir, false);
|
||||
// we don't really need a keystore
|
||||
//File ksDir = new File(ctx.getConfigDir(), "keystore");
|
||||
//ksDir.mkDir();
|
||||
//KeyStoreProvider ksp = new KeyStoreProvider(ksDir.getAbsolutePath());
|
||||
//_secMan = new SecurityManager(ctx, ksp, _conf);
|
||||
_secMan = new SecurityManager(ctx, null, _conf);
|
||||
_helper = new JSONRPC2Helper(_secMan);
|
||||
_log = ctx.logManager().getLog(JSONRPC2Servlet.class);
|
||||
_conf.writeConfFile();
|
||||
_isWebapp = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin
|
||||
*/
|
||||
public JSONRPC2Servlet(RouterContext ctx, SecurityManager secMan) {
|
||||
_context = ctx;
|
||||
_secMan = secMan;
|
||||
_helper = new JSONRPC2Helper(_secMan);
|
||||
if (ctx != null)
|
||||
_log = ctx.logManager().getLog(JSONRPC2Servlet.class);
|
||||
else
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(JSONRPC2Servlet.class);
|
||||
_conf = null;
|
||||
_isWebapp = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws ServletException {
|
||||
super.init();
|
||||
disp = new Dispatcher();
|
||||
disp.register(new EchoHandler(_helper));
|
||||
disp.register(new GetRateHandler(_helper));
|
||||
disp.register(new AuthenticateHandler(_helper, _secMan));
|
||||
disp.register(new NetworkSettingHandler(_context, _helper));
|
||||
disp.register(new RouterInfoHandler(_context, _helper));
|
||||
disp.register(new RouterManagerHandler(_context, _helper));
|
||||
disp.register(new I2PControlHandler(_context, _helper, _secMan));
|
||||
disp.register(new AdvancedSettingsHandler(_context, _helper));
|
||||
if (_isWebapp) {
|
||||
PortMapper pm = _context.portMapper();
|
||||
int port = pm.getPort(PortMapper.SVC_CONSOLE);
|
||||
if (port > 0) {
|
||||
String host = pm.getHost(PortMapper.SVC_CONSOLE, "127.0.0.1");
|
||||
pm.register(SVC_HTTP_I2PCONTROL, host, port);
|
||||
_isHTTP = true;
|
||||
}
|
||||
port = pm.getPort(PortMapper.SVC_HTTPS_CONSOLE);
|
||||
if (port > 0) {
|
||||
String host = pm.getHost(PortMapper.SVC_HTTPS_CONSOLE, "127.0.0.1");
|
||||
pm.register(SVC_HTTPS_I2PCONTROL, host, port);
|
||||
_isHTTPS = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (_isWebapp) {
|
||||
PortMapper pm = _context.portMapper();
|
||||
if (_isHTTP)
|
||||
pm.unregister(SVC_HTTP_I2PCONTROL);
|
||||
if (_isHTTPS)
|
||||
pm.unregister(SVC_HTTPS_I2PCONTROL);
|
||||
_secMan.stopTimedEvents();
|
||||
_conf.writeConfFile();
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
|
||||
httpServletResponse.setContentType("text/html");
|
||||
PrintWriter out = httpServletResponse.getWriter();
|
||||
out.println("<p>I2PControl RPC Service version " + I2PControlVersion.VERSION + " : Running");
|
||||
if ("/password".equals(httpServletRequest.getServletPath())) {
|
||||
out.println("<form method=\"POST\" action=\"password\">");
|
||||
if (_secMan.isDefaultPasswordValid()) {
|
||||
out.println("<p>The current API password is the default, \"" + _secMan.DEFAULT_AUTH_PASSWORD + "\". You should change it.");
|
||||
} else {
|
||||
out.println("<p>Current API password:<input name=\"password\" type=\"password\">");
|
||||
}
|
||||
out.println("<p>New API password (twice):<input name=\"password2\" type=\"password\">" +
|
||||
"<input name=\"password3\" type=\"password\">" +
|
||||
"<input name=\"save\" type=\"submit\" value=\"Change API Password\">" +
|
||||
"<p>If you forget the API password, stop i2pcontrol, delete the file <tt>" + _conf.getConfFile() +
|
||||
"</tt>, and restart i2pcontrol.");
|
||||
} else {
|
||||
out.println("<p><a href=\"password\">Change API Password</a>");
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
|
||||
/** @since 0.12 */
|
||||
private void doPasswordChange(HttpServletRequest req, HttpServletResponse httpServletResponse) throws ServletException, IOException {
|
||||
httpServletResponse.setContentType("text/html");
|
||||
PrintWriter out = httpServletResponse.getWriter();
|
||||
String pw = req.getParameter("password");
|
||||
if (pw == null)
|
||||
pw = _secMan.DEFAULT_AUTH_PASSWORD;
|
||||
else
|
||||
pw = pw.trim();
|
||||
String pw2 = req.getParameter("password2");
|
||||
String pw3 = req.getParameter("password3");
|
||||
if (pw2 == null || pw3 == null) {
|
||||
out.println("<p>Enter new password twice!");
|
||||
} else {
|
||||
pw2 = pw2.trim();
|
||||
pw3 = pw3.trim();
|
||||
if (!pw2.equals(pw3)) {
|
||||
out.println("<p>New passwords don't match!");
|
||||
} else if (pw2.length() <= 0) {
|
||||
out.println("<p>Enter new password twice!");
|
||||
} else if (_secMan.isValid(pw)) {
|
||||
_secMan.setPasswd(pw2);
|
||||
out.println("<p>API Password changed");
|
||||
} else {
|
||||
out.println("<p>Incorrect old password, not changed");
|
||||
}
|
||||
}
|
||||
out.println("<p><a href=\"password\">Change API Password</a>");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
|
||||
if ("/password".equals(httpServletRequest.getServletPath())) {
|
||||
doPasswordChange(httpServletRequest, httpServletResponse);
|
||||
return;
|
||||
}
|
||||
String req = getRequest(httpServletRequest.getInputStream());
|
||||
httpServletResponse.setContentType("application/json");
|
||||
PrintWriter out = httpServletResponse.getWriter();
|
||||
JSONRPC2Message msg = null;
|
||||
JSONRPC2Response jsonResp = null;
|
||||
try {
|
||||
msg = JSONRPC2Message.parse(req);
|
||||
|
||||
if (msg instanceof JSONRPC2Request) {
|
||||
jsonResp = disp.process((JSONRPC2Request)msg, null);
|
||||
jsonResp.toJSONObject().put("API", I2PControlVersion.API_VERSION);
|
||||
if (_log.shouldDebug()) {
|
||||
_log.debug("Request: " + msg);
|
||||
_log.debug("Response: " + jsonResp);
|
||||
}
|
||||
}
|
||||
else if (msg instanceof JSONRPC2Notification) {
|
||||
disp.process((JSONRPC2Notification)msg, null);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Notification: " + msg);
|
||||
}
|
||||
|
||||
out.println(jsonResp);
|
||||
out.close();
|
||||
} catch (JSONRPC2ParseException e) {
|
||||
_log.error("Unable to parse JSONRPC2Message: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String getRequest(ServletInputStream sis) throws IOException {
|
||||
Writer writer = new StringWriter();
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(sis, "UTF-8"));
|
||||
char[] readBuffer = new char[BUFFER_LENGTH];
|
||||
int n;
|
||||
while ((n = reader.read(readBuffer)) != -1) {
|
||||
writer.write(readBuffer, 0, n);
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package net.i2p.i2pcontrol.servlets.configuration;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Manage the configuration of I2PControl.
|
||||
* @author mathias
|
||||
* modified: hottuna
|
||||
*
|
||||
*/
|
||||
public class ConfigurationManager {
|
||||
private final String CONFIG_FILE = "I2PControl.conf";
|
||||
private final String WEBAPP_CONFIG_FILE = "i2pcontrol.config";
|
||||
private final File configLocation;
|
||||
private final Log _log;
|
||||
private boolean _changed;
|
||||
|
||||
//Configurations with a String as value
|
||||
private final Map<String, String> stringConfigurations = new HashMap<String, String>();
|
||||
//Configurations with a Boolean as value
|
||||
private final Map<String, Boolean> booleanConfigurations = new HashMap<String, Boolean>();
|
||||
//Configurations with an Integer as value
|
||||
private final Map<String, Integer> integerConfigurations = new HashMap<String, Integer>();
|
||||
|
||||
|
||||
|
||||
public ConfigurationManager(I2PAppContext ctx, File dir, boolean isPlugin) {
|
||||
_log = ctx.logManager().getLog(ConfigurationManager.class);
|
||||
if (isPlugin) {
|
||||
configLocation = new File(dir, CONFIG_FILE);
|
||||
} else {
|
||||
configLocation = new File(dir, WEBAPP_CONFIG_FILE);
|
||||
}
|
||||
readConfFile();
|
||||
}
|
||||
|
||||
/** @since 0.12 */
|
||||
public File getConfFile() {
|
||||
return configLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects arguments of the form --word, --word=otherword and -blah
|
||||
* to determine user parameters.
|
||||
* @param settingNames Command line arguments to the application
|
||||
*/
|
||||
/****
|
||||
public void loadArguments(String[] settingNames) {
|
||||
for (int i = 0; i < settingNames.length; i++) {
|
||||
String settingName = settingNames[i];
|
||||
if (settingName.startsWith("--")) {
|
||||
parseConfigStr(settingName.substring(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* Reads configuration from file, every line is parsed as key=value.
|
||||
*/
|
||||
public synchronized void readConfFile() {
|
||||
try {
|
||||
Properties input = new Properties();
|
||||
// true: map to lower case
|
||||
DataHelper.loadProps(input, configLocation, true);
|
||||
parseConfigStr(input);
|
||||
_changed = false;
|
||||
} catch (FileNotFoundException e) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Unable to find config file, " + configLocation);
|
||||
} catch (IOException e) {
|
||||
_log.error("Unable to read from config file, " + configLocation, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write configuration into default config file.
|
||||
* As of 0.12, doesn't actually write unless something changed.
|
||||
*/
|
||||
public synchronized void writeConfFile() {
|
||||
if (!_changed)
|
||||
return;
|
||||
Properties tree = new OrderedProperties();
|
||||
tree.putAll(stringConfigurations);
|
||||
for (Entry<String, Integer> e : integerConfigurations.entrySet()) {
|
||||
tree.put(e.getKey(), e.getValue().toString());
|
||||
}
|
||||
for (Entry<String, Boolean> e : booleanConfigurations.entrySet()) {
|
||||
tree.put(e.getKey(), e.getValue().toString());
|
||||
}
|
||||
try {
|
||||
DataHelper.storeProps(tree, configLocation);
|
||||
_changed = false;
|
||||
} catch (IOException e1) {
|
||||
_log.error("Couldn't open file, " + configLocation + " for writing config.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to parse the input as 'key=value',
|
||||
* where value will (in order) be parsed as integer/boolean/string.
|
||||
* @param str
|
||||
*/
|
||||
private void parseConfigStr(Properties input) {
|
||||
for (Entry<Object, Object> entry : input.entrySet()) {
|
||||
String key = (String) entry.getKey();
|
||||
String value = (String) entry.getValue();
|
||||
//Try parse as integer.
|
||||
try {
|
||||
int i = Integer.parseInt(value);
|
||||
integerConfigurations.put(key, i);
|
||||
continue;
|
||||
} catch (NumberFormatException e) {}
|
||||
//Check if value is a bool
|
||||
if (value.toLowerCase().equals("true")) {
|
||||
booleanConfigurations.put(key, Boolean.TRUE);
|
||||
continue;
|
||||
} else if (value.toLowerCase().equals("false")) {
|
||||
booleanConfigurations.put(key, Boolean.FALSE);
|
||||
continue;
|
||||
}
|
||||
stringConfigurations.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a specific boolean configuration exists.
|
||||
* @param settingName The key for the configuration.
|
||||
* @param defaultValue If the configuration is not found, we use a default value.
|
||||
* @return The value of a configuration: true if found, defaultValue if not found.
|
||||
*/
|
||||
public synchronized boolean getConf(String settingName, boolean defaultValue) {
|
||||
Boolean value = booleanConfigurations.get(settingName);
|
||||
if (value != null) {
|
||||
return value;
|
||||
} else {
|
||||
booleanConfigurations.put(settingName, defaultValue);
|
||||
_changed = true;
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a specific boolean configuration exists.
|
||||
* @param settingName The key for the configuration.
|
||||
* @param defaultValue If the configuration is not found, we use a default value.
|
||||
* @return The value of a configuration: true if found, defaultValue if not found.
|
||||
*/
|
||||
public synchronized int getConf(String settingName, int defaultValue) {
|
||||
Integer value = integerConfigurations.get(settingName);
|
||||
if (value != null) {
|
||||
return value;
|
||||
} else {
|
||||
integerConfigurations.put(settingName, defaultValue);
|
||||
_changed = true;
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific String configuration.
|
||||
* @param settingName The key for the configuration.
|
||||
* @param defaultValue If the configuration is not found, we use a default value.
|
||||
* @return The value of the configuration, or the defaultValue.
|
||||
*/
|
||||
public synchronized String getConf(String settingName, String defaultValue) {
|
||||
String value = stringConfigurations.get(settingName);
|
||||
if (value != null) {
|
||||
return value;
|
||||
} else {
|
||||
stringConfigurations.put(settingName, defaultValue);
|
||||
_changed = true;
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific int setting
|
||||
* @param settingName
|
||||
* @param nbr
|
||||
*/
|
||||
public synchronized void setConf(String settingName, int nbr) {
|
||||
integerConfigurations.put(settingName, nbr);
|
||||
_changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific string setting
|
||||
* @param settingName
|
||||
* @param str
|
||||
*/
|
||||
public synchronized void setConf(String settingName, String str) {
|
||||
stringConfigurations.put(settingName, str);
|
||||
_changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific boolean setting
|
||||
* @param settingName
|
||||
* @param bool
|
||||
*/
|
||||
public synchronized void setConf(String settingName, boolean bool) {
|
||||
booleanConfigurations.put(settingName, bool);
|
||||
_changed = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package net.i2p.i2pcontrol.servlets.jsonrpc2handlers;
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
|
||||
import com.thetransactioncompany.jsonrpc2.server.MessageContext;
|
||||
import com.thetransactioncompany.jsonrpc2.server.RequestHandler;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class AdvancedSettingsHandler implements RequestHandler {
|
||||
|
||||
private final RouterContext _context;
|
||||
private final Log _log;
|
||||
private final JSONRPC2Helper _helper;
|
||||
private static final String[] requiredArgs = {};
|
||||
|
||||
public AdvancedSettingsHandler(RouterContext ctx, JSONRPC2Helper helper) {
|
||||
_helper = helper;
|
||||
_context = ctx;
|
||||
if (ctx != null)
|
||||
_log = ctx.logManager().getLog(AdvancedSettingsHandler.class);
|
||||
else
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(AdvancedSettingsHandler.class);
|
||||
}
|
||||
|
||||
// Reports the method names of the handled requests
|
||||
public String[] handledRequests() {
|
||||
return new String[] {"AdvancedSettings"};
|
||||
}
|
||||
|
||||
// Processes the requests
|
||||
@SuppressWarnings("unchecked")
|
||||
public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) {
|
||||
if (req.getMethod().equals("AdvancedSettings")) {
|
||||
JSONRPC2Error err = _helper.validateParams(requiredArgs, req);
|
||||
if (err != null) {
|
||||
return new JSONRPC2Response(err, req.getID());
|
||||
}
|
||||
|
||||
if (_context == null) {
|
||||
return new JSONRPC2Response(new JSONRPC2Error(
|
||||
JSONRPC2Error.INTERNAL_ERROR.getCode(),
|
||||
"RouterContext was not initialized. Query failed"),
|
||||
req.getID());
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
Map<String, Object> inParams = req.getNamedParams();
|
||||
Map outParams = new HashMap();
|
||||
|
||||
if (inParams.containsKey("setAll")) {
|
||||
Object obj = inParams.get("setAll");
|
||||
if (!(obj instanceof Map)) {
|
||||
JSONRPC2Error rpcErr = new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"Value of \"setAll\" is not a Map");
|
||||
return new JSONRPC2Response(rpcErr, req.getID());
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
Map objMap = (Map) inParams.get("setAll");
|
||||
if (objMap.size() > 0)
|
||||
{
|
||||
if (!(objMap.keySet().toArray()[0] instanceof String) &&
|
||||
!(objMap.values().toArray()[0] instanceof String)) {
|
||||
JSONRPC2Error rpcErr = new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"Map of settings does not contain String keys and values");
|
||||
return new JSONRPC2Response(rpcErr, req.getID());
|
||||
}
|
||||
|
||||
if (!checkTypes(objMap)) {
|
||||
JSONRPC2Error rpcErr = new JSONRPC2Error(JSONRPC2Error.INTERNAL_ERROR.getCode(),
|
||||
"Some of the supplied values are not strings");
|
||||
return new JSONRPC2Response(rpcErr, req.getID());
|
||||
}
|
||||
|
||||
Map<String, String> allSettings = (Map<String, String>) objMap;
|
||||
boolean success = setAdvancedSettings(allSettings, true);
|
||||
if (!success) {
|
||||
JSONRPC2Error rpcErr = new JSONRPC2Error(JSONRPC2Error.INTERNAL_ERROR.getCode(),
|
||||
"Failed to save new config");
|
||||
return new JSONRPC2Response(rpcErr, req.getID());
|
||||
}
|
||||
|
||||
} else {
|
||||
// Empty list of settings submitted
|
||||
boolean success = setAdvancedSettings(null, true);
|
||||
if (!success) {
|
||||
JSONRPC2Error rpcErr = new JSONRPC2Error(JSONRPC2Error.INTERNAL_ERROR.getCode(),
|
||||
"Failed to save new config");
|
||||
return new JSONRPC2Response(rpcErr, req.getID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams.containsKey("getAll")) {
|
||||
outParams.put("getAll", getAdvancedSettings());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("set")) {
|
||||
Object obj = inParams.get("set");
|
||||
if (!(obj instanceof Map)) {
|
||||
JSONRPC2Error rpcErr = new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"Value of \"set\" is not a Map");
|
||||
return new JSONRPC2Response(rpcErr, req.getID());
|
||||
}
|
||||
|
||||
Map objMap = (Map) inParams.get("set");
|
||||
if (objMap.size() > 0)
|
||||
{
|
||||
if (!(objMap.keySet().toArray()[0] instanceof String) &&
|
||||
!(objMap.values().toArray()[0] instanceof String)) {
|
||||
JSONRPC2Error rpcErr = new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"Map of settings does not contain String keys and values");
|
||||
return new JSONRPC2Response(rpcErr, req.getID());
|
||||
}
|
||||
|
||||
if (!checkTypes(objMap)) {
|
||||
JSONRPC2Error rpcErr = new JSONRPC2Error(JSONRPC2Error.INTERNAL_ERROR.getCode(),
|
||||
"Some of the supplied values are not strings");
|
||||
return new JSONRPC2Response(rpcErr, req.getID());
|
||||
}
|
||||
|
||||
Map<String, String> allSettings = (Map<String, String>) objMap;
|
||||
boolean success = setAdvancedSettings(allSettings, false);
|
||||
if (!success) {
|
||||
JSONRPC2Error rpcErr = new JSONRPC2Error(JSONRPC2Error.INTERNAL_ERROR.getCode(),
|
||||
"Failed to save new config");
|
||||
return new JSONRPC2Response(rpcErr, req.getID());
|
||||
}
|
||||
|
||||
} else {
|
||||
// Empty list of settings submitted
|
||||
JSONRPC2Error rpcErr = new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"Map of settings does not contain any entries");
|
||||
return new JSONRPC2Response(rpcErr, req.getID());
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams.containsKey("get")) {
|
||||
Object obj = inParams.get("get");
|
||||
if (!(obj instanceof String)) {
|
||||
JSONRPC2Error rpcErr = new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"Value of \"get\" is not a string");
|
||||
return new JSONRPC2Response(rpcErr, req.getID());
|
||||
}
|
||||
String getStr = (String) obj;
|
||||
String getVal = getAdvancedSetting(getStr);
|
||||
Map<String, String> outMap = new HashMap<String, String>();
|
||||
outMap.put(getStr, getVal);
|
||||
outParams.put("get", outMap);
|
||||
}
|
||||
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
} else {
|
||||
// Method name not supported
|
||||
return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, req.getID());
|
||||
}
|
||||
}
|
||||
|
||||
private String getAdvancedSetting(String key) {
|
||||
return _context.router().getConfigSetting(key);
|
||||
}
|
||||
|
||||
|
||||
private Map<String, String> getAdvancedSettings() {
|
||||
return _context.router().getConfigMap();
|
||||
}
|
||||
|
||||
private boolean checkTypes(Map<String, Object> newSettings) {
|
||||
for (String key : newSettings.keySet()) {
|
||||
if (!(newSettings.get(key) instanceof String)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setAdvancedSettings(Map<String, String> newSettings, boolean clearConfig) {
|
||||
Set<String> unsetKeys = null;
|
||||
|
||||
if (clearConfig) {
|
||||
unsetKeys = new HashSet<String>(_context.router().getConfigSettings());
|
||||
|
||||
for (String key : newSettings.keySet()) {
|
||||
unsetKeys.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
return _context.router().saveConfig(newSettings, unsetKeys);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package net.i2p.i2pcontrol.servlets.jsonrpc2handlers;
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
|
||||
import com.thetransactioncompany.jsonrpc2.server.MessageContext;
|
||||
import com.thetransactioncompany.jsonrpc2.server.RequestHandler;
|
||||
import net.i2p.i2pcontrol.I2PControlVersion;
|
||||
import net.i2p.i2pcontrol.security.AuthToken;
|
||||
import net.i2p.i2pcontrol.security.SecurityManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* Copyright 2011 hottuna (dev@robertfoss.se)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
public class AuthenticateHandler implements RequestHandler {
|
||||
|
||||
private static final String[] requiredArgs = {"Password", "API"};
|
||||
private final JSONRPC2Helper _helper;
|
||||
private final SecurityManager _secMan;
|
||||
|
||||
public AuthenticateHandler(JSONRPC2Helper helper, SecurityManager secMan) {
|
||||
_helper = helper;
|
||||
_secMan = secMan;
|
||||
}
|
||||
|
||||
// Reports the method names of the handled requests
|
||||
public String[] handledRequests() {
|
||||
return new String[] {"Authenticate"};
|
||||
}
|
||||
|
||||
// Processes the requests
|
||||
public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) {
|
||||
if (req.getMethod().equals("Authenticate")) {
|
||||
JSONRPC2Error err = _helper.validateParams(requiredArgs, req, JSONRPC2Helper.USE_NO_AUTH);
|
||||
if (err != null)
|
||||
return new JSONRPC2Response(err, req.getID());
|
||||
|
||||
Map<String, Object> inParams = req.getNamedParams();
|
||||
|
||||
String pwd = (String) inParams.get("Password");
|
||||
|
||||
// Try get an AuthToken
|
||||
|
||||
AuthToken token = _secMan.validatePasswd(pwd);
|
||||
if (token == null) {
|
||||
return new JSONRPC2Response(JSONRPC2ExtendedError.INVALID_PASSWORD, req.getID());
|
||||
}
|
||||
|
||||
Object api = inParams.get("API");
|
||||
err = validateAPIVersion(api);
|
||||
if (err != null)
|
||||
return new JSONRPC2Response(err, req.getID());
|
||||
|
||||
|
||||
Map<String, Object> outParams = new HashMap<String, Object>(4);
|
||||
outParams.put("Token", token.getId());
|
||||
outParams.put("API", I2PControlVersion.API_VERSION);
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
} else {
|
||||
// Method name not supported
|
||||
return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, req.getID());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the provided I2PControl API version against the ones supported by I2PControl.
|
||||
*/
|
||||
private static JSONRPC2Error validateAPIVersion(Object api) {
|
||||
|
||||
Integer apiVersion;
|
||||
try {
|
||||
apiVersion = ((Long) api).intValue();
|
||||
} catch (ClassCastException e) {
|
||||
e.printStackTrace();
|
||||
return JSONRPC2ExtendedError.UNSPECIFIED_API_VERSION;
|
||||
}
|
||||
|
||||
if (!I2PControlVersion.SUPPORTED_API_VERSIONS.contains(apiVersion)) {
|
||||
String supportedAPIVersions = "";
|
||||
for (Integer i : I2PControlVersion.SUPPORTED_API_VERSIONS) {
|
||||
supportedAPIVersions += ", " + i;
|
||||
}
|
||||
return new JSONRPC2Error(JSONRPC2ExtendedError.UNSUPPORTED_API_VERSION.getCode(),
|
||||
"The provided API version \'" + apiVersion + "\' is not supported. The supported versions are" + supportedAPIVersions + ".");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package net.i2p.i2pcontrol.servlets.jsonrpc2handlers;
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
|
||||
import com.thetransactioncompany.jsonrpc2.server.MessageContext;
|
||||
import com.thetransactioncompany.jsonrpc2.server.RequestHandler;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class EchoHandler implements RequestHandler {
|
||||
|
||||
private static final String[] requiredArgs = {"Echo"};
|
||||
private final JSONRPC2Helper _helper;
|
||||
|
||||
public EchoHandler(JSONRPC2Helper helper) {
|
||||
_helper = helper;
|
||||
}
|
||||
|
||||
// Reports the method names of the handled requests
|
||||
public String[] handledRequests() {
|
||||
return new String[] {"Echo"};
|
||||
}
|
||||
|
||||
// Processes the requests
|
||||
public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) {
|
||||
if (req.getMethod().equals("Echo")) {
|
||||
JSONRPC2Error err = _helper.validateParams(requiredArgs, req);
|
||||
if (err != null)
|
||||
return new JSONRPC2Response(err, req.getID());
|
||||
|
||||
Map<String, Object> inParams = req.getNamedParams();
|
||||
String echo = (String) inParams.get("Echo");
|
||||
Map<String, Object> outParams = new HashMap<String, Object>(4);
|
||||
outParams.put("Result", echo);
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
}
|
||||
else {
|
||||
// Method name not supported
|
||||
return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, req.getID());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package net.i2p.i2pcontrol.servlets.jsonrpc2handlers;
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
|
||||
import com.thetransactioncompany.jsonrpc2.server.MessageContext;
|
||||
import com.thetransactioncompany.jsonrpc2.server.RequestHandler;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.stat.Rate;
|
||||
import net.i2p.stat.RateStat;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* Copyright 2011 hottuna (dev@robertfoss.se)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
public class GetRateHandler implements RequestHandler {
|
||||
|
||||
private static final String[] requiredArgs = {"Stat", "Period"};
|
||||
private final JSONRPC2Helper _helper;
|
||||
|
||||
public GetRateHandler(JSONRPC2Helper helper) {
|
||||
_helper = helper;
|
||||
}
|
||||
|
||||
// Reports the method names of the handled requests
|
||||
public String[] handledRequests() {
|
||||
return new String[] {"GetRate"};
|
||||
}
|
||||
|
||||
// Processes the requests
|
||||
public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) {
|
||||
if (req.getMethod().equals("GetRate")) {
|
||||
JSONRPC2Error err = _helper.validateParams(requiredArgs, req);
|
||||
if (err != null)
|
||||
return new JSONRPC2Response(err, req.getID());
|
||||
|
||||
Map<String, Object> inParams = req.getNamedParams();
|
||||
|
||||
String input = (String) inParams.get("Stat");
|
||||
if (input == null) {
|
||||
return new JSONRPC2Response(JSONRPC2Error.INVALID_PARAMS, req.getID());
|
||||
}
|
||||
long period;
|
||||
try {
|
||||
period = (Long) inParams.get("Period");
|
||||
} catch (NumberFormatException e) {
|
||||
return new JSONRPC2Response(JSONRPC2Error.INVALID_PARAMS, req.getID());
|
||||
}
|
||||
|
||||
RateStat rateStat = I2PAppContext.getGlobalContext().statManager().getRate(input);
|
||||
|
||||
// If RateStat or the requested period doesn't already exist, create them.
|
||||
if (rateStat == null || rateStat.getRate(period) == null) {
|
||||
long[] tempArr = new long[1];
|
||||
tempArr[0] = period;
|
||||
I2PAppContext.getGlobalContext().statManager().createRequiredRateStat(input, "I2PControl", "I2PControl", tempArr);
|
||||
rateStat = I2PAppContext.getGlobalContext().statManager().getRate(input);
|
||||
}
|
||||
if (rateStat.getRate(period) == null)
|
||||
return new JSONRPC2Response(JSONRPC2Error.INTERNAL_ERROR, req.getID());
|
||||
Map<String, Object> outParams = new HashMap<String, Object>(4);
|
||||
Rate rate = rateStat.getRate(period);
|
||||
rate.coalesce();
|
||||
outParams.put("Result", rate.getAverageValue());
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
}
|
||||
return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, req.getID());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
package net.i2p.i2pcontrol.servlets.jsonrpc2handlers;
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
|
||||
import com.thetransactioncompany.jsonrpc2.server.MessageContext;
|
||||
import com.thetransactioncompany.jsonrpc2.server.RequestHandler;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.i2pcontrol.I2PControlController;
|
||||
import net.i2p.i2pcontrol.security.SecurityManager;
|
||||
import net.i2p.i2pcontrol.servlets.configuration.ConfigurationManager;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* Copyright 2011 hottuna (dev@robertfoss.se)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
public class I2PControlHandler implements RequestHandler {
|
||||
|
||||
private static final int BW_BURST_PCT = 110;
|
||||
private static final int BW_BURST_TIME = 20;
|
||||
private final RouterContext _context;
|
||||
private final Log _log;
|
||||
//private final ConfigurationManager _conf;
|
||||
private final SecurityManager _secMan;
|
||||
private final JSONRPC2Helper _helper;
|
||||
|
||||
public I2PControlHandler(RouterContext ctx, JSONRPC2Helper helper, SecurityManager secMan) {
|
||||
_helper = helper;
|
||||
_secMan = secMan;
|
||||
_context = ctx;
|
||||
if (ctx != null)
|
||||
_log = ctx.logManager().getLog(I2PControlHandler.class);
|
||||
else
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(I2PControlHandler.class);
|
||||
}
|
||||
|
||||
|
||||
// Reports the method names of the handled requests
|
||||
public String[] handledRequests() {
|
||||
return new String[] {"I2PControl"};
|
||||
}
|
||||
|
||||
// Processes the requests
|
||||
public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) {
|
||||
if (req.getMethod().equals("I2PControl")) {
|
||||
return process(req);
|
||||
} else {
|
||||
// Method name not supported
|
||||
return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, req.getID());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private JSONRPC2Response process(JSONRPC2Request req) {
|
||||
JSONRPC2Error err = _helper.validateParams(null, req);
|
||||
if (err != null)
|
||||
return new JSONRPC2Response(err, req.getID());
|
||||
|
||||
/**** only if we enable host/port changes
|
||||
if (_context == null) {
|
||||
return new JSONRPC2Response(
|
||||
new JSONRPC2Error(JSONRPC2Error.INTERNAL_ERROR.getCode(),
|
||||
"RouterContext was not initialized. Query failed"),
|
||||
req.getID());
|
||||
}
|
||||
****/
|
||||
Map<String, Object> inParams = req.getNamedParams();
|
||||
Map<String, Object> outParams = new HashMap<String, Object>(4);
|
||||
|
||||
boolean restartNeeded = false;
|
||||
boolean settingsSaved = false;
|
||||
String inParam;
|
||||
|
||||
/****
|
||||
if (inParams.containsKey("i2pcontrol.port")) {
|
||||
Integer oldPort = _conf.getConf("i2pcontrol.listen.port", 7650);
|
||||
if ((inParam = (String) inParams.get("i2pcontrol.port")) != null) {
|
||||
if (oldPort == null || !inParam.equals(oldPort.toString())) {
|
||||
Integer newPort;
|
||||
try {
|
||||
newPort = Integer.valueOf(inParam);
|
||||
if (newPort < 1 || newPort > 65535) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return new JSONRPC2Response(
|
||||
new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"\"i2pcontrol.port\" must be a string representing a number in the range 1-65535. " + inParam + " isn't valid."),
|
||||
req.getID());
|
||||
}
|
||||
try {
|
||||
SslSocketConnector ssl = I2PControlController.buildSslListener(_conf.getConf("i2pcontrol.listen.address", "127.0.0.1"), newPort);
|
||||
I2PControlController.clearListeners();
|
||||
I2PControlController.replaceListener(ssl);
|
||||
|
||||
_conf.setConf("i2pcontrol.listen.port", newPort);
|
||||
|
||||
|
||||
ConfigurationManager.writeConfFile();
|
||||
outParams.put("i2pcontrol.port", null);
|
||||
settingsSaved = true;
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
_conf.setConf("i2pcontrol.listen.port", oldPort);
|
||||
SslSocketConnector ssl = I2PControlController.buildSslListener(_conf.getConf("i2pcontrol.listen.address", "127.0.0.1"), oldPort);
|
||||
I2PControlController.clearListeners();
|
||||
I2PControlController.replaceListener(ssl);
|
||||
} catch (Exception e2) {
|
||||
_log.log(Log.CRIT, "Unable to resume server on previous listening port.");
|
||||
}
|
||||
_log.error("Client tried to set listen port to, " + newPort + " which isn't valid.", e);
|
||||
return new JSONRPC2Response(
|
||||
new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"\"i2pcontrol.port\" has been set to a port that is already in use, reverting. " +
|
||||
inParam + " is an already used port.\n"
|
||||
+ "Exception: " + e.toString()),
|
||||
req.getID());
|
||||
}
|
||||
}
|
||||
}
|
||||
outParams.put("RestartNeeded", restartNeeded);
|
||||
}
|
||||
****/
|
||||
|
||||
if (inParams.containsKey("i2pcontrol.password")) {
|
||||
if ((inParam = (String) inParams.get("i2pcontrol.password")) != null) {
|
||||
if (_secMan.setPasswd(inParam)) {
|
||||
outParams.put("i2pcontrol.password", null);
|
||||
settingsSaved = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****
|
||||
if (inParams.containsKey("i2pcontrol.address")) {
|
||||
String oldAddress = _conf.getConf("i2pcontrol.listen.address", "127.0.0.1");
|
||||
if ((inParam = (String) inParams.get("i2pcontrol.address")) != null) {
|
||||
if ((oldAddress == null || !inParam.equals(oldAddress.toString()) &&
|
||||
(inParam.equals("0.0.0.0") || inParam.equals("127.0.0.1")))) {
|
||||
InetAddress[] newAddress;
|
||||
|
||||
try {
|
||||
newAddress = InetAddress.getAllByName(inParam);
|
||||
} catch (UnknownHostException e) {
|
||||
return new JSONRPC2Response(
|
||||
new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"\"i2pcontrol.address\" must be a string representing a hostname or ipaddress. " + inParam + " isn't valid."),
|
||||
req.getID());
|
||||
}
|
||||
try {
|
||||
SslSocketConnector ssl = I2PControlController.buildSslListener(inParam, _conf.getConf("i2pcontrol.listen.port", 7650));
|
||||
I2PControlController.clearListeners();
|
||||
I2PControlController.replaceListener(ssl);
|
||||
_conf.setConf("i2pcontrol.listen.address", inParam);
|
||||
|
||||
ConfigurationManager.writeConfFile();
|
||||
outParams.put("i2pcontrol.address", null);
|
||||
settingsSaved = true;
|
||||
} catch (Exception e) {
|
||||
_conf.setConf("i2pcontrol.listen.address", oldAddress);
|
||||
try {
|
||||
SslSocketConnector ssl = I2PControlController.buildSslListener(inParam, _conf.getConf("i2pcontrol.listen.port", 7650));
|
||||
I2PControlController.clearListeners();
|
||||
I2PControlController.replaceListener(ssl);
|
||||
} catch (Exception e2) {
|
||||
_log.log(Log.CRIT, "Unable to resume server on previous listening ip.");
|
||||
}
|
||||
_log.error("Client tried to set listen address to, " + newAddress.toString() + " which isn't valid.", e);
|
||||
return new JSONRPC2Response(
|
||||
new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"\"i2pcontrol.address\" has been set to an invalid address, reverting. "), req.getID());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
outParams.put("i2pcontrol.address", oldAddress);
|
||||
}
|
||||
outParams.put("RestartNeeded", restartNeeded);
|
||||
}
|
||||
****/
|
||||
|
||||
outParams.put("SettingsSaved", settingsSaved);
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package net.i2p.i2pcontrol.servlets.jsonrpc2handlers;
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
/*
|
||||
* Copyright 2011 hottuna (dev@robertfoss.se)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a JSON-RPC 2.0 error that occured during the processing of a
|
||||
* request.
|
||||
*
|
||||
* <p>The protocol expects error objects to be structured like this:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code code} An integer that indicates the error type.
|
||||
* <li>{@code message} A string providing a short description of the
|
||||
* error. The message should be limited to a concise single sentence.
|
||||
* <li>{@code data} Additional information, which may be omitted. Its
|
||||
* contents is entirely defined by the application.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note that the "Error" word in the class name was put there solely to
|
||||
* comply with the parlance of the JSON-RPC spec. This class doesn't inherit
|
||||
* from {@code java.lang.Error}. It's a regular subclass of
|
||||
* {@code java.lang.Exception} and, if thrown, it's to indicate a condition
|
||||
* that a reasonable application might want to catch.
|
||||
*
|
||||
* <p>This class also includes convenient final static instances for all
|
||||
* standard JSON-RPC 2.0 errors:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #PARSE_ERROR} JSON parse error (-32700)
|
||||
* <li>{@link #INVALID_REQUEST} Invalid JSON-RPC 2.0 Request (-32600)
|
||||
* <li>{@link #METHOD_NOT_FOUND} Method not found (-32601)
|
||||
* <li>{@link #INVALID_PARAMS} Invalid parameters (-32602)
|
||||
* <li>{@link #INTERNAL_ERROR} Internal error (-32603)
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note that the range -32099..-32000 is reserved for additional server
|
||||
* errors.
|
||||
*
|
||||
* <p id="map">The mapping between JSON and Java entities (as defined by the
|
||||
* underlying JSON.simple library):
|
||||
* <pre>
|
||||
* true|false <---> java.lang.Boolean
|
||||
* number <---> java.lang.Number
|
||||
* string <---> java.lang.String
|
||||
* array <---> java.util.List
|
||||
* object <---> java.util.Map
|
||||
* null <---> null
|
||||
* </pre>
|
||||
*
|
||||
* <p>The JSON-RPC 2.0 specification and user group forum can be found
|
||||
* <a href="http://groups.google.com/group/json-rpc">here</a>.
|
||||
*
|
||||
* @author <a href="http://dzhuvinov.com">Vladimir Dzhuvinov</a>
|
||||
* @version 1.16 (2010-10-04)
|
||||
*/
|
||||
public class JSONRPC2ExtendedError extends JSONRPC2Error {
|
||||
|
||||
private static final long serialVersionUID = -6574632977222371077L;
|
||||
|
||||
/** Invalid JSON-RPC 2.0, implementation defined error (-32099 .. -32000) */
|
||||
public static final JSONRPC2Error INVALID_PASSWORD = new JSONRPC2ExtendedError(-32001, "Invalid password provided.");
|
||||
|
||||
/** Invalid JSON-RPC 2.0, implementation defined error (-32099 .. -32000) */
|
||||
public static final JSONRPC2Error NO_TOKEN = new JSONRPC2ExtendedError(-32002, "No authentication token presented.");
|
||||
|
||||
/** Invalid JSON-RPC 2.0, implementation defined error (-32099 .. -32000) */
|
||||
public static final JSONRPC2Error INVALID_TOKEN = new JSONRPC2ExtendedError(-32003, "Authentication token doesn't exist.");
|
||||
|
||||
/** Invalid JSON-RPC 2.0, implementation defined error (-32099 .. -32000) */
|
||||
public static final JSONRPC2Error TOKEN_EXPIRED = new JSONRPC2ExtendedError(-32004, "Provided authentication token was expired and will be removed.");
|
||||
|
||||
/** Invalid JSON-RPC 2.0, implementation defined error (-32099 .. -32000) */
|
||||
public static final JSONRPC2Error UNSPECIFIED_API_VERSION = new JSONRPC2ExtendedError(-32005, "The version of the I2PControl API wasn't specified, but is required to be specified.");
|
||||
|
||||
/** Invalid JSON-RPC 2.0, implementation defined error (-32099 .. -32000) */
|
||||
public static final JSONRPC2Error UNSUPPORTED_API_VERSION = new JSONRPC2ExtendedError(-32006, "The version of the I2PControl API specified is not supported by I2PControl.");
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 error with the specified code and
|
||||
* message. The optional data is omitted.
|
||||
*
|
||||
* @param code The error code (standard pre-defined or
|
||||
* application-specific).
|
||||
* @param message The error message.
|
||||
*/
|
||||
public JSONRPC2ExtendedError(int code, String message) {
|
||||
super(code, message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 error with the specified code,
|
||||
* message and data.
|
||||
*
|
||||
* @param code The error code (standard pre-defined or
|
||||
* application-specific).
|
||||
* @param message The error message.
|
||||
* @param data Optional error data, must <a href="#map">map</a>
|
||||
* to a valid JSON type.
|
||||
*/
|
||||
public JSONRPC2ExtendedError(int code, String message, Object data) {
|
||||
super(code, message, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package net.i2p.i2pcontrol.servlets.jsonrpc2handlers;
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2ParamsType;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
|
||||
import net.i2p.i2pcontrol.security.ExpiredAuthTokenException;
|
||||
import net.i2p.i2pcontrol.security.InvalidAuthTokenException;
|
||||
import net.i2p.i2pcontrol.security.SecurityManager;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* Copyright 2011 hottuna (dev@robertfoss.se)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
public class JSONRPC2Helper {
|
||||
public final static Boolean USE_NO_AUTH = false;
|
||||
public final static Boolean USE_AUTH = true;
|
||||
|
||||
private final SecurityManager _secMan;
|
||||
|
||||
public JSONRPC2Helper(SecurityManager secMan) {
|
||||
_secMan = secMan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check incoming request for required arguments, to make sure they are valid.
|
||||
* @param requiredArgs - Array of names of required arguments. If null don't check for any parameters.
|
||||
* @param req - Incoming JSONRPC2 request
|
||||
* @param useAuth - If true, will validate authentication token.
|
||||
* @return - null if no errors were found. Corresponding JSONRPC2Error if error is found.
|
||||
*/
|
||||
public JSONRPC2Error validateParams(String[] requiredArgs, JSONRPC2Request req, Boolean useAuth) {
|
||||
|
||||
// Error on unnamed parameters
|
||||
if (req.getParamsType() != JSONRPC2ParamsType.OBJECT) {
|
||||
return JSONRPC2Error.INVALID_PARAMS;
|
||||
}
|
||||
Map<String, Object> params = req.getNamedParams();
|
||||
|
||||
// Validate authentication token.
|
||||
if (useAuth) {
|
||||
JSONRPC2Error err = validateToken(params);
|
||||
if (err != null) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// If there exist any required arguments.
|
||||
if (requiredArgs != null && requiredArgs.length > 0) {
|
||||
String missingArgs = "";
|
||||
for (int i = 0; i < requiredArgs.length; i++) {
|
||||
if (!params.containsKey(requiredArgs[i])) {
|
||||
missingArgs = missingArgs.concat(requiredArgs[i] + ",");
|
||||
}
|
||||
}
|
||||
if (missingArgs.length() > 0) {
|
||||
missingArgs = missingArgs.substring(0, missingArgs.length() - 1);
|
||||
return new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(), "Missing parameter(s): " + missingArgs);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check incoming request for required arguments, to make sure they are valid. Will authenticate req.
|
||||
* @param requiredArgs - Array of names of required arguments. If null don't check for any parameters.
|
||||
* @param req - Incoming JSONRPC2 request
|
||||
* @return - null if no errors were found. Corresponding JSONRPC2Error if error is found.
|
||||
*/
|
||||
public JSONRPC2Error validateParams(String[] requiredArgs, JSONRPC2Request req) {
|
||||
return validateParams(requiredArgs, req, JSONRPC2Helper.USE_AUTH);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Will check incoming parameters to make sure they contain a valid token.
|
||||
* @param req - Parameters of incoming request
|
||||
* @return null if everything is fine, JSONRPC2Error for any corresponding error.
|
||||
*/
|
||||
private JSONRPC2Error validateToken(Map<String, Object> params) {
|
||||
String tokenID = (String) params.get("Token");
|
||||
if (tokenID == null) {
|
||||
return JSONRPC2ExtendedError.NO_TOKEN;
|
||||
}
|
||||
try {
|
||||
_secMan.verifyToken(tokenID);
|
||||
} catch (InvalidAuthTokenException e) {
|
||||
return JSONRPC2ExtendedError.INVALID_TOKEN;
|
||||
} catch (ExpiredAuthTokenException e) {
|
||||
JSONRPC2Error err = new JSONRPC2ExtendedError(JSONRPC2ExtendedError.TOKEN_EXPIRED.getCode(),
|
||||
"Provided authentication token expired " + e.getExpirytime() + ", will be removed.");
|
||||
return err;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
package net.i2p.i2pcontrol.servlets.jsonrpc2handlers;
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
|
||||
import com.thetransactioncompany.jsonrpc2.server.MessageContext;
|
||||
import com.thetransactioncompany.jsonrpc2.server.RequestHandler;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.transport.FIFOBandwidthRefiller;
|
||||
import net.i2p.router.transport.TransportManager;
|
||||
import net.i2p.router.transport.ntcp.NTCPTransport;
|
||||
import net.i2p.router.transport.udp.UDPTransport;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* Copyright 2011 hottuna (dev@robertfoss.se)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
public class NetworkSettingHandler implements RequestHandler {
|
||||
private static final int BW_BURST_PCT = 110;
|
||||
private static final int BW_BURST_TIME = 20;
|
||||
private final JSONRPC2Helper _helper;
|
||||
private final RouterContext _context;
|
||||
|
||||
public NetworkSettingHandler(RouterContext ctx, JSONRPC2Helper helper) {
|
||||
_helper = helper;
|
||||
_context = ctx;
|
||||
}
|
||||
|
||||
// Reports the method names of the handled requests
|
||||
public String[] handledRequests() {
|
||||
return new String[] {"NetworkSetting"};
|
||||
}
|
||||
|
||||
// Processes the requests
|
||||
public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) {
|
||||
if (req.getMethod().equals("NetworkSetting")) {
|
||||
return process(req);
|
||||
} else {
|
||||
// Method name not supported
|
||||
return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, req.getID());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private JSONRPC2Response process(JSONRPC2Request req) {
|
||||
JSONRPC2Error err = _helper.validateParams(null, req);
|
||||
if (err != null)
|
||||
return new JSONRPC2Response(err, req.getID());
|
||||
|
||||
if (_context == null) {
|
||||
return new JSONRPC2Response(
|
||||
new JSONRPC2Error(JSONRPC2Error.INTERNAL_ERROR.getCode(),
|
||||
"RouterContext was not initialized. Query failed"),
|
||||
req.getID());
|
||||
}
|
||||
Map<String, Object> inParams = req.getNamedParams();
|
||||
Map<String, Object> outParams = new HashMap<String, Object>(4);
|
||||
|
||||
boolean restartNeeded = false;
|
||||
boolean settingsSaved = false;
|
||||
String inParam;
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.ntcp.port")) {
|
||||
String oldNTCPPort = _context.getProperty(NTCPTransport.PROP_I2NP_NTCP_PORT);
|
||||
if ((inParam = (String) inParams.get("i2p.router.net.ntcp.port")) != null) {
|
||||
if (oldNTCPPort == null || !oldNTCPPort.equals(inParam.trim())) {
|
||||
Integer newPort;
|
||||
try {
|
||||
newPort = Integer.valueOf(inParam);
|
||||
if (newPort < 1 || newPort > 65535) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return new JSONRPC2Response(
|
||||
new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"\"i2p.router.net.ntcp.port\" must be a string representing a number in the range 1-65535. " + inParam + " isn't valid."),
|
||||
req.getID());
|
||||
}
|
||||
Map<String, String> config = new HashMap<String, String>();
|
||||
config.put(NTCPTransport.PROP_I2NP_NTCP_PORT, String.valueOf(newPort));
|
||||
config.put(NTCPTransport.PROP_I2NP_NTCP_AUTO_PORT, "false");
|
||||
_context.router().saveConfig(config, null);
|
||||
restartNeeded = true;
|
||||
}
|
||||
settingsSaved = true;
|
||||
} else {
|
||||
String sAutoPort = _context.getProperty(NTCPTransport.PROP_I2NP_NTCP_AUTO_PORT, "true");
|
||||
boolean oldAutoPort = "true".equalsIgnoreCase(sAutoPort);
|
||||
if (oldAutoPort) {
|
||||
String oldSSUPort = "" + _context.getProperty(UDPTransport.PROP_INTERNAL_PORT, 8887);
|
||||
outParams.put("i2p.router.net.ntcp.port", oldSSUPort);
|
||||
} else {
|
||||
outParams.put("i2p.router.net.ntcp.port", oldNTCPPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.ntcp.hostname")) {
|
||||
String oldNTCPHostname = _context.getProperty(NTCPTransport.PROP_I2NP_NTCP_HOSTNAME);
|
||||
if ((inParam = (String) inParams.get("i2p.router.net.ntcp.hostname")) != null) {
|
||||
if (oldNTCPHostname == null || !oldNTCPHostname.equals(inParam.trim())) {
|
||||
_context.router().saveConfig(NTCPTransport.PROP_I2NP_NTCP_HOSTNAME, inParam);
|
||||
restartNeeded = true;
|
||||
}
|
||||
settingsSaved = true;
|
||||
} else {
|
||||
outParams.put("i2p.router.net.ntcp.hostname", oldNTCPHostname);
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.ntcp.autoip")) {
|
||||
String oldNTCPAutoIP = _context.getProperty(NTCPTransport.PROP_I2NP_NTCP_AUTO_IP);
|
||||
if ((inParam = (String) inParams.get("i2p.router.net.ntcp.autoip")) != null) {
|
||||
inParam = inParam.trim().toLowerCase();
|
||||
if (oldNTCPAutoIP == null || !oldNTCPAutoIP.equals(inParam)) {
|
||||
if ("always".equals(inParam) || "true".equals(inParam) || "false".equals(inParam)) {
|
||||
_context.router().saveConfig(NTCPTransport.PROP_I2NP_NTCP_AUTO_IP, inParam);
|
||||
restartNeeded = true;
|
||||
} else {
|
||||
return new JSONRPC2Response(
|
||||
new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"\"i2p.router.net.ntcp.autoip\" can only be always, true or false. " + inParam + " isn't valid."),
|
||||
req.getID());
|
||||
}
|
||||
}
|
||||
settingsSaved = true;
|
||||
} else {
|
||||
outParams.put("i2p.router.net.ntcp.autoip", oldNTCPAutoIP);
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.ssu.port")) {
|
||||
String oldSSUPort = "" + _context.getProperty(UDPTransport.PROP_INTERNAL_PORT, 8887);
|
||||
if ((inParam = (String) inParams.get("i2p.router.net.ssu.port")) != null) {
|
||||
if (oldSSUPort == null || !oldSSUPort.equals(inParam.trim())) {
|
||||
Integer newPort;
|
||||
try {
|
||||
newPort = Integer.valueOf(inParam);
|
||||
if (newPort < 1 || newPort > 65535) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return new JSONRPC2Response(
|
||||
new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"\"i2p.router.net.ssu.port\" must be a string representing a number in the range 1-65535. " + inParam + " isn't valid."),
|
||||
req.getID());
|
||||
}
|
||||
Map<String, String> config = new HashMap<String, String>();
|
||||
config.put(UDPTransport.PROP_EXTERNAL_PORT, String.valueOf(newPort));
|
||||
config.put(UDPTransport.PROP_INTERNAL_PORT, String.valueOf(newPort));
|
||||
_context.router().saveConfig(config, null);
|
||||
restartNeeded = true;
|
||||
}
|
||||
settingsSaved = true;
|
||||
} else {
|
||||
outParams.put("i2p.router.net.ssu.port", oldSSUPort);
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.ssu.hostname")) {
|
||||
String oldSSUHostname = _context.getProperty(UDPTransport.PROP_EXTERNAL_HOST);
|
||||
if ((inParam = (String) inParams.get("i2p.router.net.ssu.hostname")) != null) {
|
||||
if (oldSSUHostname == null || !oldSSUHostname.equals(inParam.trim())) {
|
||||
_context.router().saveConfig(UDPTransport.PROP_EXTERNAL_HOST, inParam);
|
||||
restartNeeded = true;
|
||||
}
|
||||
settingsSaved = true;
|
||||
} else {
|
||||
outParams.put("i2p.router.net.ssu.hostname", oldSSUHostname);
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.ssu.autoip")) {
|
||||
String oldSSUAutoIP = _context.getProperty(UDPTransport.PROP_SOURCES);
|
||||
if ((inParam = (String) inParams.get("i2p.router.net.ssu.autoip")) != null) {
|
||||
inParam = inParam.trim().toLowerCase();
|
||||
if (oldSSUAutoIP == null || !oldSSUAutoIP.equals(inParam)) {
|
||||
if (inParam.equals("ssu") || inParam.equals("local,ssu") || inParam.equals("upnp,ssu") || inParam.equals("local,upnp,ssu")) {
|
||||
_context.router().saveConfig(UDPTransport.PROP_SOURCES, inParam);
|
||||
restartNeeded = true;
|
||||
} else {
|
||||
return new JSONRPC2Response(
|
||||
new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"\"i2p.router.net.ssu.autoip\" can only be ssu/local,upnp,ssu/local/ssu/upnp,ssu. " + inParam + " isn't valid."),
|
||||
req.getID());
|
||||
}
|
||||
}
|
||||
settingsSaved = true;
|
||||
} else {
|
||||
outParams.put("i2p.router.net.ssu.autoip", oldSSUAutoIP);
|
||||
}
|
||||
}
|
||||
|
||||
// Non-setable key.
|
||||
if (inParams.containsKey("i2p.router.net.ssu.detectedip")) {
|
||||
if ((inParam = (String) inParams.get("i2p.router.net.ssu.autoip")) == null) {
|
||||
byte[] ipBytes = _context.router().getRouterInfo().getTargetAddress("SSU").getIP();
|
||||
try {
|
||||
InetAddress i = InetAddress.getByAddress(ipBytes);
|
||||
outParams.put("i2p.router.net.ssu.detectedip", i.getHostAddress());
|
||||
} catch (UnknownHostException e) {
|
||||
outParams.put("i2p.router.net.ssu.detectedip", "Failed to parse ip address");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.upnp")) {
|
||||
String oldUPNP = _context.getProperty(TransportManager.PROP_ENABLE_UPNP);
|
||||
if ((inParam = (String) inParams.get("i2p.router.net.upnp")) != null) {
|
||||
if (oldUPNP == null || !oldUPNP.equals(inParam.trim())) {
|
||||
_context.router().saveConfig(TransportManager.PROP_ENABLE_UPNP, inParam);
|
||||
restartNeeded = true;
|
||||
}
|
||||
settingsSaved = true;
|
||||
} else {
|
||||
outParams.put("i2p.router.net.upnp", oldUPNP);
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.bw.share")) {
|
||||
String oldShare = _context.router().getConfigSetting(Router.PROP_BANDWIDTH_SHARE_PERCENTAGE);
|
||||
if ((inParam = (String) inParams.get("i2p.router.net.bw.share")) != null) {
|
||||
if (oldShare == null || !oldShare.equals(inParam.trim())) {
|
||||
Integer percent;
|
||||
try {
|
||||
percent = Integer.parseInt(inParam);
|
||||
if (percent < 0 || percent > 100 || inParam.length() == 0) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return new JSONRPC2Response(
|
||||
new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"\"i2p.router.net.bw.share\" A positive integer must supplied, \"" + inParam + "\" isn't valid"),
|
||||
req.getID());
|
||||
}
|
||||
_context.router().saveConfig(Router.PROP_BANDWIDTH_SHARE_PERCENTAGE, inParam);
|
||||
}
|
||||
settingsSaved = true;
|
||||
} else {
|
||||
outParams.put("i2p.router.net.bw.share", oldShare);
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.bw.in")) {
|
||||
String oldBWIn = _context.getProperty(FIFOBandwidthRefiller.PROP_INBOUND_BANDWIDTH);
|
||||
if ((inParam = (String) inParams.get("i2p.router.net.bw.in")) != null) {
|
||||
Integer rate;
|
||||
try {
|
||||
rate = Integer.parseInt(inParam);
|
||||
if (rate < 0 || inParam.length() == 0) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return new JSONRPC2Response(
|
||||
new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"\"i2p.router.net.bw.in\" A positive integer must supplied, " + inParam + " isn't valid"),
|
||||
req.getID());
|
||||
}
|
||||
Integer burstRate = (rate * BW_BURST_PCT) / 100;
|
||||
Integer burstSize = (burstRate * BW_BURST_TIME);
|
||||
if (oldBWIn == null || !oldBWIn.equals(rate.toString())) {
|
||||
Map<String, String> config = new HashMap<String, String>();
|
||||
config.put(FIFOBandwidthRefiller.PROP_INBOUND_BANDWIDTH, rate.toString());
|
||||
config.put(FIFOBandwidthRefiller.PROP_INBOUND_BURST_BANDWIDTH, burstRate.toString());
|
||||
config.put(FIFOBandwidthRefiller.PROP_INBOUND_BANDWIDTH_PEAK, burstSize.toString());
|
||||
_context.router().saveConfig(config, null);
|
||||
_context.bandwidthLimiter().reinitialize();
|
||||
}
|
||||
settingsSaved = true;
|
||||
} else {
|
||||
outParams.put("i2p.router.net.bw.in", oldBWIn);
|
||||
}
|
||||
}
|
||||
if (inParams.containsKey("i2p.router.net.bw.out")) {
|
||||
String oldBWOut = _context.getProperty(FIFOBandwidthRefiller.PROP_OUTBOUND_BANDWIDTH);
|
||||
if ((inParam = (String) inParams.get("i2p.router.net.bw.out")) != null) {
|
||||
Integer rate;
|
||||
try {
|
||||
rate = Integer.parseInt(inParam);
|
||||
if (rate < 0 || inParam.length() == 0)
|
||||
throw new NumberFormatException();
|
||||
} catch (NumberFormatException e) {
|
||||
return new JSONRPC2Response(
|
||||
new JSONRPC2Error(JSONRPC2Error.INVALID_PARAMS.getCode(),
|
||||
"\"i2p.router.net.bw.out\" A positive integer must supplied, " + inParam + " isn't valid"),
|
||||
req.getID());
|
||||
}
|
||||
Integer burstRate = (rate * BW_BURST_PCT) / 100;
|
||||
Integer burstSize = (burstRate * BW_BURST_TIME);
|
||||
if (oldBWOut == null || !oldBWOut.equals(rate.toString())) {
|
||||
Map<String, String> config = new HashMap<String, String>();
|
||||
config.put(FIFOBandwidthRefiller.PROP_OUTBOUND_BANDWIDTH, rate.toString());
|
||||
config.put(FIFOBandwidthRefiller.PROP_OUTBOUND_BURST_BANDWIDTH, burstRate.toString());
|
||||
config.put(FIFOBandwidthRefiller.PROP_OUTBOUND_BANDWIDTH_PEAK, burstSize.toString());
|
||||
_context.router().saveConfig(config, null);
|
||||
_context.bandwidthLimiter().reinitialize();
|
||||
}
|
||||
settingsSaved = true;
|
||||
} else {
|
||||
outParams.put("i2p.router.net.bw.out", oldBWOut);
|
||||
}
|
||||
}
|
||||
if (inParams.containsKey("i2p.router.net.laptopmode")) {
|
||||
String oldLaptopMode = _context.getProperty(UDPTransport.PROP_LAPTOP_MODE);
|
||||
if ((inParam = (String) inParams.get("i2p.router.net.laptopmode")) != null) {
|
||||
if (oldLaptopMode == null || !oldLaptopMode.equals(inParam.trim())) {
|
||||
_context.router().saveConfig(UDPTransport.PROP_LAPTOP_MODE, String.valueOf(inParam));
|
||||
}
|
||||
settingsSaved = true;
|
||||
} else {
|
||||
outParams.put("i2p.router.net.laptopmode", oldLaptopMode);
|
||||
}
|
||||
}
|
||||
|
||||
if (settingsSaved)
|
||||
_context.router().saveConfig();
|
||||
|
||||
outParams.put("SettingsSaved", settingsSaved);
|
||||
outParams.put("RestartNeeded", restartNeeded);
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package net.i2p.i2pcontrol.servlets.jsonrpc2handlers;
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
|
||||
import com.thetransactioncompany.jsonrpc2.server.MessageContext;
|
||||
import com.thetransactioncompany.jsonrpc2.server.RequestHandler;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.router.CommSystemFacade;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.RouterVersion;
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import net.i2p.router.transport.TransportUtil;
|
||||
import net.i2p.router.transport.ntcp.NTCPTransport;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* Copyright 2011 hottuna (dev@robertfoss.se)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
public class RouterInfoHandler implements RequestHandler {
|
||||
private final JSONRPC2Helper _helper;
|
||||
private final RouterContext _context;
|
||||
|
||||
public RouterInfoHandler(RouterContext ctx, JSONRPC2Helper helper) {
|
||||
_helper = helper;
|
||||
_context = ctx;
|
||||
}
|
||||
|
||||
|
||||
// Reports the method names of the handled requests
|
||||
public String[] handledRequests() {
|
||||
return new String[] { "RouterInfo" };
|
||||
}
|
||||
|
||||
// Processes the requests
|
||||
public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) {
|
||||
if (req.getMethod().equals("RouterInfo")) {
|
||||
return process(req);
|
||||
} else {
|
||||
// Method name not supported
|
||||
return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND,
|
||||
req.getID());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private JSONRPC2Response process(JSONRPC2Request req) {
|
||||
JSONRPC2Error err = _helper.validateParams(null, req);
|
||||
if (err != null)
|
||||
return new JSONRPC2Response(err, req.getID());
|
||||
|
||||
if (_context == null) {
|
||||
return new JSONRPC2Response(new JSONRPC2Error(
|
||||
JSONRPC2Error.INTERNAL_ERROR.getCode(),
|
||||
"RouterContext was not initialized. Query failed"),
|
||||
req.getID());
|
||||
}
|
||||
Map<String, Object> inParams = req.getNamedParams();
|
||||
Map outParams = new HashMap();
|
||||
|
||||
if (inParams.containsKey("i2p.router.version")) {
|
||||
try {
|
||||
Class rvClass = Class.forName("net.i2p.router.RouterVersion");
|
||||
java.lang.reflect.Field field = rvClass.getDeclaredField("FULL_VERSION");
|
||||
String fullVersion = (String) field.get(new RouterVersion());
|
||||
outParams.put("i2p.router.version", fullVersion);
|
||||
} catch (Exception e) {} // Ignore
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.uptime")) {
|
||||
Router router = _context.router();
|
||||
if (router == null) {
|
||||
outParams.put("i2p.router.uptime", 0);
|
||||
} else {
|
||||
outParams.put("i2p.router.uptime", router.getUptime());
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.status")) {
|
||||
outParams.put("i2p.router.status", _context.throttle().getTunnelStatus());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.status")) {
|
||||
outParams.put("i2p.router.net.status", getNetworkStatus().ordinal());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.bw.inbound.1s")) {
|
||||
outParams.put("i2p.router.net.bw.inbound.1s", _context.bandwidthLimiter().getReceiveBps());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.bw.outbound.1s")) {
|
||||
outParams.put("i2p.router.net.bw.outbound.1s", _context.bandwidthLimiter().getSendBps());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.bw.inbound.15s")) {
|
||||
outParams.put("i2p.router.net.bw.inbound.15s", _context.bandwidthLimiter().getReceiveBps15s());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.bw.outbound.15s")) {
|
||||
outParams.put("i2p.router.net.bw.outbound.15s", _context.bandwidthLimiter().getSendBps15s());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.tunnels.participating")) {
|
||||
outParams.put("i2p.router.net.tunnels.participating", _context.tunnelManager().getParticipatingCount());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.netdb.knownpeers")) {
|
||||
// Why max(-1, 0) is used I don't know, it is the implementation used in the router console.
|
||||
outParams.put("i2p.router.netdb.knownpeers", Math.max(_context.netDb().getKnownRouters() - 1, 0));
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.netdb.activepeers")) {
|
||||
outParams.put("i2p.router.netdb.activepeers", _context.commSystem().countActivePeers());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.netdb.fastpeers")) {
|
||||
outParams.put("i2p.router.netdb.fastpeers", _context.profileOrganizer().countFastPeers());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.netdb.highcapacitypeers")) {
|
||||
outParams.put("i2p.router.netdb.highcapacitypeers", _context.profileOrganizer().countHighCapacityPeers());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.netdb.isreseeding")) {
|
||||
outParams.put("i2p.router.netdb.isreseeding", Boolean.valueOf(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress")).booleanValue());
|
||||
}
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
}
|
||||
|
||||
private static enum NETWORK_STATUS {
|
||||
OK,
|
||||
TESTING,
|
||||
FIREWALLED,
|
||||
HIDDEN,
|
||||
WARN_FIREWALLED_AND_FAST,
|
||||
WARN_FIREWALLED_AND_FLOODFILL,
|
||||
WARN_FIREWALLED_WITH_INBOUND_TCP,
|
||||
WARN_FIREWALLED_WITH_UDP_DISABLED,
|
||||
ERROR_I2CP,
|
||||
ERROR_CLOCK_SKEW,
|
||||
ERROR_PRIVATE_TCP_ADDRESS,
|
||||
ERROR_SYMMETRIC_NAT,
|
||||
ERROR_UDP_PORT_IN_USE,
|
||||
ERROR_NO_ACTIVE_PEERS_CHECK_CONNECTION_AND_FIREWALL,
|
||||
ERROR_UDP_DISABLED_AND_TCP_UNSET,
|
||||
};
|
||||
|
||||
// Ripped out of SummaryHelper.java
|
||||
private NETWORK_STATUS getNetworkStatus() {
|
||||
if (_context.router().getUptime() > 60 * 1000
|
||||
&& (!_context.router().gracefulShutdownInProgress())
|
||||
&& !_context.clientManager().isAlive())
|
||||
return (NETWORK_STATUS.ERROR_I2CP);
|
||||
long skew = _context.commSystem().getFramedAveragePeerClockSkew(33);
|
||||
// Display the actual skew, not the offset
|
||||
if (Math.abs(skew) > 60 * 1000)
|
||||
return NETWORK_STATUS.ERROR_CLOCK_SKEW;
|
||||
if (_context.router().isHidden())
|
||||
return (NETWORK_STATUS.HIDDEN);
|
||||
|
||||
int status = _context.commSystem().getStatus().getCode();
|
||||
switch (status) {
|
||||
case CommSystemFacade.STATUS_OK:
|
||||
RouterAddress ra = _context.router().getRouterInfo().getTargetAddress("NTCP");
|
||||
if (ra == null || TransportUtil.isPubliclyRoutable(ra.getIP(), true))
|
||||
return NETWORK_STATUS.OK;
|
||||
return NETWORK_STATUS.ERROR_PRIVATE_TCP_ADDRESS;
|
||||
case CommSystemFacade.STATUS_DIFFERENT:
|
||||
return NETWORK_STATUS.ERROR_SYMMETRIC_NAT;
|
||||
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
|
||||
if (_context.router().getRouterInfo().getTargetAddress("NTCP") != null)
|
||||
return NETWORK_STATUS.WARN_FIREWALLED_WITH_INBOUND_TCP;
|
||||
if (((FloodfillNetworkDatabaseFacade) _context.netDb()).floodfillEnabled())
|
||||
return NETWORK_STATUS.WARN_FIREWALLED_AND_FLOODFILL;
|
||||
if (_context.router().getRouterInfo().getCapabilities().indexOf('O') >= 0)
|
||||
return NETWORK_STATUS.WARN_FIREWALLED_AND_FAST;
|
||||
return NETWORK_STATUS.FIREWALLED;
|
||||
case CommSystemFacade.STATUS_HOSED:
|
||||
return NETWORK_STATUS.ERROR_UDP_PORT_IN_USE;
|
||||
case CommSystemFacade.STATUS_UNKNOWN: // fallthrough
|
||||
default:
|
||||
ra = _context.router().getRouterInfo().getTargetAddress("SSU");
|
||||
if (ra == null && _context.router().getUptime() > 5 * 60 * 1000) {
|
||||
if (_context.commSystem().countActivePeers() <= 0)
|
||||
return NETWORK_STATUS.ERROR_NO_ACTIVE_PEERS_CHECK_CONNECTION_AND_FIREWALL;
|
||||
else if (_context.getProperty(NTCPTransport.PROP_I2NP_NTCP_HOSTNAME) == null || _context.getProperty(NTCPTransport.PROP_I2NP_NTCP_PORT) == null)
|
||||
return NETWORK_STATUS.ERROR_UDP_DISABLED_AND_TCP_UNSET;
|
||||
else
|
||||
return NETWORK_STATUS.WARN_FIREWALLED_WITH_UDP_DISABLED;
|
||||
}
|
||||
return NETWORK_STATUS.TESTING;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package net.i2p.i2pcontrol.servlets.jsonrpc2handlers;
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
|
||||
import com.thetransactioncompany.jsonrpc2.server.MessageContext;
|
||||
import com.thetransactioncompany.jsonrpc2.server.RequestHandler;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.networkdb.reseed.ReseedChecker;
|
||||
import net.i2p.update.UpdateManager;
|
||||
import net.i2p.update.UpdateType;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import org.tanukisoftware.wrapper.WrapperManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* Copyright 2011 hottuna (dev@robertfoss.se)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
public class RouterManagerHandler implements RequestHandler {
|
||||
private final JSONRPC2Helper _helper;
|
||||
private final RouterContext _context;
|
||||
|
||||
private final static int SHUTDOWN_WAIT = 1500;
|
||||
|
||||
|
||||
public RouterManagerHandler(RouterContext ctx, JSONRPC2Helper helper) {
|
||||
_helper = helper;
|
||||
_context = ctx;
|
||||
}
|
||||
|
||||
// Reports the method names of the handled requests
|
||||
public String[] handledRequests() {
|
||||
return new String[] { "RouterManager" };
|
||||
}
|
||||
|
||||
// Processes the requests
|
||||
public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) {
|
||||
if (req.getMethod().equals("RouterManager")) {
|
||||
return process(req);
|
||||
} else {
|
||||
// Method name not supported
|
||||
return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND,
|
||||
req.getID());
|
||||
}
|
||||
}
|
||||
|
||||
private JSONRPC2Response process(JSONRPC2Request req) {
|
||||
JSONRPC2Error err = _helper.validateParams(null, req);
|
||||
if (err != null)
|
||||
return new JSONRPC2Response(err, req.getID());
|
||||
|
||||
if (_context == null) {
|
||||
return new JSONRPC2Response(new JSONRPC2Error(
|
||||
JSONRPC2Error.INTERNAL_ERROR.getCode(),
|
||||
"RouterContext was not initialized. Query failed"),
|
||||
req.getID());
|
||||
}
|
||||
Map<String, Object> inParams = req.getNamedParams();
|
||||
final Map<String, Object> outParams = new HashMap<String, Object>(4);
|
||||
|
||||
if (inParams.containsKey("Shutdown")) {
|
||||
outParams.put("Shutdown", null);
|
||||
(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(SHUTDOWN_WAIT);
|
||||
} catch (InterruptedException e) {}
|
||||
_context.addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_HARD));
|
||||
_context.router().shutdown(Router.EXIT_HARD);
|
||||
}
|
||||
}).start();
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("Restart")) {
|
||||
outParams.put("Restart", null);
|
||||
(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(SHUTDOWN_WAIT);
|
||||
} catch (InterruptedException e) {}
|
||||
_context.addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_HARD_RESTART));
|
||||
_context.router().shutdown(Router.EXIT_HARD_RESTART);
|
||||
}
|
||||
}).start();
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("ShutdownGraceful")) {
|
||||
outParams.put("ShutdownGraceful", null);
|
||||
(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(SHUTDOWN_WAIT);
|
||||
} catch (InterruptedException e) {}
|
||||
_context.addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_GRACEFUL));
|
||||
_context.router().shutdownGracefully();
|
||||
}
|
||||
}).start();
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("RestartGraceful")) {
|
||||
outParams.put("RestartGraceful", null);
|
||||
(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(SHUTDOWN_WAIT);
|
||||
} catch (InterruptedException e) {}
|
||||
_context.addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART));
|
||||
_context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART);
|
||||
}
|
||||
}).start();
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("Reseed")) {
|
||||
outParams.put("Reseed", null);
|
||||
(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
ReseedChecker reseeder = new ReseedChecker(_context);
|
||||
reseeder.requestReseed();
|
||||
}
|
||||
}).start();
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("FindUpdates")) {
|
||||
Thread t = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
ClientAppManager clmgr = I2PAppContext.getCurrentContext().clientAppManager();
|
||||
if (clmgr == null) {
|
||||
outParams.put("FindUpdates", "ClientAppManager is null");
|
||||
return;
|
||||
}
|
||||
UpdateManager upmgr = (UpdateManager) clmgr.getRegisteredApp(UpdateManager.APP_NAME);
|
||||
if (upmgr == null) {
|
||||
outParams.put("FindUpdates", "UpdateManager is null");
|
||||
return;
|
||||
}
|
||||
boolean updateIsAvailable = upmgr.checkAvailable(UpdateType.ROUTER_SIGNED) != null;
|
||||
outParams.put("FindUpdates", updateIsAvailable);
|
||||
}
|
||||
};
|
||||
t.start();
|
||||
try {
|
||||
t.join();
|
||||
} catch (InterruptedException e) {}
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("Update")) {
|
||||
Thread t = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
ClientAppManager clmgr = I2PAppContext.getCurrentContext().clientAppManager();
|
||||
if (clmgr == null) {
|
||||
outParams.put("Update", "ClientAppManager is null");
|
||||
return;
|
||||
}
|
||||
UpdateManager upmgr = (UpdateManager) clmgr.getRegisteredApp(UpdateManager.APP_NAME);
|
||||
if (upmgr == null) {
|
||||
outParams.put("Update", "UpdateManager is null");
|
||||
return;
|
||||
}
|
||||
boolean updateStarted = upmgr.update(UpdateType.ROUTER_SIGNED);
|
||||
if (!updateStarted) {
|
||||
outParams.put("Update", "Update not started");
|
||||
return;
|
||||
}
|
||||
boolean isUpdating = upmgr.isUpdateInProgress(UpdateType.ROUTER_SIGNED);
|
||||
while (isUpdating) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (Exception e) {}
|
||||
isUpdating = upmgr.isUpdateInProgress(UpdateType.ROUTER_SIGNED);
|
||||
}
|
||||
outParams.put("Update", upmgr.getStatus());
|
||||
}
|
||||
};
|
||||
t.start();
|
||||
try {
|
||||
t.join();
|
||||
} catch (InterruptedException e) {}
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
}
|
||||
|
||||
return new JSONRPC2Response(outParams, req.getID());
|
||||
}
|
||||
|
||||
public static class UpdateWrapperManagerTask implements Runnable {
|
||||
private int _exitCode;
|
||||
public UpdateWrapperManagerTask(int exitCode) {
|
||||
_exitCode = exitCode;
|
||||
}
|
||||
public void run() {
|
||||
try {
|
||||
WrapperManager.signalStopped(_exitCode);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
777
apps/i2pcontrol/java/org/mindrot/jbcrypt/BCrypt.java
Normal file
777
apps/i2pcontrol/java/org/mindrot/jbcrypt/BCrypt.java
Normal file
@@ -0,0 +1,777 @@
|
||||
// Copyright (c) 2006 Damien Miller <djm@mindrot.org>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package org.mindrot.jbcrypt;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* BCrypt implements OpenBSD-style Blowfish password hashing using
|
||||
* the scheme described in "A Future-Adaptable Password Scheme" by
|
||||
* Niels Provos and David Mazieres.
|
||||
* <p>
|
||||
* This password hashing system tries to thwart off-line password
|
||||
* cracking using a computationally-intensive hashing algorithm,
|
||||
* based on Bruce Schneier's Blowfish cipher. The work factor of
|
||||
* the algorithm is parameterised, so it can be increased as
|
||||
* computers get faster.
|
||||
* <p>
|
||||
* Usage is really simple. To hash a password for the first time,
|
||||
* call the hashpw method with a random salt, like this:
|
||||
* <p>
|
||||
* <code>
|
||||
* String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
|
||||
* </code>
|
||||
* <p>
|
||||
* To check whether a plaintext password matches one that has been
|
||||
* hashed previously, use the checkpw method:
|
||||
* <p>
|
||||
* <code>
|
||||
* if (BCrypt.checkpw(candidate_password, stored_hash))
|
||||
* System.out.println("It matches");
|
||||
* else
|
||||
* System.out.println("It does not match");
|
||||
* </code>
|
||||
* <p>
|
||||
* The gensalt() method takes an optional parameter (log_rounds)
|
||||
* that determines the computational complexity of the hashing:
|
||||
* <p>
|
||||
* <code>
|
||||
* String strong_salt = BCrypt.gensalt(10)
|
||||
* String stronger_salt = BCrypt.gensalt(12)
|
||||
* </code>
|
||||
* <p>
|
||||
* The amount of work increases exponentially (2**log_rounds), so
|
||||
* each increment is twice as much work. The default log_rounds is
|
||||
* 10, and the valid range is 4 to 30.
|
||||
*
|
||||
* @author Damien Miller
|
||||
* @version 0.2
|
||||
*/
|
||||
public class BCrypt {
|
||||
// BCrypt parameters
|
||||
private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
|
||||
private static final int BCRYPT_SALT_LEN = 16;
|
||||
|
||||
// Blowfish parameters
|
||||
private static final int BLOWFISH_NUM_ROUNDS = 16;
|
||||
|
||||
// Initial contents of key schedule
|
||||
private static final int P_orig[] = {
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
|
||||
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
|
||||
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
|
||||
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
|
||||
0x9216d5d9, 0x8979fb1b
|
||||
};
|
||||
private static final int S_orig[] = {
|
||||
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
|
||||
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
|
||||
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
|
||||
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
|
||||
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
|
||||
0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
|
||||
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
|
||||
0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
|
||||
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
|
||||
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
|
||||
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
|
||||
0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
|
||||
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
|
||||
0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
|
||||
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
|
||||
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
|
||||
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
|
||||
0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
|
||||
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
|
||||
0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
|
||||
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
|
||||
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
|
||||
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
|
||||
0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
|
||||
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
|
||||
0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
|
||||
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
|
||||
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
|
||||
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
|
||||
0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
|
||||
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
|
||||
0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
|
||||
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
|
||||
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
|
||||
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
|
||||
0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
|
||||
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
|
||||
0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
|
||||
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
|
||||
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
|
||||
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
|
||||
0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
|
||||
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
|
||||
0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
|
||||
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
|
||||
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
|
||||
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
|
||||
0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
|
||||
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
|
||||
0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
|
||||
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
|
||||
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
|
||||
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
|
||||
0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
|
||||
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
|
||||
0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
|
||||
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
|
||||
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
|
||||
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
|
||||
0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
|
||||
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
|
||||
0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
|
||||
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
|
||||
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
|
||||
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
|
||||
0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
|
||||
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
|
||||
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
|
||||
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
|
||||
0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
|
||||
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
|
||||
0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
|
||||
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
|
||||
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
|
||||
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
|
||||
0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
|
||||
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
|
||||
0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
|
||||
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
|
||||
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
|
||||
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
|
||||
0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
|
||||
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
|
||||
0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
|
||||
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
|
||||
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
|
||||
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
|
||||
0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
|
||||
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
|
||||
0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
|
||||
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
|
||||
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
|
||||
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
|
||||
0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
|
||||
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
|
||||
0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
|
||||
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
|
||||
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
|
||||
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
|
||||
0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
|
||||
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
|
||||
0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
|
||||
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
|
||||
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
|
||||
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
|
||||
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
|
||||
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
|
||||
0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
|
||||
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
|
||||
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
|
||||
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
|
||||
0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
|
||||
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
|
||||
0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
|
||||
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
|
||||
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
|
||||
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
|
||||
0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
|
||||
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
|
||||
0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
|
||||
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
|
||||
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
|
||||
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
|
||||
0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
|
||||
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
|
||||
0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
|
||||
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
|
||||
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
|
||||
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
|
||||
0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
|
||||
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
|
||||
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
|
||||
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
|
||||
0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
|
||||
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
|
||||
0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
|
||||
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
|
||||
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
|
||||
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
|
||||
0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
|
||||
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
|
||||
0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
|
||||
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
|
||||
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
|
||||
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
|
||||
0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
|
||||
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
|
||||
0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
|
||||
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
|
||||
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
|
||||
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
|
||||
0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
|
||||
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
|
||||
0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
|
||||
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
|
||||
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
|
||||
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
|
||||
0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
|
||||
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
|
||||
0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
|
||||
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
|
||||
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
|
||||
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
|
||||
0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
|
||||
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
|
||||
0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
|
||||
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
|
||||
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
|
||||
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
|
||||
0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
|
||||
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
|
||||
0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
|
||||
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
|
||||
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
|
||||
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
|
||||
0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
|
||||
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
|
||||
0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
|
||||
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
|
||||
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
|
||||
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
|
||||
0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
|
||||
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
|
||||
0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
|
||||
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
|
||||
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
|
||||
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
|
||||
0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
|
||||
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
|
||||
0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
|
||||
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
|
||||
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
|
||||
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
|
||||
0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
|
||||
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
|
||||
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
|
||||
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
|
||||
0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
|
||||
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
|
||||
0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
|
||||
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
|
||||
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
|
||||
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
|
||||
0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
|
||||
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
|
||||
0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
|
||||
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
|
||||
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
|
||||
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
|
||||
0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
|
||||
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
|
||||
0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
|
||||
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
|
||||
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
|
||||
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
|
||||
0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
|
||||
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
|
||||
0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
|
||||
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
|
||||
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
|
||||
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
|
||||
0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
|
||||
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
|
||||
0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
|
||||
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
|
||||
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
|
||||
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
|
||||
0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
|
||||
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
|
||||
0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
|
||||
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
|
||||
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
|
||||
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
|
||||
0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
|
||||
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
|
||||
0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
|
||||
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
|
||||
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
|
||||
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
|
||||
0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
|
||||
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
|
||||
0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
|
||||
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
|
||||
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
|
||||
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
|
||||
0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
|
||||
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
|
||||
0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
|
||||
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
|
||||
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
|
||||
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
|
||||
0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
|
||||
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
|
||||
0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
|
||||
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
|
||||
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
|
||||
};
|
||||
|
||||
// bcrypt IV: "OrpheanBeholderScryDoubt". The C implementation calls
|
||||
// this "ciphertext", but it is really plaintext or an IV. We keep
|
||||
// the name to make code comparison easier.
|
||||
static private final int bf_crypt_ciphertext[] = {
|
||||
0x4f727068, 0x65616e42, 0x65686f6c,
|
||||
0x64657253, 0x63727944, 0x6f756274
|
||||
};
|
||||
|
||||
// Table for Base64 encoding
|
||||
static private final char base64_code[] = {
|
||||
'.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
||||
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
|
||||
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
||||
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
|
||||
'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9'
|
||||
};
|
||||
|
||||
// Table for Base64 decoding
|
||||
static private final byte index_64[] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, 0, 1, 54, 55,
|
||||
56, 57, 58, 59, 60, 61, 62, 63, -1, -1,
|
||||
-1, -1, -1, -1, -1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
|
||||
-1, -1, -1, -1, -1, -1, 28, 29, 30,
|
||||
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
|
||||
51, 52, 53, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
// Expanded Blowfish key
|
||||
private int P[];
|
||||
private int S[];
|
||||
|
||||
/**
|
||||
* Encode a byte array using bcrypt's slightly-modified base64
|
||||
* encoding scheme. Note that this is *not* compatible with
|
||||
* the standard MIME-base64 encoding.
|
||||
*
|
||||
* @param d the byte array to encode
|
||||
* @param len the number of bytes to encode
|
||||
* @return base64-encoded string
|
||||
* @exception IllegalArgumentException if the length is invalid
|
||||
*/
|
||||
private static String encode_base64(byte d[], int len)
|
||||
throws IllegalArgumentException {
|
||||
int off = 0;
|
||||
StringBuffer rs = new StringBuffer();
|
||||
int c1, c2;
|
||||
|
||||
if (len <= 0 || len > d.length)
|
||||
throw new IllegalArgumentException ("Invalid len");
|
||||
|
||||
while (off < len) {
|
||||
c1 = d[off++] & 0xff;
|
||||
rs.append(base64_code[(c1 >> 2) & 0x3f]);
|
||||
c1 = (c1 & 0x03) << 4;
|
||||
if (off >= len) {
|
||||
rs.append(base64_code[c1 & 0x3f]);
|
||||
break;
|
||||
}
|
||||
c2 = d[off++] & 0xff;
|
||||
c1 |= (c2 >> 4) & 0x0f;
|
||||
rs.append(base64_code[c1 & 0x3f]);
|
||||
c1 = (c2 & 0x0f) << 2;
|
||||
if (off >= len) {
|
||||
rs.append(base64_code[c1 & 0x3f]);
|
||||
break;
|
||||
}
|
||||
c2 = d[off++] & 0xff;
|
||||
c1 |= (c2 >> 6) & 0x03;
|
||||
rs.append(base64_code[c1 & 0x3f]);
|
||||
rs.append(base64_code[c2 & 0x3f]);
|
||||
}
|
||||
return rs.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the 3 bits base64-encoded by the specified character,
|
||||
* range-checking againt conversion table
|
||||
* @param x the base64-encoded value
|
||||
* @return the decoded value of x
|
||||
*/
|
||||
private static byte char64(char x) {
|
||||
if ((int)x < 0 || (int)x > index_64.length)
|
||||
return -1;
|
||||
return index_64[(int)x];
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a string encoded using bcrypt's base64 scheme to a
|
||||
* byte array. Note that this is *not* compatible with
|
||||
* the standard MIME-base64 encoding.
|
||||
* @param s the string to decode
|
||||
* @param maxolen the maximum number of bytes to decode
|
||||
* @return an array containing the decoded bytes
|
||||
* @throws IllegalArgumentException if maxolen is invalid
|
||||
*/
|
||||
private static byte[] decode_base64(String s, int maxolen)
|
||||
throws IllegalArgumentException {
|
||||
StringBuffer rs = new StringBuffer();
|
||||
int off = 0, slen = s.length(), olen = 0;
|
||||
byte ret[];
|
||||
byte c1, c2, c3, c4, o;
|
||||
|
||||
if (maxolen <= 0)
|
||||
throw new IllegalArgumentException ("Invalid maxolen");
|
||||
|
||||
while (off < slen - 1 && olen < maxolen) {
|
||||
c1 = char64(s.charAt(off++));
|
||||
c2 = char64(s.charAt(off++));
|
||||
if (c1 == -1 || c2 == -1)
|
||||
break;
|
||||
o = (byte)(c1 << 2);
|
||||
o |= (c2 & 0x30) >> 4;
|
||||
rs.append((char)o);
|
||||
if (++olen >= maxolen || off >= slen)
|
||||
break;
|
||||
c3 = char64(s.charAt(off++));
|
||||
if (c3 == -1)
|
||||
break;
|
||||
o = (byte)((c2 & 0x0f) << 4);
|
||||
o |= (c3 & 0x3c) >> 2;
|
||||
rs.append((char)o);
|
||||
if (++olen >= maxolen || off >= slen)
|
||||
break;
|
||||
c4 = char64(s.charAt(off++));
|
||||
o = (byte)((c3 & 0x03) << 6);
|
||||
o |= c4;
|
||||
rs.append((char)o);
|
||||
++olen;
|
||||
}
|
||||
|
||||
ret = new byte[olen];
|
||||
for (off = 0; off < olen; off++)
|
||||
ret[off] = (byte)rs.charAt(off);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blowfish encipher a single 64-bit block encoded as
|
||||
* two 32-bit halves
|
||||
* @param lr an array containing the two 32-bit half blocks
|
||||
* @param off the position in the array of the blocks
|
||||
*/
|
||||
private final void encipher(int lr[], int off) {
|
||||
int i, n, l = lr[off], r = lr[off + 1];
|
||||
|
||||
l ^= P[0];
|
||||
for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) {
|
||||
// Feistel substitution on left word
|
||||
n = S[(l >> 24) & 0xff];
|
||||
n += S[0x100 | ((l >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((l >> 8) & 0xff)];
|
||||
n += S[0x300 | (l & 0xff)];
|
||||
r ^= n ^ P[++i];
|
||||
|
||||
// Feistel substitution on right word
|
||||
n = S[(r >> 24) & 0xff];
|
||||
n += S[0x100 | ((r >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((r >> 8) & 0xff)];
|
||||
n += S[0x300 | (r & 0xff)];
|
||||
l ^= n ^ P[++i];
|
||||
}
|
||||
lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1];
|
||||
lr[off + 1] = l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycically extract a word of key material
|
||||
* @param data the string to extract the data from
|
||||
* @param offp a "pointer" (as a one-entry array) to the
|
||||
* current offset into data
|
||||
* @return the next word of material from data
|
||||
*/
|
||||
private static int streamtoword(byte data[], int offp[]) {
|
||||
int i;
|
||||
int word = 0;
|
||||
int off = offp[0];
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
word = (word << 8) | (data[off] & 0xff);
|
||||
off = (off + 1) % data.length;
|
||||
}
|
||||
|
||||
offp[0] = off;
|
||||
return word;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the Blowfish key schedule
|
||||
*/
|
||||
private void init_key() {
|
||||
P = (int[])P_orig.clone();
|
||||
S = (int[])S_orig.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Key the Blowfish cipher
|
||||
* @param key an array containing the key
|
||||
*/
|
||||
private void key(byte key[]) {
|
||||
int i;
|
||||
int koffp[] = { 0 };
|
||||
int lr[] = { 0, 0 };
|
||||
int plen = P.length, slen = S.length;
|
||||
|
||||
for (i = 0; i < plen; i++)
|
||||
P[i] = P[i] ^ streamtoword(key, koffp);
|
||||
|
||||
for (i = 0; i < plen; i += 2) {
|
||||
encipher(lr, 0);
|
||||
P[i] = lr[0];
|
||||
P[i + 1] = lr[1];
|
||||
}
|
||||
|
||||
for (i = 0; i < slen; i += 2) {
|
||||
encipher(lr, 0);
|
||||
S[i] = lr[0];
|
||||
S[i + 1] = lr[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the "enhanced key schedule" step described by
|
||||
* Provos and Mazieres in "A Future-Adaptable Password Scheme"
|
||||
* http://www.openbsd.org/papers/bcrypt-paper.ps
|
||||
* @param data salt information
|
||||
* @param key password information
|
||||
*/
|
||||
private void ekskey(byte data[], byte key[]) {
|
||||
int i;
|
||||
int koffp[] = { 0 }, doffp[] = { 0 };
|
||||
int lr[] = { 0, 0 };
|
||||
int plen = P.length, slen = S.length;
|
||||
|
||||
for (i = 0; i < plen; i++)
|
||||
P[i] = P[i] ^ streamtoword(key, koffp);
|
||||
|
||||
for (i = 0; i < plen; i += 2) {
|
||||
lr[0] ^= streamtoword(data, doffp);
|
||||
lr[1] ^= streamtoword(data, doffp);
|
||||
encipher(lr, 0);
|
||||
P[i] = lr[0];
|
||||
P[i + 1] = lr[1];
|
||||
}
|
||||
|
||||
for (i = 0; i < slen; i += 2) {
|
||||
lr[0] ^= streamtoword(data, doffp);
|
||||
lr[1] ^= streamtoword(data, doffp);
|
||||
encipher(lr, 0);
|
||||
S[i] = lr[0];
|
||||
S[i + 1] = lr[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the central password hashing step in the
|
||||
* bcrypt scheme
|
||||
* @param password the password to hash
|
||||
* @param salt the binary salt to hash with the password
|
||||
* @param log_rounds the binary logarithm of the number
|
||||
* of rounds of hashing to apply
|
||||
* @param cdata the plaintext to encrypt
|
||||
* @return an array containing the binary hashed password
|
||||
*/
|
||||
public byte[] crypt_raw(byte password[], byte salt[], int log_rounds,
|
||||
int cdata[]) {
|
||||
int rounds, i, j;
|
||||
int clen = cdata.length;
|
||||
byte ret[];
|
||||
|
||||
if (log_rounds < 4 || log_rounds > 30)
|
||||
throw new IllegalArgumentException ("Bad number of rounds");
|
||||
rounds = 1 << log_rounds;
|
||||
if (salt.length != BCRYPT_SALT_LEN)
|
||||
throw new IllegalArgumentException ("Bad salt length");
|
||||
|
||||
init_key();
|
||||
ekskey(salt, password);
|
||||
for (i = 0; i != rounds; i++) {
|
||||
key(password);
|
||||
key(salt);
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
for (j = 0; j < (clen >> 1); j++)
|
||||
encipher(cdata, j << 1);
|
||||
}
|
||||
|
||||
ret = new byte[clen * 4];
|
||||
for (i = 0, j = 0; i < clen; i++) {
|
||||
ret[j++] = (byte)((cdata[i] >> 24) & 0xff);
|
||||
ret[j++] = (byte)((cdata[i] >> 16) & 0xff);
|
||||
ret[j++] = (byte)((cdata[i] >> 8) & 0xff);
|
||||
ret[j++] = (byte)(cdata[i] & 0xff);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a password using the OpenBSD bcrypt scheme
|
||||
* @param password the password to hash
|
||||
* @param salt the salt to hash with (perhaps generated
|
||||
* using BCrypt.gensalt)
|
||||
* @return the hashed password
|
||||
*/
|
||||
public static String hashpw(String password, String salt) {
|
||||
BCrypt B;
|
||||
String real_salt;
|
||||
byte passwordb[], saltb[], hashed[];
|
||||
char minor = (char)0;
|
||||
int rounds, off = 0;
|
||||
StringBuffer rs = new StringBuffer();
|
||||
|
||||
if (salt.charAt(0) != '$' || salt.charAt(1) != '2')
|
||||
throw new IllegalArgumentException ("Invalid salt version");
|
||||
if (salt.charAt(2) == '$')
|
||||
off = 3;
|
||||
else {
|
||||
minor = salt.charAt(2);
|
||||
if (minor != 'a' || salt.charAt(3) != '$')
|
||||
throw new IllegalArgumentException ("Invalid salt revision");
|
||||
off = 4;
|
||||
}
|
||||
|
||||
// Extract number of rounds
|
||||
if (salt.charAt(off + 2) > '$')
|
||||
throw new IllegalArgumentException ("Missing salt rounds");
|
||||
rounds = Integer.parseInt(salt.substring(off, off + 2));
|
||||
|
||||
real_salt = salt.substring(off + 3, off + 25);
|
||||
try {
|
||||
passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
throw new AssertionError("UTF-8 is not supported");
|
||||
}
|
||||
|
||||
saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);
|
||||
|
||||
B = new BCrypt();
|
||||
hashed = B.crypt_raw(passwordb, saltb, rounds,
|
||||
(int[])bf_crypt_ciphertext.clone());
|
||||
|
||||
rs.append("$2");
|
||||
if (minor >= 'a')
|
||||
rs.append(minor);
|
||||
rs.append("$");
|
||||
if (rounds < 10)
|
||||
rs.append("0");
|
||||
if (rounds > 30) {
|
||||
throw new IllegalArgumentException(
|
||||
"rounds exceeds maximum (30)");
|
||||
}
|
||||
rs.append(Integer.toString(rounds));
|
||||
rs.append("$");
|
||||
rs.append(encode_base64(saltb, saltb.length));
|
||||
rs.append(encode_base64(hashed,
|
||||
bf_crypt_ciphertext.length * 4 - 1));
|
||||
return rs.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method
|
||||
* @param log_rounds the log2 of the number of rounds of
|
||||
* hashing to apply - the work factor therefore increases as
|
||||
* 2**log_rounds.
|
||||
* @param random an instance of SecureRandom to use
|
||||
* @return an encoded salt value
|
||||
*/
|
||||
public static String gensalt(int log_rounds, SecureRandom random) {
|
||||
StringBuffer rs = new StringBuffer();
|
||||
byte rnd[] = new byte[BCRYPT_SALT_LEN];
|
||||
|
||||
random.nextBytes(rnd);
|
||||
|
||||
rs.append("$2a$");
|
||||
if (log_rounds < 10)
|
||||
rs.append("0");
|
||||
if (log_rounds > 30) {
|
||||
throw new IllegalArgumentException(
|
||||
"log_rounds exceeds maximum (30)");
|
||||
}
|
||||
rs.append(Integer.toString(log_rounds));
|
||||
rs.append("$");
|
||||
rs.append(encode_base64(rnd, rnd.length));
|
||||
return rs.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method
|
||||
* @param log_rounds the log2 of the number of rounds of
|
||||
* hashing to apply - the work factor therefore increases as
|
||||
* 2**log_rounds.
|
||||
* @return an encoded salt value
|
||||
*/
|
||||
public static String gensalt(int log_rounds) {
|
||||
return gensalt(log_rounds, new SecureRandom());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method,
|
||||
* selecting a reasonable default for the number of hashing
|
||||
* rounds to apply
|
||||
* @return an encoded salt value
|
||||
*/
|
||||
public static String gensalt() {
|
||||
return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a plaintext password matches a previously hashed
|
||||
* one
|
||||
* @param plaintext the plaintext password to verify
|
||||
* @param hashed the previously-hashed password
|
||||
* @return true if the passwords match, false otherwise
|
||||
*/
|
||||
public static boolean checkpw(String plaintext, String hashed) {
|
||||
byte hashed_bytes[];
|
||||
byte try_bytes[];
|
||||
try {
|
||||
String try_pw = hashpw(plaintext, hashed);
|
||||
hashed_bytes = hashed.getBytes("UTF-8");
|
||||
try_bytes = try_pw.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
return false;
|
||||
}
|
||||
if (hashed_bytes.length != try_bytes.length)
|
||||
return false;
|
||||
byte ret = 0;
|
||||
for (int i = 0; i < try_bytes.length; i++)
|
||||
ret |= hashed_bytes[i] ^ try_bytes[i];
|
||||
return ret == 0;
|
||||
}
|
||||
}
|
||||
15
apps/i2pcontrol/web.xml
Normal file
15
apps/i2pcontrol/web.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
|
||||
|
||||
<servlet>
|
||||
<servlet-name>net.i2p.i2pcontrol.servlets.JSONRPC2Servlet</servlet-name>
|
||||
<servlet-class>net.i2p.i2pcontrol.servlets.JSONRPC2Servlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>net.i2p.i2pcontrol.servlets.JSONRPC2Servlet</servlet-name>
|
||||
<url-pattern>/</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="java/src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/i2p_sdk"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/jetty"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/ministreaming"/>
|
||||
<classpathentry kind="output" path="java/build/obj"/>
|
||||
</classpath>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>i2psnark</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
46
apps/i2psnark/build.gradle
Normal file
46
apps/i2psnark/build.gradle
Normal file
@@ -0,0 +1,46 @@
|
||||
plugins {
|
||||
id 'war'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir 'java/src'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':core')
|
||||
compile project(':apps:systray')
|
||||
compile 'gnu.getopt:java-getopt:1.0.13'
|
||||
providedCompile project(':apps:ministreaming')
|
||||
providedCompile project(':apps:jetty')
|
||||
}
|
||||
|
||||
task i2psnarkJar(type: Jar) {
|
||||
from sourceSets.main.output
|
||||
exclude 'org/klomp/snark/standalone/**'
|
||||
exclude 'org/klomp/snark/web/**'
|
||||
manifest {
|
||||
attributes 'Main-Class': 'org.klomp.snark.Snark'
|
||||
attributes 'Class-Path': 'i2p.jar mstreaming.jar streaming.jar'
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: standalone jar. This is rather involved!
|
||||
|
||||
artifacts {
|
||||
archives i2psnarkJar
|
||||
}
|
||||
|
||||
war {
|
||||
into '.icons', {
|
||||
from 'icons'
|
||||
}
|
||||
webInf {
|
||||
into 'classes/org/klomp/snark/web'
|
||||
from 'mime.properties'
|
||||
}
|
||||
webXml = file('web.xml')
|
||||
}
|
||||
4
apps/i2psnark/i2psnark-appctx.config
Normal file
4
apps/i2psnark/i2psnark-appctx.config
Normal file
@@ -0,0 +1,4 @@
|
||||
#
|
||||
# This is for app context configuration of standalone i2psnark.
|
||||
# Almost all configuration settings are in i2psnark.config.d/i2psnark.config
|
||||
#
|
||||
@@ -1,7 +1,7 @@
|
||||
# NOTE: This I2P config file must use UTF-8 encoding
|
||||
#
|
||||
# If you have a 'split' directory installation, with configuration
|
||||
# files in ~/.i2p (Linux), %APPDATA%\I2P (Windows),
|
||||
# files in ~/.i2p (Linux), %LOCALAPPDATA%\I2P (Windows),
|
||||
# or /Users/(user)/Library/Application Support/i2p (Mac), be sure to
|
||||
# edit the file in the configuration directory, NOT the install directory.
|
||||
# When running as a Linux daemon, the configuration directory is /var/lib/i2p
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="web" name="Web">
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<deploymentDescriptor name="web.xml" url="file://$MODULE_DIR$/web.xml" />
|
||||
</descriptors>
|
||||
<webroots>
|
||||
<root url="file://$MODULE_DIR$" relative="/WEB-INF" />
|
||||
</webroots>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/java/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="jettylib" level="project" />
|
||||
<orderEntry type="module" module-name="core" />
|
||||
<orderEntry type="module" module-name="ministreaming" />
|
||||
<orderEntry type="module" module-name="jetty" />
|
||||
<orderEntry type="module" module-name="systray" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -25,9 +25,11 @@
|
||||
</depend>
|
||||
</target>
|
||||
|
||||
<!-- only used if not set by a higher build.xml -->
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="javac.version" value="1.7" />
|
||||
<property name="require.gettext" value="true" />
|
||||
<property name="manifest.classpath.name" value="Class-Path" />
|
||||
|
||||
<condition property="no.bundle">
|
||||
<isfalse value="${require.gettext}" />
|
||||
@@ -40,6 +42,7 @@
|
||||
srcdir="./src"
|
||||
debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
|
||||
destdir="./build/obj"
|
||||
encoding="UTF-8"
|
||||
includeAntRuntime="false" >
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
<classpath>
|
||||
@@ -50,10 +53,11 @@
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<!-- jsp-api.jar only present for debian builds -->
|
||||
<pathelement location="../../jetty/jettylib/jsp-api.jar" />
|
||||
<!-- jetty-i2p.jar only for RunStandalone -->
|
||||
<!-- following jars only for standalone builds -->
|
||||
<pathelement location="../../jetty/jettylib/jetty-i2p.jar" />
|
||||
<!-- systray.jar only for RunStandalone -->
|
||||
<pathelement location="../../systray/java/build/systray.jar" />
|
||||
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-util.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
@@ -78,7 +82,7 @@
|
||||
<jar destfile="./build/i2psnark.jar" basedir="./build/obj" includes="**/*.class" excludes="**/web/* **/messages_*.class, **/standalone/*">
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="org.klomp.snark.CommandLine" />
|
||||
<attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" />
|
||||
<attribute name="${manifest.classpath.name}" value="i2p.jar mstreaming.jar streaming.jar" />
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
<attribute name="Build-Date" value="${build.timestamp}" />
|
||||
@@ -161,6 +165,7 @@
|
||||
</exec>
|
||||
<javac source="${javac.version}" target="${javac.version}"
|
||||
includeAntRuntime="false"
|
||||
encoding="UTF-8"
|
||||
srcdir="build/messages-src" destdir="build/obj">
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
</javac>
|
||||
@@ -168,15 +173,20 @@
|
||||
|
||||
<target name="poupdate" depends="builddep, compile">
|
||||
<!-- Update the messages_*.po files. -->
|
||||
<!-- set if unset -->
|
||||
<property name="lg2" value="" />
|
||||
<exec executable="sh" osfamily="unix" failifexecutionfails="true" failonerror="true" >
|
||||
<env key="LG2" value="${lg2}" />
|
||||
<arg value="./bundle-messages.sh" />
|
||||
<arg value="-p" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="mac" failifexecutionfails="true" failonerror="true" >
|
||||
<env key="LG2" value="${lg2}" />
|
||||
<arg value="./bundle-messages.sh" />
|
||||
<arg value="-p" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="windows" failifexecutionfails="true" failonerror="true" >
|
||||
<env key="LG2" value="${lg2}" />
|
||||
<arg value="./bundle-messages.sh" />
|
||||
<arg value="-p" />
|
||||
</exec>
|
||||
@@ -196,7 +206,6 @@
|
||||
<fileset dir="build/obj" includes="**/standalone/*.class" />
|
||||
<zipfileset src="build/i2psnark.jar" />
|
||||
<zipfileset src="../../../core/java/build/i2p.jar" />
|
||||
<zipfileset src="../../jetty/jettylib/commons-logging.jar" />
|
||||
<!-- without this we get a warning about 'no JSP support' but that's it
|
||||
<zipfileset src="../../jetty/jettylib/jasper-runtime.jar" />
|
||||
-->
|
||||
@@ -243,12 +252,6 @@
|
||||
<fileset dir="../../../installer/resources/themes/snark/" />
|
||||
</copy>
|
||||
|
||||
<replace dir="build/standalone-resources/.resources/themes/snark"
|
||||
summary="true"
|
||||
token="url(/themes/console/classic/images/"
|
||||
value="url(/i2psnark/.resources/themes/snark/classic/images/" >
|
||||
<include name="**/*.css" />
|
||||
</replace>
|
||||
<replace dir="build/standalone-resources/.resources/themes/snark"
|
||||
summary="true"
|
||||
token="url(/themes/console/dark/images/"
|
||||
@@ -291,8 +294,6 @@
|
||||
todir="build/standalone-resources/.resources/themes/snark/dark/images" />
|
||||
<copy file="../../../installer/resources/themes/console/images/info/errortriangle.png"
|
||||
todir="build/standalone-resources/.resources/themes/snark/ubergine/images" />
|
||||
<copy file="../../../installer/resources/themes/console/classic/images/bg0.png"
|
||||
todir="build/standalone-resources/.resources/themes/snark/classic/images" />
|
||||
|
||||
<mkdir dir="build/standalone-resources/.resources/js" />
|
||||
<copy file="../../routerconsole/jsp/js/ajax.js" todir="build/standalone-resources/.resources/js" />
|
||||
@@ -314,6 +315,7 @@
|
||||
<mkdir dir="./dist/webapps" />
|
||||
<copy file="../i2psnark.war" tofile="./dist/webapps/i2psnark.war" />
|
||||
<copy file="../jetty-i2psnark.xml" tofile="./dist/jetty-i2psnark.xml" />
|
||||
<copy file="../i2psnark-appctx.config" tofile="./dist/i2psnark-appctx.config" />
|
||||
<copy file="./build/i2psnark-standalone.jar" tofile="./dist/i2psnark.jar" />
|
||||
<copy file="../readme-standalone.txt" tofile="./dist/readme.txt" />
|
||||
<!-- temp so announces work -->
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user