mirror of
https://github.com/kiwix/libkiwix.git
synced 2026-08-07 02:53:58 -04:00
Compare commits
1724
Commits
3.0.0
...
can-revert
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
795bfecb9e | ||
|
|
4efa093af8 | ||
|
|
98853a0708 | ||
|
|
95bde675ef | ||
|
|
fcde243117 | ||
|
|
9fd7f7da34 | ||
|
|
453f02cc85 | ||
|
|
a6659cbe96 | ||
|
|
e13fed8670 | ||
|
|
25f589ee73 | ||
|
|
208f0f5f69 | ||
|
|
951e15c665 | ||
|
|
cc35fe503f | ||
|
|
37aadb86fb | ||
|
|
f843ea48f0 | ||
|
|
a48e2e6f06 | ||
|
|
0f7e11bd86 | ||
|
|
dbded6eee2 | ||
|
|
c1d7cc37fd | ||
|
|
6071b98fb7 | ||
|
|
dca47d35f7 | ||
|
|
d8656ec149 | ||
|
|
f1873876b2 | ||
|
|
cb20317047 | ||
|
|
ae58f009fb | ||
|
|
d7a3a417e1 | ||
|
|
68c6c93945 | ||
|
|
4c256e97c7 | ||
|
|
7478217ad4 | ||
|
|
ea33a3b65e | ||
|
|
f4e8f688ad | ||
|
|
4c4969d95a | ||
|
|
676a5d11f5 | ||
|
|
6b57ad89b7 | ||
|
|
174deddf35 | ||
|
|
782a25bba8 | ||
|
|
24ed5491fd | ||
|
|
88de978a9c | ||
|
|
eb002ae306 | ||
|
|
2550306052 | ||
|
|
51fcb90dc0 | ||
|
|
b1ad319d52 | ||
|
|
12826a57bd | ||
|
|
5bda7fd45c | ||
|
|
30725136c8 | ||
|
|
571b6089a4 | ||
|
|
32b4bca745 | ||
|
|
f838314435 | ||
|
|
08d6376eed | ||
|
|
3cdc6c41c4 | ||
|
|
973ac28dcb | ||
|
|
a855b422c7 | ||
|
|
28673c1bb8 | ||
|
|
df4b16e485 | ||
|
|
936707f73b | ||
|
|
9e2a601d52 | ||
|
|
1d074cda40 | ||
|
|
5850e0d489 | ||
|
|
904615a51a | ||
|
|
763fb86ad0 | ||
|
|
fbf6d97f5e | ||
|
|
c85466995d | ||
|
|
514d6e6514 | ||
|
|
351bc87231 | ||
|
|
ac742e9da2 | ||
|
|
0581da44fe | ||
|
|
2825c4c63d | ||
|
|
fa7d044037 | ||
|
|
d42fa22450 | ||
|
|
7307a9a1b7 | ||
|
|
bf80367b5a | ||
|
|
a04646b7b2 | ||
|
|
cfe3f8e3d9 | ||
|
|
2d0cff2dc1 | ||
|
|
b24157ddf9 | ||
|
|
c57b5ba1ad | ||
|
|
fe646511d1 | ||
|
|
cc31846152 | ||
|
|
cb4938c5f8 | ||
|
|
b1055e814a | ||
|
|
13951c13df | ||
|
|
60fbe7f714 | ||
|
|
595817852d | ||
|
|
2e0124710a | ||
|
|
340fadd9be | ||
|
|
4bdc1d76c6 | ||
|
|
738c06ada6 | ||
|
|
93bb0f098b | ||
|
|
e8c8a297b5 | ||
|
|
f4f7879ff3 | ||
|
|
706108256b | ||
|
|
12f0614350 | ||
|
|
29519df906 | ||
|
|
6b8f9aa6ab | ||
|
|
e3a211e41c | ||
|
|
fa80be87be | ||
|
|
51206f4037 | ||
|
|
c2fffacbbd | ||
|
|
02f631fdb6 | ||
|
|
05a66ead6e | ||
|
|
97f0314fe6 | ||
|
|
a7fe4193e3 | ||
|
|
2c5e84b6b3 | ||
|
|
71a66e0528 | ||
|
|
a807ce27f1 | ||
|
|
58bb8b9843 | ||
|
|
2e9bec95b0 | ||
|
|
2f419996ab | ||
|
|
1ba588272c | ||
|
|
2c3b7409aa | ||
|
|
f239f2de18 | ||
|
|
18b7b5f277 | ||
|
|
0e612de4d1 | ||
|
|
52ae5c3a5f | ||
|
|
d1fe1b89ae | ||
|
|
1aa8521e15 | ||
|
|
95ebb6a492 | ||
|
|
a74aaa5b13 | ||
|
|
4bf4b66b27 | ||
|
|
57484fd63d | ||
|
|
3a40b6b6d7 | ||
|
|
2781da3221 | ||
|
|
4629673161 | ||
|
|
fe30438854 | ||
|
|
291fca2b17 | ||
|
|
6fd54c7e6e | ||
|
|
a9e4d8a0a1 | ||
|
|
f3c0d5d422 | ||
|
|
a620c8658b | ||
|
|
d59cfb1fa2 | ||
|
|
ca65dd9000 | ||
|
|
6c2f229d31 | ||
|
|
eba7e15358 | ||
|
|
e42719c9df | ||
|
|
2995a00cd0 | ||
|
|
9f34613473 | ||
|
|
430bcb17c2 | ||
|
|
37bf993759 | ||
|
|
886a92a795 | ||
|
|
2b01b8168f | ||
|
|
35aacf7a48 | ||
|
|
0e0044f840 | ||
|
|
76dfc03751 | ||
|
|
ca079a72cc | ||
|
|
471c5b89f4 | ||
|
|
3bf8211b70 | ||
|
|
ec81d5904d | ||
|
|
82dcba542a | ||
|
|
63e0d5c7c2 | ||
|
|
772243e832 | ||
|
|
bad13d76b4 | ||
|
|
0bde4d9412 | ||
|
|
239b108fa7 | ||
|
|
c5ccbd37e2 | ||
|
|
822fb3748a | ||
|
|
aa2e443eb8 | ||
|
|
82d477009d | ||
|
|
e49081da80 | ||
|
|
07c7d3931d | ||
|
|
cf59a93cf1 | ||
|
|
e35e7585e0 | ||
|
|
fcb97c3c06 | ||
|
|
0edee4d066 | ||
|
|
b9937e6859 | ||
|
|
59012c50b4 | ||
|
|
7a98878273 | ||
|
|
8eb527389e | ||
|
|
78b2c1a273 | ||
|
|
497c0700b5 | ||
|
|
bac12010aa | ||
|
|
dad33a850c | ||
|
|
0968fc98ee | ||
|
|
ff44d88f21 | ||
|
|
1e7baee9d7 | ||
|
|
d9342acf5b | ||
|
|
b3f1ab6579 | ||
|
|
f5c9b2404a | ||
|
|
8b1fe21e4e | ||
|
|
815c59ff6d | ||
|
|
90318dfb6b | ||
|
|
f3d2f474a7 | ||
|
|
12140098e6 | ||
|
|
c7d8081e9a | ||
|
|
a10067e6b6 | ||
|
|
28e9fb48b6 | ||
|
|
634f3fcf14 | ||
|
|
88597e1834 | ||
|
|
69b3e1f8a7 | ||
|
|
669d8898ac | ||
|
|
14f0f79061 | ||
|
|
600ff07986 | ||
|
|
1d74b5e311 | ||
|
|
c0fe6f4aee | ||
|
|
aa7053bbe8 | ||
|
|
99f24eb598 | ||
|
|
6790a144a1 | ||
|
|
cd3d2110d9 | ||
|
|
b404241d0b | ||
|
|
2d42d6dc60 | ||
|
|
e65c9c41d8 | ||
|
|
0ae31bd181 | ||
|
|
0d8971ef88 | ||
|
|
2812b5ca5c | ||
|
|
4dc8973cdc | ||
|
|
160c95e317 | ||
|
|
956289d9f8 | ||
|
|
3568ccd511 | ||
|
|
d66cc6286c | ||
|
|
7743e73ede | ||
|
|
4966f4155d | ||
|
|
c727de6591 | ||
|
|
0f0ae1cfed | ||
|
|
da78aae62b | ||
|
|
abcd4ade99 | ||
|
|
7a9780eb90 | ||
|
|
51bd881211 | ||
|
|
f36f1661d5 | ||
|
|
18f4a58237 | ||
|
|
6285599b7c | ||
|
|
764f68f7d8 | ||
|
|
777c5e1f7a | ||
|
|
8031ffa447 | ||
|
|
0c8ceac117 | ||
|
|
ec31882e94 | ||
|
|
8cec014691 | ||
|
|
bf9aeffbfa | ||
|
|
7765769e6f | ||
|
|
7d69ece27d | ||
|
|
c0d027e8a4 | ||
|
|
c87add1419 | ||
|
|
a52138e5ba | ||
|
|
d1b85192c0 | ||
|
|
cb02dbd92a | ||
|
|
9409e8bd91 | ||
|
|
cd62b5dd91 | ||
|
|
414d7ae4fe | ||
|
|
9d2cc35447 | ||
|
|
7167ca1e6a | ||
|
|
8cc1c47133 | ||
|
|
e5b94fa1bb | ||
|
|
b0d719431d | ||
|
|
0e20f50443 | ||
|
|
18a18c17a9 | ||
|
|
602c20f160 | ||
|
|
415ec41099 | ||
|
|
b9f60ecfe9 | ||
|
|
12a638750e | ||
|
|
b62486c2f9 | ||
|
|
6bc7e0178d | ||
|
|
ce8b2bf9d9 | ||
|
|
9fd1423100 | ||
|
|
6b8d6232f0 | ||
|
|
c91df1cb26 | ||
|
|
b249edee60 | ||
|
|
a31ccb6588 | ||
|
|
43c8da9b04 | ||
|
|
190156e095 | ||
|
|
5471819021 | ||
|
|
7feef320d9 | ||
|
|
73191fb8f8 | ||
|
|
a844bc4000 | ||
|
|
f13ca55ef6 | ||
|
|
dc194683bb | ||
|
|
0841472004 | ||
|
|
ebb713cb85 | ||
|
|
cd6cbe3655 | ||
|
|
582c8d868a | ||
|
|
f6ae75e41d | ||
|
|
ffbda34b75 | ||
|
|
f61fc07121 | ||
|
|
de7fa771fc | ||
|
|
24c1ca5a4a | ||
|
|
15f5abad3c | ||
|
|
0a866fa914 | ||
|
|
ff192cba49 | ||
|
|
0dd638f261 | ||
|
|
229c0ceaf9 | ||
|
|
70f7be4202 | ||
|
|
60148717e1 | ||
|
|
266e29dff2 | ||
|
|
11051b4eed | ||
|
|
86eacea74e | ||
|
|
3a75facfdc | ||
|
|
0a0f52f1e2 | ||
|
|
0994a8f1b0 | ||
|
|
fa67b45f50 | ||
|
|
defa38719d | ||
|
|
cac2d212c6 | ||
|
|
4e06bb6a08 | ||
|
|
796e729f52 | ||
|
|
ae01790375 | ||
|
|
da23e4eca4 | ||
|
|
2be9ac342f | ||
|
|
369406fb5d | ||
|
|
b81cb3a8e9 | ||
|
|
6cc677b8ad | ||
|
|
a674561110 | ||
|
|
685e7f8ad4 | ||
|
|
0ce36e6246 | ||
|
|
eb0a45b13e | ||
|
|
c988511561 | ||
|
|
c73e6f9a81 | ||
|
|
0cf4850a9b | ||
|
|
40c496d401 | ||
|
|
9a193735fb | ||
|
|
2083c390b5 | ||
|
|
29efb88d48 | ||
|
|
948435794f | ||
|
|
7ed01e7678 | ||
|
|
eadc0ac72b | ||
|
|
77d9777208 | ||
|
|
4a55b136f6 | ||
|
|
a9446714ea | ||
|
|
17ff2a094d | ||
|
|
0c4d9e8730 | ||
|
|
7be7a8ed5f | ||
|
|
f41e71b2d7 | ||
|
|
58e45711ff | ||
|
|
5b545d81bd | ||
|
|
7c6c315ead | ||
|
|
228e31cddd | ||
|
|
4105be9bd2 | ||
|
|
e5f97d95b1 | ||
|
|
4db443eca6 | ||
|
|
dea674ef38 | ||
|
|
4b6c6452c0 | ||
|
|
5130bf9774 | ||
|
|
ee3514d2d6 | ||
|
|
e1847cb058 | ||
|
|
dd2b82a6be | ||
|
|
1062bd73a3 | ||
|
|
cd56277123 | ||
|
|
5e8b977bec | ||
|
|
9f545718c2 | ||
|
|
e323dcf6c9 | ||
|
|
3b98987cb3 | ||
|
|
fd36d11ccf | ||
|
|
dc56f82c29 | ||
|
|
1b1c1e352e | ||
|
|
a4b18893aa | ||
|
|
d737db666a | ||
|
|
cff143b4ec | ||
|
|
8e6d893f7f | ||
|
|
111aab0c23 | ||
|
|
dd90ca1018 | ||
|
|
3facd594f6 | ||
|
|
4cd52b0809 | ||
|
|
baf22c2516 | ||
|
|
f8a530100f | ||
|
|
a0db199388 | ||
|
|
f0f473b829 | ||
|
|
1e247d75bb | ||
|
|
4f3ec817db | ||
|
|
98bcf8acd6 | ||
|
|
b69bf4d062 | ||
|
|
6891ce3b57 | ||
|
|
16197afc95 | ||
|
|
abccd9d706 | ||
|
|
d0adb4e722 | ||
|
|
88c25b3a6c | ||
|
|
5aa74c62d6 | ||
|
|
2b6da38c46 | ||
|
|
dfc6cad9c2 | ||
|
|
28f8dbcf20 | ||
|
|
81865c0f0e | ||
|
|
538a46f262 | ||
|
|
e1d1d202bd | ||
|
|
71e2df7406 | ||
|
|
69931fb347 | ||
|
|
12e0fb6934 | ||
|
|
43ab6dfb6a | ||
|
|
93f2686a94 | ||
|
|
19a9c84e13 | ||
|
|
f034018b5c | ||
|
|
596b223a9d | ||
|
|
0c549af307 | ||
|
|
37b39430d1 | ||
|
|
947744caea | ||
|
|
b9e03d2772 | ||
|
|
e9b7eeb3c9 | ||
|
|
15cb9025bb | ||
|
|
1139f2cb4c | ||
|
|
0086049d4f | ||
|
|
e3e4bfa533 | ||
|
|
6938253e59 | ||
|
|
c5d3ffe0d7 | ||
|
|
3938d791e7 | ||
|
|
1572956da0 | ||
|
|
84b1321545 | ||
|
|
0f3bca442a | ||
|
|
83a9e54399 | ||
|
|
baa97cadf0 | ||
|
|
06d7a2320f | ||
|
|
f279769435 | ||
|
|
7d4867194a | ||
|
|
0340a49780 | ||
|
|
ed6aa5a89a | ||
|
|
75796ed6a5 | ||
|
|
ddd639eaa1 | ||
|
|
1c98b00128 | ||
|
|
600acb76c7 | ||
|
|
3bbbd1b15d | ||
|
|
ae47e5ee4e | ||
|
|
b442e2371e | ||
|
|
69c5c88c30 | ||
|
|
70382d15e2 | ||
|
|
62306373be | ||
|
|
01c384bb64 | ||
|
|
56167dc23e | ||
|
|
cc8ad9ebf2 | ||
|
|
bfcf317f09 | ||
|
|
ee01859984 | ||
|
|
9ec8593f8c | ||
|
|
7cb98f7f4e | ||
|
|
8100977cda | ||
|
|
a0cf91157a | ||
|
|
cadd2a5cbb | ||
|
|
e51a5b9ebc | ||
|
|
5d6b0ea96a | ||
|
|
e5df5e936f | ||
|
|
c4f706863c | ||
|
|
fbc7656b3f | ||
|
|
d196496802 | ||
|
|
3704d8ab87 | ||
|
|
a7651d0e9b | ||
|
|
3bca43344f | ||
|
|
b857293cfd | ||
|
|
b483a8e4e4 | ||
|
|
e2ab7fd62e | ||
|
|
f45962c697 | ||
|
|
3b3d7ad9c4 | ||
|
|
1514661c26 | ||
|
|
e5ea210d2c | ||
|
|
2b38d2cf1b | ||
|
|
0081b4d8e7 | ||
|
|
b74910b2af | ||
|
|
cf30233358 | ||
|
|
f0065fdd6f | ||
|
|
c72132054d | ||
|
|
077ceac5a5 | ||
|
|
39d0a56be8 | ||
|
|
76d5fafb72 | ||
|
|
4438106c2f | ||
|
|
76ebfd7ea4 | ||
|
|
22996e4a6b | ||
|
|
98c54b2279 | ||
|
|
854623618c | ||
|
|
fd0edbba80 | ||
|
|
f5af0633ec | ||
|
|
740581c55c | ||
|
|
582e3ec46d | ||
|
|
28fb76bbc2 | ||
|
|
7c688a4acc | ||
|
|
d4da05e591 | ||
|
|
66b2449800 | ||
|
|
aad95e3413 | ||
|
|
f0dd34b6db | ||
|
|
bbdde93f49 | ||
|
|
cb62da65c3 | ||
|
|
288b4ae7df | ||
|
|
52c12b0c2f | ||
|
|
4695f47dd2 | ||
|
|
f42f6a60df | ||
|
|
717c39f2ef | ||
|
|
aa1f73472d | ||
|
|
090c2fd31a | ||
|
|
ff2c7b1fb2 | ||
|
|
963362e1ea | ||
|
|
1a8d874a2c | ||
|
|
8e7658bb10 | ||
|
|
8f2f93371b | ||
|
|
eeca88573b | ||
|
|
4521249452 | ||
|
|
21e183c2e4 | ||
|
|
d56ccbd019 | ||
|
|
825cf1c948 | ||
|
|
57c31a43a4 | ||
|
|
84c68d4d7b | ||
|
|
f2cf42427a | ||
|
|
612ecc975d | ||
|
|
ae56d399b7 | ||
|
|
eaa8c3c91c | ||
|
|
26c06d8c2a | ||
|
|
eee6803328 | ||
|
|
d19ae1b054 | ||
|
|
abe2fa0179 | ||
|
|
6e93bad565 | ||
|
|
5fb919e73e | ||
|
|
2771a95d40 | ||
|
|
8dbf015689 | ||
|
|
6cdc47eb62 | ||
|
|
cbd37073e8 | ||
|
|
d131b732d8 | ||
|
|
17c1b3b82f | ||
|
|
744dd87fb0 | ||
|
|
d469e2aed8 | ||
|
|
73d2d47ca7 | ||
|
|
55149407d2 | ||
|
|
2eff5b55a6 | ||
|
|
26eccb5a5f | ||
|
|
1b81ccc5e5 | ||
|
|
091786c7d8 | ||
|
|
c0b9e2a466 | ||
|
|
03ab2f67dd | ||
|
|
157f01e951 | ||
|
|
42fd6e8926 | ||
|
|
707df3d10b | ||
|
|
c016dfd2ce | ||
|
|
150851b33d | ||
|
|
3b9f28b2b5 | ||
|
|
fc85215ea0 | ||
|
|
acdc1dfb27 | ||
|
|
f90cc39a52 | ||
|
|
fba0f09f4f | ||
|
|
0d294c50a5 | ||
|
|
dc42f831c0 | ||
|
|
1757f7f168 | ||
|
|
c43c637bea | ||
|
|
927c12574a | ||
|
|
9987fbd488 | ||
|
|
a0d9a824e1 | ||
|
|
5052d4018c | ||
|
|
11be821c46 | ||
|
|
527a606281 | ||
|
|
3da81a3d0f | ||
|
|
ed7717c1e7 | ||
|
|
f73be3cde7 | ||
|
|
c2bfeb4030 | ||
|
|
901664b097 | ||
|
|
6f3db20078 | ||
|
|
fbd23a8329 | ||
|
|
d2c864b010 | ||
|
|
779382642b | ||
|
|
ca7e0fb4a0 | ||
|
|
52d4f73e89 | ||
|
|
1ace16229d | ||
|
|
cb5ae01fd8 | ||
|
|
b2526c7a98 | ||
|
|
387f977d6c | ||
|
|
202ec81d8b | ||
|
|
577b6e29f9 | ||
|
|
e4a0a029ff | ||
|
|
507e111f34 | ||
|
|
d029c2b8d5 | ||
|
|
c574735f51 | ||
|
|
a18dd82d82 | ||
|
|
e22e073d43 | ||
|
|
6dcf4ee034 | ||
|
|
61ccbc65fb | ||
|
|
85a9d35488 | ||
|
|
a17258fcc9 | ||
|
|
ae1bf39023 | ||
|
|
dbcbdff275 | ||
|
|
c1823b8ee4 | ||
|
|
3f41ce8337 | ||
|
|
2a20e87341 | ||
|
|
2028bf3a98 | ||
|
|
545d409150 | ||
|
|
89dc9afc28 | ||
|
|
647118dd5e | ||
|
|
d8a60db739 | ||
|
|
f4059f3faf | ||
|
|
800cc5b68a | ||
|
|
b1f03385e4 | ||
|
|
feb30d08aa | ||
|
|
95d4dd63ac | ||
|
|
311f783ea9 | ||
|
|
f2a1c0f106 | ||
|
|
2cc4befb12 | ||
|
|
3641dbf14d | ||
|
|
1962262f94 | ||
|
|
7407f30790 | ||
|
|
d740ffe465 | ||
|
|
e7293346be | ||
|
|
b1643e422e | ||
|
|
574c1ad690 | ||
|
|
59364a737a | ||
|
|
49f24d18df | ||
|
|
ec2e10b40e | ||
|
|
2da8ea1650 | ||
|
|
0eb8f09f79 | ||
|
|
0ecbdbcf63 | ||
|
|
9bc09a815c | ||
|
|
48d377ca44 | ||
|
|
d5ae92e4e2 | ||
|
|
1a5e2eda0f | ||
|
|
89785a259a | ||
|
|
668063205c | ||
|
|
df98c58d07 | ||
|
|
ff8da65c68 | ||
|
|
ae60ba806b | ||
|
|
8cfcf2ea86 | ||
|
|
26c16bb1b2 | ||
|
|
ca965d448f | ||
|
|
6d16d7386d | ||
|
|
40e9a19c48 | ||
|
|
d487c78ea4 | ||
|
|
96cbd2bf26 | ||
|
|
941c3b5df3 | ||
|
|
b9e40def88 | ||
|
|
116ecd1c78 | ||
|
|
8f2faf37dc | ||
|
|
ddc4c3ec2c | ||
|
|
511261cc81 | ||
|
|
aaf232bee4 | ||
|
|
a3460f6f48 | ||
|
|
e4a4b2f961 | ||
|
|
389d29c92e | ||
|
|
c64fce52e7 | ||
|
|
a5baafd09f | ||
|
|
ed46541b6f | ||
|
|
e93ccd18d4 | ||
|
|
f893777dc0 | ||
|
|
04d682486a | ||
|
|
8136138492 | ||
|
|
e48b550b68 | ||
|
|
6523d9f563 | ||
|
|
7cb4c1361f | ||
|
|
a51f8d66a7 | ||
|
|
833bbc89ba | ||
|
|
4bd02f07eb | ||
|
|
9488842416 | ||
|
|
34b50ba30e | ||
|
|
cfab560d74 | ||
|
|
422f4c7dd7 | ||
|
|
cc3545ac3b | ||
|
|
609bc24cbe | ||
|
|
d9124ed40b | ||
|
|
921671eb4d | ||
|
|
ec18eb40ea | ||
|
|
a11abcf480 | ||
|
|
ae2d7d20dc | ||
|
|
42ee14c8f5 | ||
|
|
afb556bf64 | ||
|
|
5c38300504 | ||
|
|
cb2226c11f | ||
|
|
4cce4dce0b | ||
|
|
34d069e61f | ||
|
|
7a6562395a | ||
|
|
92f9ee9280 | ||
|
|
ae2d9b234f | ||
|
|
0ba452aece | ||
|
|
5f4256b900 | ||
|
|
a34dc725f9 | ||
|
|
892db07a2d | ||
|
|
58be502f3f | ||
|
|
62ba2f4861 | ||
|
|
c782cc718a | ||
|
|
9a6aef4dba | ||
|
|
943cbbf6ce | ||
|
|
ec94d9bfd9 | ||
|
|
f2088d7fe0 | ||
|
|
19dd068e5a | ||
|
|
d56e56293b | ||
|
|
dc4f9a4939 | ||
|
|
261adf0ef9 | ||
|
|
ce24b1fa5f | ||
|
|
9193719c8f | ||
|
|
d0d253beed | ||
|
|
cf95d513d6 | ||
|
|
e72c0b75f6 | ||
|
|
4d996584fa | ||
|
|
dd3338c2d0 | ||
|
|
b19eb1ea61 | ||
|
|
6d14639f77 | ||
|
|
89e3a57a05 | ||
|
|
b94e4b7e3b | ||
|
|
68465079f0 | ||
|
|
f6309bb4c8 | ||
|
|
45e9b76b19 | ||
|
|
5a9dbf85ec | ||
|
|
cd412867d9 | ||
|
|
01edd830bc | ||
|
|
ceb46f1069 | ||
|
|
270773d6ba | ||
|
|
234606b170 | ||
|
|
b8328a78f6 | ||
|
|
08c3a9d8b2 | ||
|
|
744065276b | ||
|
|
e3f8046915 | ||
|
|
3198a849e2 | ||
|
|
fcae21c55b | ||
|
|
7da81acaa3 | ||
|
|
a7d19b170a | ||
|
|
000029166b | ||
|
|
2ccea8e370 | ||
|
|
84587e7f03 | ||
|
|
38fc187303 | ||
|
|
2dc8fc9ab3 | ||
|
|
60b4be2286 | ||
|
|
a8a68c54a2 | ||
|
|
367e5d2636 | ||
|
|
fcd865bb81 | ||
|
|
715151d725 | ||
|
|
e5eeb08206 | ||
|
|
ac6f91798f | ||
|
|
e5d26a4699 | ||
|
|
3d64a9d9a9 | ||
|
|
fb7d9f02c8 | ||
|
|
96e0d15ab4 | ||
|
|
39732e2bcf | ||
|
|
7dfafe0196 | ||
|
|
3052d0787a | ||
|
|
d4db090bb9 | ||
|
|
0633f68f80 | ||
|
|
2a5db3e7ab | ||
|
|
468a080b09 | ||
|
|
1705f938b5 | ||
|
|
0112e6102d | ||
|
|
e4d99f0374 | ||
|
|
aa50845e22 | ||
|
|
3dbcbe542b | ||
|
|
854058f842 | ||
|
|
c9eb3196f7 | ||
|
|
dc15a9a824 | ||
|
|
160a74f5f8 | ||
|
|
78c10346f2 | ||
|
|
6f1799db9f | ||
|
|
e108fb0e47 | ||
|
|
9482bfb95b | ||
|
|
66c40817ee | ||
|
|
22e5327dcf | ||
|
|
d29611ed75 | ||
|
|
b1bc883bf5 | ||
|
|
a8577d7a01 | ||
|
|
8bdcb90818 | ||
|
|
91a4491b74 | ||
|
|
f36d8e9851 | ||
|
|
5e1988640c | ||
|
|
aa3bd8560b | ||
|
|
9c047844c0 | ||
|
|
843d315b93 | ||
|
|
f1035fa472 | ||
|
|
6c95458f5e | ||
|
|
9554ab5db0 | ||
|
|
4b563e567e | ||
|
|
a77fae0993 | ||
|
|
ed2f914e10 | ||
|
|
872ddd9cb3 | ||
|
|
20b5a2b971 | ||
|
|
d8c525289b | ||
|
|
f7b853373c | ||
|
|
d21879ebda | ||
|
|
35dc59d4bb | ||
|
|
a89924a23f | ||
|
|
7f6a6055a9 | ||
|
|
90910c5cab | ||
|
|
250f46c7f9 | ||
|
|
0be00b791f | ||
|
|
ff050dc811 | ||
|
|
9f3459f3f3 | ||
|
|
7ea12697f4 | ||
|
|
ab53e0cff1 | ||
|
|
27414f7731 | ||
|
|
e1db9164c8 | ||
|
|
fa8d0f8e07 | ||
|
|
3589a51fff | ||
|
|
01ac0b2fe1 | ||
|
|
405ea29900 | ||
|
|
7161db8e2a | ||
|
|
262e13845c | ||
|
|
1d5383435d | ||
|
|
ad2eb52553 | ||
|
|
473d2d2a69 | ||
|
|
02b9e32d18 | ||
|
|
c2927ce6f7 | ||
|
|
b712c732f2 | ||
|
|
298247ca9b | ||
|
|
3aeeeeee76 | ||
|
|
226dac2604 | ||
|
|
76a5e3a877 | ||
|
|
2d6a7fe88d | ||
|
|
6199c11505 | ||
|
|
8fffa59974 | ||
|
|
4ccbdcb740 | ||
|
|
5f3c34ed93 | ||
|
|
d62c4fd521 | ||
|
|
339f845fb0 | ||
|
|
3296a020a1 | ||
|
|
571e417d1e | ||
|
|
913a368a12 | ||
|
|
0e48baf9f9 | ||
|
|
c7b88398bd | ||
|
|
4a01081e83 | ||
|
|
eb6a0d6456 | ||
|
|
e2544799a1 | ||
|
|
9f42884507 | ||
|
|
8a6adddc16 | ||
|
|
c8da5eea2b | ||
|
|
bd29c4c7ef | ||
|
|
e52a4a646b | ||
|
|
537ba7e6b9 | ||
|
|
f4bc3c8ced | ||
|
|
5263f6880c | ||
|
|
c129952605 | ||
|
|
9f0db6b7fa | ||
|
|
7d8a83cc97 | ||
|
|
ec5a423924 | ||
|
|
811b73a4f1 | ||
|
|
3e5372ef29 | ||
|
|
abfd9d88d8 | ||
|
|
4f65811011 | ||
|
|
59e5c7ff4e | ||
|
|
ef1ad4bf47 | ||
|
|
8d50d5e293 | ||
|
|
d76e670d5b | ||
|
|
8f5ffc5ef5 | ||
|
|
98f9c57e12 | ||
|
|
db06b6b797 | ||
|
|
513c547d99 | ||
|
|
c3d2e01157 | ||
|
|
4adad9b281 | ||
|
|
251f3a01ed | ||
|
|
0fcf166111 | ||
|
|
eea6f9fe27 | ||
|
|
0b9ff92e42 | ||
|
|
1f6fb238ba | ||
|
|
9479c0685d | ||
|
|
09a55d71d6 | ||
|
|
503eb5c4ce | ||
|
|
f714ff8d3e | ||
|
|
08e3d52957 | ||
|
|
30e4c549e4 | ||
|
|
b7b385d87b | ||
|
|
e46b0c07b5 | ||
|
|
cd9fb541fc | ||
|
|
3b942bb745 | ||
|
|
c0bda426b4 | ||
|
|
b3f7556096 | ||
|
|
4c657c082e | ||
|
|
e773a29f29 | ||
|
|
e15a0f4338 | ||
|
|
12d9b69806 | ||
|
|
027854e4f4 | ||
|
|
417e7471ac | ||
|
|
51ac1240f8 | ||
|
|
ea6413ff88 | ||
|
|
61209ea0d7 | ||
|
|
e9eaadde9e | ||
|
|
8a4080baba | ||
|
|
ba05999cba | ||
|
|
a4c3cad018 | ||
|
|
83e757a530 | ||
|
|
5e8f3a5505 | ||
|
|
fe93035a4c | ||
|
|
6e26c5aa75 | ||
|
|
452283cfe6 | ||
|
|
e5168d8b3d | ||
|
|
b8aee8a42c | ||
|
|
9addd82d2d | ||
|
|
e74e7f5623 | ||
|
|
a032d65eb8 | ||
|
|
19afe9442f | ||
|
|
a3ba7619df | ||
|
|
8b12434ff2 | ||
|
|
b4f7dfa5a2 | ||
|
|
ab3095745e | ||
|
|
45adda44b3 | ||
|
|
96cf7e78a5 | ||
|
|
dd118df612 | ||
|
|
8a4248e48e | ||
|
|
5f90f5ee2a | ||
|
|
64b55dbdc7 | ||
|
|
18871b4b15 | ||
|
|
b2027b397c | ||
|
|
49322f5961 | ||
|
|
0466b9759c | ||
|
|
20cdefcdb8 | ||
|
|
6ea40f57da | ||
|
|
a312d2218d | ||
|
|
15839df594 | ||
|
|
03a929e88e | ||
|
|
646502f9cf | ||
|
|
a8a96a99f4 | ||
|
|
a517d3b529 | ||
|
|
60f0f81286 | ||
|
|
2ed9a50eca | ||
|
|
bce922ab89 | ||
|
|
ad7a63a471 | ||
|
|
6e8200637e | ||
|
|
cc45c840d1 | ||
|
|
0590f27fa1 | ||
|
|
dd27c3a873 | ||
|
|
736841818d | ||
|
|
c1868e22f4 | ||
|
|
aabfc1d82e | ||
|
|
2effb3490e | ||
|
|
55672b0288 | ||
|
|
0abbeabfe2 | ||
|
|
1bf52e8ebe | ||
|
|
e2db1b3688 | ||
|
|
0b6b6716de | ||
|
|
18b6433322 | ||
|
|
b70c92cade | ||
|
|
09d843da3a | ||
|
|
fa83a61a54 | ||
|
|
967eb10cbf | ||
|
|
feeee25eac | ||
|
|
1c0b4502cd | ||
|
|
6f639144ab | ||
|
|
a94a03cd22 | ||
|
|
bc821638da | ||
|
|
bcece66960 | ||
|
|
c046f64d83 | ||
|
|
75b4d311d7 | ||
|
|
a236751c74 | ||
|
|
7d68926539 | ||
|
|
940368b8ac | ||
|
|
0594e60df3 | ||
|
|
b5c1b26761 | ||
|
|
4124ad30d5 | ||
|
|
3c5d73027d | ||
|
|
d88bdd3ebf | ||
|
|
5cfe34a5c2 | ||
|
|
ad133bc9a3 | ||
|
|
8eabae6286 | ||
|
|
f3c96b23fd | ||
|
|
a92e9d8756 | ||
|
|
8d39b2c4c1 | ||
|
|
6d237ff1d5 | ||
|
|
78083f1f4a | ||
|
|
dd60235010 | ||
|
|
e799f2ff1e | ||
|
|
312f2cb560 | ||
|
|
fa42cbc48f | ||
|
|
f1797993af | ||
|
|
f886c8c07b | ||
|
|
9ca6bd006f | ||
|
|
cdacc0caf1 | ||
|
|
dfad1c3815 | ||
|
|
07252a127a | ||
|
|
b60e3ffb26 | ||
|
|
70d42aec98 | ||
|
|
4aa3c792aa | ||
|
|
208dece7e3 | ||
|
|
19b59fd72f | ||
|
|
92c2de8d46 | ||
|
|
feeb9f206e | ||
|
|
a1520ce7f1 | ||
|
|
2e53b51696 | ||
|
|
b259afa408 | ||
|
|
3c3cf08a1a | ||
|
|
54b78eaf56 | ||
|
|
1e0ff1fbb0 | ||
|
|
5b272ac49c | ||
|
|
0a3d293ae0 | ||
|
|
86ef2e2199 | ||
|
|
a0332e7599 | ||
|
|
2ef488816c | ||
|
|
1ccafe2d97 | ||
|
|
d6c62b3cd3 | ||
|
|
f39c558d2a | ||
|
|
5b46ad5934 | ||
|
|
49dbd0aa52 | ||
|
|
179f0faeb1 | ||
|
|
bb92f26b60 | ||
|
|
3a4e8303a0 | ||
|
|
063bb8cd65 | ||
|
|
b54e5ab969 | ||
|
|
2632a21d24 | ||
|
|
5c97b1fff9 | ||
|
|
4f7175ad59 | ||
|
|
f4b8d0c303 | ||
|
|
188694f2a1 | ||
|
|
e2f6d91d51 | ||
|
|
c35f6f9142 | ||
|
|
7f0d3004c9 | ||
|
|
5567d8ca49 | ||
|
|
5315034afe | ||
|
|
3288cd80e5 | ||
|
|
56434de79e | ||
|
|
e9ba151e6f | ||
|
|
5f83944699 | ||
|
|
9c0ae835e2 | ||
|
|
e5fac30cee | ||
|
|
7ef08b670b | ||
|
|
2736a46cfe | ||
|
|
672b4fc907 | ||
|
|
012973d14a | ||
|
|
67984cca5b | ||
|
|
d4e35c7067 | ||
|
|
6e37cabaea | ||
|
|
c8b7f8772a | ||
|
|
fc7484ac86 | ||
|
|
c236f3a32b | ||
|
|
3c7faddb6e | ||
|
|
cd02b4de3b | ||
|
|
5188355878 | ||
|
|
05cc3d015f | ||
|
|
39b62c6108 | ||
|
|
9c43353b72 | ||
|
|
b82fff9855 | ||
|
|
68189de162 | ||
|
|
6aab9b6981 | ||
|
|
41276341d0 | ||
|
|
02c3dff142 | ||
|
|
be6b58c6ad | ||
|
|
ab0ffb55bc | ||
|
|
950e742116 | ||
|
|
69257610e8 | ||
|
|
700d4becb9 | ||
|
|
dd795bd56d | ||
|
|
5fdc51b23e | ||
|
|
32cc6b0dcb | ||
|
|
3879b82112 | ||
|
|
7336dcab1d | ||
|
|
63e9a09259 | ||
|
|
4178c169dd | ||
|
|
59e9a0cd77 | ||
|
|
f751aff2fb | ||
|
|
87dc9d2723 | ||
|
|
9c7366890d | ||
|
|
19e195cb7d | ||
|
|
3d5fd8f585 | ||
|
|
d3d5abe14d | ||
|
|
e805f68994 | ||
|
|
a759ab989f | ||
|
|
7ccd9ffcce | ||
|
|
0c0a37073b | ||
|
|
415c65cf03 | ||
|
|
8287f351e7 | ||
|
|
ea779ac200 | ||
|
|
80cd1fc989 | ||
|
|
2d76f8395e | ||
|
|
29a6a34ecf | ||
|
|
2f3f1a4859 | ||
|
|
b9be742085 | ||
|
|
95c354a5fa | ||
|
|
cdd272fc5a | ||
|
|
ef962a9174 | ||
|
|
f063d350c6 | ||
|
|
d8fe593f59 | ||
|
|
22b8625033 | ||
|
|
0f277ffa34 | ||
|
|
068f7e5e95 | ||
|
|
8c810d2d2f | ||
|
|
8c18a37961 | ||
|
|
db3e0d7f72 | ||
|
|
d134ad417f | ||
|
|
965b9622c2 | ||
|
|
11db5dec4e | ||
|
|
9d4370403b | ||
|
|
cb57178c23 | ||
|
|
9ba5ab4678 | ||
|
|
a597870025 | ||
|
|
611146aa37 | ||
|
|
6d2f227c42 | ||
|
|
0c7d19ab45 | ||
|
|
b54215f146 | ||
|
|
9033f2f28e | ||
|
|
5c289abd0e | ||
|
|
ec9186b174 | ||
|
|
aaaa5a637e | ||
|
|
49940a30d0 | ||
|
|
24ed96a38c | ||
|
|
ccdc316217 | ||
|
|
ba44033273 | ||
|
|
5cb276a933 | ||
|
|
e4be97a032 | ||
|
|
aa2a031ba4 | ||
|
|
803cb1c2c5 | ||
|
|
7872734f44 | ||
|
|
d061697de7 | ||
|
|
c557bb271b | ||
|
|
1f45c42c32 | ||
|
|
93264f7409 | ||
|
|
baed447dd3 | ||
|
|
20b487da8d | ||
|
|
e214efecd4 | ||
|
|
09233bf4f3 | ||
|
|
47c67a4202 | ||
|
|
6b600a18eb | ||
|
|
9e887cadf1 | ||
|
|
a599fb3892 | ||
|
|
a17fc0ef2d | ||
|
|
db06b2c7ca | ||
|
|
a20f9e2ce1 | ||
|
|
f7c867f8a7 | ||
|
|
b7b0bdbdd8 | ||
|
|
a870e05621 | ||
|
|
4abc4f8518 | ||
|
|
6b2067c236 | ||
|
|
e55bf514e8 | ||
|
|
80d4f7e349 | ||
|
|
f270724b1f | ||
|
|
58186ffb26 | ||
|
|
6d43fd065f | ||
|
|
071d2bedd3 | ||
|
|
0b1740e6c5 | ||
|
|
9913f748e2 | ||
|
|
c5c40cb189 | ||
|
|
ae32ff40c0 | ||
|
|
26331b401e | ||
|
|
0f368791a2 | ||
|
|
fb26f6b9c5 | ||
|
|
32643fbd94 | ||
|
|
faa9e1f8b5 | ||
|
|
bd781f8e8b | ||
|
|
c7d77395e7 | ||
|
|
a7fea462b0 | ||
|
|
89d7e68a39 | ||
|
|
67caae6c32 | ||
|
|
d3f2e08b35 | ||
|
|
839fc10a4f | ||
|
|
5a8b825c70 | ||
|
|
7a465e66d7 | ||
|
|
5a99634dfd | ||
|
|
e028bcbb04 | ||
|
|
9cdf7a44c0 | ||
|
|
4d23e44de7 | ||
|
|
98d69ef59b | ||
|
|
e40827fbac | ||
|
|
a798e0c0a1 | ||
|
|
971374e049 | ||
|
|
f7608c378e | ||
|
|
c13a43010a | ||
|
|
eea10ec3f5 | ||
|
|
c9bc2b48b0 | ||
|
|
b1689e0d3c | ||
|
|
c70c370ae0 | ||
|
|
c0ec5eeffd | ||
|
|
c81c2a4630 | ||
|
|
24b2e6e585 | ||
|
|
3fd1310008 | ||
|
|
17bc1a3e1a | ||
|
|
f5d9a3714d | ||
|
|
4749656828 | ||
|
|
e0bcafd89a | ||
|
|
84895c4036 | ||
|
|
a8bf9dd5b4 | ||
|
|
a61c94ef10 | ||
|
|
a8a864f70e | ||
|
|
a231dfd8e4 | ||
|
|
321d08e3d5 | ||
|
|
8c43fd8d36 | ||
|
|
9e032b6eea | ||
|
|
3e2810dff4 | ||
|
|
18afa97674 | ||
|
|
44c4aa931a | ||
|
|
95b32b168d | ||
|
|
2659f323cd | ||
|
|
1002c15e0d | ||
|
|
d51000c4a9 | ||
|
|
ba302bed33 | ||
|
|
9941d245e1 | ||
|
|
6900b4e506 | ||
|
|
6fa20f6dcf | ||
|
|
8eacb0d635 | ||
|
|
84c320d5d3 | ||
|
|
1a5a2e7a8e | ||
|
|
d87079ec13 | ||
|
|
fbd4332c87 | ||
|
|
7c4517ca3c | ||
|
|
bc4b6846ef | ||
|
|
f408fecdd0 | ||
|
|
038e86e0d8 | ||
|
|
41cee1cfe0 | ||
|
|
7e2d174cfb | ||
|
|
52da58a294 | ||
|
|
0f8caba3a5 | ||
|
|
0f8fe1f63f | ||
|
|
ed32e16db2 | ||
|
|
08464f23bc | ||
|
|
ef42abea4b | ||
|
|
4407dd12bd | ||
|
|
d546ae38c4 | ||
|
|
7a11ec6ea4 | ||
|
|
095c86cf90 | ||
|
|
632583ede2 | ||
|
|
9c925f6778 | ||
|
|
61f9d4ab3a | ||
|
|
de6b8ba4de | ||
|
|
ab7349dbc8 | ||
|
|
292004703e | ||
|
|
c3e1e46d58 | ||
|
|
aba2f35092 | ||
|
|
b3a3dfb79f | ||
|
|
470bfc3f1f | ||
|
|
ea3180cb8c | ||
|
|
72d3f8f8e2 | ||
|
|
af9e03904c | ||
|
|
39611cbd60 | ||
|
|
4e98a76c19 | ||
|
|
14767e6edb | ||
|
|
39bc8828cd | ||
|
|
c183f57670 | ||
|
|
009eb7f905 | ||
|
|
eb7a8beb77 | ||
|
|
6f0d3003ac | ||
|
|
ee17b0739a | ||
|
|
47436f7bdd | ||
|
|
3352c95314 | ||
|
|
77123ac74c | ||
|
|
9078f0ac6e | ||
|
|
8d6567d067 | ||
|
|
6d5cddca12 | ||
|
|
a3939e9a05 | ||
|
|
eee621d15b | ||
|
|
7b2ee37437 | ||
|
|
f014fb2895 | ||
|
|
1011d1ff0b | ||
|
|
9e351b279e | ||
|
|
a0bdc0821c | ||
|
|
a819d9e3e0 | ||
|
|
49f3c56680 | ||
|
|
1657b1744c | ||
|
|
a126482d69 | ||
|
|
c74b935a9b | ||
|
|
015444cfd5 | ||
|
|
a55d504017 | ||
|
|
87b5adcaf4 | ||
|
|
c14c148af7 | ||
|
|
56e46c43f8 | ||
|
|
c4e6313c90 | ||
|
|
18e46969b7 | ||
|
|
e8cc6e4205 | ||
|
|
5f0bcd2bfa | ||
|
|
a3bef76083 | ||
|
|
66563d93cc | ||
|
|
3f25a3d005 | ||
|
|
98a10d2ca1 | ||
|
|
fdc59b1ec9 | ||
|
|
1b399fc0a2 | ||
|
|
5f8c099829 | ||
|
|
599aaa4c1b | ||
|
|
1884081ebe | ||
|
|
3d425f44de | ||
|
|
d8991c5459 | ||
|
|
c7cb87dd57 | ||
|
|
1145af43e7 | ||
|
|
d5b2742fbb | ||
|
|
a0eb972595 | ||
|
|
3bf70f4315 | ||
|
|
a8130bd4f2 | ||
|
|
a35d95207e | ||
|
|
b18c8e079e | ||
|
|
4e9f563b45 | ||
|
|
7ece383004 | ||
|
|
4ca9558e30 | ||
|
|
89582d526e | ||
|
|
5913f7efab | ||
|
|
1367c5630f | ||
|
|
0ca27d8edf | ||
|
|
60c6cc35d5 | ||
|
|
6b783b3998 | ||
|
|
55515f2fc6 | ||
|
|
7fe07c65fd | ||
|
|
ee204a9b5e | ||
|
|
e743e04b94 | ||
|
|
cf8e8b94eb | ||
|
|
d9557da813 | ||
|
|
c19b983914 | ||
|
|
f997fdb232 | ||
|
|
f0b037f37f | ||
|
|
4d307e18eb | ||
|
|
e05bd8efd6 | ||
|
|
71462696bd | ||
|
|
fb79cde729 | ||
|
|
c986290d83 | ||
|
|
af9afab821 | ||
|
|
14af7b756e | ||
|
|
ff605873ed | ||
|
|
6c49c7ee0a | ||
|
|
157d1664cf | ||
|
|
6f92b7e120 | ||
|
|
fd62acd232 | ||
|
|
1cdf830217 | ||
|
|
0b48ab20bb | ||
|
|
081a2b2fa6 | ||
|
|
bf93d10cde | ||
|
|
05ef5d5f51 | ||
|
|
4cdae3ca98 | ||
|
|
7dcaeed33a | ||
|
|
f52b220d01 | ||
|
|
50a850f3a9 | ||
|
|
886ae17274 | ||
|
|
a9b6d481cc | ||
|
|
85d6daabac | ||
|
|
5f1918d005 | ||
|
|
16bd79fa1b | ||
|
|
c2ebdefe8d | ||
|
|
37032892a4 | ||
|
|
6b43438b74 | ||
|
|
7301bf89bb | ||
|
|
ff23b28e7c | ||
|
|
931e95f391 | ||
|
|
f7571b5b69 | ||
|
|
801ad18a89 | ||
|
|
67a347c0c4 | ||
|
|
693905eb68 | ||
|
|
f3e79c6b4c | ||
|
|
52f207eaa6 | ||
|
|
67294217a8 | ||
|
|
d111a40ce8 | ||
|
|
0c5bb3fcfe | ||
|
|
3fba8c20a0 | ||
|
|
54db6049b7 | ||
|
|
81c38d6b2b | ||
|
|
e6a86c02ae | ||
|
|
a0f7f32570 | ||
|
|
c39fce8839 | ||
|
|
bd2d0bc489 | ||
|
|
de37489c53 | ||
|
|
2a35a86de6 | ||
|
|
0a30a77c08 | ||
|
|
1a99bacfe3 | ||
|
|
2bf35f1651 | ||
|
|
48cc277468 | ||
|
|
d8498fd655 | ||
|
|
7ec8e33b83 | ||
|
|
8eb2d0c2a1 | ||
|
|
5995cc276d | ||
|
|
94c2ab4395 | ||
|
|
15179db945 | ||
|
|
0f07cab920 | ||
|
|
4f8b397081 | ||
|
|
2df74d9755 | ||
|
|
0b25492edc | ||
|
|
5f0a9d0b08 | ||
|
|
54f5dbbd35 | ||
|
|
95a5cde359 | ||
|
|
3d08ef43f2 | ||
|
|
381ff186cb | ||
|
|
e32fa28a6c | ||
|
|
1842e8f51c | ||
|
|
bfa51c2d87 | ||
|
|
81e781133d | ||
|
|
9ec7757efe | ||
|
|
7bd7ec4937 | ||
|
|
14d8583c83 | ||
|
|
a004d96cd7 | ||
|
|
21c6de2f80 | ||
|
|
a8e78f27e1 | ||
|
|
6c7ab6ff54 | ||
|
|
659ee6ba71 | ||
|
|
83ee8dec15 | ||
|
|
87cbbed9e3 | ||
|
|
a058520628 | ||
|
|
1ef5ebfb52 | ||
|
|
bbc06931ad | ||
|
|
2d3bf9b981 | ||
|
|
fd80f2a89f | ||
|
|
abb3dec700 | ||
|
|
80fcbceeb3 | ||
|
|
9a893a854e | ||
|
|
b0f65a02f2 | ||
|
|
9bf6d0621f | ||
|
|
fcadacb0ad | ||
|
|
8f07689c57 | ||
|
|
0630298fb9 | ||
|
|
9f61301423 | ||
|
|
9c101daad7 | ||
|
|
0586ef6d41 | ||
|
|
8fc42558d3 | ||
|
|
5d98a6964e | ||
|
|
9d8bf8ddcb | ||
|
|
4c8aad0e68 | ||
|
|
eb6f0f710c | ||
|
|
d9d2a702ef | ||
|
|
cbf5bd57a8 | ||
|
|
2bf6b04726 | ||
|
|
12a0660342 | ||
|
|
ab478ed4cf | ||
|
|
533541cf45 | ||
|
|
bab0fd3d4f | ||
|
|
375792a686 | ||
|
|
7155c788e2 | ||
|
|
3ac926b39b | ||
|
|
ea3444e2fe | ||
|
|
083c06d170 | ||
|
|
286fbba9f6 | ||
|
|
778603e1bf | ||
|
|
411d4b0779 | ||
|
|
bce0e8c37c | ||
|
|
507002d34e | ||
|
|
34a8144f51 | ||
|
|
4709a42f4f | ||
|
|
d04d9bf7f3 | ||
|
|
412f0d9c61 | ||
|
|
0ad8bf45fc | ||
|
|
3ab3ffe3ea | ||
|
|
064d5f3fa6 | ||
|
|
46626a3f98 | ||
|
|
90ba27fbab | ||
|
|
76c293e403 | ||
|
|
78e57c1a51 | ||
|
|
7c49dc6af9 | ||
|
|
2e60a088ab | ||
|
|
9ab0f825f4 | ||
|
|
411ca28598 | ||
|
|
cc2b5e63ca | ||
|
|
ea29557a33 | ||
|
|
b53f531f2b | ||
|
|
bd74ebed1e | ||
|
|
1632e9c55b | ||
|
|
6dd6a5dd80 | ||
|
|
b65b90d78c | ||
|
|
6a975994cc | ||
|
|
3ae596783d | ||
|
|
ce6e956434 | ||
|
|
f560a1f815 | ||
|
|
d14ba0c2e8 | ||
|
|
34257cfc1f | ||
|
|
8f990feabb | ||
|
|
fff2524ee2 | ||
|
|
a756e7f8f3 | ||
|
|
bc257d2d6d | ||
|
|
7275f9b8e3 | ||
|
|
b63827196c | ||
|
|
2881face70 | ||
|
|
77ba09c310 | ||
|
|
49aa0fbb9f | ||
|
|
7846b45bef | ||
|
|
7e26f3502d | ||
|
|
47a866400c | ||
|
|
4b6c26bd0b | ||
|
|
94c47383a9 | ||
|
|
6bcecc2677 | ||
|
|
e20c3520dc | ||
|
|
24fb1868d7 | ||
|
|
82afb804e1 | ||
|
|
0951546356 | ||
|
|
f09c739c1f | ||
|
|
df9ddd5451 | ||
|
|
fe513951d3 | ||
|
|
7f0d509a88 | ||
|
|
54f671b2f1 | ||
|
|
c2c89c6c86 | ||
|
|
75652d0e9f | ||
|
|
6535dc2e38 | ||
|
|
6b2f768c8f | ||
|
|
6099c3113f | ||
|
|
21d8a6952f | ||
|
|
b84f4e03ea | ||
|
|
ff21a095cb | ||
|
|
fa091a19c6 | ||
|
|
e3ba9fa5cc | ||
|
|
560f67522f | ||
|
|
11118efd84 | ||
|
|
9c2bc6affc | ||
|
|
4fe31a20e3 | ||
|
|
0f99c1ad20 | ||
|
|
91db055d86 | ||
|
|
5540149e2b | ||
|
|
7c7e351d34 | ||
|
|
c5051b343e | ||
|
|
02a0d592f9 | ||
|
|
071e9e3fec | ||
|
|
4a01303438 | ||
|
|
8095a87bf1 | ||
|
|
bb55527508 | ||
|
|
b7c5e5f339 | ||
|
|
316deeb485 | ||
|
|
721d981825 | ||
|
|
2244074f3c | ||
|
|
0a1e01eb2b | ||
|
|
abd2fa3bf3 | ||
|
|
ea3349f37c | ||
|
|
e3c6ca0d1b | ||
|
|
52e165cf78 | ||
|
|
3b7c805183 | ||
|
|
9c4867a95a | ||
|
|
223f7ee78a | ||
|
|
20690bd5f5 | ||
|
|
de7b7c34b5 | ||
|
|
f0ac66aea1 | ||
|
|
20a2c78733 | ||
|
|
9850be7267 | ||
|
|
0dd996c6a3 | ||
|
|
2500cc8e63 | ||
|
|
bd6797143c | ||
|
|
c9a15c9961 | ||
|
|
f1d55f8e86 | ||
|
|
a2c2955f41 | ||
|
|
9975e0b369 | ||
|
|
efe1c2dea3 | ||
|
|
2af9ba4eab | ||
|
|
c007373b46 | ||
|
|
e1acf9acff | ||
|
|
daaadf3e1c | ||
|
|
74bd482335 | ||
|
|
2aebffb27c | ||
|
|
da247b3242 | ||
|
|
f85ec9ea6f | ||
|
|
b4fac9d0df | ||
|
|
4f2ede80e5 | ||
|
|
c2ecb9d126 | ||
|
|
5883dba0ef | ||
|
|
7ad6aedd66 | ||
|
|
67170709bb | ||
|
|
dfd16155af | ||
|
|
0db06d98a8 | ||
|
|
742156d366 | ||
|
|
598dd3c175 | ||
|
|
49c0c5ff47 | ||
|
|
65ebc7fe7f | ||
|
|
2f4636e2df | ||
|
|
f4e9148b1d | ||
|
|
891666b8c4 | ||
|
|
57a2b98e7a | ||
|
|
9b4419f3fc | ||
|
|
15d5b4ed58 | ||
|
|
2f91149da3 | ||
|
|
6ee174b546 | ||
|
|
2a6772b76d | ||
|
|
660d5d7fb7 | ||
|
|
157c1c939c | ||
|
|
bd91e89785 | ||
|
|
1245d4e467 | ||
|
|
420be55bfa | ||
|
|
651cb9165c | ||
|
|
49046248fd | ||
|
|
e42e061d45 | ||
|
|
d90774450d | ||
|
|
9e36c876f5 | ||
|
|
1a4c434e3c | ||
|
|
3294508d87 | ||
|
|
351e573bce | ||
|
|
a32363e6a2 | ||
|
|
56f8b7a876 | ||
|
|
87dc145dc7 | ||
|
|
a13244dc0e | ||
|
|
78dbd66522 | ||
|
|
fdc291b7c2 | ||
|
|
d372cea146 | ||
|
|
e1fcd12e48 | ||
|
|
828bd032c4 | ||
|
|
26d32a36ad | ||
|
|
c031547461 | ||
|
|
d0833bdcd4 | ||
|
|
1bb5e278ed | ||
|
|
0a331f8ba9 | ||
|
|
b1a4bbd345 | ||
|
|
14dbe843b9 | ||
|
|
e111316636 | ||
|
|
bbb346b685 | ||
|
|
56a08f49b2 | ||
|
|
fc2ad81185 | ||
|
|
af78aa5fd0 | ||
|
|
2ea0e5bab0 | ||
|
|
b92a4b2e04 | ||
|
|
ef758aa0a6 | ||
|
|
b8e5de6a47 | ||
|
|
aed808ae5e | ||
|
|
e25b27b354 | ||
|
|
12a93c3e29 | ||
|
|
b3abb3a35b | ||
|
|
8c3b51b81c | ||
|
|
5f81fab1e2 | ||
|
|
cea201b394 | ||
|
|
d0f6fadda2 | ||
|
|
8672aede97 | ||
|
|
8edac07fcc | ||
|
|
6ab52e2b9e | ||
|
|
858c2fecbe | ||
|
|
d90a27af11 | ||
|
|
a8668db2fe | ||
|
|
3e3e1683f5 | ||
|
|
1e0c9a120a | ||
|
|
ca27ddf41a | ||
|
|
99dcdd849a | ||
|
|
a65e192f0f | ||
|
|
513cc9c90f | ||
|
|
231ae095f6 | ||
|
|
9a0c6da018 | ||
|
|
52299ef767 | ||
|
|
44bec86f31 | ||
|
|
c4963268ba | ||
|
|
fee2da57e6 | ||
|
|
0154131d74 | ||
|
|
3fa503efb1 | ||
|
|
73a29ccb24 | ||
|
|
1e94665c07 | ||
|
|
7060afae66 | ||
|
|
de819dff25 | ||
|
|
4d3df4e889 | ||
|
|
cd050ddcc8 | ||
|
|
635d4438e5 | ||
|
|
ce09375c6c | ||
|
|
fae0918f49 | ||
|
|
d90f8b0f05 | ||
|
|
abb5db0193 | ||
|
|
e452f5cf36 | ||
|
|
c890e1c87e | ||
|
|
e5ef3780db | ||
|
|
2aeed65205 | ||
|
|
c1faf55ae8 | ||
|
|
64dfea2547 | ||
|
|
cca5980b27 | ||
|
|
68a768f58a | ||
|
|
ce8fff0b42 | ||
|
|
e56335109c | ||
|
|
656bf183b7 | ||
|
|
cbe8e20118 | ||
|
|
e013d38cc6 | ||
|
|
6234457920 | ||
|
|
72223d69fe | ||
|
|
ddeb862395 | ||
|
|
61c28f0e3d | ||
|
|
c8e719101e | ||
|
|
1b8e7849bd | ||
|
|
efce01aec4 | ||
|
|
d55976271f | ||
|
|
5eeccbae21 | ||
|
|
77770e1bd4 | ||
|
|
654a8e304c | ||
|
|
ed7a8343fa | ||
|
|
5adf7891cc | ||
|
|
93c404a952 | ||
|
|
f33f4eb381 | ||
|
|
4bece7017f | ||
|
|
cab8eec00b | ||
|
|
f41155e060 | ||
|
|
c17abdae5e | ||
|
|
8164b4dadc | ||
|
|
31c9375a3a | ||
|
|
21592af8b2 | ||
|
|
15b3ed24b7 | ||
|
|
3d689e790b | ||
|
|
e740a511c6 | ||
|
|
87f5b56b72 | ||
|
|
cfdd634c3c | ||
|
|
df76db4f47 | ||
|
|
15f7eaa400 | ||
|
|
6fe6f88b10 | ||
|
|
687a15877a | ||
|
|
4e746916a7 | ||
|
|
7c1d051305 | ||
|
|
5dc96d7145 | ||
|
|
3721d7439d | ||
|
|
701829ae11 | ||
|
|
91a0e100cc | ||
|
|
1f672056a9 | ||
|
|
48347825a9 | ||
|
|
d0d7e11093 | ||
|
|
519dd110f5 | ||
|
|
568b2b0e0c | ||
|
|
491b6d655b | ||
|
|
ec8f1ffe9c | ||
|
|
12ffad55f7 | ||
|
|
8698407e1e | ||
|
|
0b2c9fa7ce | ||
|
|
f93c31754a | ||
|
|
fe682f855a | ||
|
|
9321c589c8 | ||
|
|
ee330398b2 | ||
|
|
ec4525fd47 | ||
|
|
e1980d190f | ||
|
|
03b1750313 | ||
|
|
28f144796d | ||
|
|
6c27743663 | ||
|
|
e7e88617d5 | ||
|
|
4e90f3614d | ||
|
|
f8522fb26e | ||
|
|
938e2a81c1 | ||
|
|
d9e72685ba | ||
|
|
d40982f760 | ||
|
|
42b7692f9b | ||
|
|
cd654b9cae | ||
|
|
15dafcaa80 | ||
|
|
f71f2935e0 | ||
|
|
c6254d9504 | ||
|
|
f1a046757e | ||
|
|
93af3aa2d1 | ||
|
|
336a987bb2 | ||
|
|
72b4af4d65 | ||
|
|
9aa1c65d7a | ||
|
|
ad6b20a530 | ||
|
|
c1d04cc5b5 | ||
|
|
af9734c87f | ||
|
|
a7a0798f99 | ||
|
|
0154fdd190 | ||
|
|
788d16ec01 | ||
|
|
35d812a5f7 | ||
|
|
432f9c30a3 | ||
|
|
ab94ac0ee8 | ||
|
|
1ac6d4cb20 | ||
|
|
26b61a2d09 | ||
|
|
aab88c9022 | ||
|
|
af7689e3e8 | ||
|
|
ecb2a80baf | ||
|
|
b996a2877c | ||
|
|
a98594c084 | ||
|
|
b9696dceac | ||
|
|
550b6df414 | ||
|
|
be498c3b16 | ||
|
|
92c9a47a0d | ||
|
|
c73ac9f2cd | ||
|
|
5159d985c6 | ||
|
|
cb98f11ddc | ||
|
|
29046bfc05 | ||
|
|
dd5dd14ec9 | ||
|
|
49a606a043 | ||
|
|
b641f7b116 | ||
|
|
e6d7ba06fb | ||
|
|
0f812c6584 | ||
|
|
716c87dd20 | ||
|
|
090c4f5970 | ||
|
|
cf28af4439 | ||
|
|
6777bfeecf | ||
|
|
12498e2cfe | ||
|
|
b5ce60a627 | ||
|
|
c9cc58973c | ||
|
|
062124a2a0 | ||
|
|
622b22b2cc | ||
|
|
2821b9e06a | ||
|
|
ac49776792 | ||
|
|
94a053e821 | ||
|
|
84e831eae9 | ||
|
|
4b9692bbd5 | ||
|
|
be6f96adc0 | ||
|
|
4b31842c4a | ||
|
|
cf1cfe774e | ||
|
|
82b38b96e2 | ||
|
|
8c4b9fbe95 | ||
|
|
ab63cb2fb8 | ||
|
|
3958b2a06f | ||
|
|
9fa7d78ba1 | ||
|
|
57d3552b97 | ||
|
|
d4ecda40ff | ||
|
|
802df71410 | ||
|
|
4d904c4d8b | ||
|
|
9ab44e6a5f | ||
|
|
5f4c04e79e |
No files matched your search
@@ -0,0 +1,12 @@
|
|||||||
|
codecov:
|
||||||
|
notify:
|
||||||
|
require_ci_to_pass: yes
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
status:
|
||||||
|
project:
|
||||||
|
default:
|
||||||
|
threshold: 1%
|
||||||
|
|
||||||
|
ignore:
|
||||||
|
- "test"
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: kiwix
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: # https://kiwix.org/support-us/
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
daysUntilClose: false
|
||||||
|
staleLabel: stale
|
||||||
|
|
||||||
|
issues:
|
||||||
|
daysUntilStale: 60
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be now be reviewed manually. Thank you
|
||||||
|
for your contributions.
|
||||||
|
pulls:
|
||||||
|
daysUntilStale: 7
|
||||||
|
markComment: >
|
||||||
|
This pull request has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be now be reviewed manually. Thank you
|
||||||
|
for your contributions.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- can-revert
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
macOS:
|
||||||
|
runs-on: macos-12
|
||||||
|
env:
|
||||||
|
HOME: /Users/runner
|
||||||
|
steps:
|
||||||
|
- name: Retrieve source code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install packages
|
||||||
|
run: |
|
||||||
|
brew update
|
||||||
|
brew install pkg-config ninja meson
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
env:
|
||||||
|
ARCHIVE_NAME: deps2_osx_native_dyn_libkiwix.tar.xz
|
||||||
|
run: |
|
||||||
|
wget -O- https://tmp.kiwix.org/ci/${{env.ARCHIVE_NAME}} | tar -xJ -C ${{env.HOME}}
|
||||||
|
|
||||||
|
- name: Compile source code
|
||||||
|
env:
|
||||||
|
PKG_CONFIG_PATH: ${{env.HOME}}/BUILD_native_dyn/INSTALL/lib/pkgconfig
|
||||||
|
CPPFLAGS: -I${{env.HOME}}/BUILD_native_dyn/INSTALL/include
|
||||||
|
run: |
|
||||||
|
meson . build --default-library=shared -Db_coverage=true
|
||||||
|
ninja -C build
|
||||||
|
|
||||||
|
- name: Test libkiwix
|
||||||
|
env:
|
||||||
|
SKIP_BIG_MEMORY_TEST: 1
|
||||||
|
LD_LIBRARY_PATH: ${{env.HOME}}/BUILD_native_dyn/INSTALL/lib:${{env.HOME}}/BUILD_native_dyn/INSTALL/lib64
|
||||||
|
run: meson test -C build --verbose
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
name: Packages
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-deb:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
distro:
|
||||||
|
- ubuntu-kinetic
|
||||||
|
- ubuntu-jammy
|
||||||
|
- ubuntu-focal
|
||||||
|
- ubuntu-bionic
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# Determine which PPA we should upload to
|
||||||
|
- name: PPA
|
||||||
|
id: ppa
|
||||||
|
run: |
|
||||||
|
if [[ $REF == refs/tags* ]]
|
||||||
|
then
|
||||||
|
echo "::set-output name=ppa::kiwixteam/release"
|
||||||
|
else
|
||||||
|
echo "::set-output name=ppa::kiwixteam/dev"
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
REF: ${{ github.ref }}
|
||||||
|
|
||||||
|
- uses: legoktm/gh-action-auto-dch@master
|
||||||
|
with:
|
||||||
|
fullname: Kiwix builder
|
||||||
|
email: release+launchpad@kiwix.org
|
||||||
|
distro: ${{ matrix.distro }}
|
||||||
|
|
||||||
|
- uses: legoktm/gh-action-build-deb@ubuntu-kinetic
|
||||||
|
if: matrix.distro == 'ubuntu-kinetic'
|
||||||
|
name: Build package for ubuntu-kinetic
|
||||||
|
id: build-ubuntu-kinetic
|
||||||
|
with:
|
||||||
|
args: --no-sign
|
||||||
|
ppa: ${{ steps.ppa.outputs.ppa }}
|
||||||
|
|
||||||
|
- uses: legoktm/gh-action-build-deb@ubuntu-jammy
|
||||||
|
if: matrix.distro == 'ubuntu-jammy'
|
||||||
|
name: Build package for ubuntu-jammy
|
||||||
|
id: build-ubuntu-jammy
|
||||||
|
with:
|
||||||
|
args: --no-sign
|
||||||
|
ppa: ${{ steps.ppa.outputs.ppa }}
|
||||||
|
|
||||||
|
- uses: legoktm/gh-action-build-deb@ubuntu-focal
|
||||||
|
if: matrix.distro == 'ubuntu-focal'
|
||||||
|
name: Build package for ubuntu-focal
|
||||||
|
id: build-ubuntu-focal
|
||||||
|
with:
|
||||||
|
args: --no-sign
|
||||||
|
ppa: ${{ steps.ppa.outputs.ppa }}
|
||||||
|
|
||||||
|
- uses: legoktm/gh-action-build-deb@ubuntu-bionic
|
||||||
|
if: matrix.distro == 'ubuntu-bionic'
|
||||||
|
name: Build package for ubuntu-bionic
|
||||||
|
id: build-ubuntu-bionic
|
||||||
|
with:
|
||||||
|
args: --no-sign
|
||||||
|
ppa: ${{ steps.ppa.outputs.ppa }}
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Packages for ${{ matrix.distro }}
|
||||||
|
path: output
|
||||||
|
|
||||||
|
- uses: legoktm/gh-action-dput@master
|
||||||
|
name: Upload dev package
|
||||||
|
# Only upload on pushes to git default branch
|
||||||
|
if: github.event_name == 'push' && github.event.ref == 'refs/heads/main' && startswith(matrix.distro, 'ubuntu-')
|
||||||
|
with:
|
||||||
|
gpg_key: ${{ secrets.LAUNCHPAD_GPG }}
|
||||||
|
repository: ppa:kiwixteam/dev
|
||||||
|
packages: output/*_source.changes
|
||||||
|
|
||||||
|
- uses: legoktm/gh-action-dput@master
|
||||||
|
name: Upload release package
|
||||||
|
# Only upload on pushes to master or tag
|
||||||
|
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && startswith(matrix.distro, 'ubuntu-')
|
||||||
|
with:
|
||||||
|
gpg_key: ${{ secrets.LAUNCHPAD_GPG }}
|
||||||
|
repository: ppa:kiwixteam/release
|
||||||
|
packages: output/*_source.changes
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
subprojects/googletest-release*
|
||||||
|
*.class
|
||||||
|
build/
|
||||||
|
.vscode/
|
||||||
|
builddir/
|
||||||
|
.cache/
|
||||||
|
.clangd/
|
||||||
-36
@@ -1,36 +0,0 @@
|
|||||||
language: cpp
|
|
||||||
dist: trusty
|
|
||||||
sudo: false
|
|
||||||
cache: ccache
|
|
||||||
before_install:
|
|
||||||
- PATH=$PATH:$HOME/bin
|
|
||||||
install: travis/install_deps.sh
|
|
||||||
script: travis/compile.sh
|
|
||||||
env:
|
|
||||||
matrix:
|
|
||||||
- PLATFORM="native_static"
|
|
||||||
- PLATFORM="native_dyn"
|
|
||||||
- PLATFORM="win32_static"
|
|
||||||
- PLATFORM="win32_dyn"
|
|
||||||
- PLATFORM="android_arm"
|
|
||||||
- PLATFORM="android_arm64"
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- cmake
|
|
||||||
- python3-pip
|
|
||||||
- libbz2-dev
|
|
||||||
- ccache
|
|
||||||
- zlib1g-dev
|
|
||||||
- uuid-dev
|
|
||||||
- libctpp2-dev
|
|
||||||
- ctpp2-utils
|
|
||||||
- libmicrohttpd-dev
|
|
||||||
- g++-mingw-w64-i686
|
|
||||||
- gcc-mingw-w64-i686
|
|
||||||
- gcc-mingw-w64-base
|
|
||||||
- mingw-w64-tools
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- env: PLATFORM="native_dyn"
|
|
||||||
os: osx
|
|
||||||
@@ -77,7 +77,7 @@ modification follow.
|
|||||||
|
|
||||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
works, such as semiconductor masks.
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
"The Program" refers to any copyrightable work licensed under this
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
License. Each licensee is addressed as "you". "Licensees" and
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
"recipients" may be individuals or organizations.
|
"recipients" may be individuals or organizations.
|
||||||
@@ -510,7 +510,7 @@ actual knowledge that, but for the patent license, your conveying the
|
|||||||
covered work in a country, or your recipient's use of the covered work
|
covered work in a country, or your recipient's use of the covered work
|
||||||
in a country, would infringe one or more identifiable patents in that
|
in a country, would infringe one or more identifiable patents in that
|
||||||
country that you have reason to believe are valid.
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
If, pursuant to or in connection with a single transaction or
|
If, pursuant to or in connection with a single transaction or
|
||||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
covered work, and grant a patent license to some of the parties
|
covered work, and grant a patent license to some of the parties
|
||||||
|
|||||||
@@ -1,3 +1,453 @@
|
|||||||
|
libkiwix 12.0.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* [API Break] Remove wrapper around libzim (@mgautierfr #789)
|
||||||
|
* Allow kiwix-serve to use custom resource files (@veloman-yunkan #779)
|
||||||
|
* Properly handle searchProtocolPrefix when rendering search result (@veloman-yunkan #823)
|
||||||
|
* Prevent search on multi language content (@veloman-yunkan #838)
|
||||||
|
* Use new `zim::Archive::getMediaCount` from libzim (@mgautierfr #836)
|
||||||
|
* Catalog:
|
||||||
|
- Include tags in free text catalog search (@veloman-yunkan #802)
|
||||||
|
- Illustration's url is based on book's uuid (@veloman-yunkan #804)
|
||||||
|
- Cleanup of the opds-dumper (@veloman-yunkan #829)
|
||||||
|
- Allow filtering of catalog content using multiple languages (@veloman-yunkan #841)
|
||||||
|
- Make opds-dumper respect the namemapper (@mgautierfr #837)
|
||||||
|
* Server:
|
||||||
|
- Correctly handle `\` in suggestion json generation (@veloman-yunkan #843)
|
||||||
|
- Better http caching (@veloman-yunkan #833)
|
||||||
|
- Make `/suggest` endpoint thread-safe (@veloman-yunkan #834)
|
||||||
|
- Better redirection of main page (@veloman-yunkan #827)
|
||||||
|
- Remove jquery (@mgautierfr @juuz0 #796)
|
||||||
|
- Better Viewer of zim content :
|
||||||
|
. Introduce `/content` endpoints (@veloman-yunkan #806)
|
||||||
|
. Switch to iframe based content viewer (@veloman-yunkan #716)
|
||||||
|
- Optimised design of the welcome page:
|
||||||
|
. Alignement (@juuz0 @kelson42 #786)
|
||||||
|
. Exit download modal on pressing escape key (@juzz0 #800)
|
||||||
|
. Add favicon for different devices (@juzz0 #805)
|
||||||
|
. Fix auto hidding of the toolbar (@veloman-yunkan #821)
|
||||||
|
. Allow user to filter books by tags in the front page (@juuz0 #711)
|
||||||
|
* CI :
|
||||||
|
- Trigger CI on pull_request (@kelson42 #791)
|
||||||
|
- Drop Ubuntu Impish packaging (@legoktm #825)
|
||||||
|
- Add Ubuntu Kinetic packaging (@legoktm #801)
|
||||||
|
* Testing:
|
||||||
|
- Test ICULanguageInfo (@veloman-yunkan #795)
|
||||||
|
- Introduce fake `test` language to test i18n (@veloman-yunkan #848)
|
||||||
|
* Fix documentation (@kelson42 #816)
|
||||||
|
* Udpate translation (#787 #839 #847)
|
||||||
|
|
||||||
|
|
||||||
|
libkiwix 11.0.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* [server] Add support for internationalization (@veloman-yunkan #679)
|
||||||
|
* [server] Use gzip compression instead of deflat (mgautierfr #757)
|
||||||
|
* [server] Version the static resources. This allow better invalidating
|
||||||
|
browser cache when resources are changed (@veloman-yunkan #712)
|
||||||
|
* [server|front] Use integer to query the host for page length (@juuz0 #772)
|
||||||
|
* [server] Improve multizim search API:
|
||||||
|
- Improvement of the cache system
|
||||||
|
- Better API to select on which books to search in.
|
||||||
|
- SysAdmin is now able to limit the number of book we search in for a multizim search
|
||||||
|
* [server] Introduce a opensearch API for multizim fulltext search
|
||||||
|
* [wrapper] Remove java wrapper
|
||||||
|
* Testing:
|
||||||
|
- Testing of search result pages content (@veloman-yunkan #765)
|
||||||
|
- Better testing structure of xml search result (@veloman-yunkan #780)
|
||||||
|
|
||||||
|
libkiwix 10.1.1
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Correctly detect the number of article for older zims (<=6) (@mgautier #743)
|
||||||
|
* [server] Fix fulltext search (@mgautierfr #724)
|
||||||
|
* [server][internal] New way to build Error message (@veloman-yunkan #732 #738 #744)
|
||||||
|
* Fix CI (@mgautierfr #736)
|
||||||
|
|
||||||
|
libkiwix 10.1.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
This release is an important one as it fixes a Xss vulnerability introduced
|
||||||
|
in libkiwix 10.0.0
|
||||||
|
|
||||||
|
* [SECURITY] Fix a Xss attack vulnerability (introduced in 10.0.0) (@juuz0 #721)
|
||||||
|
* [server] Add a option to set a limit on the number of connexion per IP (@kelson42 #700)
|
||||||
|
* [server] Do not display a lang tag in the UI if the book has no language (@juuz0 #706)
|
||||||
|
* [server] Add the book title associated to a search results (@thavelick #705, @mgautierfr #718)
|
||||||
|
* Add `dc:issued` to opds output stream (@veloman-yunkan #715)
|
||||||
|
* Add handling of several languages not provided by ICU (@juuz0 #701)
|
||||||
|
* [server] Add a caching system for search and suggestion (@maneeshpm #620)
|
||||||
|
* Fix cross-compilation (@kelson42 #703)
|
||||||
|
* Add unit-testing of suggestions and error pages (@veloman-yunkan #709 #710 #727)
|
||||||
|
* Better testing system of html response (@veloman-yunkan #725)
|
||||||
|
|
||||||
|
libkiwix 10.0.1
|
||||||
|
===============
|
||||||
|
|
||||||
|
* [server] The catalog search interpret `count=0` as no limit.
|
||||||
|
This was the case for a long time. This was changed unintentionally
|
||||||
|
(@veloman-yunkan #686)
|
||||||
|
* [server] Correctly generere a human friendly title in the server frontend.
|
||||||
|
(@juuz0 #687, @kelson42 #689)
|
||||||
|
* [server] Fix download button if there is no url do download from.
|
||||||
|
(@juuz0 #691)
|
||||||
|
* Add non-minified isotope.pkdg.js
|
||||||
|
Needed for debian packaging as we need the source and minified version is
|
||||||
|
not the source (@legoktm #693)
|
||||||
|
* [server] Add a tooltip with the full language for the lang tag.
|
||||||
|
* CI fixes (@kelson42 @legoktm)
|
||||||
|
|
||||||
|
libkiwix 10.0.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
This release is huge release.
|
||||||
|
The project has been renamed to libkiwix, it is more coherent with the library name.
|
||||||
|
|
||||||
|
* Server front page :
|
||||||
|
- Use js in the front page to display the available book,
|
||||||
|
using the OPDS stream as source. The front page is now populated only with
|
||||||
|
the visible books and user can search for books. (@MananJethwany #530, #541, #534)
|
||||||
|
(@kelson42 #628)
|
||||||
|
- Revamp css (@MananJethwany #559)
|
||||||
|
- Correctly Convert 3iso language code to 2iso (@juuz0 #672)
|
||||||
|
|
||||||
|
* Server suggestions search :
|
||||||
|
- Add pagination for suggestion search (@maneeshpm #591)
|
||||||
|
- Fix suggestion system (@MananJethwany #498)
|
||||||
|
- Provide the kind and path (when adapted) to the suggestion answer (@MananJethwany #464)
|
||||||
|
- The displayed suggestion have now highligth on the searched terms (@maneeshpm #505)
|
||||||
|
- Properly handle html encoding of suggestions (@veloman-yunkan #458)
|
||||||
|
|
||||||
|
* Server improvements :
|
||||||
|
- Remove meta endpoints (@mgautier #669)
|
||||||
|
- Add raw endpoints to get the raw content of a zim (@mgautierfr #646)
|
||||||
|
- Add details on 404 error pages (@soumyankar #490)
|
||||||
|
- Fix headbar insertion when `<head>` tag has attributes (@kelson42 #440)
|
||||||
|
- Better headbar insertion (after charset definition) (@kelson42 #442)
|
||||||
|
|
||||||
|
* New OPDS Stream v2 :
|
||||||
|
- Add a list of categories (@veloman-yunkan)
|
||||||
|
- Support for partial entries (@veloman-yunkan #602)
|
||||||
|
- Support multiple icons size in the OPDS stream (@veloman-yunkan #577 #630)
|
||||||
|
- Add language endpoint to catalog (@veloman-yunkan #553)
|
||||||
|
- Add illustration API to get the illustration of a book (@mgautierfr #645)
|
||||||
|
- OPDS search can now filter books by category (@veloman-yunkan #459)
|
||||||
|
|
||||||
|
* Library improvements :
|
||||||
|
- Allow the libray to be live reloaded when the library.xml changes (@veloman-yunkan #636)
|
||||||
|
- Properly handle removing of book from the library (@veloman-yunkan #485)
|
||||||
|
- Use xapian to search for books in the library (@veloman-yunkan #460, #488)
|
||||||
|
|
||||||
|
* Added methods/functions :
|
||||||
|
- Fix `fileExist` and introduce `fileReadable` (@juuz0 #668)
|
||||||
|
- Add `getVersions` and `printVersions` functions (@kelson42 #665)
|
||||||
|
- Add `getNetworkInterfaces()` and `getBestPublicIP()` functions (@juuz0 #622)
|
||||||
|
- Add `get_zimid()` method to the search result (@maneeshpm #510)
|
||||||
|
|
||||||
|
* Various improvements :
|
||||||
|
- Better secret value for aria2c rpc (@juuz0 #666)
|
||||||
|
- Avoid duplicated Archive/Reader in the Searcher (@veloman-yunkan #648)
|
||||||
|
- Add basic documentation (@mgautierfr #640)
|
||||||
|
- Do not use Reader internally (@maneeshpm #536 #576)
|
||||||
|
- Remove dependency headers from our public headers (@mgautierfr #574)
|
||||||
|
- Downloader now don't write metalink on the filesystem (@kelson42 #502)
|
||||||
|
- Support opening a zim file using a fd (@veloman-yukan #429)
|
||||||
|
- Use C++11 std::thread instead of pthread (@mgautierfr #445)
|
||||||
|
- [READER] Do not crash if zim file has no `Counter` metadata (@mgautierfr #449)
|
||||||
|
- Ensure libzim dependency is compiled with xapian (@mgautierfr #434)
|
||||||
|
- Support video and audio mimetype in `getMediaCount` (@kelson42 #439)
|
||||||
|
- Better parsing of the counterMap (@kelson42 #437)
|
||||||
|
- Adapt libkiwix to libzim 7.0.0 (@mgautierfr #428)
|
||||||
|
- Remove deprecated methods (@mgautierfr)
|
||||||
|
- CI: Build package for Ubuntu Hirsute, Impish and Jammy (@legoktm #431 #568) and remove Groovy
|
||||||
|
- Fix compilation for FreeBSD (@swills g#432)
|
||||||
|
- Many fixes and improvement (@MananJethwany, @maneeshpm, @veloman-yunkan, @mgautierfr)
|
||||||
|
|
||||||
|
kiwix-lib 9.4.1
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Fix `M/Counter` parsing.
|
||||||
|
* [SERVER] Adjust body padding-top for taskbar
|
||||||
|
* Fix potential crash when stoping a server not started.
|
||||||
|
* Various fix in build system and the CI.
|
||||||
|
|
||||||
|
kiwix-lib 9.4.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* [SERVER] Make the headers handling case insensitive.
|
||||||
|
* [SERVER] Make server answer 204 http status code for empty search
|
||||||
|
* [PACKAGING] Made CI build deb packages.
|
||||||
|
* [SERVER] Add a way to prevent taskbar and external link bloquer at article
|
||||||
|
level.
|
||||||
|
* Fix meson file to be compatible with meson 0.45
|
||||||
|
* [SERVER] Update search requests to use pageStart/pageLength instead of
|
||||||
|
pageStart/pageEnd arguments.
|
||||||
|
* [SERVER] Set a fixed favicon size in the main page.
|
||||||
|
* [SERVER] Refactor the response system code to better handling future new
|
||||||
|
libzim api.
|
||||||
|
* Fix segmentation fault around exchange with aria2 process making
|
||||||
|
kiwix-desktop crash at exit.
|
||||||
|
|
||||||
|
kiwix-lib 9.3.1
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Fix handling of samba path on windows.
|
||||||
|
* Do not include `kiwix_config.h` in public header.
|
||||||
|
* Fix compilation with libmicrohttpd v0.97.1
|
||||||
|
* Increase default test timeout to 160seconds/test.
|
||||||
|
* Add automatic debian packaging.
|
||||||
|
* Use non-minified version of jquery-ui.js
|
||||||
|
* Pass `-latomic` compile option for sh4 architecture.
|
||||||
|
* Make mesion install `kiwix-compile-resources` man page.
|
||||||
|
|
||||||
|
kiwix-lib 9.3.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Add a thread safe method to search suggestions.
|
||||||
|
Old methods are now deprecated.
|
||||||
|
|
||||||
|
kiwix-lib 9.2.3
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Add test on byte-range
|
||||||
|
* Fix compilation on bionic and windows.
|
||||||
|
* Allow building using debian packaged kainjow-mustache
|
||||||
|
* Pass `-latomic` compile option for architectures that need it
|
||||||
|
|
||||||
|
kiwix-lib 9.2.2
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Fix handling on empty content in byte range management (wrong assert)
|
||||||
|
|
||||||
|
kiwix-lib 9.2.1
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Fix support of byte range request.
|
||||||
|
|
||||||
|
kiwix-lib 9.2
|
||||||
|
=============
|
||||||
|
|
||||||
|
* Add tests
|
||||||
|
* Refactoring server code.
|
||||||
|
* [SERVER] Add HEAD, Etag and If-None-Match support.
|
||||||
|
* [SERVER] Compress opds catalog answers.
|
||||||
|
|
||||||
|
kiwix-lib 9.1.2
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Do not use the pathToSave if it is empty.
|
||||||
|
|
||||||
|
kiwix-lib 9.1.1
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Fix the detection of the dataDirectory on windows.
|
||||||
|
|
||||||
|
kiwix-lib 9.1.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* [JAVA] Add a method to get the size of an article.
|
||||||
|
* Add a new method to the libray to get the book by path.
|
||||||
|
* Add a option to make the server blocks external link.
|
||||||
|
Links are intercepted by js an redirected to a "portail" page.
|
||||||
|
* [ODPS] Correctly handle book's articleCount and mediaCount.
|
||||||
|
|
||||||
|
kiwix-lib 9.0.1
|
||||||
|
===============
|
||||||
|
|
||||||
|
* [JAVA] Use a long to store the offset of an article in the zim file instead
|
||||||
|
of an int.
|
||||||
|
|
||||||
|
kiwix-lib 9.0.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* [OPDS] Correctly set the id of the OPDS stream.
|
||||||
|
* [OPDS] Do not try to filter the catalog if no filter field is given in the
|
||||||
|
request.
|
||||||
|
* [WINDOWS] Correctly convert path to wide chars when opening the library.xml
|
||||||
|
* [LIBRARY] Remove the function the read file using a native path.
|
||||||
|
All path must be utf8, no need to pass a native path along the utf8 path.
|
||||||
|
* [TEST] Fix tests using the main function of gtest instead of custom one.
|
||||||
|
* [CI] Move to github CI instead of Travis.
|
||||||
|
* The `Book::update` method always update the book's fields. Even if they are
|
||||||
|
not empty.
|
||||||
|
* [JAVA] Add wrapping around the library manager (opds parsing)
|
||||||
|
* [ARIA2] Add api option to start download with option (destination folder)
|
||||||
|
* [OPDS] Fixes about opds parsing, generation (missing attributes)
|
||||||
|
and requesting (server)
|
||||||
|
* Add methods on `Book` to get specific tag values (was on `Reader` only)
|
||||||
|
* Add flavour attribute to `Book`
|
||||||
|
* Fix opensearch description.
|
||||||
|
* Trust the given library.xml (by default) instead of reading the value from
|
||||||
|
the zim files.
|
||||||
|
* [OPDS] Be able to filter the content by name or size.
|
||||||
|
* [WINDOWS] Fix launching subcommand when there is spaces in the path.
|
||||||
|
|
||||||
|
kiwix-lib 8.2.2
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Improve a few compilation scripts
|
||||||
|
|
||||||
|
kiwix-lib 8.2.1
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Reintroduce kiwix-serve taskbar
|
||||||
|
|
||||||
|
kiwix-lib 8.2.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* More debug information if aria2c command fails
|
||||||
|
* Allow to set kiwix-serve port
|
||||||
|
* Better (dead) bookmarks mgmt
|
||||||
|
|
||||||
|
kiwix-lib 8.1.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Fix pathTools manipulation.
|
||||||
|
* Add missing implementation of getArticleCount and getMediaCount on android.
|
||||||
|
* Correctly convert windows path to utf8.
|
||||||
|
* Add code coverage in the CI
|
||||||
|
|
||||||
|
kiwix-lib 8.0.1
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Fix join function
|
||||||
|
|
||||||
|
kiwix-lib 8.0.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Add new methods to get all (and new) metadata from the zim file.
|
||||||
|
* Add methods to get the value of a specific tag.
|
||||||
|
* [API Change] Convert tags value to the new convention.
|
||||||
|
* [API Change] Rename `getMatatag` method to `getMetadata`
|
||||||
|
* [ABI Change] Correctly detect executable path in appimage.
|
||||||
|
|
||||||
|
kiwix-lib 7.0.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* [API break] Add a argument to kiwix-serve to specify the library to use.
|
||||||
|
|
||||||
|
kiwix-lib 6.0.4
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Fix HTML rendering of the search result if there is no result.
|
||||||
|
* Do not crash at html rendering if request ask for 0 results (start == end)
|
||||||
|
* Correctly find the executable path if we are using AppImage
|
||||||
|
|
||||||
|
kiwix-lib 6.0.3
|
||||||
|
===============
|
||||||
|
|
||||||
|
* force one column suggestion in kiwix-serve suggestions
|
||||||
|
* fix fulltext search link in suggestions
|
||||||
|
* UI fixes in kiwix-serve rendering
|
||||||
|
|
||||||
|
kiwix-lib 6.0.2
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Correctly set the groupId in the pom file.
|
||||||
|
|
||||||
|
kiwix-lib 6.0.1
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Generate the pom file for android/maven
|
||||||
|
|
||||||
|
kiwix-lib 6.0.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Move the server code in kiwix-lib (from kiwix-serve).
|
||||||
|
* Add unit test on regex functions.
|
||||||
|
* Fix computerAbsolutePath (thread safe, memory leak).
|
||||||
|
* Correctly set the book's path as valid if we construct the book from a
|
||||||
|
reader.
|
||||||
|
* [JNI] Add a method to know if a article is a redirection.
|
||||||
|
* Do not embed the gtest dependency.
|
||||||
|
* [JNI] Add a constructor to JNIKiwixString.
|
||||||
|
* Change order of search of the favicon urls.
|
||||||
|
* Clean a lot of unecessary includes in headers. (potential "API break")
|
||||||
|
|
||||||
|
kiwix-lib 5.2.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* kiwix-serve integration (as a seperated process).
|
||||||
|
* Fix crash in the suggestion search.
|
||||||
|
* Better API to filter the library books.
|
||||||
|
* New kiwix-lib application for android. (.aar)
|
||||||
|
* Use ReLinker to link with libkiwix.so in android.
|
||||||
|
* Correctly set the verbosity of zim search.
|
||||||
|
|
||||||
|
kiwix-lib 5.1.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Add function to pause, resume and stop downloads.
|
||||||
|
* Add zim's tags in the opds stream.
|
||||||
|
* Addapt to new libzim 5.0.0 API.
|
||||||
|
|
||||||
|
kiwix-lib 5.0.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Remove error message when trying to open an wrong zim file.
|
||||||
|
* Rewrite `makeTmpDirectory` to not use uuid functions on windows.
|
||||||
|
* [API break] Remove `getNetworkInterfaces` and `getBestPublicIp`.
|
||||||
|
* Remove rpath
|
||||||
|
* Detect infinite (and too long) redirect loops.
|
||||||
|
|
||||||
|
kiwix-lib 4.1.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Allow the library to be filtered by tags.
|
||||||
|
* Fix language mapping.
|
||||||
|
* Update README about mustache dependency.
|
||||||
|
|
||||||
|
kiwix-lib 4.0.1
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Fix "maybe uninitialize variable" issue.
|
||||||
|
* Ensure path are stored correctly (absolute path) in the library.
|
||||||
|
* [CI] Use the new deps archive xz
|
||||||
|
|
||||||
|
kiwix-lib 4.0.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* [API break] Remove support for external index.
|
||||||
|
* Move to the mustache templating system instead of ctpp2.
|
||||||
|
* Make meson.build works for meson>=0.43.0
|
||||||
|
* [API break] Move the basic tools from the `common` directory to `tools`.
|
||||||
|
|
||||||
|
kiwix-lib 3.1.1
|
||||||
|
===============
|
||||||
|
|
||||||
|
* The OPDS feed book's date must be the date of the book, not the date of the
|
||||||
|
feed generation.
|
||||||
|
* Convert the standard opds date to our format (YYYY-MM-DD)
|
||||||
|
* Remove duplicate language attribute in the libxml dumper.
|
||||||
|
* Create the datadirectory to not fail to write a file in a non-existent
|
||||||
|
directory
|
||||||
|
|
||||||
|
kiwix-lib 3.1.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Add a method to get the favicon url of book (if available).
|
||||||
|
* Move dump code of library.xml in a specific class.
|
||||||
|
* Add a first support to bookmarks
|
||||||
|
|
||||||
|
kiwix-lib 3.0.3
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Add the 'en' language to the mapping alpha2-code ('en') to alpha3-code
|
||||||
|
('eng').
|
||||||
|
* Correctly write the 'ArticleCount' and 'MediaCount' in the library.xml.
|
||||||
|
* Correctly fill the book size for the zim file size.
|
||||||
|
* Fix launch of aria2c.
|
||||||
|
|
||||||
|
kiwix-lib 3.0.2
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Use the correct path separator when computing relativePath on Windows.
|
||||||
|
|
||||||
|
kiwix-lib 3.0.1
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Small fix about parsing the opdsStream.
|
||||||
|
|
||||||
kiwix-lib 3.0.0
|
kiwix-lib 3.0.0
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +1,55 @@
|
|||||||
Kiwix library
|
Libkiwix
|
||||||
=============
|
========
|
||||||
|
|
||||||
The Kiwix library provides the Kiwix software core. It contains the
|
The Libkiwix provides the [Kiwix](https://kiwix.org) software suite
|
||||||
code shared by all Kiwix ports (Windows, Linux, OSX, Android, ...).
|
core. It contains the code shared by all Kiwix ports (Windows,
|
||||||
|
GNU/Linux, macOS, Android, iOS, ...).
|
||||||
|
|
||||||
|
[](https://download.kiwix.org/release/libkiwix/)
|
||||||
|
[](https://github.com/kiwix/libkiwix/wiki/Repology)
|
||||||
|
[](https://github.com/kiwix/libkiwix/actions?query=branch%3Amain)
|
||||||
|
[](https://libkiwix.readthedocs.org/en/latest/?badge=latest)
|
||||||
|
[](https://www.codefactor.io/repository/github/kiwix/libkiwix)
|
||||||
|
[](https://codecov.io/gh/kiwix/libkiwix)
|
||||||
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||||
|
|
||||||
Disclaimer
|
Disclaimer
|
||||||
----------
|
----------
|
||||||
|
|
||||||
This document assumes you have a little knowledge about software
|
This document assumes you have a little knowledge about software
|
||||||
compilation. If you experience difficulties with the dependencies or
|
compilation. If you experience difficulties with the dependencies or
|
||||||
with the Kiwix libary compilation itself, we recommend to have a look
|
with the Libkiwix compilation itself, we recommend to have a look to
|
||||||
to [kiwix-build](https://github.com/kiwix/kiwix-build).
|
[kiwix-build](https://github.com/kiwix/kiwix-build).
|
||||||
|
|
||||||
Preamble
|
Preamble
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Although the Kiwix library can be (cross-)compiled on/for many
|
Although the Libkiwix can be (cross-)compiled on/for many sytems, the
|
||||||
sytems, the following documentation explains how to do it on POSIX
|
following documentation explains how to do it on POSIX ones. It is
|
||||||
ones. It is primarly thought for GNU/Linux systems and has been tested
|
primarly thought for GNU/Linux systems and has been tested on recent
|
||||||
on recent releases of Ubuntu and Fedora.
|
releases of Ubuntu and Fedora.
|
||||||
|
|
||||||
Dependencies
|
Dependencies
|
||||||
------------
|
------------
|
||||||
|
|
||||||
The Kiwix library relies on many third parts software libraries. They
|
The Libkiwix relies on many third party software libraries. They are
|
||||||
are prerequisites to the Kiwix library compilation. Following
|
prerequisites to the Libkiwix compilation. Following libraries need to
|
||||||
libraries need to be available:
|
be available:
|
||||||
|
* [ICU](https://site.icu-project.org/) (package `libicu-dev` on Ubuntu)
|
||||||
|
* [ZIM](https://openzim.org/) (package `libzim-dev` on Ubuntu)
|
||||||
|
* [Pugixml](https://pugixml.org/) (package `libpugixml-dev` on Ubuntu)
|
||||||
|
* [Mustache](https://github.com/kainjow/Mustache) (Just copy the
|
||||||
|
header `mustache.hpp` somewhere it can be found by the compiler and/or
|
||||||
|
set CPPFLAGS with correct `-I` option). Use Mustache version 4.1 or above.
|
||||||
|
* [Libcurl](https://curl.se/libcurl) (`libcurl4-gnutls-dev`, `libcurl4-nss-dev` or `libcurl4-openssl-dev` on Ubuntu)
|
||||||
|
* [Microhttpd](https://www.gnu.org/software/libmicrohttpd) (package `libmicrohttpd-dev` on Ubuntu)
|
||||||
|
* [Zlib](https://zlib.net/) (package `zlib1g-dev` on Ubuntu)
|
||||||
|
|
||||||
* ICU ................................... http://site.icu-project.org/
|
To test the code:
|
||||||
(package libicu-dev on Ubuntu)
|
* [Google Test](https://github.com/google/googletest) (package `googletest` on Ubuntu)
|
||||||
* ZIM ........................................ http://www.openzim.org/
|
|
||||||
(package libzim-dev on Ubuntu)
|
The following dependency needs to be available at runtime:
|
||||||
* Pugixml ........................................ http://pugixml.org/
|
* [Aria2](https://aria2.github.io/) (package `aria2` on Ubuntu)
|
||||||
(package libpugixml-dev on Ubuntu)
|
|
||||||
* ctpp2 ........................................ http://ctpp.havoc.ru/
|
|
||||||
(package libctpp2-dev on Ubuntu)
|
|
||||||
* Xapian ......................................... https://xapian.org/
|
|
||||||
(package libxapian-dev on Ubuntu)
|
|
||||||
* libaria2 .................................. https://aria2.github.io/
|
|
||||||
(no package on Ubuntu)
|
|
||||||
|
|
||||||
These dependencies may or may not be packaged by your operating
|
These dependencies may or may not be packaged by your operating
|
||||||
system. They may also be packaged but only in an older version. The
|
system. They may also be packaged but only in an older version. The
|
||||||
@@ -47,75 +58,163 @@ In the worse case, you will have to download and compile bleeding edge
|
|||||||
version by hand.
|
version by hand.
|
||||||
|
|
||||||
If you want to install these dependencies locally, then use the
|
If you want to install these dependencies locally, then use the
|
||||||
kiwix-lib directory as install prefix.
|
`libkiwix` directory as install prefix.
|
||||||
|
|
||||||
If you compile ctpp2 from source and want to compile the Kiwix library
|
|
||||||
statically then you will probably need to rename ctpp2 static library
|
|
||||||
from ctpp2-st.a to ctpp2.a.
|
|
||||||
|
|
||||||
Environment
|
Environment
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
The Kiwix library builds using [Meson](http://mesonbuild.com/) version
|
The Libkiwix builds using [Meson](https://mesonbuild.com/) version
|
||||||
0.39 or higher. Meson relies itself on Ninja, pkg-config and few other
|
0.45 or higher. Meson relies itself on Ninja, pkg-config and few other
|
||||||
compilation tools.
|
compilation tools.
|
||||||
|
|
||||||
Install first the few common compilation tools:
|
Install first the few common compilation tools:
|
||||||
* Meson
|
* [Meson](https://mesonbuild.com/)
|
||||||
* Ninja
|
* [Ninja](https://ninja-build.org/)
|
||||||
* Pkg-config
|
* [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/)
|
||||||
|
|
||||||
These tools should be packaged if you use a cutting edge operating
|
These tools should be packaged if you use a cutting edge operating
|
||||||
system. If not, have a look to the "Troubleshooting" section.
|
system. If not, have a look to the [Troubleshooting](#Troubleshooting)
|
||||||
|
section.
|
||||||
|
|
||||||
Compilation
|
Compilation
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Once all dependencies are installed, you can compile the Kiwix library
|
Once all dependencies are installed, you can compile the Libkiwix
|
||||||
with:
|
with:
|
||||||
```
|
```bash
|
||||||
meson . build
|
meson . build
|
||||||
ninja -C build
|
ninja -C build
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, it will compile dynamic linked libraries. All binary files
|
By default, it will compile dynamic linked libraries. All binary files
|
||||||
will be created in the "build" directory created automatically by
|
will be created in the `build` directory created automatically by
|
||||||
Meson. If you want statically linked libraries, you can add
|
Meson. If you want statically linked libraries, you can add
|
||||||
`--default-library=static` option to the Meson command.
|
`--default-library=static` option to the Meson command.
|
||||||
|
|
||||||
Depending of you system, `ninja` may be called `ninja-build`.
|
Depending of you system, `ninja` may be called `ninja-build`.
|
||||||
|
|
||||||
|
The android wrapper uses deprecated methods of libkiwix so it cannot be compiled
|
||||||
|
with `werror=true` (the default). So you must pass `-Dwerror=false` to meson:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
meson . build -Dwrapper=android -Dwerror=false
|
||||||
|
ninja -C build
|
||||||
|
```
|
||||||
|
|
||||||
|
Static files compilation
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Libkiwix has a few static files 'compiled' within the binary
|
||||||
|
code. This is mostly Javascript/HTML/pictures necessary for the HTTP
|
||||||
|
daemon.
|
||||||
|
|
||||||
|
These static files are available in the `static` directory and are
|
||||||
|
compiled by custom Python code available in this repository `scripts`
|
||||||
|
directory. This happens automatically at compilation time without any
|
||||||
|
additional command to run.
|
||||||
|
|
||||||
|
To avoid HTTP caching issues, the URLs (to the static content) are
|
||||||
|
appended with a `cacheid` parameter (this is called "cache
|
||||||
|
busting"). This `cacheid` value derived from the
|
||||||
|
[sha1sum](https://en.wikipedia.org/wiki/Sha1sum) of each targeted
|
||||||
|
static file. As a consequence, each time you change a static file, the
|
||||||
|
corresponding `cacheid` value will change.
|
||||||
|
|
||||||
|
To properly test this feature, this `cacheid` needs to be added
|
||||||
|
manually to the automated tests and has to be commited. After
|
||||||
|
modifying the needed static file, [run the automated
|
||||||
|
tests](#Testing). They will fail, but the inspection of the testing
|
||||||
|
log will give you the new `cacheid` value(s). Finally update
|
||||||
|
`test/server.cpp` with the appropriate `cacheid` value(s) which have
|
||||||
|
changed.
|
||||||
|
|
||||||
|
Testing
|
||||||
|
-------
|
||||||
|
|
||||||
|
To run the automated tests:
|
||||||
|
```bash
|
||||||
|
cd build
|
||||||
|
meson test
|
||||||
|
```
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
If you want to install the Kiwix library and the headers you just have
|
If you want to install the Libkiwix and the headers you just have
|
||||||
compiled on your system, here we go:
|
compiled on your system, here we go:
|
||||||
|
```bash
|
||||||
```
|
|
||||||
ninja -C build install
|
ninja -C build install
|
||||||
```
|
```
|
||||||
|
|
||||||
You might need to run the command as root (or using 'sudo'), depending
|
You might need to run the command as root (or using `sudo`), depending
|
||||||
where you want to install the libraries. After the installation
|
where you want to install the libraries. After the installation
|
||||||
succeeded, you may need to run ldconfig (as root).
|
succeeded, you may need to run `ldconfig` (as `root`).
|
||||||
|
|
||||||
Uninstallation
|
Uninstallation
|
||||||
------------
|
--------------
|
||||||
|
|
||||||
If you want to uninstall the Kiwix library:
|
If you want to uninstall the Kiwix library:
|
||||||
|
```bash
|
||||||
```
|
|
||||||
ninja -C build uninstall
|
ninja -C build uninstall
|
||||||
```
|
```
|
||||||
|
|
||||||
Like for the installation, you might need to run the command as root
|
Like for the installation, you might need to run the command as `root`
|
||||||
(or using 'sudo').
|
(or using `sudo`).
|
||||||
|
|
||||||
|
Custom Index Page
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
to use custom welcome page mention `customIndexPage` argument in `kiwix::internalServer()` or use `kiwix::server->setCustomIndexTemplate()`.
|
||||||
|
(note - while using custom html file please mention all external links as absolute path.)
|
||||||
|
|
||||||
|
to create a HTML template with custom JS you need to have a look at various OPDS based endpoints as mentioned [here](https://wiki.kiwix.org/wiki/OPDS) to load books.
|
||||||
|
|
||||||
|
To use JS provided by kiwix-serve you can use the following template to start with ->
|
||||||
|
|
||||||
|
```
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
|
<title><-- Custom Tittle --></title>
|
||||||
|
<script src="{{root}}/skin/isotope.pkgd.min.js" defer></script>
|
||||||
|
<script src="{{root}}/skin/iso6391To3.js"></script>
|
||||||
|
<script type="text/javascript" src="{{root}}/skin/index.js" defer></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
- To get books listed using `index.js` add - `<div class="book__list"></div>` under body tag.
|
||||||
|
- To get number of books listed add - `<h3 class="kiwixHomeBody__results"></h3>` under body tag.
|
||||||
|
- To add language select box add - `<select id="languageFilter"></select>` under body tag.
|
||||||
|
- To add category select box add - `<select id="categoryFilter"></select>` under body tag.
|
||||||
|
- To add search box for books use following form -
|
||||||
|
```
|
||||||
|
<form id='kiwixSearchForm'>
|
||||||
|
<input type="text" name="q" placeholder="Search" id="searchFilter" class='kiwixSearch filter'>
|
||||||
|
<input type="submit" class="kiwixButton" value="Search"/>
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
If you compile manually Libmicrohttpd, you might need to compile it
|
||||||
|
without GNU TLS, a bug here will empeach further compilation
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
If the compilation still fails, you might need to get a more recent
|
||||||
|
version of a dependency than the one packaged by your Linux
|
||||||
|
distribution. Try then with a source tarball distributed by the
|
||||||
|
problematic upstream project or even directly from the source code
|
||||||
|
repository.
|
||||||
|
|
||||||
Troubleshooting
|
Troubleshooting
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
If you need to install Meson "manually":
|
If you need to install Meson "manually":
|
||||||
```
|
```bash
|
||||||
virtualenv -p python3 ./ # Create virtualenv
|
virtualenv -p python3 ./ # Create virtualenv
|
||||||
source bin/activate # Activate the virtualenv
|
source bin/activate # Activate the virtualenv
|
||||||
pip3 install meson # Install Meson
|
pip3 install meson # Install Meson
|
||||||
@@ -123,7 +222,7 @@ hash -r # Refresh bash paths
|
|||||||
```
|
```
|
||||||
|
|
||||||
If you need to install Ninja "manually":
|
If you need to install Ninja "manually":
|
||||||
```
|
```bash
|
||||||
git clone git://github.com/ninja-build/ninja.git
|
git clone git://github.com/ninja-build/ninja.git
|
||||||
cd ninja
|
cd ninja
|
||||||
git checkout release
|
git checkout release
|
||||||
@@ -133,13 +232,8 @@ cp ninja ../bin
|
|||||||
cd ..
|
cd ..
|
||||||
```
|
```
|
||||||
|
|
||||||
If the compilation still fails, you might need to get a more recent
|
|
||||||
version of a dependency than the one packaged by your Linux
|
|
||||||
distribution. Try then with a source tarball distributed by the
|
|
||||||
problematic upstream project or even directly from the source code
|
|
||||||
repository.
|
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
GPLv3 or later, see COPYING for more details.
|
[GPLv3](https://www.gnu.org/licenses/gpl-3.0) or later, see
|
||||||
|
[COPYING](COPYING) for more details.
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
libkiwix (0.0.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Initial release
|
||||||
|
|
||||||
|
-- Kunal Mehta <legoktm@debian.org> Wed, 08 Jul 2020 18:12:32 -0700
|
||||||
Vendored
+46
@@ -0,0 +1,46 @@
|
|||||||
|
Source: libkiwix
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Kiwix team <kiwix@kiwix.org>
|
||||||
|
Build-Depends: debhelper-compat (= 13),
|
||||||
|
meson,
|
||||||
|
pkg-config,
|
||||||
|
libzim-dev (>= 7.2.0~),
|
||||||
|
libcurl4-gnutls-dev,
|
||||||
|
libicu-dev,
|
||||||
|
libgtest-dev,
|
||||||
|
libkainjow-mustache-dev,
|
||||||
|
liblzma-dev,
|
||||||
|
libmicrohttpd-dev,
|
||||||
|
libpugixml-dev,
|
||||||
|
zlib1g-dev
|
||||||
|
Standards-Version: 4.5.0
|
||||||
|
Section: libs
|
||||||
|
Homepage: https://github.com/kiwix/libkiwix
|
||||||
|
Rules-Requires-Root: no
|
||||||
|
|
||||||
|
Package: libkiwix-dev
|
||||||
|
Section: libdevel
|
||||||
|
Architecture: any
|
||||||
|
Multi-Arch: same
|
||||||
|
Depends: libkiwix10 (= ${binary:Version}), ${misc:Depends}, python3,
|
||||||
|
libzim-dev (>= 7.2.0~),
|
||||||
|
libicu-dev,
|
||||||
|
libpugixml-dev,
|
||||||
|
libcurl4-gnutls-dev,
|
||||||
|
libmicrohttpd-dev
|
||||||
|
Description: library of common code for Kiwix (development)
|
||||||
|
Kiwix is an offline Wikipedia reader. libkiwix provides the
|
||||||
|
software core for Kiwix, and contains the code shared by all
|
||||||
|
Kiwix ports (Windows, Linux, OSX, Android, etc.).
|
||||||
|
.
|
||||||
|
This package contains development files.
|
||||||
|
|
||||||
|
Package: libkiwix10
|
||||||
|
Architecture: any
|
||||||
|
Multi-Arch: same
|
||||||
|
Depends: ${shlibs:Depends}, ${misc:Depends}, aria2
|
||||||
|
Conflicts: libkiwix0, libkiwix3, libkiwix9
|
||||||
|
Description: library of common code for Kiwix
|
||||||
|
Kiwix is an offline Wikipedia reader. libkiwix provides the
|
||||||
|
software core for Kiwix, and contains the code shared by all
|
||||||
|
Kiwix ports (Windows, Linux, OSX, Android, etc.).
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
See COPYING in the repository root.
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
usr/include
|
||||||
|
usr/lib/*/libkiwix.so
|
||||||
|
usr/lib/*/pkgconfig
|
||||||
|
usr/bin
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
usr/share/man/man1/kiwix-compile-resources.1*
|
||||||
|
usr/share/man/man1/kiwix-compile-i18n.1*
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
usr/lib/*/libkiwix.so.*
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@ --buildsystem=meson
|
||||||
|
|
||||||
|
override_dh_auto_test:
|
||||||
|
dh_auto_test -- -t 3
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
3.0 (native)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
api
|
||||||
|
xml
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# Configuration file for the Sphinx documentation builder.
|
||||||
|
#
|
||||||
|
# This file only contains a selection of the most common options. For a full
|
||||||
|
# list see the documentation:
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||||
|
|
||||||
|
# -- Path setup --------------------------------------------------------------
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#
|
||||||
|
import os
|
||||||
|
# import sys
|
||||||
|
# sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
|
||||||
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
|
project = 'libkiwix'
|
||||||
|
copyright = '2022, libkiwix-team'
|
||||||
|
author = 'libkiwix-team'
|
||||||
|
|
||||||
|
|
||||||
|
# -- General configuration ---------------------------------------------------
|
||||||
|
|
||||||
|
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
|
# ones.
|
||||||
|
extensions = [
|
||||||
|
'breathe',
|
||||||
|
'exhale'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
# This pattern also affects html_static_path and html_extra_path.
|
||||||
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||||
|
|
||||||
|
|
||||||
|
if not on_rtd:
|
||||||
|
html_theme = 'sphinx_rtd_theme'
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
breathe_projects = {
|
||||||
|
"libkiwix": "./xml"
|
||||||
|
}
|
||||||
|
breathe_default_project = 'libkiwix'
|
||||||
|
|
||||||
|
exhale_args = {
|
||||||
|
"containmentFolder": "./api",
|
||||||
|
"rootFileName": "ref_api.rst",
|
||||||
|
"rootFileTitle": "Reference API",
|
||||||
|
"doxygenStripFromPath":"..",
|
||||||
|
"treeViewIsBootstrap": True,
|
||||||
|
"createTreeView" : True,
|
||||||
|
"exhaleExecutesDoxygen": True,
|
||||||
|
"exhaleDoxygenStdin": "INPUT = ../include"
|
||||||
|
}
|
||||||
|
|
||||||
|
primary_domain = 'cpp'
|
||||||
|
|
||||||
|
highlight_language = 'cpp'
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
.. libkiwix documentation master file, created by
|
||||||
|
sphinx-quickstart on Fri Jul 24 15:40:50 2020.
|
||||||
|
You can adapt this file completely to your liking, but it should at least
|
||||||
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
|
Welcome to libkiwix's documentation!
|
||||||
|
==================================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Contents:
|
||||||
|
|
||||||
|
usage
|
||||||
|
api/ref_api
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
sphinx = find_program('sphinx-build', native:true)
|
||||||
|
|
||||||
|
sphinx_target = run_target('doc',
|
||||||
|
command: [sphinx, '-bhtml',
|
||||||
|
meson.current_source_dir(),
|
||||||
|
meson.current_build_dir()])
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
breathe
|
||||||
|
exhale
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
Libkiwix programming
|
||||||
|
====================
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
libkiwix is written in C++. To use the library, you need the include files of libkiwix have
|
||||||
|
to link against libzim.
|
||||||
|
|
||||||
|
Errors are handled with exceptions. When something goes wrong, libkiwix throws an error,
|
||||||
|
which is always derived from std::exception.
|
||||||
|
|
||||||
|
All classes are defined in the namespace kiwix.
|
||||||
|
|
||||||
|
libkiwix is a set of tools to manage zim files and provide some common functionnality.
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#!/usr/bin/bash
|
|
||||||
|
|
||||||
files=(
|
|
||||||
"include/library.h"
|
|
||||||
"include/common/stringTools.h"
|
|
||||||
"include/common/pathTools.h"
|
|
||||||
"include/common/otherTools.h"
|
|
||||||
"include/common/regexTools.h"
|
|
||||||
"include/common/networkTools.h"
|
|
||||||
"include/manager.h"
|
|
||||||
"include/reader.h"
|
|
||||||
"include/kiwix.h"
|
|
||||||
"include/xapianSearcher.h"
|
|
||||||
"include/searcher.h"
|
|
||||||
"src/library.cpp"
|
|
||||||
"src/android/kiwix.cpp"
|
|
||||||
"src/android/org/kiwix/kiwixlib/JNIKiwixBool.java"
|
|
||||||
"src/android/org/kiwix/kiwixlib/JNIKiwix.java"
|
|
||||||
"src/android/org/kiwix/kiwixlib/JNIKiwixString.java"
|
|
||||||
"src/android/org/kiwix/kiwixlib/JNIKiwixInt.java"
|
|
||||||
"src/searcher.cpp"
|
|
||||||
"src/common/pathTools.cpp"
|
|
||||||
"src/common/regexTools.cpp"
|
|
||||||
"src/common/otherTools.cpp"
|
|
||||||
"src/common/networkTools.cpp"
|
|
||||||
"src/common/stringTools.cpp"
|
|
||||||
"src/xapianSearcher.cpp"
|
|
||||||
"src/manager.cpp"
|
|
||||||
"src/reader.cpp"
|
|
||||||
)
|
|
||||||
|
|
||||||
for i in "${files[@]}"
|
|
||||||
do
|
|
||||||
echo $i
|
|
||||||
clang-format -i -style=file $i
|
|
||||||
done
|
|
||||||
+62
-25
@@ -21,62 +21,94 @@
|
|||||||
#define KIWIX_BOOK_H
|
#define KIWIX_BOOK_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
namespace pugi {
|
namespace pugi {
|
||||||
class xml_node;
|
class xml_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace zim {
|
||||||
|
class Archive;
|
||||||
|
}
|
||||||
|
|
||||||
namespace kiwix
|
namespace kiwix
|
||||||
{
|
{
|
||||||
enum supportedIndexType { UNKNOWN, XAPIAN };
|
|
||||||
|
|
||||||
class OPDSDumper;
|
class OPDSDumper;
|
||||||
class Reader;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to store information about a book (a zim file)
|
* A class to store information about a book (a zim file)
|
||||||
*/
|
*/
|
||||||
class Book
|
class Book
|
||||||
{
|
{
|
||||||
public:
|
public: // types
|
||||||
|
class Illustration
|
||||||
|
{
|
||||||
|
friend class Book;
|
||||||
|
public:
|
||||||
|
uint16_t width = 48;
|
||||||
|
uint16_t height = 48;
|
||||||
|
std::string mimeType;
|
||||||
|
std::string url;
|
||||||
|
|
||||||
|
const std::string& getData() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::string data;
|
||||||
|
mutable std::mutex mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::vector<std::shared_ptr<const Illustration>> Illustrations;
|
||||||
|
|
||||||
|
public: // functions
|
||||||
Book();
|
Book();
|
||||||
~Book();
|
~Book();
|
||||||
|
|
||||||
bool update(const Book& other);
|
bool update(const Book& other);
|
||||||
void update(const Reader& reader);
|
void update(const zim::Archive& archive);
|
||||||
void updateFromXml(const pugi::xml_node& node, const std::string& baseDir);
|
void updateFromXml(const pugi::xml_node& node, const std::string& baseDir);
|
||||||
void updateFromOpds(const pugi::xml_node& node, const std::string& urlHost);
|
void updateFromOpds(const pugi::xml_node& node, const std::string& urlHost);
|
||||||
std::string getHumanReadableIdFromPath();
|
std::string getHumanReadableIdFromPath() const;
|
||||||
|
|
||||||
bool readOnly() const { return m_readOnly; }
|
bool readOnly() const { return m_readOnly; }
|
||||||
const std::string& getId() const { return m_id; }
|
const std::string& getId() const { return m_id; }
|
||||||
const std::string& getPath() const { return m_path; }
|
const std::string& getPath() const { return m_path; }
|
||||||
bool isPathValid() const { return m_pathValid; }
|
bool isPathValid() const { return m_pathValid; }
|
||||||
const std::string& getIndexPath() const { return m_indexPath; }
|
|
||||||
const supportedIndexType& getIndexType() const { return m_indexType; }
|
|
||||||
const std::string& getTitle() const { return m_title; }
|
const std::string& getTitle() const { return m_title; }
|
||||||
const std::string& getDescription() const { return m_description; }
|
const std::string& getDescription() const { return m_description; }
|
||||||
const std::string& getLanguage() const { return m_language; }
|
DEPRECATED const std::string& getLanguage() const { return m_language; }
|
||||||
|
const std::string& getCommaSeparatedLanguages() const { return m_language; }
|
||||||
|
const std::vector<std::string> getLanguages() const;
|
||||||
const std::string& getCreator() const { return m_creator; }
|
const std::string& getCreator() const { return m_creator; }
|
||||||
const std::string& getPublisher() const { return m_publisher; }
|
const std::string& getPublisher() const { return m_publisher; }
|
||||||
const std::string& getDate() const { return m_date; }
|
const std::string& getDate() const { return m_date; }
|
||||||
const std::string& getUrl() const { return m_url; }
|
const std::string& getUrl() const { return m_url; }
|
||||||
const std::string& getName() const { return m_name; }
|
const std::string& getName() const { return m_name; }
|
||||||
|
std::string getCategory() const;
|
||||||
const std::string& getTags() const { return m_tags; }
|
const std::string& getTags() const { return m_tags; }
|
||||||
|
std::string getTagStr(const std::string& tagName) const;
|
||||||
|
bool getTagBool(const std::string& tagName) const;
|
||||||
|
const std::string& getFlavour() const { return m_flavour; }
|
||||||
const std::string& getOrigId() const { return m_origId; }
|
const std::string& getOrigId() const { return m_origId; }
|
||||||
const uint64_t& getArticleCount() const { return m_articleCount; }
|
const uint64_t& getArticleCount() const { return m_articleCount; }
|
||||||
const uint64_t& getMediaCount() const { return m_mediaCount; }
|
const uint64_t& getMediaCount() const { return m_mediaCount; }
|
||||||
const uint64_t& getSize() const { return m_size; }
|
const uint64_t& getSize() const { return m_size; }
|
||||||
const std::string& getFavicon() const;
|
DEPRECATED const std::string& getFavicon() const;
|
||||||
const std::string& getFaviconMimeType() const { return m_faviconMimeType; }
|
DEPRECATED const std::string& getFaviconUrl() const;
|
||||||
|
DEPRECATED const std::string& getFaviconMimeType() const;
|
||||||
|
|
||||||
|
Illustrations getIllustrations() const;
|
||||||
|
std::shared_ptr<const Illustration> getIllustration(unsigned int size) const;
|
||||||
|
|
||||||
const std::string& getDownloadId() const { return m_downloadId; }
|
const std::string& getDownloadId() const { return m_downloadId; }
|
||||||
|
|
||||||
void setReadOnly(bool readOnly) { m_readOnly = readOnly; }
|
void setReadOnly(bool readOnly) { m_readOnly = readOnly; }
|
||||||
void setId(const std::string& id) { m_id = id; }
|
void setId(const std::string& id) { m_id = id; }
|
||||||
void setPath(const std::string& path);
|
void setPath(const std::string& path);
|
||||||
void setPathValid(bool valid) { m_pathValid = valid; }
|
void setPathValid(bool valid) { m_pathValid = valid; }
|
||||||
void setIndexPath(const std::string& indexPath);
|
|
||||||
void setIndexType(supportedIndexType indexType) { m_indexType = indexType;}
|
|
||||||
void setTitle(const std::string& title) { m_title = title; }
|
void setTitle(const std::string& title) { m_title = title; }
|
||||||
void setDescription(const std::string& description) { m_description = description; }
|
void setDescription(const std::string& description) { m_description = description; }
|
||||||
void setLanguage(const std::string& language) { m_language = language; }
|
void setLanguage(const std::string& language) { m_language = language; }
|
||||||
@@ -85,39 +117,44 @@ class Book
|
|||||||
void setDate(const std::string& date) { m_date = date; }
|
void setDate(const std::string& date) { m_date = date; }
|
||||||
void setUrl(const std::string& url) { m_url = url; }
|
void setUrl(const std::string& url) { m_url = url; }
|
||||||
void setName(const std::string& name) { m_name = name; }
|
void setName(const std::string& name) { m_name = name; }
|
||||||
|
void setFlavour(const std::string& flavour) { m_flavour = flavour; }
|
||||||
void setTags(const std::string& tags) { m_tags = tags; }
|
void setTags(const std::string& tags) { m_tags = tags; }
|
||||||
void setOrigId(const std::string& origId) { m_origId = origId; }
|
void setOrigId(const std::string& origId) { m_origId = origId; }
|
||||||
void setArticleCount(uint64_t articleCount) { m_articleCount = articleCount; }
|
void setArticleCount(uint64_t articleCount) { m_articleCount = articleCount; }
|
||||||
void setMediaCount(uint64_t mediaCount) { m_mediaCount = mediaCount; }
|
void setMediaCount(uint64_t mediaCount) { m_mediaCount = mediaCount; }
|
||||||
void setSize(uint64_t size) { m_size = size; }
|
void setSize(uint64_t size) { m_size = size; }
|
||||||
void setFavicon(const std::string& favicon) { m_favicon = favicon; }
|
|
||||||
void setFaviconMimeType(const std::string& faviconMimeType) { m_faviconMimeType = faviconMimeType; }
|
|
||||||
void setDownloadId(const std::string& downloadId) { m_downloadId = downloadId; }
|
void setDownloadId(const std::string& downloadId) { m_downloadId = downloadId; }
|
||||||
|
|
||||||
protected:
|
private: // functions
|
||||||
|
std::string getCategoryFromTags() const;
|
||||||
|
const Illustration& getDefaultIllustration() const;
|
||||||
|
|
||||||
|
protected: // data
|
||||||
std::string m_id;
|
std::string m_id;
|
||||||
std::string m_downloadId;
|
std::string m_downloadId;
|
||||||
std::string m_path;
|
std::string m_path;
|
||||||
bool m_pathValid;
|
bool m_pathValid = false;
|
||||||
std::string m_indexPath;
|
|
||||||
supportedIndexType m_indexType;
|
|
||||||
std::string m_title;
|
std::string m_title;
|
||||||
std::string m_description;
|
std::string m_description;
|
||||||
|
std::string m_category;
|
||||||
std::string m_language;
|
std::string m_language;
|
||||||
std::string m_creator;
|
std::string m_creator;
|
||||||
std::string m_publisher;
|
std::string m_publisher;
|
||||||
std::string m_date;
|
std::string m_date;
|
||||||
std::string m_url;
|
std::string m_url;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
std::string m_flavour;
|
||||||
std::string m_tags;
|
std::string m_tags;
|
||||||
std::string m_origId;
|
std::string m_origId;
|
||||||
uint64_t m_articleCount;
|
uint64_t m_articleCount = 0;
|
||||||
uint64_t m_mediaCount;
|
uint64_t m_mediaCount = 0;
|
||||||
bool m_readOnly;
|
bool m_readOnly = false;
|
||||||
uint64_t m_size;
|
uint64_t m_size = 0;
|
||||||
mutable std::string m_favicon;
|
Illustrations m_illustrations;
|
||||||
std::string m_faviconUrl;
|
|
||||||
std::string m_faviconMimeType;
|
// Used as the return value of getDefaultIllustration() when no default
|
||||||
|
// illustration is found in the book
|
||||||
|
static const Illustration missingDefaultIllustration;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Matthieu Gautier <mgautier@kymeria.fr>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KIWIX_BOOKMARK_H
|
||||||
|
#define KIWIX_BOOKMARK_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace pugi {
|
||||||
|
class xml_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to store information about a bookmark (an article in a book)
|
||||||
|
*/
|
||||||
|
class Bookmark
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Bookmark();
|
||||||
|
~Bookmark();
|
||||||
|
|
||||||
|
void updateFromXml(const pugi::xml_node& node);
|
||||||
|
|
||||||
|
const std::string& getBookId() const { return m_bookId; }
|
||||||
|
const std::string& getBookTitle() const { return m_bookTitle; }
|
||||||
|
const std::string& getUrl() const { return m_url; }
|
||||||
|
const std::string& getTitle() const { return m_title; }
|
||||||
|
const std::string& getLanguage() const { return m_language; }
|
||||||
|
const std::string& getDate() const { return m_date; }
|
||||||
|
|
||||||
|
void setBookId(const std::string& bookId) { m_bookId = bookId; }
|
||||||
|
void setBookTitle(const std::string& bookTitle) { m_bookTitle = bookTitle; }
|
||||||
|
void setUrl(const std::string& url) { m_url = url; }
|
||||||
|
void setTitle(const std::string& title) { m_title = title; }
|
||||||
|
void setLanguage(const std::string& language) { m_language = language; }
|
||||||
|
void setDate(const std::string& date) { m_date = date; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string m_bookId;
|
||||||
|
std::string m_bookTitle;
|
||||||
|
std::string m_url;
|
||||||
|
std::string m_title;
|
||||||
|
std::string m_language;
|
||||||
|
std::string m_date;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef KIWIX_PATHTOOLS_H
|
|
||||||
#define KIWIX_PATHTOOLS_H
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <fstream>
|
|
||||||
#include <ios>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <direct.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "stringTools.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
bool isRelativePath(const string& path);
|
|
||||||
string computeAbsolutePath(const string path, const string relativePath);
|
|
||||||
string computeRelativePath(const string path, const string absolutePath);
|
|
||||||
string removeLastPathElement(const string path,
|
|
||||||
const bool removePreSeparator = false,
|
|
||||||
const bool removePostSeparator = false);
|
|
||||||
string appendToDirectory(const string& directoryPath, const string& filename);
|
|
||||||
|
|
||||||
unsigned int getFileSize(const string& path);
|
|
||||||
string getFileSizeAsString(const string& path);
|
|
||||||
string getFileContent(const string& path);
|
|
||||||
bool fileExists(const string& path);
|
|
||||||
bool makeDirectory(const string& path);
|
|
||||||
string makeTmpDirectory();
|
|
||||||
bool copyFile(const string& sourcePath, const string& destPath);
|
|
||||||
string getLastPathElement(const string& path);
|
|
||||||
string getExecutablePath();
|
|
||||||
string getCurrentDirectory();
|
|
||||||
string getDataDirectory();
|
|
||||||
bool writeTextFile(const string& path, const string& content);
|
|
||||||
#endif
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013 Renaud Gaudin <reg@kiwix.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _CTPP2_VM_STRING_LOADER_HPP__
|
|
||||||
#define _CTPP2_VM_STRING_LOADER_HPP__ 1
|
|
||||||
|
|
||||||
#include <ctpp2/CTPP2VMLoader.hpp>
|
|
||||||
#include <ctpp2/CTPP2Util.hpp>
|
|
||||||
#include <ctpp2/CTPP2Exception.hpp>
|
|
||||||
#include <ctpp2/CTPP2VMExecutable.hpp>
|
|
||||||
#include <ctpp2/CTPP2VMInstruction.hpp>
|
|
||||||
#include <ctpp2/CTPP2VMMemoryCore.hpp>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
/**
|
|
||||||
@file VMStringLoader.hpp
|
|
||||||
@brief Load program core from file
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace CTPP // C++ Template Engine
|
|
||||||
{
|
|
||||||
// FWD
|
|
||||||
struct VMExecutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
@class VMStringLoader CTPP2VMStringLoader.hpp <CTPP2VMStringLoader.hpp>
|
|
||||||
@brief Load program core from file
|
|
||||||
*/
|
|
||||||
class CTPP2DECL VMStringLoader:
|
|
||||||
public VMLoader
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
VMStringLoader(CCHAR_P rawContent, size_t rawContentSize);
|
|
||||||
/**
|
|
||||||
@brief Get ready-to-run program
|
|
||||||
*/
|
|
||||||
const VMMemoryCore * GetCore() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
@brief A destructor
|
|
||||||
*/
|
|
||||||
~VMStringLoader() throw();
|
|
||||||
private:
|
|
||||||
/** Program core */
|
|
||||||
VMExecutable * oCore;
|
|
||||||
/** Ready-to-run program */
|
|
||||||
VMMemoryCore * pVMMemoryCore;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace CTPP
|
|
||||||
#endif // _CTPP2_VM_STRING_LOADER_HPP__
|
|
||||||
// End.
|
|
||||||
+128
-16
@@ -23,8 +23,9 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <pthread.h>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace kiwix
|
namespace kiwix
|
||||||
{
|
{
|
||||||
@@ -43,6 +44,14 @@ class AriaError : public std::runtime_error {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of a current download.
|
||||||
|
*
|
||||||
|
* `Download` is not thread safe. User must care to not call method on a
|
||||||
|
* same download from different threads.
|
||||||
|
* However, it is safe to use different `Download`s from different threads.
|
||||||
|
*/
|
||||||
|
|
||||||
class Download {
|
class Download {
|
||||||
public:
|
public:
|
||||||
typedef enum { K_ACTIVE, K_WAITING, K_PAUSED, K_ERROR, K_COMPLETE, K_REMOVED, K_UNKNOWN } StatusResult;
|
typedef enum { K_ACTIVE, K_WAITING, K_PAUSED, K_ERROR, K_COMPLETE, K_REMOVED, K_UNKNOWN } StatusResult;
|
||||||
@@ -53,16 +62,89 @@ class Download {
|
|||||||
: mp_aria(p_aria),
|
: mp_aria(p_aria),
|
||||||
m_status(K_UNKNOWN),
|
m_status(K_UNKNOWN),
|
||||||
m_did(did) {};
|
m_did(did) {};
|
||||||
void updateStatus(bool follow=false);
|
|
||||||
StatusResult getStatus() { return m_status; }
|
/**
|
||||||
std::string getDid() { return m_did; }
|
* Update the status of the download.
|
||||||
std::string getFollowedBy() { return m_followedBy; }
|
*
|
||||||
uint64_t getTotalLength() { return m_totalLength; }
|
* This call make an aria rpc call and is blocking.
|
||||||
uint64_t getCompletedLength() { return m_completedLength; }
|
* Some download (started with a metalink) are in fact several downloads.
|
||||||
uint64_t getDownloadSpeed() { return m_downloadSpeed; }
|
* - A first one to download the metadlink.
|
||||||
uint64_t getVerifiedLength() { return m_verifiedLength; }
|
* - A second one to download the real file.
|
||||||
std::string getPath() { return m_path; }
|
*
|
||||||
std::vector<std::string>& getUris() { return m_uris; }
|
* If `follow` is true, updateStatus tries to detect that and tracks
|
||||||
|
* the second download when the first one is finished.
|
||||||
|
* By passing false to `follow`, `Download` will only track the first download.
|
||||||
|
*
|
||||||
|
* `getFoo` methods are based on the last statusUpdate.
|
||||||
|
*
|
||||||
|
* @param follow: Do we have to follow following downloads.
|
||||||
|
*/
|
||||||
|
void updateStatus(bool follow);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pause the download (and call updateStatus)
|
||||||
|
*/
|
||||||
|
void pauseDownload();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resume the download (and call updateStatus)
|
||||||
|
*/
|
||||||
|
void resumeDownload();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the download.
|
||||||
|
*
|
||||||
|
* A canceled downlod cannot be resume and updateStatus does nothing.
|
||||||
|
* However, you can still get information based on the last known information.
|
||||||
|
*/
|
||||||
|
void cancelDownload();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the status of the download.
|
||||||
|
*/
|
||||||
|
StatusResult getStatus() const { return m_status; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the id of the download.
|
||||||
|
*/
|
||||||
|
const std::string& getDid() const { return m_did; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the id of the "second" download.
|
||||||
|
*
|
||||||
|
* Set only if the "first" download is a metalink and is complete.
|
||||||
|
*/
|
||||||
|
const std::string& getFollowedBy() const { return m_followedBy; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the total length of the download.
|
||||||
|
*/
|
||||||
|
uint64_t getTotalLength() const { return m_totalLength; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the completed length of the download.
|
||||||
|
*/
|
||||||
|
uint64_t getCompletedLength() const { return m_completedLength; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the download speed of the download.
|
||||||
|
*/
|
||||||
|
uint64_t getDownloadSpeed() const { return m_downloadSpeed; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the verified length of the download.
|
||||||
|
*/
|
||||||
|
uint64_t getVerifiedLength() const { return m_verifiedLength; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the path (local file) of the download.
|
||||||
|
*/
|
||||||
|
const std::string& getPath() const { return m_path; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the download uris of the download.
|
||||||
|
*/
|
||||||
|
const std::vector<std::string>& getUris() const { return m_uris; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<Aria2> mp_aria;
|
std::shared_ptr<Aria2> mp_aria;
|
||||||
@@ -80,6 +162,9 @@ class Download {
|
|||||||
/**
|
/**
|
||||||
* A tool to download things.
|
* A tool to download things.
|
||||||
*
|
*
|
||||||
|
* A Downloader manages `Download` using aria2 in the background.
|
||||||
|
* `Downloader` is threadsafe.
|
||||||
|
* However, the returned `Download`s are NOT threadsafe.
|
||||||
*/
|
*/
|
||||||
class Downloader
|
class Downloader
|
||||||
{
|
{
|
||||||
@@ -89,14 +174,41 @@ class Downloader
|
|||||||
|
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
Download* startDownload(const std::string& uri);
|
/**
|
||||||
Download* getDownload(const std::string& did);
|
* Start a new download.
|
||||||
|
*
|
||||||
|
* This method is thread safe and return a pointer to a newly created `Download`.
|
||||||
|
* User should call `update` on the returned `Download` to have an accurate status.
|
||||||
|
*
|
||||||
|
* @param uri: The uri of the thing to download.
|
||||||
|
* @param options: A series of pair <option_name, option_value> to pass to aria.
|
||||||
|
* @return: The newly created Download.
|
||||||
|
*/
|
||||||
|
std::shared_ptr<Download> startDownload(const std::string& uri, const std::vector<std::pair<std::string, std::string>>& options = {});
|
||||||
|
|
||||||
size_t getNbDownload() { return m_knownDownloads.size(); }
|
/**
|
||||||
std::vector<std::string> getDownloadIds();
|
* Get a download corrsponding to a download id (did)
|
||||||
|
* User should call `update` on the returned `Download` to have an accurate status.
|
||||||
|
*
|
||||||
|
* @param did: The download id to search for.
|
||||||
|
* @return: The Download corresponding to did.
|
||||||
|
* @throw: Throw std::out_of_range if did is not found.
|
||||||
|
*/
|
||||||
|
std::shared_ptr<Download> getDownload(const std::string& did);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of downloads currently managed.
|
||||||
|
*/
|
||||||
|
size_t getNbDownload() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ids of the managed downloads.
|
||||||
|
*/
|
||||||
|
std::vector<std::string> getDownloadIds() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<std::string, std::unique_ptr<Download>> m_knownDownloads;
|
mutable std::mutex m_lock;
|
||||||
|
std::map<std::string, std::shared_ptr<Download>> m_knownDownloads;
|
||||||
std::shared_ptr<Aria2> mp_aria;
|
std::shared_ptr<Aria2> mp_aria;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
-191
@@ -1,191 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Matthieu Gautier <mgautier@kymeria.fr>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef KIWIX_ENTRY_H
|
|
||||||
#define KIWIX_ENTRY_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <zim/article.h>
|
|
||||||
#include <exception>
|
|
||||||
#include <string>
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace kiwix
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
class NoEntry : public std::exception {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A entry represent an.. entry in a zim file.
|
|
||||||
*/
|
|
||||||
class Entry
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Default constructor.
|
|
||||||
*
|
|
||||||
* Construct an invalid entry.
|
|
||||||
*/
|
|
||||||
Entry() = default;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct an entry making reference to an zim article.
|
|
||||||
*
|
|
||||||
* @param article a zim::Article object
|
|
||||||
*/
|
|
||||||
Entry(zim::Article article);
|
|
||||||
virtual ~Entry() = default;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the path of the entry.
|
|
||||||
*
|
|
||||||
* The path is the "key" of an entry.
|
|
||||||
*
|
|
||||||
* @return the path of the entry.
|
|
||||||
*/
|
|
||||||
std::string getPath() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the title of the entry.
|
|
||||||
*
|
|
||||||
* @return the title of the entry.
|
|
||||||
*/
|
|
||||||
std::string getTitle() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the content of the entry.
|
|
||||||
*
|
|
||||||
* The string is a copy of the content.
|
|
||||||
* If you don't want to do a copy, use get_blob.
|
|
||||||
*
|
|
||||||
* @return the content of the entry.
|
|
||||||
*/
|
|
||||||
std::string getContent() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the blob of the entry.
|
|
||||||
*
|
|
||||||
* A blob make reference to the content without copying it.
|
|
||||||
*
|
|
||||||
* @param offset The starting offset of the blob.
|
|
||||||
* @return the blob of the entry.
|
|
||||||
*/
|
|
||||||
zim::Blob getBlob(offset_type offset = 0) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the blob of the entry.
|
|
||||||
*
|
|
||||||
* A blob make reference to the content without copying it.
|
|
||||||
*
|
|
||||||
* @param offset The starting offset of the blob.
|
|
||||||
* @param size The size of the blob.
|
|
||||||
* @return the blob of the entry.
|
|
||||||
*/
|
|
||||||
zim::Blob getBlob(offset_type offset, size_type size) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the info for direct access to the content of the entry.
|
|
||||||
*
|
|
||||||
* Some entry (ie binary ones) have their content plain stored
|
|
||||||
* in the zim file. Knowing the offset where the content is stored
|
|
||||||
* an user can directly read the content in the zim file bypassing the
|
|
||||||
* kiwix-lib/libzim.
|
|
||||||
*
|
|
||||||
* @return A pair specifying where to read the content.
|
|
||||||
* The string is the real file to read (may be different that .zim
|
|
||||||
* file if zim is cut).
|
|
||||||
* The offset is the offset to read in the file.
|
|
||||||
* Return <"",0> if is not possible to read directly.
|
|
||||||
*/
|
|
||||||
std::pair<std::string, offset_type> getDirectAccessInfo() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the size of the entry.
|
|
||||||
*
|
|
||||||
* @return the size of the entry.
|
|
||||||
*/
|
|
||||||
size_type getSize() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the mime_type of the entry.
|
|
||||||
*
|
|
||||||
* @return the mime_type of the entry.
|
|
||||||
*/
|
|
||||||
std::string getMimetype() const;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get if the entry is a redirect entry.
|
|
||||||
*
|
|
||||||
* @return True if the entry is a redirect.
|
|
||||||
*/
|
|
||||||
bool isRedirect() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get if the entry is a link target entry.
|
|
||||||
*
|
|
||||||
* @return True if the entry is a link target.
|
|
||||||
*/
|
|
||||||
bool isLinkTarget() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get if the entry is a deleted entry.
|
|
||||||
*
|
|
||||||
* @return True if the entry is a deleted entry.
|
|
||||||
*/
|
|
||||||
bool isDeleted() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the entry pointed by this entry.
|
|
||||||
*
|
|
||||||
* @return the entry pointed.
|
|
||||||
* @throw NoEntry if the entry is not a redirected entry.
|
|
||||||
*/
|
|
||||||
Entry getRedirectEntry() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the final entry pointed by this entry.
|
|
||||||
*
|
|
||||||
* Follow the redirection until a "not redirecting" entry is found.
|
|
||||||
* If the entry is not a redirected entry, return the entry itself.
|
|
||||||
*
|
|
||||||
* @return the final entry.
|
|
||||||
*/
|
|
||||||
Entry getFinalEntry() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the entry to a boolean value.
|
|
||||||
*
|
|
||||||
* @return True if the entry is valid.
|
|
||||||
*/
|
|
||||||
explicit operator bool() const { return good(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
zim::Article article;
|
|
||||||
mutable zim::Article final_article;
|
|
||||||
|
|
||||||
bool good() const { return article.good(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // KIWIX_ENTRY_H
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Emmanuel Engelhart <kelson@kiwix.org>
|
* Copyright 2023 Nikhil Tanwar <2002nikhiltanwar@gmail.com>
|
||||||
* Copyright (C) 2017 Matthieu Gautier <mgautier@kymeria.fr>
|
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,14 +17,34 @@
|
|||||||
* MA 02110-1301, USA.
|
* MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.kiwix.kiwixlib;
|
#ifndef KIWIX_HTML_DUMPER_H
|
||||||
|
#define KIWIX_HTML_DUMPER_H
|
||||||
|
|
||||||
import org.kiwix.kiwixlib.JNIKiwixReader;
|
#include <string>
|
||||||
import org.kiwix.kiwixlib.JNIKiwixString;
|
|
||||||
|
|
||||||
public class JNIKiwix
|
#include "library_dumper.h"
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
{
|
{
|
||||||
static { System.loadLibrary("kiwix"); }
|
|
||||||
|
|
||||||
public native void setDataDirectory(String icuDataDir);
|
/**
|
||||||
|
* A class to dump Library in HTML format.
|
||||||
|
*/
|
||||||
|
class HTMLDumper : public LibraryDumper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HTMLDumper(const Library* library, const NameMapper* NameMapper);
|
||||||
|
~HTMLDumper();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump library in HTML
|
||||||
|
*
|
||||||
|
* @return HTML content
|
||||||
|
*/
|
||||||
|
std::string dumpPlainHTML(kiwix::Filter filter) const;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // KIWIX_HTML_DUMPER_H
|
||||||
+1
-1
@@ -22,4 +22,4 @@
|
|||||||
|
|
||||||
#include "library.h"
|
#include "library.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef KIWIXLIB_KIWIX_SERVE_H_
|
||||||
|
#define KIWIXLIB_KIWIX_SERVE_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Subprocess;
|
||||||
|
namespace kiwix {
|
||||||
|
|
||||||
|
class KiwixServe
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KiwixServe(const std::string& libraryPath, int port = 8181);
|
||||||
|
~KiwixServe();
|
||||||
|
|
||||||
|
void run();
|
||||||
|
void shutDown();
|
||||||
|
bool isRunning();
|
||||||
|
int getPort() { return m_port; }
|
||||||
|
int setPort(int port);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Subprocess> mp_kiwixServe;
|
||||||
|
int m_port;
|
||||||
|
std::string m_libraryPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
}; //end namespace kiwix
|
||||||
|
|
||||||
|
#endif // KIWIXLIB_KIWIX_SERVE_H_
|
||||||
+256
-45
@@ -23,14 +23,22 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <zim/archive.h>
|
||||||
|
#include <zim/search.h>
|
||||||
|
|
||||||
|
#include "book.h"
|
||||||
|
#include "bookmark.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
#define KIWIX_LIBRARY_VERSION "20110515"
|
#define KIWIX_LIBRARY_VERSION "20110515"
|
||||||
|
|
||||||
namespace kiwix
|
namespace kiwix
|
||||||
{
|
{
|
||||||
|
|
||||||
class Book;
|
|
||||||
class OPDSDumper;
|
class OPDSDumper;
|
||||||
|
class Library;
|
||||||
|
|
||||||
enum supportedListSortBy { UNSORTED, TITLE, SIZE, DATE, CREATOR, PUBLISHER };
|
enum supportedListSortBy { UNSORTED, TITLE, SIZE, DATE, CREATOR, PUBLISHER };
|
||||||
enum supportedListMode {
|
enum supportedListMode {
|
||||||
@@ -42,17 +50,149 @@ enum supportedListMode {
|
|||||||
VALID = 1 << 4,
|
VALID = 1 << 4,
|
||||||
NOVALID = 1 << 5
|
NOVALID = 1 << 5
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Filter {
|
||||||
|
public: // types
|
||||||
|
using Tags = std::vector<std::string>;
|
||||||
|
|
||||||
|
private: // data
|
||||||
|
uint64_t activeFilters;
|
||||||
|
Tags _acceptTags;
|
||||||
|
Tags _rejectTags;
|
||||||
|
std::string _category;
|
||||||
|
std::string _lang;
|
||||||
|
std::string _publisher;
|
||||||
|
std::string _creator;
|
||||||
|
size_t _maxSize;
|
||||||
|
std::string _query;
|
||||||
|
bool _queryIsPartial;
|
||||||
|
std::string _name;
|
||||||
|
|
||||||
|
public: // functions
|
||||||
|
Filter();
|
||||||
|
~Filter() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the filter to check local.
|
||||||
|
*
|
||||||
|
* A local book is a book with a path.
|
||||||
|
* If accept is true, only local book are accepted.
|
||||||
|
* If accept is false, only non local book are accepted.
|
||||||
|
*/
|
||||||
|
Filter& local(bool accept);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the filter to check remote.
|
||||||
|
*
|
||||||
|
* A remote book is a book with a url.
|
||||||
|
* If accept is true, only remote book are accepted.
|
||||||
|
* If accept is false, only non remote book are accepted.
|
||||||
|
*/
|
||||||
|
Filter& remote(bool accept);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the filter to check validity.
|
||||||
|
*
|
||||||
|
* A valid book is a book with a path pointing to a existing zim file.
|
||||||
|
* If accept is true, only valid book are accepted.
|
||||||
|
* If accept is false, only non valid book are accepted.
|
||||||
|
*/
|
||||||
|
Filter& valid(bool accept);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the filter to only accept book with corresponding tag.
|
||||||
|
*/
|
||||||
|
Filter& acceptTags(const Tags& tags);
|
||||||
|
Filter& rejectTags(const Tags& tags);
|
||||||
|
|
||||||
|
Filter& category(std::string category);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the filter to only accept books in the specified language.
|
||||||
|
*
|
||||||
|
* Multiple languages can be specified as a comma-separated list (in
|
||||||
|
* which case a book in any of those languages will match).
|
||||||
|
*/
|
||||||
|
Filter& lang(std::string lang);
|
||||||
|
|
||||||
|
Filter& publisher(std::string publisher);
|
||||||
|
Filter& creator(std::string creator);
|
||||||
|
Filter& maxSize(size_t size);
|
||||||
|
Filter& query(std::string query, bool partial=true);
|
||||||
|
Filter& name(std::string name);
|
||||||
|
Filter& clearLang();
|
||||||
|
Filter& clearCategory();
|
||||||
|
|
||||||
|
bool hasQuery() const;
|
||||||
|
const std::string& getQuery() const { return _query; }
|
||||||
|
bool queryIsPartial() const { return _queryIsPartial; }
|
||||||
|
|
||||||
|
bool hasName() const;
|
||||||
|
const std::string& getName() const { return _name; }
|
||||||
|
|
||||||
|
bool hasCategory() const;
|
||||||
|
const std::string& getCategory() const { return _category; }
|
||||||
|
|
||||||
|
bool hasLang() const;
|
||||||
|
const std::string& getLang() const { return _lang; }
|
||||||
|
|
||||||
|
bool hasPublisher() const;
|
||||||
|
const std::string& getPublisher() const { return _publisher; }
|
||||||
|
|
||||||
|
bool hasCreator() const;
|
||||||
|
const std::string& getCreator() const { return _creator; }
|
||||||
|
|
||||||
|
const Tags& getAcceptTags() const { return _acceptTags; }
|
||||||
|
const Tags& getRejectTags() const { return _rejectTags; }
|
||||||
|
|
||||||
|
private: // functions
|
||||||
|
friend class Library;
|
||||||
|
|
||||||
|
bool accept(const Book& book) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ZimSearcher : public zim::Searcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ZimSearcher(zim::Searcher&& searcher)
|
||||||
|
: zim::Searcher(searcher)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> getLock() {
|
||||||
|
return std::unique_lock<std::mutex>(m_mutex);
|
||||||
|
}
|
||||||
|
virtual ~ZimSearcher() = default;
|
||||||
|
private:
|
||||||
|
std::mutex m_mutex;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Library store several books.
|
* A Library store several books.
|
||||||
*/
|
*/
|
||||||
class Library
|
class Library
|
||||||
{
|
{
|
||||||
std::map<std::string, kiwix::Book> books;
|
// all data fields must be added in LibraryBase
|
||||||
|
mutable std::mutex m_mutex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef uint64_t Revision;
|
||||||
|
typedef std::vector<std::string> BookIdCollection;
|
||||||
|
typedef std::map<std::string, int> AttributeCounts;
|
||||||
|
typedef std::set<std::string> BookIdSet;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Library();
|
Library();
|
||||||
~Library();
|
~Library();
|
||||||
|
|
||||||
std::string version;
|
/**
|
||||||
|
* Library is not a copiable object. However it can be moved.
|
||||||
|
*/
|
||||||
|
Library(const Library& ) = delete;
|
||||||
|
Library(Library&& );
|
||||||
|
void operator=(const Library& ) = delete;
|
||||||
|
Library& operator=(Library&& );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a book to the library.
|
* Add a book to the library.
|
||||||
*
|
*
|
||||||
@@ -65,7 +205,39 @@ class Library
|
|||||||
*/
|
*/
|
||||||
bool addBook(const Book& book);
|
bool addBook(const Book& book);
|
||||||
|
|
||||||
Book& getBookById(const std::string& id);
|
/**
|
||||||
|
* A self-explanatory alias for addBook()
|
||||||
|
*/
|
||||||
|
bool addOrUpdateBook(const Book& book) { return addBook(book); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a bookmark to the library.
|
||||||
|
*
|
||||||
|
* @param bookmark the book to add.
|
||||||
|
*/
|
||||||
|
void addBookmark(const Bookmark& bookmark);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a bookmarkk
|
||||||
|
*
|
||||||
|
* @param zimId The zimId of the bookmark.
|
||||||
|
* @param url The url of the bookmark.
|
||||||
|
* @return True if the bookmark has been removed.
|
||||||
|
*/
|
||||||
|
bool removeBookmark(const std::string& zimId, const std::string& url);
|
||||||
|
|
||||||
|
// XXX: This is a non-thread-safe operation
|
||||||
|
const Book& getBookById(const std::string& id) const;
|
||||||
|
// XXX: This is a non-thread-safe operation
|
||||||
|
const Book& getBookByPath(const std::string& path) const;
|
||||||
|
|
||||||
|
Book getBookByIdThreadSafe(const std::string& id) const;
|
||||||
|
|
||||||
|
std::shared_ptr<zim::Archive> getArchiveById(const std::string& id);
|
||||||
|
std::shared_ptr<ZimSearcher> getSearcherById(const std::string& id) {
|
||||||
|
return getSearcherByIds(BookIdSet{id});
|
||||||
|
}
|
||||||
|
std::shared_ptr<ZimSearcher> getSearcherByIds(const BookIdSet& ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a book from the library.
|
* Remove a book from the library.
|
||||||
@@ -79,9 +251,17 @@ class Library
|
|||||||
* Write the library to a file.
|
* Write the library to a file.
|
||||||
*
|
*
|
||||||
* @param path the path of the file to write to.
|
* @param path the path of the file to write to.
|
||||||
* @return True if the library has been correctly save.
|
* @return True if the library has been correctly saved.
|
||||||
*/
|
*/
|
||||||
bool writeToFile(const std::string& path);
|
bool writeToFile(const std::string& path) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the library bookmarks to a file.
|
||||||
|
*
|
||||||
|
* @param path the path of the file to write to.
|
||||||
|
* @return True if the library has been correctly saved.
|
||||||
|
*/
|
||||||
|
bool writeBookmarksToFile(const std::string& path) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of book in the library.
|
* Get the number of book in the library.
|
||||||
@@ -90,80 +270,111 @@ class Library
|
|||||||
* @param remoteBooks If we must count remote books (books with an url)
|
* @param remoteBooks If we must count remote books (books with an url)
|
||||||
* @return The number of books.
|
* @return The number of books.
|
||||||
*/
|
*/
|
||||||
unsigned int getBookCount(const bool localBooks, const bool remoteBooks);
|
unsigned int getBookCount(const bool localBooks, const bool remoteBooks) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all langagues of the books in the library.
|
* Get all languagues of the books in the library.
|
||||||
*
|
*
|
||||||
* @return A list of languages.
|
* @return A list of languages.
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> getBooksLanguages();
|
std::vector<std::string> getBooksLanguages() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all languagues of the books in the library with counts.
|
||||||
|
*
|
||||||
|
* @return A list of languages with the count of books in each language.
|
||||||
|
*/
|
||||||
|
AttributeCounts getBooksLanguagesWithCounts() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all categories of the books in the library.
|
||||||
|
*
|
||||||
|
* @return A list of categories.
|
||||||
|
*/
|
||||||
|
std::vector<std::string> getBooksCategories() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all book creators of the books in the library.
|
* Get all book creators of the books in the library.
|
||||||
*
|
*
|
||||||
* @return A list of book creators.
|
* @return A list of book creators.
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> getBooksCreators();
|
std::vector<std::string> getBooksCreators() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all book publishers of the books in the library.
|
* Get all book publishers of the books in the library.
|
||||||
*
|
*
|
||||||
* @return A list of book publishers.
|
* @return A list of book publishers.
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> getBooksPublishers();
|
std::vector<std::string> getBooksPublishers() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all bookmarks.
|
||||||
|
*
|
||||||
|
* @return A list of bookmarks
|
||||||
|
*/
|
||||||
|
const std::vector<kiwix::Bookmark> getBookmarks(bool onlyValidBookmarks = true) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all book ids of the books in the library.
|
* Get all book ids of the books in the library.
|
||||||
*
|
*
|
||||||
* @return A list of book ids.
|
* @return A list of book ids.
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> getBooksIds();
|
BookIdCollection getBooksIds() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter the library and generate a new one with the keep elements.
|
* Filter the library and return the id of the keep elements.
|
||||||
*
|
*
|
||||||
* This is equivalent to `listBookIds(ALL, UNSORTED, search)`.
|
* @param filter The filter to use.
|
||||||
*
|
* @return The list of bookIds corresponding to the filter.
|
||||||
* @param search List only books with search in the title or description.
|
|
||||||
* @return The list of bookIds corresponding to the query.
|
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> filter(const std::string& search);
|
BookIdCollection filter(const Filter& filter) const;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List books in the library.
|
* Sort (in place) bookIds using the given comparator.
|
||||||
*
|
*
|
||||||
* @param mode The mode of listing :
|
* @param bookIds the list of book Ids to sort
|
||||||
* - LOCAL : list only local books (with a path).
|
* @param comparator how to sort the books
|
||||||
* - REMOTE : list only remote books (with an url).
|
* @return The sorted list of books
|
||||||
* - VALID : list only valid books (without a path or with a
|
|
||||||
* path pointing to a valid zim file).
|
|
||||||
* - NOLOCAL : list only books without valid path.
|
|
||||||
* - NOREMOTE : list only books without url.
|
|
||||||
* - NOVALID : list only books not valid.
|
|
||||||
* - ALL : Do not do any filter (LOCAL or REMOTE)
|
|
||||||
* - Flags can be combined.
|
|
||||||
* @param sortBy Attribute to sort by the book list.
|
|
||||||
* @param search List only books with search in the title, description.
|
|
||||||
* @param language List only books in this language.
|
|
||||||
* @param creator List only books of this creator.
|
|
||||||
* @param publisher List only books of this publisher.
|
|
||||||
* @param maxSize Do not list book bigger than maxSize.
|
|
||||||
* Set to 0 to cancel this filter.
|
|
||||||
* @return The list of bookIds corresponding to the query.
|
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> listBooksIds(
|
void sort(BookIdCollection& bookIds, supportedListSortBy sortBy, bool ascending) const;
|
||||||
int supportedListMode = ALL,
|
|
||||||
supportedListSortBy sortBy = UNSORTED,
|
/**
|
||||||
const std::string& search = "",
|
* Return the current revision of the library.
|
||||||
const std::string& language = "",
|
*
|
||||||
const std::string& creator = "",
|
* The revision of the library is updated (incremented by one) by
|
||||||
const std::string& publisher = "",
|
* the addBook() and removeBookById() operations.
|
||||||
size_t maxSize = 0);
|
*
|
||||||
|
* @return Current revision of the library.
|
||||||
|
*/
|
||||||
|
Revision getRevision() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove books that have not been updated since the specified revision.
|
||||||
|
*
|
||||||
|
* @param rev the library revision to use
|
||||||
|
* @return Count of books that were removed by this operation.
|
||||||
|
*/
|
||||||
|
uint32_t removeBooksNotUpdatedSince(Revision rev);
|
||||||
|
|
||||||
friend class OPDSDumper;
|
friend class OPDSDumper;
|
||||||
|
friend class libXMLDumper;
|
||||||
|
|
||||||
|
private: // types
|
||||||
|
typedef const std::string& (Book::*BookStrPropMemFn)() const;
|
||||||
|
struct Impl;
|
||||||
|
|
||||||
|
private: // functions
|
||||||
|
AttributeCounts getBookAttributeCounts(BookStrPropMemFn p) const;
|
||||||
|
std::vector<std::string> getBookPropValueSet(BookStrPropMemFn p) const;
|
||||||
|
BookIdCollection filterViaBookDB(const Filter& filter) const;
|
||||||
|
void updateBookDB(const Book& book);
|
||||||
|
void dropCache(const std::string& bookId);
|
||||||
|
|
||||||
|
private: //data
|
||||||
|
std::unique_ptr<Impl> mp_impl;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 Nikhil Tanwar <2002nikhiltanwar@gmail.com>
|
||||||
|
* Copyright 2017 Matthieu Gautier <mgautier@kymeria.fr>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KIWIX_LIBRARY_DUMPER_H
|
||||||
|
#define KIWIX_LIBRARY_DUMPER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
#include "library.h"
|
||||||
|
#include "name_mapper.h"
|
||||||
|
#include <mustache.hpp>
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base class to dump Library in various formats.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class LibraryDumper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LibraryDumper(const Library* library, const NameMapper* NameMapper);
|
||||||
|
~LibraryDumper();
|
||||||
|
|
||||||
|
void setLibraryId(const std::string& id) { this->libraryId = id;}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the root location used when generating url.
|
||||||
|
*
|
||||||
|
* @param rootLocation the root location to use.
|
||||||
|
*/
|
||||||
|
void setRootLocation(const std::string& rootLocation) { this->rootLocation = rootLocation; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set some informations about the search results.
|
||||||
|
*
|
||||||
|
* @param totalResult the total number of results of the search.
|
||||||
|
* @param startIndex the start index of the result.
|
||||||
|
* @param count the number of result of the current set (or page).
|
||||||
|
*/
|
||||||
|
void setOpenSearchInfo(int totalResult, int startIndex, int count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets user default language
|
||||||
|
*
|
||||||
|
* @param userLang the user language to be set
|
||||||
|
*/
|
||||||
|
void setUserLanguage(std::string userLang) { this->m_userLang = userLang; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data of categories
|
||||||
|
*/
|
||||||
|
kainjow::mustache::list getCategoryData() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data of languages
|
||||||
|
*/
|
||||||
|
kainjow::mustache::list getLanguageData() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const kiwix::Library* const library;
|
||||||
|
const kiwix::NameMapper* const nameMapper;
|
||||||
|
std::string libraryId;
|
||||||
|
std::string rootLocation;
|
||||||
|
std::string m_userLang;
|
||||||
|
int m_totalResults;
|
||||||
|
int m_startIndex;
|
||||||
|
int m_count;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // KIWIX_LIBRARY_DUMPER_H
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Matthieu Gautier <mgautier@kymeria.fr>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KIWIX_LIBXML_DUMPER_H
|
||||||
|
#define KIWIX_LIBXML_DUMPER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <pugixml.hpp>
|
||||||
|
|
||||||
|
#include "library.h"
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tool to dump a `Library` into a basic library.xml
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class LibXMLDumper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LibXMLDumper() = default;
|
||||||
|
LibXMLDumper(const Library* library);
|
||||||
|
~LibXMLDumper();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump the library.xml
|
||||||
|
*
|
||||||
|
* @param id The id of the library.
|
||||||
|
* @return The library.xml content.
|
||||||
|
*/
|
||||||
|
std::string dumpLibXMLContent(const std::vector<std::string>& bookIds);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump the bookmark of the library.
|
||||||
|
*
|
||||||
|
* @return The bookmark.xml content.
|
||||||
|
*/
|
||||||
|
std::string dumpLibXMLBookmark();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the base directory used.
|
||||||
|
*
|
||||||
|
* @param baseDir the base directory to use.
|
||||||
|
*/
|
||||||
|
void setBaseDir(const std::string& baseDir) { this->baseDir = baseDir; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the library to dump.
|
||||||
|
*
|
||||||
|
* @param library The library to dump.
|
||||||
|
*/
|
||||||
|
void setLibrary(const Library* library) { this->library = library; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const kiwix::Library* library;
|
||||||
|
std::string baseDir;
|
||||||
|
private:
|
||||||
|
void handleBook(Book book, pugi::xml_node root_node);
|
||||||
|
void handleBookmark(Bookmark bookmark, pugi::xml_node root_node);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // KIWIX_OPDS_DUMPER_H
|
||||||
+54
-92
@@ -22,10 +22,10 @@
|
|||||||
|
|
||||||
#include "book.h"
|
#include "book.h"
|
||||||
#include "library.h"
|
#include "library.h"
|
||||||
#include "reader.h"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace pugi {
|
namespace pugi {
|
||||||
class xml_document;
|
class xml_document;
|
||||||
@@ -34,60 +34,60 @@ class xml_document;
|
|||||||
namespace kiwix
|
namespace kiwix
|
||||||
{
|
{
|
||||||
|
|
||||||
class LibraryManipulator {
|
class LibraryManipulator
|
||||||
public:
|
{
|
||||||
virtual ~LibraryManipulator() {}
|
public: // functions
|
||||||
virtual bool addBookToLibrary(Book book) = 0;
|
explicit LibraryManipulator(Library* library);
|
||||||
};
|
virtual ~LibraryManipulator();
|
||||||
|
|
||||||
class DefaultLibraryManipulator : public LibraryManipulator {
|
Library& getLibrary() const { return library; }
|
||||||
public:
|
|
||||||
DefaultLibraryManipulator(Library* library) :
|
bool addBookToLibrary(const Book& book);
|
||||||
library(library) {}
|
void addBookmarkToLibrary(const Bookmark& bookmark);
|
||||||
virtual ~DefaultLibraryManipulator() {}
|
uint32_t removeBooksNotUpdatedSince(Library::Revision rev);
|
||||||
bool addBookToLibrary(Book book) {
|
|
||||||
return library->addBook(book);
|
protected: // overrides
|
||||||
}
|
virtual void bookWasAddedToLibrary(const Book& book);
|
||||||
private:
|
virtual void bookmarkWasAddedToLibrary(const Bookmark& bookmark);
|
||||||
kiwix::Library* library;
|
virtual void booksWereRemovedFromLibrary();
|
||||||
|
|
||||||
|
private: // data
|
||||||
|
kiwix::Library& library;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tool to manage a `Library`.
|
* A tool to manage a `Library`.
|
||||||
*
|
|
||||||
* A `Manager` handle a internal `Library`.
|
|
||||||
* This `Library` can be retrived with `cloneLibrary` method.
|
|
||||||
*/
|
*/
|
||||||
class Manager
|
class Manager
|
||||||
{
|
{
|
||||||
public:
|
public: // types
|
||||||
Manager(LibraryManipulator* manipulator);
|
typedef std::vector<std::string> Paths;
|
||||||
Manager(Library* library);
|
|
||||||
~Manager();
|
public: // functions
|
||||||
|
explicit Manager(LibraryManipulator* manipulator);
|
||||||
|
explicit Manager(Library* library);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a `library.xml` and add book in the file to the library.
|
* Read a `library.xml` and add book in the file to the library.
|
||||||
*
|
*
|
||||||
* @param path The path to the `library.xml`.
|
* @param path The (utf8) path to the `library.xml`.
|
||||||
* @param readOnly Set if the libray path could be overwritten latter with
|
* @param readOnly Set if the libray path could be overwritten latter with
|
||||||
* updated content.
|
* updated content.
|
||||||
|
* @param trustLibrary use book metadata coming from XML.
|
||||||
* @return True if file has been properly parsed.
|
* @return True if file has been properly parsed.
|
||||||
*/
|
*/
|
||||||
bool readFile(const std::string& path, const bool readOnly = true);
|
bool readFile(const std::string& path, bool readOnly = true, bool trustLibrary = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a `library.xml` and add book in the file to the library.
|
* Sync the contents of the library with one or more `library.xml` files.
|
||||||
*
|
*
|
||||||
* @param nativePath The path of the `library.xml`
|
* The metadata of the library files is trusted unconditionally.
|
||||||
* @param UTF8Path The utf8 version (?) of the path. Also the path where the
|
* Any books not present in the input library.xml files are removed
|
||||||
* library will be writen i readOnly is False.
|
* from the library.
|
||||||
* @param readOnly Set if the libray path could be overwritten latter with
|
*
|
||||||
* updated content.
|
* @param paths The (utf8) paths to the `library.xml` files.
|
||||||
* @return True if file has been properly parsed.
|
|
||||||
*/
|
*/
|
||||||
bool readFile(const std::string& nativePath,
|
void reload(const Paths& paths);
|
||||||
const std::string& UTF8Path,
|
|
||||||
const bool readOnly = true);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a library content store in the string.
|
* Load a library content store in the string.
|
||||||
@@ -100,7 +100,8 @@ class Manager
|
|||||||
*/
|
*/
|
||||||
bool readXml(const std::string& xml,
|
bool readXml(const std::string& xml,
|
||||||
const bool readOnly = true,
|
const bool readOnly = true,
|
||||||
const std::string& libraryPath = "");
|
const std::string& libraryPath = "",
|
||||||
|
bool trustLibrary = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a library content stored in a OPDS stream.
|
* Load a library content stored in a OPDS stream.
|
||||||
@@ -113,6 +114,15 @@ class Manager
|
|||||||
*/
|
*/
|
||||||
bool readOpds(const std::string& content, const std::string& urlHost);
|
bool readOpds(const std::string& content, const std::string& urlHost);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a bookmark file.
|
||||||
|
*
|
||||||
|
* @param path The path of the file to read.
|
||||||
|
* @return True if the content has been properly parsed.
|
||||||
|
*/
|
||||||
|
bool readBookmarkFile(const std::string& path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a book to the library.
|
* Add a book to the library.
|
||||||
*
|
*
|
||||||
@@ -145,72 +155,24 @@ class Manager
|
|||||||
const std::string& url = "",
|
const std::string& url = "",
|
||||||
const bool checkMetaData = false);
|
const bool checkMetaData = false);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the book corresponding to an id.
|
|
||||||
*
|
|
||||||
* @param[in] id The id of the book
|
|
||||||
* @param[out] book The book corresponding to the id.
|
|
||||||
* @return True if the book has been found.
|
|
||||||
*/
|
|
||||||
bool getBookById(const std::string& id, Book& book);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the "last open date" of a book
|
|
||||||
*
|
|
||||||
* @param id the id of the book.
|
|
||||||
* @return True if the book is in the library.
|
|
||||||
*/
|
|
||||||
bool updateBookLastOpenDateById(const std::string& id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove (set to empty) paths of all books in the library.
|
|
||||||
*/
|
|
||||||
void removeBookPaths();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List books in the library.
|
|
||||||
*
|
|
||||||
* The books list will be available in public vector member `bookIdList`.
|
|
||||||
*
|
|
||||||
* @param mode The mode of listing :
|
|
||||||
* - LASTOPEN sort by last opened book.
|
|
||||||
* - LOCAL list only local file.
|
|
||||||
* - REMOTE list only remote file.
|
|
||||||
* @param sortBy Attribute to sort by the book list.
|
|
||||||
* @param maxSize Do not list book bigger than maxSize MiB.
|
|
||||||
* Set to 0 to cancel this filter.
|
|
||||||
* @param language List only books in this language.
|
|
||||||
* @param creator List only books of this creator.
|
|
||||||
* @param publisher List only books of this publisher.
|
|
||||||
* @param search List only books with search in the title, description or
|
|
||||||
* language.
|
|
||||||
* @return True
|
|
||||||
*/
|
|
||||||
bool listBooks(const supportedListMode mode,
|
|
||||||
const supportedListSortBy sortBy,
|
|
||||||
const unsigned int maxSize,
|
|
||||||
const std::string& language,
|
|
||||||
const std::string& creator,
|
|
||||||
const std::string& publisher,
|
|
||||||
const std::string& search);
|
|
||||||
|
|
||||||
std::string writableLibraryPath;
|
std::string writableLibraryPath;
|
||||||
|
|
||||||
std::vector<std::string> bookIdList;
|
bool m_hasSearchResult = false;
|
||||||
|
uint64_t m_totalBooks = 0;
|
||||||
|
uint64_t m_startIndex = 0;
|
||||||
|
uint64_t m_itemsPerPage = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
kiwix::LibraryManipulator* manipulator;
|
std::shared_ptr<kiwix::LibraryManipulator> manipulator;
|
||||||
bool mustDeleteManipulator;
|
|
||||||
|
|
||||||
bool readBookFromPath(const std::string& path, Book* book);
|
bool readBookFromPath(const std::string& path, Book* book);
|
||||||
bool parseXmlDom(const pugi::xml_document& doc,
|
bool parseXmlDom(const pugi::xml_document& doc,
|
||||||
const bool readOnly,
|
bool readOnly,
|
||||||
const std::string& libraryPath);
|
const std::string& libraryPath,
|
||||||
|
bool trustLibrary);
|
||||||
bool parseOpdsDom(const pugi::xml_document& doc,
|
bool parseOpdsDom(const pugi::xml_document& doc,
|
||||||
const std::string& urlHost);
|
const std::string& urlHost);
|
||||||
|
|
||||||
private:
|
|
||||||
void checkAndCleanBookPaths(Book& book, const std::string& libraryPath);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-25
@@ -1,35 +1,18 @@
|
|||||||
headers = [
|
headers = [
|
||||||
'book.h',
|
'book.h',
|
||||||
|
'bookmark.h',
|
||||||
'common.h',
|
'common.h',
|
||||||
'library.h',
|
'library.h',
|
||||||
'manager.h',
|
'manager.h',
|
||||||
|
'libxml_dumper.h',
|
||||||
'opds_dumper.h',
|
'opds_dumper.h',
|
||||||
'downloader.h',
|
'downloader.h',
|
||||||
'reader.h',
|
'search_renderer.h',
|
||||||
'entry.h',
|
'server.h',
|
||||||
'searcher.h'
|
'kiwixserve.h',
|
||||||
|
'name_mapper.h',
|
||||||
|
'tools.h',
|
||||||
|
'version.h'
|
||||||
]
|
]
|
||||||
|
|
||||||
if xapian_dep.found()
|
|
||||||
headers += ['xapianSearcher.h']
|
|
||||||
endif
|
|
||||||
|
|
||||||
install_headers(headers, subdir:'kiwix')
|
install_headers(headers, subdir:'kiwix')
|
||||||
|
|
||||||
install_headers(
|
|
||||||
'common/base64.h',
|
|
||||||
'common/networkTools.h',
|
|
||||||
'common/otherTools.h',
|
|
||||||
'common/pathTools.h',
|
|
||||||
'common/regexTools.h',
|
|
||||||
'common/stringTools.h',
|
|
||||||
subdir:'kiwix/common'
|
|
||||||
)
|
|
||||||
|
|
||||||
if has_ctpp2_dep
|
|
||||||
install_headers(
|
|
||||||
'ctpp2/CTPP2VMStringLoader.hpp',
|
|
||||||
subdir:'kiwix/ctpp2'
|
|
||||||
)
|
|
||||||
endif
|
|
||||||
|
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Matthieu Gautier <mgautier@kymeria.fr>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KIWIX_NAMEMAPPER_H
|
||||||
|
#define KIWIX_NAMEMAPPER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
|
{
|
||||||
|
|
||||||
|
class Library;
|
||||||
|
|
||||||
|
class NameMapper {
|
||||||
|
public:
|
||||||
|
virtual ~NameMapper() = default;
|
||||||
|
virtual std::string getNameForId(const std::string& id) const = 0;
|
||||||
|
virtual std::string getIdForName(const std::string& name) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class IdNameMapper : public NameMapper {
|
||||||
|
public:
|
||||||
|
virtual std::string getNameForId(const std::string& id) const { return id; };
|
||||||
|
virtual std::string getIdForName(const std::string& name) const { return name; };
|
||||||
|
};
|
||||||
|
|
||||||
|
class HumanReadableNameMapper : public NameMapper {
|
||||||
|
private:
|
||||||
|
std::map<std::string, std::string> m_idToName;
|
||||||
|
std::map<std::string, std::string> m_nameToId;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HumanReadableNameMapper(kiwix::Library& library, bool withAlias);
|
||||||
|
virtual ~HumanReadableNameMapper() = default;
|
||||||
|
virtual std::string getNameForId(const std::string& id) const;
|
||||||
|
virtual std::string getIdForName(const std::string& name) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UpdatableNameMapper : public NameMapper {
|
||||||
|
typedef std::shared_ptr<NameMapper> NameMapperHandle;
|
||||||
|
public:
|
||||||
|
UpdatableNameMapper(Library& library, bool withAlias);
|
||||||
|
|
||||||
|
virtual std::string getNameForId(const std::string& id) const;
|
||||||
|
virtual std::string getIdForName(const std::string& name) const;
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
NameMapperHandle currentNameMapper() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::mutex mutex;
|
||||||
|
Library& library;
|
||||||
|
NameMapperHandle nameMapper;
|
||||||
|
const bool withAlias;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Emmanuel Engelhart <kelson@kiwix.org>
|
* Copyright 2021 Veloman Yunkan <veloman.yunkan@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,9 +17,17 @@
|
|||||||
* MA 02110-1301, USA.
|
* MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.kiwix.kiwixlib;
|
#ifndef KIWIX_OPDS_CATALOG_H
|
||||||
|
#define KIWIX_OPDS_CATALOG_H
|
||||||
|
|
||||||
public class JNIKiwixBool
|
|
||||||
|
#include "library.h"
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
{
|
{
|
||||||
public boolean value;
|
|
||||||
}
|
std::string getSearchUrl(const Filter& f);
|
||||||
|
|
||||||
|
} // namespace kiwix
|
||||||
|
|
||||||
|
#endif // KIWIX_OPDS_CATALOG_H
|
||||||
+23
-52
@@ -26,11 +26,9 @@
|
|||||||
|
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
|
|
||||||
#include "common/base64.h"
|
|
||||||
#include "common/pathTools.h"
|
|
||||||
#include "common/regexTools.h"
|
|
||||||
#include "library.h"
|
#include "library.h"
|
||||||
#include "reader.h"
|
#include "name_mapper.h"
|
||||||
|
#include "library_dumper.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@@ -41,79 +39,52 @@ namespace kiwix
|
|||||||
* A tool to dump a `Library` into a opds stream.
|
* A tool to dump a `Library` into a opds stream.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class OPDSDumper
|
class OPDSDumper : public LibraryDumper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OPDSDumper() = default;
|
OPDSDumper(const Library* library, const NameMapper* NameMapper);
|
||||||
OPDSDumper(Library* library);
|
|
||||||
~OPDSDumper();
|
~OPDSDumper();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dump the OPDS feed.
|
* Dump the OPDS feed.
|
||||||
*
|
*
|
||||||
* @param id The id of the library.
|
* @param bookIds the ids of the books to include in the feed
|
||||||
|
* @param query the query used to obtain the list of book ids
|
||||||
* @return The OPDS feed.
|
* @return The OPDS feed.
|
||||||
*/
|
*/
|
||||||
std::string dumpOPDSFeed(const std::vector<std::string>& bookIds);
|
std::string dumpOPDSFeed(const std::vector<std::string>& bookIds, const std::string& query) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the id of the opds stream.
|
* Dump the OPDS feed.
|
||||||
*
|
*
|
||||||
* @param id the id to use.
|
* @param bookIds the ids of the books to include in the feed
|
||||||
|
* @param query the query used to obtain the list of book ids
|
||||||
|
* @param partial whether the feed should include partial or complete entries
|
||||||
|
* @return The OPDS feed.
|
||||||
*/
|
*/
|
||||||
void setId(const std::string& id) { this->id = id;}
|
std::string dumpOPDSFeedV2(const std::vector<std::string>& bookIds, const std::string& query, bool partial) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the title oft the opds stream.
|
* Dump the OPDS complete entry document.
|
||||||
*
|
*
|
||||||
* @param title the title to use.
|
* @param bookId the id of the book
|
||||||
|
* @return The OPDS complete entry document.
|
||||||
*/
|
*/
|
||||||
void setTitle(const std::string& title) { this->title = title; }
|
std::string dumpOPDSCompleteEntry(const std::string& bookId) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the root location used when generating url.
|
* Dump the categories OPDS feed.
|
||||||
*
|
*
|
||||||
* @param rootLocation the root location to use.
|
* @return The OPDS feed.
|
||||||
*/
|
*/
|
||||||
void setRootLocation(const std::string& rootLocation) { this->rootLocation = rootLocation; }
|
std::string categoriesOPDSFeed() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the search url.
|
* Dump the languages OPDS feed.
|
||||||
*
|
*
|
||||||
* @param searchUrl the search url to use.
|
* @return The OPDS feed.
|
||||||
*/
|
*/
|
||||||
void setSearchDescriptionUrl(const std::string& searchDescriptionUrl) { this->searchDescriptionUrl = searchDescriptionUrl; }
|
std::string languagesOPDSFeed() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Set some informations about the search results.
|
|
||||||
*
|
|
||||||
* @param totalResult the total number of results of the search.
|
|
||||||
* @param startIndex the start index of the result.
|
|
||||||
* @param count the number of result of the current set (or page).
|
|
||||||
*/
|
|
||||||
void setOpenSearchInfo(int totalResult, int startIndex, int count);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the library to dump.
|
|
||||||
*
|
|
||||||
* @param library The library to dump.
|
|
||||||
*/
|
|
||||||
void setLibrary(Library* library) { this->library = library; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
kiwix::Library* library;
|
|
||||||
std::string id;
|
|
||||||
std::string title;
|
|
||||||
std::string date;
|
|
||||||
std::string rootLocation;
|
|
||||||
std::string searchDescriptionUrl;
|
|
||||||
int m_totalResults;
|
|
||||||
int m_startIndex;
|
|
||||||
int m_count;
|
|
||||||
bool m_isSearchResult = false;
|
|
||||||
|
|
||||||
private:
|
|
||||||
pugi::xml_node handleBook(Book book, pugi::xml_node root_node);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,505 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef KIWIX_READER_H
|
|
||||||
#define KIWIX_READER_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <zim/article.h>
|
|
||||||
#include <zim/file.h>
|
|
||||||
#include <zim/fileiterator.h>
|
|
||||||
#include <zim/zim.h>
|
|
||||||
#include <exception>
|
|
||||||
#include <map>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#include "common.h"
|
|
||||||
#include "entry.h"
|
|
||||||
#include "common/pathTools.h"
|
|
||||||
#include "common/stringTools.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace kiwix
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Reader class is the class who allow to get an entry content from a zim
|
|
||||||
* file.
|
|
||||||
*/
|
|
||||||
class Reader
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Create a Reader to read a zim file specified by zimFilePath.
|
|
||||||
*
|
|
||||||
* @param zimFilePath The path to the zim file to read.
|
|
||||||
* The zim file can be splitted (.zimaa, .zimab, ...).
|
|
||||||
* In this case, the file path must still point to the
|
|
||||||
* unsplitted path as if the file were not splitted
|
|
||||||
* (.zim extesion).
|
|
||||||
*/
|
|
||||||
Reader(const string zimFilePath);
|
|
||||||
~Reader();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of "displayable" entries in the zim file.
|
|
||||||
*
|
|
||||||
* @return If the zim file has a /M/Counter metadata, return the number of
|
|
||||||
* entries with the 'text/html' MIMEtype specified in the metadata.
|
|
||||||
* Else return the number of entries in the 'A' namespace.
|
|
||||||
*/
|
|
||||||
unsigned int getArticleCount() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of media in the zim file.
|
|
||||||
*
|
|
||||||
* @return If the zim file has a /M/Counter metadata, return the number of
|
|
||||||
* entries with the 'image/jpeg', 'image/gif' and 'image/png' in
|
|
||||||
* the metadata.
|
|
||||||
* Else return the number of entries in the 'I' namespace.
|
|
||||||
*/
|
|
||||||
unsigned int getMediaCount() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of all entries in the zim file.
|
|
||||||
*
|
|
||||||
* @return Return the number of all the entries, whatever their MIMEtype or
|
|
||||||
* their namespace.
|
|
||||||
*/
|
|
||||||
unsigned int getGlobalCount() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the path of the zim file.
|
|
||||||
*
|
|
||||||
* @return the path of the zim file as given in the constructor.
|
|
||||||
*/
|
|
||||||
string getZimFilePath() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the Id of the zim file.
|
|
||||||
*
|
|
||||||
* @return The uuid stored in the zim file.
|
|
||||||
*/
|
|
||||||
string getId() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the url of a random page.
|
|
||||||
*
|
|
||||||
* Deprecated : Use `getRandomPage` instead.
|
|
||||||
*
|
|
||||||
* @return Url of a random page. The page is picked from all entries in
|
|
||||||
* the 'A' namespace.
|
|
||||||
* The main page is excluded from the potential results.
|
|
||||||
*/
|
|
||||||
DEPRECATED string getRandomPageUrl() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a random page.
|
|
||||||
*
|
|
||||||
* @return A random Entry. The entry is picked from all entries in
|
|
||||||
* the 'A' namespace.
|
|
||||||
* The main entry is excluded from the potential results.
|
|
||||||
*/
|
|
||||||
Entry getRandomPage() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the url of the first page.
|
|
||||||
*
|
|
||||||
* Deprecated : Use `getFirstPage` instead.
|
|
||||||
*
|
|
||||||
* @return Url of the first entry in the 'A' namespace.
|
|
||||||
*/
|
|
||||||
DEPRECATED string getFirstPageUrl() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the entry of the first page.
|
|
||||||
*
|
|
||||||
* @return The first entry in the 'A' namespace.
|
|
||||||
*/
|
|
||||||
Entry getFirstPage() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the url of the main page.
|
|
||||||
*
|
|
||||||
* Deprecated : Use `getMainPage` instead.
|
|
||||||
*
|
|
||||||
* @return Url of the main page as specified in the zim file.
|
|
||||||
*/
|
|
||||||
DEPRECATED string getMainPageUrl() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the entry of the main page.
|
|
||||||
*
|
|
||||||
* @return Entry of the main page as specified in the zim file.
|
|
||||||
*/
|
|
||||||
Entry getMainPage() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the content of a metadata.
|
|
||||||
*
|
|
||||||
* @param[in] name The name of the metadata.
|
|
||||||
* @param[out] value The value will be set to the content of the metadata.
|
|
||||||
* @return True if it was possible to get the content of the metadata.
|
|
||||||
*/
|
|
||||||
bool getMetatag(const string& name, string& value) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the title of the zim file.
|
|
||||||
*
|
|
||||||
* @return The title of zim file as specified in the zim metadata.
|
|
||||||
* If no title has been set, return a title computed from the
|
|
||||||
* file path.
|
|
||||||
*/
|
|
||||||
string getTitle() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the description of the zim file.
|
|
||||||
*
|
|
||||||
* @return The description of the zim file as specified in the zim metadata.
|
|
||||||
* If no description has been set, return the subtitle.
|
|
||||||
*/
|
|
||||||
string getDescription() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the language of the zim file.
|
|
||||||
*
|
|
||||||
* @return The language of the zim file as specified in the zim metadata.
|
|
||||||
*/
|
|
||||||
string getLanguage() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the name of the zim file.
|
|
||||||
*
|
|
||||||
* @return The name of the zim file as specified in the zim metadata.
|
|
||||||
*/
|
|
||||||
string getName() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the tags of the zim file.
|
|
||||||
*
|
|
||||||
* @return The tags of the zim file as specified in the zim metadata.
|
|
||||||
*/
|
|
||||||
string getTags() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the date of the zim file.
|
|
||||||
*
|
|
||||||
* @return The date of the zim file as specified in the zim metadata.
|
|
||||||
*/
|
|
||||||
string getDate() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the creator of the zim file.
|
|
||||||
*
|
|
||||||
* @return The creator of the zim file as specified in the zim metadata.
|
|
||||||
*/
|
|
||||||
string getCreator() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the publisher of the zim file.
|
|
||||||
*
|
|
||||||
* @return The publisher of the zim file as specified in the zim metadata.
|
|
||||||
*/
|
|
||||||
string getPublisher() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the origId of the zim file.
|
|
||||||
*
|
|
||||||
* The origId is only used in the case of patch zim file and is the Id
|
|
||||||
* of the original zim file.
|
|
||||||
*
|
|
||||||
* @return The origId of the zim file as specified in the zim metadata.
|
|
||||||
*/
|
|
||||||
string getOrigId() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the favicon of the zim file.
|
|
||||||
*
|
|
||||||
* @param[out] content The content of the favicon.
|
|
||||||
* @param[out] mimeType The mimeType of the favicon.
|
|
||||||
* @return True if a favicon has been found.
|
|
||||||
*/
|
|
||||||
bool getFavicon(string& content, string& mimeType) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an entry associated to an path.
|
|
||||||
*
|
|
||||||
* @param path The path of the entry.
|
|
||||||
* @return The entry.
|
|
||||||
* @throw NoEntry If no entry correspond to the path.
|
|
||||||
*/
|
|
||||||
Entry getEntryFromPath(const std::string& path) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an entry associated to an url encoded path.
|
|
||||||
*
|
|
||||||
* Equivalent to `getEntryFromPath(urlDecode(path));`
|
|
||||||
*
|
|
||||||
* @param path The url encoded path.
|
|
||||||
* @return The entry.
|
|
||||||
* @throw NoEntry If no entry correspond to the path.
|
|
||||||
*/
|
|
||||||
Entry getEntryFromEncodedPath(const std::string& path) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get un entry associated to a title.
|
|
||||||
*
|
|
||||||
* @param title The title.
|
|
||||||
* @return The entry
|
|
||||||
* throw NoEntry If no entry correspond to the url.
|
|
||||||
*/
|
|
||||||
Entry getEntryFromTitle(const std::string& title) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the url of a page specified by a title.
|
|
||||||
*
|
|
||||||
* @param[in] title the title of the page.
|
|
||||||
* @param[out] url the url of the page.
|
|
||||||
* @return True if the page can be found.
|
|
||||||
*/
|
|
||||||
DEPRECATED bool getPageUrlFromTitle(const string& title, string& url) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the mimetype of a entry specified by a url.
|
|
||||||
*
|
|
||||||
* @param[in] url the url of the entry.
|
|
||||||
* @param[out] mimeType the mimeType of the entry.
|
|
||||||
* @return True if the mimeType has been found.
|
|
||||||
*/
|
|
||||||
DEPRECATED bool getMimeTypeByUrl(const string& url, string& mimeType) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the content of an entry specifed by a url.
|
|
||||||
*
|
|
||||||
* Alias to `getContentByEncodedUrl`
|
|
||||||
*/
|
|
||||||
DEPRECATED bool getContentByUrl(const string& url,
|
|
||||||
string& content,
|
|
||||||
string& title,
|
|
||||||
unsigned int& contentLength,
|
|
||||||
string& contentType) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the content of an entry specified by a url encoded url.
|
|
||||||
*
|
|
||||||
* Equivalent to getContentByDecodedUrl(urlDecode(url), ...).
|
|
||||||
*/
|
|
||||||
DEPRECATED bool getContentByEncodedUrl(const string& url,
|
|
||||||
string& content,
|
|
||||||
string& title,
|
|
||||||
unsigned int& contentLength,
|
|
||||||
string& contentType,
|
|
||||||
string& baseUrl) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the content of an entry specified by an url encoded url.
|
|
||||||
*
|
|
||||||
* Equivalent to getContentByEncodedUrl but without baseUrl.
|
|
||||||
*/
|
|
||||||
DEPRECATED bool getContentByEncodedUrl(const string& url,
|
|
||||||
string& content,
|
|
||||||
string& title,
|
|
||||||
unsigned int& contentLength,
|
|
||||||
string& contentType) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the content of an entry specified by a url.
|
|
||||||
*
|
|
||||||
* @param[in] url The url of the entry.
|
|
||||||
* @param[out] content The content of the entry.
|
|
||||||
* @param[out] title the title of the entry.
|
|
||||||
* @param[out] contentLength The size of the entry (size of content).
|
|
||||||
* @param[out] contentType The mimeType of the entry.
|
|
||||||
* @param[out] baseUrl Return the true url of the entry.
|
|
||||||
* If the specified entry is a redirection, contains
|
|
||||||
* the url of the targeted entry.
|
|
||||||
* @return True if the entry has been found.
|
|
||||||
*/
|
|
||||||
DEPRECATED bool getContentByDecodedUrl(const string& url,
|
|
||||||
string& content,
|
|
||||||
string& title,
|
|
||||||
unsigned int& contentLength,
|
|
||||||
string& contentType,
|
|
||||||
string& baseUrl) const;
|
|
||||||
/**
|
|
||||||
* Get the content of an entry specified by a url.
|
|
||||||
*
|
|
||||||
* Equivalent to getContentByDecodedUrl but withou the baseUrl.
|
|
||||||
*/
|
|
||||||
DEPRECATED bool getContentByDecodedUrl(const string& url,
|
|
||||||
string& content,
|
|
||||||
string& title,
|
|
||||||
unsigned int& contentLength,
|
|
||||||
string& contentType) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for entries with title starting with prefix (case sensitive).
|
|
||||||
*
|
|
||||||
* Suggestions are stored in an internal vector and can be retrieved using
|
|
||||||
* `getNextSuggestion` method.
|
|
||||||
*
|
|
||||||
* @param prefix The prefix to search.
|
|
||||||
* @param suggestionsCount How many suggestions to search for.
|
|
||||||
* @param reset If true, remove previous suggestions in the internal vector.
|
|
||||||
* If false, add suggestions to the internal vector
|
|
||||||
* (until internal vector size is suggestionCount (or no more
|
|
||||||
* suggestion))
|
|
||||||
* @return True if some suggestions where added to the internal vector.
|
|
||||||
*/
|
|
||||||
bool searchSuggestions(const string& prefix,
|
|
||||||
unsigned int suggestionsCount,
|
|
||||||
const bool reset = true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for entries for the given prefix.
|
|
||||||
*
|
|
||||||
* If the zim file has a internal fulltext index, the suggestions will be
|
|
||||||
* searched using it.
|
|
||||||
* Else the suggestions will be search using `searchSuggestions` while trying
|
|
||||||
* to be smart about case sensitivity (using `getTitleVariants`).
|
|
||||||
*
|
|
||||||
* In any case, suggestions are stored in an internal vector and can be
|
|
||||||
* retrieved using `getNextSuggestion` method.
|
|
||||||
* The internal vector will be reset.
|
|
||||||
*
|
|
||||||
* @param prefix The prefix to search for.
|
|
||||||
* @param suggestionsCount How many suggestions to search for.
|
|
||||||
*/
|
|
||||||
bool searchSuggestionsSmart(const string& prefix,
|
|
||||||
unsigned int suggestionsCount);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the url exists in the zim file.
|
|
||||||
*
|
|
||||||
* Deprecated : Use `pathExists` instead.
|
|
||||||
*
|
|
||||||
* @param url the url to check.
|
|
||||||
* @return True if the url exits in the zim file.
|
|
||||||
*/
|
|
||||||
DEPRECATED bool urlExists(const string& url) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the path exists in the zim file.
|
|
||||||
*
|
|
||||||
* @param path the path to check.
|
|
||||||
* @return True if the path exists in the zim file.
|
|
||||||
*/
|
|
||||||
bool pathExists(const string& path) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the zim file has a embedded fulltext index.
|
|
||||||
*
|
|
||||||
* @return True if the zim file has a embedded fulltext index
|
|
||||||
* and is not split (else the fulltext is not accessible).
|
|
||||||
*/
|
|
||||||
bool hasFulltextIndex() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get potential case title variations for a title.
|
|
||||||
*
|
|
||||||
* @param title a title.
|
|
||||||
* @return the list of variantions.
|
|
||||||
*/
|
|
||||||
std::vector<std::string> getTitleVariants(const std::string& title) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the next suggestion title.
|
|
||||||
*
|
|
||||||
* @param[out] title the title of the suggestion.
|
|
||||||
* @return True if title has been set.
|
|
||||||
*/
|
|
||||||
bool getNextSuggestion(string& title);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the next suggestion title and url.
|
|
||||||
*
|
|
||||||
* @param[out] title the title of the suggestion.
|
|
||||||
* @param[out] url the url of the suggestion.
|
|
||||||
* @return True if title and url have been set.
|
|
||||||
*/
|
|
||||||
bool getNextSuggestion(string& title, string& url);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get if we can check zim file integrity (has a checksum).
|
|
||||||
*
|
|
||||||
* @return True if zim file have a checksum.
|
|
||||||
*/
|
|
||||||
bool canCheckIntegrity() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check is zim file is corrupted.
|
|
||||||
*
|
|
||||||
* @return True if zim file is corrupted.
|
|
||||||
*/
|
|
||||||
bool isCorrupted() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a full url into a namespace and url.
|
|
||||||
*
|
|
||||||
* @param[in] url The full url ("/N/url").
|
|
||||||
* @param[out] ns The namespace (N).
|
|
||||||
* @param[out] title The url (url).
|
|
||||||
* @return True
|
|
||||||
*/
|
|
||||||
DEPRECATED bool parseUrl(const string& url, char* ns, string& title) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the total size of the zim file.
|
|
||||||
*
|
|
||||||
* If zim file is split, return the sum of all parts' size.
|
|
||||||
*
|
|
||||||
* @return Size of the size file is KiB.
|
|
||||||
*/
|
|
||||||
unsigned int getFileSize() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the zim file handler.
|
|
||||||
*
|
|
||||||
* @return The libzim file handler.
|
|
||||||
*/
|
|
||||||
zim::File* getZimFileHandler() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the zim article object associated to a url.
|
|
||||||
*
|
|
||||||
* @param[in] url The url of the article.
|
|
||||||
* @param[out] article The libzim article object.
|
|
||||||
* @return True if the url is good (article.good()).
|
|
||||||
*/
|
|
||||||
DEPRECATED bool getArticleObjectByDecodedUrl(const string& url,
|
|
||||||
zim::Article& article) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
zim::File* zimFileHandler;
|
|
||||||
zim::size_type firstArticleOffset;
|
|
||||||
zim::size_type lastArticleOffset;
|
|
||||||
zim::size_type nsACount;
|
|
||||||
zim::size_type nsICount;
|
|
||||||
std::string zimFilePath;
|
|
||||||
|
|
||||||
std::vector<std::vector<std::string>> suggestions;
|
|
||||||
std::vector<std::vector<std::string>>::iterator suggestionsOffset;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::map<const std::string, unsigned int> parseCounterMetadata() const;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Emmanuel Engelhart <kelson@kiwix.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KIWIX_SEARCH_RENDERER_H
|
||||||
|
#define KIWIX_SEARCH_RENDERER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <zim/search.h>
|
||||||
|
#include "library.h"
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
|
{
|
||||||
|
|
||||||
|
class NameMapper;
|
||||||
|
/**
|
||||||
|
* The SearcherRenderer class is used to render a search result to a html page.
|
||||||
|
*/
|
||||||
|
class SearchRenderer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Construct a SearchRenderer from a SearchResultSet.
|
||||||
|
*
|
||||||
|
* The constructed version of the SearchRenderer will not introduce
|
||||||
|
* the book name for each result. It is better to use the other constructor
|
||||||
|
* with a Library pointer to have a better html page.
|
||||||
|
*
|
||||||
|
* @param srs The `SearchResultSet` to render.
|
||||||
|
* @param mapper The `NameMapper` to use to do the rendering.
|
||||||
|
* @param start The start offset used for the srs.
|
||||||
|
* @param estimatedResultCount The estimatedResultCount of the whole search
|
||||||
|
*/
|
||||||
|
SearchRenderer(zim::SearchResultSet srs, NameMapper* mapper,
|
||||||
|
unsigned int start, unsigned int estimatedResultCount);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a SearchRenderer from a SearchResultSet.
|
||||||
|
*
|
||||||
|
* @param srs The `SearchResultSet` to render.
|
||||||
|
* @param mapper The `NameMapper` to use to do the rendering.
|
||||||
|
* @param library The `Library` to use to look up book details for search results.
|
||||||
|
* @param start The start offset used for the srs.
|
||||||
|
* @param estimatedResultCount The estimatedResultCount of the whole search
|
||||||
|
*/
|
||||||
|
SearchRenderer(zim::SearchResultSet srs, NameMapper* mapper, Library* library,
|
||||||
|
unsigned int start, unsigned int estimatedResultCount);
|
||||||
|
|
||||||
|
~SearchRenderer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the search pattern used to do the search
|
||||||
|
*/
|
||||||
|
void setSearchPattern(const std::string& pattern);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the querystring used to select books
|
||||||
|
*/
|
||||||
|
void setSearchBookQuery(const std::string& bookQuery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set protocol prefix.
|
||||||
|
*/
|
||||||
|
void setProtocolPrefix(const std::string& prefix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set search protocol prefix.
|
||||||
|
*/
|
||||||
|
void setSearchProtocolPrefix(const std::string& prefix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set result count per page
|
||||||
|
*/
|
||||||
|
void setPageLength(unsigned int pageLength){
|
||||||
|
this->pageLength = pageLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string renderTemplate(const std::string& tmpl_str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the html page with the resutls of the search.
|
||||||
|
*/
|
||||||
|
std::string getHtml();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the xml page with the resutls of the search.
|
||||||
|
*/
|
||||||
|
std::string getXml();
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string beautifyInteger(const unsigned int number);
|
||||||
|
zim::SearchResultSet m_srs;
|
||||||
|
NameMapper* mp_nameMapper;
|
||||||
|
Library* mp_library;
|
||||||
|
std::string searchBookQuery;
|
||||||
|
std::string searchPattern;
|
||||||
|
std::string protocolPrefix;
|
||||||
|
std::string searchProtocolPrefix;
|
||||||
|
unsigned int pageLength;
|
||||||
|
unsigned int estimatedResultCount;
|
||||||
|
unsigned int resultStart;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef KIWIX_SEARCHER_H
|
|
||||||
#define KIWIX_SEARCHER_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unicode/putil.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cctype>
|
|
||||||
#include <locale>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <vector>
|
|
||||||
#include "common/pathTools.h"
|
|
||||||
#include "common/stringTools.h"
|
|
||||||
#include "kiwix_config.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace kiwix
|
|
||||||
{
|
|
||||||
class Reader;
|
|
||||||
class Result
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~Result(){};
|
|
||||||
virtual std::string get_url() = 0;
|
|
||||||
virtual std::string get_title() = 0;
|
|
||||||
virtual int get_score() = 0;
|
|
||||||
virtual std::string get_snippet() = 0;
|
|
||||||
virtual std::string get_content() = 0;
|
|
||||||
virtual int get_wordCount() = 0;
|
|
||||||
virtual int get_size() = 0;
|
|
||||||
virtual int get_readerIndex() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SearcherInternal;
|
|
||||||
/**
|
|
||||||
* The Searcher class is reponsible to do different kind of search using the
|
|
||||||
* fulltext index.
|
|
||||||
*
|
|
||||||
* Historically, there are two kind of fulltext index :
|
|
||||||
* - The legacy one, is the external fulltext index. A directory stored outside
|
|
||||||
* of the zim file.
|
|
||||||
* - The new one, a embedded fulltext index in the zim file.
|
|
||||||
*
|
|
||||||
* Legacy external fulltext index has to be considered as obsolet format with
|
|
||||||
* less functionnalities:
|
|
||||||
* - No multi zim search ;
|
|
||||||
* - No geo_search ;
|
|
||||||
* - No suggestions search ;
|
|
||||||
*
|
|
||||||
* To reflect this, there is two Search creation "API":
|
|
||||||
* - One for the external fulltext index, using the constructor taking a
|
|
||||||
* xapianDirectoryPath) ;
|
|
||||||
* - One for the embedded fulltext index, using a "empty" constructor and the
|
|
||||||
* `add_reader` method".
|
|
||||||
*
|
|
||||||
* On top of that, the Searcher may (if compiled with ctpp2) be used to
|
|
||||||
* generate a html page for the search result. This use a template that need a
|
|
||||||
* humanReaderName. This feature is only used by kiwix-serve and this should be
|
|
||||||
* move outside of Searcher (and with a better API). If you don't use the html
|
|
||||||
* rendering (getHtml method), you better should simply ignore the different
|
|
||||||
* humanReadeableName attributes (or give an empty string).
|
|
||||||
*/
|
|
||||||
class Searcher
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* The default constructor.
|
|
||||||
*
|
|
||||||
* @param humanReadableName The global zim's humanReadableName.
|
|
||||||
* Used to generate pagination links.
|
|
||||||
*/
|
|
||||||
Searcher(const string& humanReadableName = "");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The constructor for legacy external fulltext index.
|
|
||||||
*
|
|
||||||
* @param xapianDirectoryPath The path to the external index directory.
|
|
||||||
* @param reader The reader associated to the external index.
|
|
||||||
* It will be used retrive the article content or generate
|
|
||||||
* the snippet.
|
|
||||||
* @param humanReadableName The humanReadableName for the zim.
|
|
||||||
*/
|
|
||||||
Searcher(const string& xapianDirectoryPath,
|
|
||||||
Reader* reader,
|
|
||||||
const string& humanReadableName);
|
|
||||||
~Searcher();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a reader (containing embedded fulltext index) to the search.
|
|
||||||
*
|
|
||||||
* @param reader The Reader for the zim containing the fulltext index.
|
|
||||||
* @param humanReaderName The human readable name of the reader.
|
|
||||||
* @return true if the reader has been added.
|
|
||||||
* false if the reader cannot be added (no embedded fulltext index present)
|
|
||||||
*/
|
|
||||||
bool add_reader(Reader* reader, const std::string& humanReaderName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start a search on the zim associated to the Searcher.
|
|
||||||
*
|
|
||||||
* Search results should be retrived using the getNextResult method.
|
|
||||||
*
|
|
||||||
* @param search The search query.
|
|
||||||
* @param resultStart the start offset of the search results (used for pagination).
|
|
||||||
* @param resultEnd the end offset of the search results (used for pagination).
|
|
||||||
* @param verbose print some info on stdout if true.
|
|
||||||
*/
|
|
||||||
void search(std::string& search,
|
|
||||||
unsigned int resultStart,
|
|
||||||
unsigned int resultEnd,
|
|
||||||
const bool verbose = false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start a geographique search.
|
|
||||||
* The search return result for entry in a disc of center latitude/longitude
|
|
||||||
* and radius distance.
|
|
||||||
*
|
|
||||||
* Search results should be retrived using the getNextResult method.
|
|
||||||
*
|
|
||||||
* @param latitude The latitude of the center point.
|
|
||||||
* @param longitude The longitude of the center point.
|
|
||||||
* @param distance The radius of the disc.
|
|
||||||
* @param resultStart the start offset of the search results (used for pagination).
|
|
||||||
* @param resultEnd the end offset of the search results (used for pagination).
|
|
||||||
* @param verbose print some info on stdout if true.
|
|
||||||
*/
|
|
||||||
void geo_search(float latitude, float longitude, float distance,
|
|
||||||
unsigned int resultStart,
|
|
||||||
unsigned int resultEnd,
|
|
||||||
const bool verbose = false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start a suggestion search.
|
|
||||||
* The search made depend of the "version" of the embedded index.
|
|
||||||
* - If the index is newer enough and have a title namespace, the search is
|
|
||||||
* made in the titles only.
|
|
||||||
* - Else the search is made on the whole article content.
|
|
||||||
* In any case, the search is made "partial" (as adding '*' at the end of the query)
|
|
||||||
*
|
|
||||||
* @param search The search query.
|
|
||||||
* @param verbose print some info on stdout if true.
|
|
||||||
*/
|
|
||||||
void suggestions(std::string& search, const bool verbose = false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the next result of a started search.
|
|
||||||
* This is the method to use to loop hover the search results.
|
|
||||||
*/
|
|
||||||
Result* getNextResult();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restart the previous search.
|
|
||||||
* Next call to getNextResult will return the first result.
|
|
||||||
*/
|
|
||||||
void restart_search();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a estimation of the result count.
|
|
||||||
*/
|
|
||||||
unsigned int getEstimatedResultCount();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set protocol prefix.
|
|
||||||
* Only used by getHtml.
|
|
||||||
*/
|
|
||||||
bool setProtocolPrefix(const std::string prefix);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set search protocol prefix.
|
|
||||||
* Only used by getHtml.
|
|
||||||
*/
|
|
||||||
bool setSearchProtocolPrefix(const std::string prefix);
|
|
||||||
|
|
||||||
#ifdef ENABLE_CTPP2
|
|
||||||
/**
|
|
||||||
* Generate the html page with the resutls of the search.
|
|
||||||
*/
|
|
||||||
string getHtml();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::string beautifyInteger(const unsigned int number);
|
|
||||||
void closeIndex();
|
|
||||||
void searchInIndex(string& search,
|
|
||||||
const unsigned int resultStart,
|
|
||||||
const unsigned int resultEnd,
|
|
||||||
const bool verbose = false);
|
|
||||||
|
|
||||||
std::vector<Reader*> readers;
|
|
||||||
std::vector<std::string> humanReaderNames;
|
|
||||||
SearcherInternal* internal;
|
|
||||||
std::string searchPattern;
|
|
||||||
std::string protocolPrefix;
|
|
||||||
std::string searchProtocolPrefix;
|
|
||||||
unsigned int resultCountPerPage;
|
|
||||||
unsigned int estimatedResultCount;
|
|
||||||
unsigned int resultStart;
|
|
||||||
unsigned int resultEnd;
|
|
||||||
std::string contentHumanReadableId;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Matthieu Gautier <mgautier@kymeria.fr>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KIWIX_SERVER_H
|
||||||
|
#define KIWIX_SERVER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
|
{
|
||||||
|
class Library;
|
||||||
|
class NameMapper;
|
||||||
|
class InternalServer;
|
||||||
|
|
||||||
|
class Server {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* The default constructor.
|
||||||
|
*
|
||||||
|
* @param library The library to serve.
|
||||||
|
*/
|
||||||
|
Server(Library* library, NameMapper* nameMapper=nullptr);
|
||||||
|
|
||||||
|
virtual ~Server();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serve the content.
|
||||||
|
*/
|
||||||
|
bool start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the daemon.
|
||||||
|
*/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
void setRoot(const std::string& root);
|
||||||
|
void setAddress(const std::string& addr) { m_addr = addr; }
|
||||||
|
void setPort(int port) { m_port = port; }
|
||||||
|
void setNbThreads(int threads) { m_nbThreads = threads; }
|
||||||
|
void setMultiZimSearchLimit(unsigned int limit) { m_multizimSearchLimit = limit; }
|
||||||
|
void setIpConnectionLimit(int limit) { m_ipConnectionLimit = limit; }
|
||||||
|
void setVerbose(bool verbose) { m_verbose = verbose; }
|
||||||
|
void setIndexTemplateString(const std::string& indexTemplateString) { m_indexTemplateString = indexTemplateString; }
|
||||||
|
void setTaskbar(bool withTaskbar, bool withLibraryButton)
|
||||||
|
{ m_withTaskbar = withTaskbar; m_withLibraryButton = withLibraryButton; }
|
||||||
|
void setBlockExternalLinks(bool blockExternalLinks)
|
||||||
|
{ m_blockExternalLinks = blockExternalLinks; }
|
||||||
|
int getPort();
|
||||||
|
std::string getAddress();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Library* mp_library;
|
||||||
|
NameMapper* mp_nameMapper;
|
||||||
|
std::string m_root = "";
|
||||||
|
std::string m_addr = "";
|
||||||
|
std::string m_indexTemplateString = "";
|
||||||
|
int m_port = 80;
|
||||||
|
int m_nbThreads = 1;
|
||||||
|
unsigned int m_multizimSearchLimit = 0;
|
||||||
|
bool m_verbose = false;
|
||||||
|
bool m_withTaskbar = true;
|
||||||
|
bool m_withLibraryButton = true;
|
||||||
|
bool m_blockExternalLinks = false;
|
||||||
|
int m_ipConnectionLimit = 0;
|
||||||
|
std::unique_ptr<InternalServer> mp_server;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
+220
@@ -0,0 +1,220 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Matthieu Gautier <mgautier@kymeria.fr>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KIWIX_TOOLS_H
|
||||||
|
#define KIWIX_TOOLS_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace kiwix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current directory.
|
||||||
|
*
|
||||||
|
* @return the current directory (utf8 encoded)
|
||||||
|
*/
|
||||||
|
std::string getCurrentDirectory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the data directory.
|
||||||
|
*
|
||||||
|
* The data directory is a directory where to put data (zim files, ...)
|
||||||
|
* It depends of the platform and it may be changed by user using environment variable.
|
||||||
|
*
|
||||||
|
* The resolution order is :
|
||||||
|
* - `KIWIX_DATA_DIR` env variable (if set).
|
||||||
|
* - On Windows :
|
||||||
|
* . `$APPDATA/kiwix` if $APPDATA is set
|
||||||
|
* . `$USERPROFILE/kiwix` if $USERPROFILE is set
|
||||||
|
* - Else :
|
||||||
|
* . `$XDG_DATA_HOME/kiwix`if $XDG_DATA_HOME is set
|
||||||
|
* . `$HOME/.local/share/kiwx` if $HOWE is set
|
||||||
|
* - current directory
|
||||||
|
*
|
||||||
|
* @return the path of the data directory (utf8 encoded)
|
||||||
|
*/
|
||||||
|
std::string getDataDirectory();
|
||||||
|
|
||||||
|
/** Return the path of the executable
|
||||||
|
*
|
||||||
|
* Some application may be packaged in auto extractible archive (Appimage) and the
|
||||||
|
* real executable is different of the path of the archive.
|
||||||
|
* If `realPathOnly` is true, return the path of the real executable instead of the
|
||||||
|
* archive launched by the user.
|
||||||
|
*
|
||||||
|
* @param realPathOnly If we must return the real path of the executable.
|
||||||
|
* @return the path of the executable (utf8 encoded)
|
||||||
|
*/
|
||||||
|
std::string getExecutablePath(bool realPathOnly = false);
|
||||||
|
|
||||||
|
/** Tell if the path is a relative path.
|
||||||
|
*
|
||||||
|
* This function is provided as a small helper. It is probably better to use native tools
|
||||||
|
* to manipulate paths.
|
||||||
|
*
|
||||||
|
* @param path A utf8 encoded path.
|
||||||
|
* @return true if the path is relative.
|
||||||
|
*/
|
||||||
|
bool isRelativePath(const std::string& path);
|
||||||
|
|
||||||
|
/** Append a path to another one.
|
||||||
|
*
|
||||||
|
* This function is provided as a small helper. It is probably better to use native tools
|
||||||
|
* to manipulate paths.
|
||||||
|
*
|
||||||
|
* @param basePath the base path.
|
||||||
|
* @param relativePath a path to add to the base path, must be a relative path.
|
||||||
|
* @return The concatenation of the paths, using the right separator.
|
||||||
|
*/
|
||||||
|
std::string appendToDirectory(const std::string& basePath, const std::string& relativePath);
|
||||||
|
|
||||||
|
/** Remove the last element of a path.
|
||||||
|
*
|
||||||
|
* This function is provided as a small helper. It is probably better to use native tools
|
||||||
|
* to manipulate paths.
|
||||||
|
*
|
||||||
|
* @param path a path.
|
||||||
|
* @return The parent directory (or empty string if none).
|
||||||
|
*/
|
||||||
|
std::string removeLastPathElement(const std::string& path);
|
||||||
|
|
||||||
|
/** Get the last element of a path.
|
||||||
|
*
|
||||||
|
* This function is provided as a small helper. It is probably better to use native tools
|
||||||
|
* to manipulate paths.
|
||||||
|
*
|
||||||
|
* @param path a path.
|
||||||
|
* @return The base name of the path or empty string if none (ending with a separator).
|
||||||
|
*/
|
||||||
|
std::string getLastPathElement(const std::string& path);
|
||||||
|
|
||||||
|
/** Compute the absolute path of a relative path based on another one
|
||||||
|
*
|
||||||
|
* Equivalent to appendToDirectory followed by a normalization of the path.
|
||||||
|
*
|
||||||
|
* This function is provided as a small helper. It is probably better to use native tools
|
||||||
|
* to manipulate paths.
|
||||||
|
*
|
||||||
|
* @param path the base path (if empty, current directory is taken).
|
||||||
|
* @param relativePath the relative path.
|
||||||
|
* @return a absolute path.
|
||||||
|
*/
|
||||||
|
std::string computeAbsolutePath(const std::string& path, const std::string& relativePath);
|
||||||
|
|
||||||
|
/** Compute the relative path of a path relative to another one
|
||||||
|
*
|
||||||
|
* This function is provided as a small helper. It is probably better to use native tools
|
||||||
|
* to manipulate paths.
|
||||||
|
*
|
||||||
|
* @param path the base path.
|
||||||
|
* @param absolutePath the absolute path to find the relative path for.
|
||||||
|
* @return a relative path (pointing to absolutePath, relative to path).
|
||||||
|
*/
|
||||||
|
std::string computeRelativePath(const std::string& path, const std::string& absolutePath);
|
||||||
|
|
||||||
|
/** Sleep the current thread.
|
||||||
|
*
|
||||||
|
* This function is provided as a small helper. It is probably better to use native tools.
|
||||||
|
*
|
||||||
|
* @param milliseconds The number of milliseconds to wait for.
|
||||||
|
*/
|
||||||
|
void sleep(unsigned int milliseconds);
|
||||||
|
|
||||||
|
/** Split a string
|
||||||
|
*
|
||||||
|
* This function is provided as a small helper. It is probably better to use native tools.
|
||||||
|
*
|
||||||
|
* Assuming text = "foo:;bar;baz,oups;"
|
||||||
|
*
|
||||||
|
* split(text, ":;", true, true) => ["foo", ":", ";", "bar", ";", "baz,oups", ";"]
|
||||||
|
* split(text, ":;", true, false) => ["foo", "bar", "baz,oups"] (default)
|
||||||
|
* split(text, ":;", false, true) => ["foo", ":", "", ";", "bar", ";", "baz,oups", ";", ""]
|
||||||
|
* split(text, ":;", false, false) => ["foo", "", "bar", "baz,oups", ""]
|
||||||
|
*
|
||||||
|
* @param str The string to split.
|
||||||
|
* @param delims A string of potential delimiters.
|
||||||
|
* Each charater in the string can be a individual delimiters.
|
||||||
|
* @param dropEmpty true if empty part must be dropped from the result.
|
||||||
|
* @param keepDelim true if delimiter must be included from the result.
|
||||||
|
* @return a list of part (potentially containing delimiters)
|
||||||
|
*/
|
||||||
|
std::vector<std::string> split(const std::string& str, const std::string& delims, bool dropEmpty=true, bool keepDelim = false);
|
||||||
|
|
||||||
|
/** Convert language code from iso2 code to iso3
|
||||||
|
*
|
||||||
|
* This function is provided as a small helper. It is probably better to use native tools
|
||||||
|
* to manipulate locales.
|
||||||
|
*
|
||||||
|
* @param a2code a iso2 code string.
|
||||||
|
* @return the corresponding iso3 code.
|
||||||
|
* @throw std::out_of_range if iso2 code is not known.
|
||||||
|
*/
|
||||||
|
std::string converta2toa3(const std::string& a2code);
|
||||||
|
|
||||||
|
/** Extracts content from given file.
|
||||||
|
*
|
||||||
|
* This function provides content of a file provided it's path.
|
||||||
|
*
|
||||||
|
* @param path The absolute path provided in string format.
|
||||||
|
* @return Content of corresponding file in string format.
|
||||||
|
*/
|
||||||
|
std::string getFileContent(const std::string& path);
|
||||||
|
|
||||||
|
/** Checks if file exists.
|
||||||
|
*
|
||||||
|
* This function returns boolean stating if file exists.
|
||||||
|
*
|
||||||
|
* @param path The absolute path provided in string format.
|
||||||
|
* @return Boolean representing if file exists or not.
|
||||||
|
*/
|
||||||
|
bool fileExists(const std::string& path);
|
||||||
|
|
||||||
|
/** Checks if file is readable.
|
||||||
|
*
|
||||||
|
* This function returns boolean stating if file is readable.
|
||||||
|
*
|
||||||
|
* @param path The absolute path provided in string format.
|
||||||
|
* @return Boolean representing if file is readale or not.
|
||||||
|
*/
|
||||||
|
bool fileReadable(const std::string& path);
|
||||||
|
|
||||||
|
/** Provides mimetype from filename.
|
||||||
|
*
|
||||||
|
* This function provides mimetype from file-name.
|
||||||
|
*
|
||||||
|
* @param filename string containing filename.
|
||||||
|
* @return mimetype from filename in string format.
|
||||||
|
*/
|
||||||
|
std::string getMimeTypeForFile(const std::string& filename);
|
||||||
|
|
||||||
|
/** Provides all available network interfaces
|
||||||
|
*
|
||||||
|
* This function provides the available IPv4 network interfaces
|
||||||
|
*/
|
||||||
|
std::map<std::string, std::string> getNetworkInterfaces();
|
||||||
|
|
||||||
|
/** Provides the best IP address
|
||||||
|
* This function provides the best IP address from the list given by getNetworkInterfaces
|
||||||
|
*/
|
||||||
|
std::string getBestPublicIp();
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // KIWIX_TOOLS_H
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014 Emmanuel Engelhart <kelson@kiwix.org>
|
* Copyright 2021 Emmanuel Engelhart <kelson@kiwix.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,22 +17,18 @@
|
|||||||
* MA 02110-1301, USA.
|
* MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef KIWIX_OTHERTOOLS_H
|
#ifndef KIWIX_VERSION_H
|
||||||
#define KIWIX_OTHERTOOLS_H
|
#define KIWIX_VERSION_H
|
||||||
|
|
||||||
#ifdef _WIN32
|
#include <string>
|
||||||
#include <windows.h>
|
#include <vector>
|
||||||
#else
|
#include <iostream>
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <pugixml.hpp>
|
|
||||||
|
|
||||||
namespace kiwix
|
namespace kiwix
|
||||||
{
|
{
|
||||||
void sleep(unsigned int milliseconds);
|
typedef std::vector<std::pair<std::string, std::string>> LibVersions;
|
||||||
std::string nodeToString(pugi::xml_node node);
|
LibVersions getVersions();
|
||||||
std::string converta2toa3(const std::string& a2code);
|
void printVersions(std::ostream& out = std::cout);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif // KIWIX_VERSION_H
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef KIWIX_XAPIAN_SEARCHER_H
|
|
||||||
#define KIWIX_XAPIAN_SEARCHER_H
|
|
||||||
|
|
||||||
#include <xapian.h>
|
|
||||||
#include "reader.h"
|
|
||||||
#include "searcher.h"
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace kiwix
|
|
||||||
{
|
|
||||||
class XapianSearcher;
|
|
||||||
|
|
||||||
class XapianResult : public Result
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
XapianResult(XapianSearcher* searcher, Xapian::MSetIterator& iterator);
|
|
||||||
virtual ~XapianResult(){};
|
|
||||||
|
|
||||||
virtual std::string get_url();
|
|
||||||
virtual std::string get_title();
|
|
||||||
virtual int get_score();
|
|
||||||
virtual std::string get_snippet();
|
|
||||||
virtual std::string get_content();
|
|
||||||
virtual int get_wordCount();
|
|
||||||
virtual int get_size();
|
|
||||||
virtual int get_readerIndex() { return 0; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
XapianSearcher* searcher;
|
|
||||||
Xapian::MSetIterator iterator;
|
|
||||||
Xapian::Document document;
|
|
||||||
};
|
|
||||||
|
|
||||||
class NoXapianIndexInZim : public exception
|
|
||||||
{
|
|
||||||
virtual const char* what() const throw()
|
|
||||||
{
|
|
||||||
return "There is no fulltext index in the zim file";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class XapianSearcher
|
|
||||||
{
|
|
||||||
friend class XapianResult;
|
|
||||||
|
|
||||||
public:
|
|
||||||
XapianSearcher(const string& xapianDirectoryPath, Reader* reader);
|
|
||||||
virtual ~XapianSearcher(){};
|
|
||||||
void searchInIndex(string& search,
|
|
||||||
const unsigned int resultStart,
|
|
||||||
const unsigned int resultEnd,
|
|
||||||
const bool verbose = false);
|
|
||||||
virtual Result* getNextResult();
|
|
||||||
void restart_search();
|
|
||||||
|
|
||||||
Xapian::MSet results;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void closeIndex();
|
|
||||||
void openIndex(const string& xapianDirectoryPath);
|
|
||||||
void setup_queryParser();
|
|
||||||
|
|
||||||
Reader* reader;
|
|
||||||
Xapian::Database readableDatabase;
|
|
||||||
std::string language;
|
|
||||||
std::string stopwords;
|
|
||||||
Xapian::QueryParser queryParser;
|
|
||||||
Xapian::Stem stemmer;
|
|
||||||
Xapian::SimpleStopper stopper;
|
|
||||||
Xapian::MSetIterator current_result;
|
|
||||||
std::map<std::string, int> valuesmap;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
+40
-86
@@ -1,101 +1,62 @@
|
|||||||
project('kiwix-lib', 'cpp',
|
project('libkiwix', 'cpp',
|
||||||
version : '3.0.0',
|
version : '12.0.0',
|
||||||
license : 'GPL',
|
license : 'GPLv3+',
|
||||||
default_options : ['c_std=c11', 'cpp_std=c++11', 'werror=true'])
|
default_options : ['c_std=c11', 'cpp_std=c++11', 'werror=true'])
|
||||||
|
|
||||||
compiler = meson.get_compiler('cpp')
|
compiler = meson.get_compiler('cpp')
|
||||||
find_library_in_compiler = meson.version().version_compare('>=0.31.0')
|
|
||||||
|
|
||||||
static_deps = get_option('android') or get_option('default_library') == 'static'
|
static_deps = get_option('static-linkage') or get_option('default_library') == 'static'
|
||||||
if get_option('android')
|
|
||||||
extra_libs = ['-llog']
|
# See https://github.com/kiwix/libkiwix/issues/371
|
||||||
|
if ['arm', 'mips', 'm68k', 'ppc', 'sh4'].contains(host_machine.cpu_family())
|
||||||
|
extra_libs = ['-latomic']
|
||||||
else
|
else
|
||||||
extra_libs = []
|
extra_libs = []
|
||||||
endif
|
endif
|
||||||
|
|
||||||
thread_dep = dependency('threads')
|
if (compiler.get_id() == 'gcc' and build_machine.system() == 'linux') or host_machine.system() == 'freebsd'
|
||||||
|
# C++ std::thread is implemented using pthread on linux by gcc
|
||||||
|
thread_dep = dependency('threads')
|
||||||
|
else
|
||||||
|
thread_dep = dependency('', required:false)
|
||||||
|
endif
|
||||||
libicu_dep = dependency('icu-i18n', static:static_deps)
|
libicu_dep = dependency('icu-i18n', static:static_deps)
|
||||||
libzim_dep = dependency('libzim', version : '>=4.0.0', static:static_deps)
|
|
||||||
pugixml_dep = dependency('pugixml', static:static_deps)
|
pugixml_dep = dependency('pugixml', static:static_deps)
|
||||||
libcurl_dep = dependency('libcurl', static:static_deps)
|
libcurl_dep = dependency('libcurl', static:static_deps)
|
||||||
|
microhttpd_dep = dependency('libmicrohttpd', static:static_deps)
|
||||||
|
zlib_dep = dependency('zlib', static:static_deps)
|
||||||
|
xapian_dep = dependency('xapian-core', static:static_deps)
|
||||||
|
|
||||||
|
if compiler.has_header('mustache.hpp')
|
||||||
|
extra_include = []
|
||||||
|
elif compiler.has_header('mustache.hpp', args: '-I/usr/include/kainjow')
|
||||||
|
extra_include = ['/usr/include/kainjow']
|
||||||
|
else
|
||||||
|
error('Cannot found header mustache.hpp')
|
||||||
|
endif
|
||||||
|
|
||||||
|
libzim_dep = dependency('libzim', version : '>=8.1.0', static:static_deps)
|
||||||
|
if not compiler.has_header_symbol('zim/zim.h', 'LIBZIM_WITH_XAPIAN')
|
||||||
|
error('Libzim seems to be compiled without xapian. Xapian support is mandatory.')
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
extra_cflags = ''
|
extra_cflags = ''
|
||||||
if target_machine.system() == 'windows' and static_deps
|
if host_machine.system() == 'windows' and static_deps
|
||||||
add_project_arguments('-DCURL_STATICLIB', language : 'cpp')
|
add_project_arguments('-DCURL_STATICLIB', language : 'cpp')
|
||||||
extra_cflags += '-DCURL_STATICLIB'
|
extra_cflags += '-DCURL_STATICLIB'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if host_machine.system() == 'windows'
|
||||||
|
add_project_arguments('-DNOMINMAX', language: 'cpp')
|
||||||
ctpp2_include_path = ''
|
|
||||||
has_ctpp2_dep = false
|
|
||||||
ctpp2_prefix_install = get_option('ctpp2-install-prefix')
|
|
||||||
ctpp2_link_args = []
|
|
||||||
if ctpp2_prefix_install == ''
|
|
||||||
if compiler.has_header('ctpp2/CTPP2Logger.hpp')
|
|
||||||
if find_library_in_compiler
|
|
||||||
ctpp2_lib = compiler.find_library('ctpp2')
|
|
||||||
else
|
|
||||||
ctpp2_lib = find_library('ctpp2')
|
|
||||||
endif
|
|
||||||
ctpp2_link_args = ['-lctpp2']
|
|
||||||
if meson.is_cross_build() and host_machine.system() == 'windows'
|
|
||||||
if find_library_in_compiler
|
|
||||||
iconv_lib = compiler.find_library('iconv', required:false)
|
|
||||||
else
|
|
||||||
iconv_lib = find_library('iconv', required:false)
|
|
||||||
endif
|
|
||||||
if iconv_lib.found()
|
|
||||||
ctpp2_link_args += ['-liconv']
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
has_ctpp2_dep = true
|
|
||||||
ctpp2_dep = declare_dependency(link_args:ctpp2_link_args)
|
|
||||||
else
|
|
||||||
message('ctpp2/CTPP2Logger.hpp not found. Compiling without CTPP2 support')
|
|
||||||
endif
|
|
||||||
else
|
|
||||||
if not find_library_in_compiler
|
|
||||||
error('For custom ctpp2_prefix_install you need a meson version >=0.31.0')
|
|
||||||
endif
|
|
||||||
ctpp2_include_path = ctpp2_prefix_install + '/include'
|
|
||||||
ctpp2_include_args = ['-I'+ctpp2_include_path]
|
|
||||||
if compiler.has_header('ctpp2/CTPP2Logger.hpp', args:ctpp2_include_args)
|
|
||||||
ctpp2_include_dir = include_directories(ctpp2_include_path, is_system:true)
|
|
||||||
ctpp2_lib_path = join_paths(ctpp2_prefix_install, get_option('libdir'))
|
|
||||||
message(ctpp2_lib_path)
|
|
||||||
ctpp2_lib = compiler.find_library('ctpp2', dirs:ctpp2_lib_path, required:false)
|
|
||||||
if not ctpp2_lib.found()
|
|
||||||
ctpp2_lib_path = join_paths(ctpp2_prefix_install, 'lib')
|
|
||||||
message(ctpp2_lib_path)
|
|
||||||
ctpp2_lib = compiler.find_library('ctpp2', dirs:ctpp2_lib_path)
|
|
||||||
endif
|
|
||||||
ctpp2_link_args = ['-L'+ctpp2_lib_path, '-lctpp2']
|
|
||||||
if meson.is_cross_build() and host_machine.system() == 'windows'
|
|
||||||
iconv_lib = compiler.find_library('iconv', required:false)
|
|
||||||
if iconv_lib.found()
|
|
||||||
ctpp2_link_args += ['-liconv']
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
has_ctpp2_dep = true
|
|
||||||
ctpp2_dep = declare_dependency(include_directories:ctpp2_include_dir, link_args:ctpp2_link_args)
|
|
||||||
else
|
|
||||||
message('ctpp2/CTPP2Logger.hpp not found. Compiling without CTPP2 support')
|
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
xapian_dep = dependency('xapian-core', required:false, static:static_deps)
|
all_deps = [thread_dep, libicu_dep, libzim_dep, pugixml_dep, libcurl_dep, microhttpd_dep, zlib_dep, xapian_dep]
|
||||||
|
|
||||||
all_deps = [thread_dep, libicu_dep, libzim_dep, xapian_dep, pugixml_dep, libcurl_dep]
|
inc = include_directories('include', extra_include)
|
||||||
if has_ctpp2_dep
|
|
||||||
all_deps += [ctpp2_dep]
|
|
||||||
endif
|
|
||||||
|
|
||||||
inc = include_directories('include')
|
|
||||||
|
|
||||||
conf = configuration_data()
|
conf = configuration_data()
|
||||||
conf.set('VERSION', '"@0@"'.format(meson.project_version()))
|
conf.set('LIBKIWIX_VERSION', '"@0@"'.format(meson.project_version()))
|
||||||
conf.set('ENABLE_CTPP2', has_ctpp2_dep)
|
|
||||||
|
|
||||||
if build_machine.system() == 'windows'
|
if build_machine.system() == 'windows'
|
||||||
extra_link_args = ['-lshlwapi', '-lwinmm']
|
extra_link_args = ['-lshlwapi', '-lwinmm']
|
||||||
@@ -108,18 +69,11 @@ subdir('scripts')
|
|||||||
subdir('static')
|
subdir('static')
|
||||||
subdir('src')
|
subdir('src')
|
||||||
subdir('test')
|
subdir('test')
|
||||||
|
if get_option('doc')
|
||||||
pkg_requires = ['libzim', 'icu-i18n', 'pugixml', 'libcurl']
|
subdir('docs')
|
||||||
if xapian_dep.found()
|
|
||||||
pkg_requires += ['xapian-core']
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if has_ctpp2_dep
|
pkg_requires = ['libzim', 'icu-i18n', 'pugixml', 'libcurl', 'libmicrohttpd', 'xapian-core']
|
||||||
extra_libs += ctpp2_link_args
|
|
||||||
if ctpp2_include_path != ''
|
|
||||||
extra_cflags = ' -I'+ctpp2_include_path
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
pkg_conf = configuration_data()
|
pkg_conf = configuration_data()
|
||||||
pkg_conf.set('prefix', get_option('prefix'))
|
pkg_conf.set('prefix', get_option('prefix'))
|
||||||
|
|||||||
+4
-4
@@ -1,4 +1,4 @@
|
|||||||
option('ctpp2-install-prefix', type : 'string', value : '',
|
option('static-linkage', type : 'boolean', value : false,
|
||||||
description : 'Prefix where ctpp libs has been installed')
|
description : 'Link statically with the dependencies.')
|
||||||
option('android', type : 'boolean', value : false,
|
option('doc', type : 'boolean', value : false,
|
||||||
description : 'Do we make a kiwix-lib for android')
|
description : 'Build the documentations.')
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
|
|
||||||
ctpp2c=$1
|
|
||||||
SOURCE=$(pwd)/$2
|
|
||||||
DEST=$3
|
|
||||||
|
|
||||||
$ctpp2c $SOURCE $DEST
|
|
||||||
Executable
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
# Compute 'src' path
|
||||||
|
SCRIPT_DIR=$(dirname "$0")
|
||||||
|
REPO_DIR=$(readlink -f "$SCRIPT_DIR"/..)
|
||||||
|
DIRS="src include"
|
||||||
|
|
||||||
|
# Apply formating to all *.cpp and *.h files
|
||||||
|
cd "$REPO_DIR"
|
||||||
|
for FILE in $(find $DIRS -name '*.h' -o -name '*.cpp')
|
||||||
|
do
|
||||||
|
echo $FILE
|
||||||
|
clang-format -i -style=file "$FILE"
|
||||||
|
done
|
||||||
Executable
+148
@@ -0,0 +1,148 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
'''
|
||||||
|
Copyright 2022 Veloman Yunkan <veloman.yunkan@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 3 of the License, or any
|
||||||
|
later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
02110-1301, USA.
|
||||||
|
'''
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
|
||||||
|
def to_identifier(name):
|
||||||
|
ident = re.sub(r'[^0-9a-zA-Z]', '_', name)
|
||||||
|
if ident[0].isnumeric():
|
||||||
|
return "_"+ident
|
||||||
|
return ident
|
||||||
|
|
||||||
|
def lang_code(filename):
|
||||||
|
filename = os.path.basename(filename)
|
||||||
|
lang = to_identifier(os.path.splitext(filename)[0])
|
||||||
|
print(filename, '->', lang)
|
||||||
|
return lang
|
||||||
|
|
||||||
|
from string import Template
|
||||||
|
|
||||||
|
def expand_cxx_template(t, **kwargs):
|
||||||
|
return Template(t).substitute(**kwargs)
|
||||||
|
|
||||||
|
def cxx_string_literal(s):
|
||||||
|
# Taking advantage of the fact the JSON string escape rules match
|
||||||
|
# those of C++
|
||||||
|
return 'u8' + json.dumps(s)
|
||||||
|
|
||||||
|
string_table_cxx_template = '''
|
||||||
|
const I18nString $TABLE_NAME[] = {
|
||||||
|
$TABLE_ENTRIES
|
||||||
|
};
|
||||||
|
'''
|
||||||
|
|
||||||
|
lang_table_entry_cxx_template = '''
|
||||||
|
{
|
||||||
|
$LANG_STRING_LITERAL,
|
||||||
|
ARRAY_ELEMENT_COUNT($STRING_TABLE_NAME),
|
||||||
|
$STRING_TABLE_NAME
|
||||||
|
}'''
|
||||||
|
|
||||||
|
cxxfile_template = '''// This file is automatically generated. Do not modify it.
|
||||||
|
|
||||||
|
#include "server/i18n.h"
|
||||||
|
|
||||||
|
namespace kiwix {
|
||||||
|
namespace i18n {
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
$STRING_DATA
|
||||||
|
|
||||||
|
} // unnamed namespace
|
||||||
|
|
||||||
|
#define ARRAY_ELEMENT_COUNT(a) (sizeof(a)/sizeof(a[0]))
|
||||||
|
|
||||||
|
extern const I18nStringTable stringTables[] = {
|
||||||
|
$LANG_TABLE
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const size_t langCount = $LANG_COUNT;
|
||||||
|
|
||||||
|
} // namespace i18n
|
||||||
|
} // namespace kiwix
|
||||||
|
'''
|
||||||
|
|
||||||
|
class Resource:
|
||||||
|
def __init__(self, filename):
|
||||||
|
filename = filename.strip()
|
||||||
|
self.filename = filename
|
||||||
|
self.lang_code = lang_code(filename)
|
||||||
|
with open(filename, 'r', encoding='utf-8') as f:
|
||||||
|
self.data = f.read()
|
||||||
|
|
||||||
|
def get_string_table_name(self):
|
||||||
|
return "string_table_for_" + self.lang_code
|
||||||
|
|
||||||
|
def get_string_table(self):
|
||||||
|
table_entries = ",\n ".join(self.get_string_table_entries())
|
||||||
|
return expand_cxx_template(string_table_cxx_template,
|
||||||
|
TABLE_NAME=self.get_string_table_name(),
|
||||||
|
TABLE_ENTRIES=table_entries)
|
||||||
|
|
||||||
|
def get_string_table_entries(self):
|
||||||
|
d = json.loads(self.data)
|
||||||
|
for k in sorted(d.keys()):
|
||||||
|
if k != "@metadata":
|
||||||
|
key_string = cxx_string_literal(k)
|
||||||
|
value_string = cxx_string_literal(d[k])
|
||||||
|
yield '{ ' + key_string + ', ' + value_string + ' }'
|
||||||
|
|
||||||
|
def get_lang_table_entry(self):
|
||||||
|
return expand_cxx_template(lang_table_entry_cxx_template,
|
||||||
|
LANG_STRING_LITERAL=cxx_string_literal(self.lang_code),
|
||||||
|
STRING_TABLE_NAME=self.get_string_table_name())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def gen_c_file(resources):
|
||||||
|
string_data = []
|
||||||
|
lang_table = []
|
||||||
|
for r in resources:
|
||||||
|
string_data.append(r.get_string_table())
|
||||||
|
lang_table.append(r.get_lang_table_entry())
|
||||||
|
|
||||||
|
return expand_cxx_template(cxxfile_template,
|
||||||
|
STRING_DATA="\n".join(string_data),
|
||||||
|
LANG_TABLE=",\n ".join(lang_table),
|
||||||
|
LANG_COUNT=len(resources)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--cxxfile',
|
||||||
|
required=True,
|
||||||
|
help='The Cpp file name to generate')
|
||||||
|
parser.add_argument('i18n_resource_files', nargs='+',
|
||||||
|
help='The list of resources to compile.')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
resources = [Resource(filename) for filename in args.i18n_resource_files]
|
||||||
|
|
||||||
|
with open(args.cxxfile, 'w') as f:
|
||||||
|
f.write(gen_c_file(resources))
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
.TH KIWIX-COMPILE-I18N "1" "January 2022" "Kiwix" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
kiwix-compile-i18n \- helper to compile Kiwix i18n (internationalization) data
|
||||||
|
.SH SYNOPSIS
|
||||||
|
\fBkiwix\-compile\-i18n\fR [\-h] \-\-cxxfile CXXFILE i18n_resource_files ...\fR
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.TP
|
||||||
|
i18n_resource_files ...
|
||||||
|
The list of i18n resources to compile.
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR, \fB\-\-help\fR
|
||||||
|
show a help message and exit
|
||||||
|
.TP
|
||||||
|
\fB\-\-cxxfile\fR CXXFILE
|
||||||
|
The Cpp file name to generate
|
||||||
|
.TP
|
||||||
|
.SH AUTHOR
|
||||||
|
Veloman Yunkan <veloman.yunkan@gmail.com>
|
||||||
@@ -52,15 +52,21 @@ resource_getter_template = """
|
|||||||
return RESOURCE::{identifier};
|
return RESOURCE::{identifier};
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
resource_cacheid_getter_template = """
|
||||||
|
if (name == "{common_name}")
|
||||||
|
return "{cacheid}";
|
||||||
|
"""
|
||||||
|
|
||||||
resource_decl_template = """{namespaces_open}
|
resource_decl_template = """{namespaces_open}
|
||||||
extern const std::string {identifier};
|
extern const std::string {identifier};
|
||||||
{namespaces_close}"""
|
{namespaces_close}"""
|
||||||
|
|
||||||
class Resource:
|
class Resource:
|
||||||
def __init__(self, base_dirs, filename):
|
def __init__(self, base_dirs, filename, cacheid=None):
|
||||||
filename = filename.strip()
|
filename = filename
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.identifier = full_identifier(filename)
|
self.identifier = full_identifier(filename)
|
||||||
|
self.cacheid = cacheid
|
||||||
found = False
|
found = False
|
||||||
for base_dir in base_dirs:
|
for base_dir in base_dirs:
|
||||||
try:
|
try:
|
||||||
@@ -71,7 +77,7 @@ class Resource:
|
|||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
continue
|
continue
|
||||||
if not found:
|
if not found:
|
||||||
raise Exception("Impossible to found {}".format(filename))
|
raise Exception("Resource not found: {}".format(filename))
|
||||||
|
|
||||||
def dump_impl(self):
|
def dump_impl(self):
|
||||||
nb_row = len(self.data)//16 + (1 if len(self.data) % 16 else 0)
|
nb_row = len(self.data)//16 + (1 if len(self.data) % 16 else 0)
|
||||||
@@ -81,28 +87,34 @@ class Resource:
|
|||||||
data_identifier="_".join([""]+self.identifier),
|
data_identifier="_".join([""]+self.identifier),
|
||||||
resource_content=",\n ".join(", ".join("{:#04x}".format(i) for i in r) for r in sliced),
|
resource_content=",\n ".join(", ".join("{:#04x}".format(i) for i in r) for r in sliced),
|
||||||
resource_len=len(self.data),
|
resource_len=len(self.data),
|
||||||
namespaces_open=" ".join("namespace {} {{".format(id) for id in self.identifier[:-1]),
|
namespaces_open=" ".join("namespace {} {{".format(id) for id in self.identifier[:-1]),
|
||||||
namespaces_close=" ".join(["}"]*(len(self.identifier)-1)),
|
namespaces_close=" ".join(["}"]*(len(self.identifier)-1)),
|
||||||
identifier=self.identifier[-1],
|
identifier=self.identifier[-1],
|
||||||
env_identifier="RES_"+"_".join(self.identifier)+"_PATH"
|
env_identifier="RES_"+"_".join(self.identifier)+"_PATH"
|
||||||
)
|
)
|
||||||
|
|
||||||
def dump_getter(self):
|
def dump_getter(self):
|
||||||
return resource_getter_template.format(
|
return resource_getter_template.format(
|
||||||
common_name=self.filename,
|
common_name=self.filename,
|
||||||
identifier="::".join(self.identifier)
|
identifier="::".join(self.identifier)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def dump_cacheid_getter(self):
|
||||||
|
return resource_cacheid_getter_template.format(
|
||||||
|
common_name=self.filename,
|
||||||
|
cacheid=self.cacheid
|
||||||
|
)
|
||||||
|
|
||||||
def dump_decl(self):
|
def dump_decl(self):
|
||||||
return resource_decl_template.format(
|
return resource_decl_template.format(
|
||||||
namespaces_open=" ".join("namespace {} {{".format(id) for id in self.identifier[:-1]),
|
namespaces_open=" ".join("namespace {} {{".format(id) for id in self.identifier[:-1]),
|
||||||
namespaces_close=" ".join(["}"]*(len(self.identifier)-1)),
|
namespaces_close=" ".join(["}"]*(len(self.identifier)-1)),
|
||||||
identifier=self.identifier[-1]
|
identifier=self.identifier[-1]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
master_c_template = """//This file is automaically generated. Do not modify it.
|
|
||||||
|
master_c_template = """//This file is automatically generated. Do not modify it.
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -113,7 +125,7 @@ static std::string init_resource(const char* name, const unsigned char* content,
|
|||||||
char * resPath = getenv(name);
|
char * resPath = getenv(name);
|
||||||
if (NULL == resPath)
|
if (NULL == resPath)
|
||||||
return std::string(reinterpret_cast<const char*>(content), len);
|
return std::string(reinterpret_cast<const char*>(content), len);
|
||||||
|
|
||||||
std::ifstream ifs(resPath);
|
std::ifstream ifs(resPath);
|
||||||
if (!ifs.good())
|
if (!ifs.good())
|
||||||
return std::string(reinterpret_cast<const char*>(content), len);
|
return std::string(reinterpret_cast<const char*>(content), len);
|
||||||
@@ -123,7 +135,12 @@ static std::string init_resource(const char* name, const unsigned char* content,
|
|||||||
|
|
||||||
const std::string& getResource_{basename}(const std::string& name) {{
|
const std::string& getResource_{basename}(const std::string& name) {{
|
||||||
{RESOURCES_GETTER}
|
{RESOURCES_GETTER}
|
||||||
throw ResourceNotFound("Resource not found.");
|
throw ResourceNotFound("Resource not found: " + name);
|
||||||
|
}}
|
||||||
|
|
||||||
|
const char* getResourceCacheId_{basename}(const std::string& name) {{
|
||||||
|
{RESOURCE_CACHEID_GETTER}
|
||||||
|
return nullptr;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
{RESOURCES}
|
{RESOURCES}
|
||||||
@@ -134,10 +151,11 @@ def gen_c_file(resources, basename):
|
|||||||
return master_c_template.format(
|
return master_c_template.format(
|
||||||
RESOURCES="\n\n".join(r.dump_impl() for r in resources),
|
RESOURCES="\n\n".join(r.dump_impl() for r in resources),
|
||||||
RESOURCES_GETTER="\n\n".join(r.dump_getter() for r in resources),
|
RESOURCES_GETTER="\n\n".join(r.dump_getter() for r in resources),
|
||||||
|
RESOURCE_CACHEID_GETTER="\n\n".join(r.dump_cacheid_getter() for r in resources if r.cacheid is not None),
|
||||||
include_file=basename,
|
include_file=basename,
|
||||||
basename=to_identifier(basename)
|
basename=to_identifier(basename)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
master_h_template = """//This file is automaically generated. Do not modify it.
|
master_h_template = """//This file is automaically generated. Do not modify it.
|
||||||
@@ -159,8 +177,10 @@ class ResourceNotFound : public std::runtime_error {{
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
const std::string& getResource_{basename}(const std::string& name);
|
const std::string& getResource_{basename}(const std::string& name);
|
||||||
|
const char* getResourceCacheId_{basename}(const std::string& name);
|
||||||
|
|
||||||
#define getResource(a) (getResource_{basename}(a))
|
#define getResource(a) (getResource_{basename}(a))
|
||||||
|
#define getResourceCacheId(a) (getResourceCacheId_{basename}(a))
|
||||||
|
|
||||||
#endif // KIWIX_{BASENAME}
|
#endif // KIWIX_{BASENAME}
|
||||||
|
|
||||||
@@ -182,15 +202,17 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument('--source_dir',
|
parser.add_argument('--source_dir',
|
||||||
help="Additional directory where to look for resources.",
|
help="Additional directory where to look for resources.",
|
||||||
action='append')
|
action='append')
|
||||||
parser.add_argument('resource_file',
|
parser.add_argument('resource_files', nargs='+',
|
||||||
help='The list of resources to compile.')
|
help='The list of resources to compile.')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
base_dir = os.path.dirname(os.path.realpath(args.resource_file))
|
|
||||||
source_dir = args.source_dir or []
|
source_dir = args.source_dir or []
|
||||||
with open(args.resource_file, 'r') as f:
|
resources = []
|
||||||
resources = [Resource([base_dir]+source_dir, filename)
|
for resfile in args.resource_files:
|
||||||
for filename in f.readlines()]
|
base_dir = os.path.dirname(os.path.realpath(resfile))
|
||||||
|
with open(resfile, 'r') as f:
|
||||||
|
resources += [Resource([base_dir]+source_dir, *line.strip().split())
|
||||||
|
for line in f.readlines()]
|
||||||
|
|
||||||
h_identifier = to_identifier(os.path.basename(args.hfile))
|
h_identifier = to_identifier(os.path.basename(args.hfile))
|
||||||
with open(args.hfile, 'w') as f:
|
with open(args.hfile, 'w') as f:
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
.TH KIWIX-COMPILE-RESOURCES "1" "August 2017" "Kiwix" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
kiwix-compile-resources \- helper to compile and generate some Kiwix resources
|
||||||
|
.SH SYNOPSIS
|
||||||
|
\fBkiwix\-compile\-resources\fR [\-h] [\-\-cxxfile CXXFILE] [\-\-hfile HFILE] resource_file ...\fR
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.TP
|
||||||
|
resource_file
|
||||||
|
The list of resources to compile.
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR, \fB\-\-help\fR
|
||||||
|
show a help message and exit
|
||||||
|
.TP
|
||||||
|
\fB\-\-cxxfile\fR CXXFILE
|
||||||
|
The Cpp file name to generate
|
||||||
|
.TP
|
||||||
|
\fB\-\-hfile\fR HFILE
|
||||||
|
The h file name to generate
|
||||||
|
.SH AUTHOR
|
||||||
|
Matthieu Gautier <mgautier@kymeria.fr>
|
||||||
Executable
+135
@@ -0,0 +1,135 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
'''
|
||||||
|
Copyright 2022 Veloman Yunkan <veloman.yunkan@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 3 of the License, or any
|
||||||
|
later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
02110-1301, USA.
|
||||||
|
'''
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
|
||||||
|
def read_resource_file(resource_file_path):
|
||||||
|
with open(resource_file_path, 'r') as f:
|
||||||
|
return [line.strip() for line in f]
|
||||||
|
|
||||||
|
def list_resources(resource_file_path):
|
||||||
|
for resource_path in read_resource_file(resource_file_path):
|
||||||
|
print(resource_path)
|
||||||
|
|
||||||
|
def compute_resource_revision(resource_path):
|
||||||
|
with open(os.path.join(OUT_DIR, resource_path), 'rb') as f:
|
||||||
|
return hashlib.sha1(f.read()).hexdigest()[:8]
|
||||||
|
|
||||||
|
resource_revisions = {}
|
||||||
|
|
||||||
|
def get_resource_revision(res):
|
||||||
|
if not res in resource_revisions:
|
||||||
|
preprocess_resource(res)
|
||||||
|
resource_revisions[res] = compute_resource_revision(res)
|
||||||
|
return resource_revisions[res]
|
||||||
|
|
||||||
|
RESOURCE_WITH_CACHEID_URL_PATTERN=r'(?P<pre>.*/(?P<resource>skin/[^"?]+)\?)KIWIXCACHEID(?P<post>[^"]*)'
|
||||||
|
|
||||||
|
def set_cacheid(resource_matchobj):
|
||||||
|
pre = resource_matchobj.group('pre')
|
||||||
|
resource = resource_matchobj.group('resource')
|
||||||
|
post = resource_matchobj.group('post')
|
||||||
|
cacheid = 'cacheid=' + get_resource_revision(resource)
|
||||||
|
return pre + cacheid + post
|
||||||
|
|
||||||
|
def preprocess_text(s):
|
||||||
|
if 'KIWIXCACHEID' in s:
|
||||||
|
s = re.sub(RESOURCE_WITH_CACHEID_URL_PATTERN, set_cacheid, s)
|
||||||
|
assert not 'KIWIXCACHEID' in s
|
||||||
|
return s
|
||||||
|
|
||||||
|
def get_preprocessed_resource(srcpath):
|
||||||
|
"""Get the transformed content of a resource
|
||||||
|
|
||||||
|
If the resource at srcpath is modified by preprocessing then this function
|
||||||
|
returns the transformed content of the resource. Otherwise it returns None.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(srcpath, 'r') as resource_file:
|
||||||
|
content = resource_file.read()
|
||||||
|
preprocessed_content = preprocess_text(content)
|
||||||
|
return preprocessed_content if preprocessed_content != content else None
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
# It was a binary resource
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def symlink_resource(src, resource_path):
|
||||||
|
if os.path.exists(resource_path):
|
||||||
|
if os.path.islink(resource_path) and os.readlink(resource_path) == src:
|
||||||
|
return
|
||||||
|
os.remove(resource_path)
|
||||||
|
os.symlink(src, resource_path)
|
||||||
|
|
||||||
|
def preprocess_resource(resource_path):
|
||||||
|
print('Preprocessing', resource_path, '...')
|
||||||
|
resource_dir = os.path.dirname(resource_path)
|
||||||
|
if resource_dir != '':
|
||||||
|
os.makedirs(os.path.join(OUT_DIR, resource_dir), exist_ok=True)
|
||||||
|
srcpath = os.path.join(BASE_DIR, resource_path)
|
||||||
|
outpath = os.path.join(OUT_DIR, resource_path)
|
||||||
|
if os.path.exists(outpath):
|
||||||
|
os.remove(outpath)
|
||||||
|
preprocessed_content = get_preprocessed_resource(srcpath)
|
||||||
|
if preprocessed_content is None:
|
||||||
|
symlink_resource(srcpath, outpath)
|
||||||
|
else:
|
||||||
|
with open(outpath, 'w') as target:
|
||||||
|
print(preprocessed_content, end='', file=target)
|
||||||
|
|
||||||
|
|
||||||
|
def copy_resource_list_file(src_path, dst_path):
|
||||||
|
with open(src_path, 'r') as src:
|
||||||
|
with open(dst_path, 'w') as dst:
|
||||||
|
for line in src:
|
||||||
|
res = line.strip()
|
||||||
|
if line.startswith("skin/") and res in resource_revisions:
|
||||||
|
dst.write(res + " " + resource_revisions[res] + "\n")
|
||||||
|
else:
|
||||||
|
dst.write(line)
|
||||||
|
|
||||||
|
def preprocess_resources(resource_file_path):
|
||||||
|
resource_filename = os.path.basename(resource_file_path)
|
||||||
|
for resource in read_resource_file(resource_file_path):
|
||||||
|
if resource.startswith('skin/'):
|
||||||
|
get_resource_revision(resource)
|
||||||
|
else:
|
||||||
|
preprocess_resource(resource)
|
||||||
|
copy_resource_list_file(resource_file_path, os.path.join(OUT_DIR, resource_filename))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
commands = parser.add_mutually_exclusive_group()
|
||||||
|
commands.add_argument('--list-all', action='store_true')
|
||||||
|
commands.add_argument('--preprocess', action='store_true')
|
||||||
|
parser.add_argument('--outdir')
|
||||||
|
parser.add_argument('resource_file')
|
||||||
|
args = parser.parse_args()
|
||||||
|
BASE_DIR = os.path.dirname(os.path.realpath(args.resource_file))
|
||||||
|
OUT_DIR = args.outdir
|
||||||
|
|
||||||
|
if args.list_all:
|
||||||
|
list_resources(args.resource_file)
|
||||||
|
elif args.preprocess:
|
||||||
|
preprocess_resources(args.resource_file)
|
||||||
+9
-1
@@ -1,5 +1,13 @@
|
|||||||
|
|
||||||
|
res_manager = find_program('kiwix-resources')
|
||||||
res_compiler = find_program('kiwix-compile-resources')
|
res_compiler = find_program('kiwix-compile-resources')
|
||||||
intermediate_ctpp2c = find_program('ctpp2c.sh')
|
|
||||||
|
|
||||||
install_data(res_compiler.path(), install_dir:get_option('bindir'))
|
install_data(res_compiler.path(), install_dir:get_option('bindir'))
|
||||||
|
|
||||||
|
install_man('kiwix-compile-resources.1')
|
||||||
|
|
||||||
|
i18n_compiler = find_program('kiwix-compile-i18n')
|
||||||
|
|
||||||
|
install_data(i18n_compiler.path(), install_dir:get_option('bindir'))
|
||||||
|
|
||||||
|
install_man('kiwix-compile-i18n.1')
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
|
|
||||||
package="kiwix.org.kiwixlib"
|
|
||||||
>
|
|
||||||
|
|
||||||
<application android:allowBackup="true"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:supportsRtl="true"
|
|
||||||
>
|
|
||||||
|
|
||||||
</application>
|
|
||||||
|
|
||||||
</manifest>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
* Copyright (C) 2017 Matthieu Gautier <mgautier@kymeria.fr>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include "org_kiwix_kiwixlib_JNIKiwix.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "unicode/putil.h"
|
|
||||||
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
pthread_mutex_t globalLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_setDataDirectory(
|
|
||||||
JNIEnv* env, jobject obj, jstring dirStr)
|
|
||||||
{
|
|
||||||
std::string cPath = jni2c(dirStr, env);
|
|
||||||
|
|
||||||
Lock l;
|
|
||||||
try {
|
|
||||||
u_setDataDirectory(cPath.c_str());
|
|
||||||
} catch (...) {
|
|
||||||
std::cerr << "Unable to set data directory " << cPath << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,424 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
* Copyright (C) 2017 Matthieu Gautier <mgautier@kymeria.fr>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <zim/file.h>
|
|
||||||
#include <android/log.h>
|
|
||||||
#include "org_kiwix_kiwixlib_JNIKiwixReader.h"
|
|
||||||
|
|
||||||
#include "common/base64.h"
|
|
||||||
#include "reader.h"
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
/* Kiwix Reader JNI functions */
|
|
||||||
JNIEXPORT jlong JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getNativeReader(
|
|
||||||
JNIEnv* env, jobject obj, jstring filename)
|
|
||||||
{
|
|
||||||
std::string cPath = jni2c(filename, env);
|
|
||||||
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, "kiwix", "Attempting to create reader with: %s", cPath.c_str());
|
|
||||||
Lock l;
|
|
||||||
try {
|
|
||||||
kiwix::Reader* reader = new kiwix::Reader(cPath);
|
|
||||||
return reinterpret_cast<jlong>(new Handle<kiwix::Reader>(reader));
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, "kiwix", "Error opening ZIM file");
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, "kiwix", e.what());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_dispose(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
Handle<kiwix::Reader>::dispose(env, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define READER (Handle<kiwix::Reader>::getHandle(env, obj))
|
|
||||||
|
|
||||||
/* Kiwix library functions */
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getMainPage(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
jstring url;
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string cUrl = READER->getMainPage().getPath();
|
|
||||||
url = c2jni(cUrl, env);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM main page");
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
url = NULL;
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getId(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
jstring id;
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string cId = READER->getId();
|
|
||||||
id = c2jni(cId, env);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM id");
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
id = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getFileSize(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
jint size;
|
|
||||||
|
|
||||||
try {
|
|
||||||
int cSize = READER->getFileSize();
|
|
||||||
size = c2jni(cSize);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM file size");
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getCreator(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
jstring creator;
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string cCreator = READER->getCreator();
|
|
||||||
creator = c2jni(cCreator, env);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM creator");
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
creator = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return creator;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getPublisher(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
jstring publisher;
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string cPublisher = READER->getPublisher();
|
|
||||||
publisher = c2jni(cPublisher, env);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM publish");
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
publisher = NULL;
|
|
||||||
}
|
|
||||||
return publisher;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getName(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
jstring name;
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string cName = READER->getName();
|
|
||||||
name = c2jni(cName, env);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM name");
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
name = NULL;
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getFavicon(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
jstring favicon;
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string cContent;
|
|
||||||
std::string cMime;
|
|
||||||
READER->getFavicon(cContent, cMime);
|
|
||||||
favicon = c2jni(
|
|
||||||
base64_encode(cContent),
|
|
||||||
env);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM favicon");
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
favicon = NULL;
|
|
||||||
}
|
|
||||||
return favicon;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getDate(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
jstring date;
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string cDate = READER->getDate();
|
|
||||||
date = c2jni(cDate, env);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM date");
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
date = NULL;
|
|
||||||
}
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getLanguage(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
jstring language;
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string cLanguage = READER->getLanguage();
|
|
||||||
language = c2jni(cLanguage, env);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM language");
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
language = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return language;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getMimeType(
|
|
||||||
JNIEnv* env, jobject obj, jstring url)
|
|
||||||
{
|
|
||||||
jstring mimeType;
|
|
||||||
|
|
||||||
std::string cUrl = jni2c(url, env);
|
|
||||||
try {
|
|
||||||
auto entry = READER->getEntryFromEncodedPath(cUrl);
|
|
||||||
auto cMimeType = entry.getMimetype();
|
|
||||||
mimeType = c2jni(cMimeType, env);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get mime-type for url: %s", cUrl.c_str());
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
mimeType = NULL;
|
|
||||||
}
|
|
||||||
return mimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getContent(
|
|
||||||
JNIEnv* env, jobject obj, jstring url, jobject titleObj, jobject mimeTypeObj, jobject sizeObj)
|
|
||||||
{
|
|
||||||
/* Default values */
|
|
||||||
setStringObjValue("", titleObj, env);
|
|
||||||
setStringObjValue("", mimeTypeObj, env);
|
|
||||||
setIntObjValue(0, sizeObj, env);
|
|
||||||
jbyteArray data = env->NewByteArray(0);
|
|
||||||
|
|
||||||
/* Retrieve the content */
|
|
||||||
std::string cUrl = jni2c(url, env);
|
|
||||||
unsigned int cSize = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
auto entry = READER->getEntryFromEncodedPath(cUrl);
|
|
||||||
entry = entry.getFinalEntry();
|
|
||||||
cSize = entry.getSize();
|
|
||||||
setIntObjValue(cSize, sizeObj, env);
|
|
||||||
|
|
||||||
data = env->NewByteArray(cSize);
|
|
||||||
env->SetByteArrayRegion(
|
|
||||||
data, 0, cSize, reinterpret_cast<const jbyte*>(entry.getBlob().data()));
|
|
||||||
|
|
||||||
setStringObjValue(entry.getMimetype(), mimeTypeObj, env);
|
|
||||||
setStringObjValue(entry.getTitle(), titleObj, env);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get content for url: %s", cUrl.c_str());
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getContentPart(
|
|
||||||
JNIEnv* env, jobject obj, jstring url, jint offset, jint len, jobject sizeObj)
|
|
||||||
{
|
|
||||||
jbyteArray data = env->NewByteArray(0);
|
|
||||||
setIntObjValue(0, sizeObj, env);
|
|
||||||
|
|
||||||
/* Default values */
|
|
||||||
/* Retrieve the content */
|
|
||||||
std::string cUrl = jni2c(url, env);
|
|
||||||
unsigned int cOffset = jni2c(offset);
|
|
||||||
unsigned int cLen = jni2c(len);
|
|
||||||
try {
|
|
||||||
auto entry = READER->getEntryFromEncodedPath(cUrl);
|
|
||||||
entry = entry.getFinalEntry();
|
|
||||||
|
|
||||||
if (cLen == 0) {
|
|
||||||
setIntObjValue(entry.getSize(), sizeObj, env);
|
|
||||||
} else if (cOffset+cLen < entry.getSize()) {
|
|
||||||
auto blob = entry.getBlob(cOffset, cLen);
|
|
||||||
data = env->NewByteArray(cLen);
|
|
||||||
env->SetByteArrayRegion(
|
|
||||||
data, 0, cLen, reinterpret_cast<const jbyte*>(blob.data()));
|
|
||||||
setIntObjValue(cLen, sizeObj, env);
|
|
||||||
}
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get partial content for url: %s (%u : %u)", cUrl.c_str(), cOffset, cLen);
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jobject JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getDirectAccessInformation(
|
|
||||||
JNIEnv* env, jobject obj, jstring url)
|
|
||||||
{
|
|
||||||
jclass classPair = env->FindClass("org/kiwix/kiwixlib/Pair");
|
|
||||||
jmethodID midPairinit = env->GetMethodID(classPair, "<init>", "()V");
|
|
||||||
jobject pair = env->NewObject(classPair, midPairinit);
|
|
||||||
setPairObjValue("", 0, pair, env);
|
|
||||||
|
|
||||||
std::string cUrl = jni2c(url, env);
|
|
||||||
try {
|
|
||||||
auto entry = READER->getEntryFromEncodedPath(cUrl);
|
|
||||||
entry = entry.getFinalEntry();
|
|
||||||
auto part_info = entry.getDirectAccessInfo();
|
|
||||||
setPairObjValue(part_info.first, part_info.second, pair, env);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get direct access info for url: %s", cUrl.c_str());
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
}
|
|
||||||
return pair;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_searchSuggestions(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring prefix,
|
|
||||||
jint count)
|
|
||||||
{
|
|
||||||
jboolean retVal = JNI_FALSE;
|
|
||||||
std::string cPrefix = jni2c(prefix, env);
|
|
||||||
unsigned int cCount = jni2c(count);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (READER->searchSuggestionsSmart(cPrefix, cCount)) {
|
|
||||||
retVal = JNI_TRUE;
|
|
||||||
}
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, "kiwix", "Unable to get search results for pattern: %s", cPrefix.c_str());
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, "kiwix", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getNextSuggestion(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jobject titleObj)
|
|
||||||
{
|
|
||||||
jboolean retVal = JNI_FALSE;
|
|
||||||
std::string cTitle;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (READER->getNextSuggestion(cTitle)) {
|
|
||||||
setStringObjValue(cTitle, titleObj, env);
|
|
||||||
retVal = JNI_TRUE;
|
|
||||||
}
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, "kiwix", "Unable to get next suggestion");
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, "kiwix", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getPageUrlFromTitle(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring title,
|
|
||||||
jobject urlObj)
|
|
||||||
{
|
|
||||||
std::string cTitle = jni2c(title, env);
|
|
||||||
|
|
||||||
try {
|
|
||||||
auto entry = READER->getEntryFromTitle(cTitle);
|
|
||||||
entry = entry.getFinalEntry();
|
|
||||||
setStringObjValue(entry.getPath(), urlObj, env);
|
|
||||||
return JNI_TRUE;
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, "kiwix", "Unable to get url for title %s: ", cTitle.c_str());
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, "kiwix", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getTitle(
|
|
||||||
JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
jstring title;
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string cTitle = READER->getTitle();
|
|
||||||
title = c2jni(cTitle, env);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get zim title");
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
title = NULL;
|
|
||||||
}
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getDescription(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
jstring description;
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string cDescription = READER->getDescription();
|
|
||||||
description = c2jni(cDescription, env);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get zim description");
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
description = NULL;
|
|
||||||
}
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getRandomPage(
|
|
||||||
JNIEnv* env, jobject obj, jobject urlObj)
|
|
||||||
{
|
|
||||||
jboolean retVal = JNI_FALSE;
|
|
||||||
std::string cUrl;
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string cUrl = READER->getRandomPage().getPath();
|
|
||||||
setStringObjValue(cUrl, urlObj, env);
|
|
||||||
retVal = JNI_TRUE;
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get random page");
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
* Copyright (C) 2017 Matthieu Gautier <mgautier@kymeria.fr>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <zim/file.h>
|
|
||||||
#include "org_kiwix_kiwixlib_JNIKiwixSearcher.h"
|
|
||||||
#include "org_kiwix_kiwixlib_JNIKiwixSearcher_Result.h"
|
|
||||||
|
|
||||||
#include "reader.h"
|
|
||||||
#include "searcher.h"
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
#define SEARCHER (Handle<kiwix::Searcher>::getHandle(env, obj))
|
|
||||||
#define RESULT (Handle<kiwix::Result>::getHandle(env, obj))
|
|
||||||
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixSearcher_dispose(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
Handle<kiwix::Searcher>::dispose(env, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Kiwix Reader JNI functions */
|
|
||||||
JNIEXPORT jlong JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixSearcher_getNativeHandle(JNIEnv* env,
|
|
||||||
jobject obj)
|
|
||||||
{
|
|
||||||
kiwix::Searcher* searcher = new kiwix::Searcher();
|
|
||||||
return reinterpret_cast<jlong>(new Handle<kiwix::Searcher>(searcher));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Kiwix library functions */
|
|
||||||
JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwixSearcher_addReader(
|
|
||||||
JNIEnv* env, jobject obj, jobject reader)
|
|
||||||
{
|
|
||||||
auto searcher = SEARCHER;
|
|
||||||
|
|
||||||
searcher->add_reader(*(Handle<kiwix::Reader>::getHandle(env, reader)), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwixSearcher_search(
|
|
||||||
JNIEnv* env, jobject obj, jstring query, jint count)
|
|
||||||
{
|
|
||||||
std::string cquery = jni2c(query, env);
|
|
||||||
unsigned int ccount = jni2c(count);
|
|
||||||
|
|
||||||
SEARCHER->search(cquery, 0, ccount);
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jobject JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixSearcher_getNextResult(JNIEnv* env,
|
|
||||||
jobject obj)
|
|
||||||
{
|
|
||||||
jobject result = nullptr;
|
|
||||||
|
|
||||||
kiwix::Result* cresult = SEARCHER->getNextResult();
|
|
||||||
if (cresult != nullptr) {
|
|
||||||
jclass resultclass
|
|
||||||
= env->FindClass("org/kiwix/kiwixlib/JNIKiwixSearcher$Result");
|
|
||||||
jmethodID ctor = env->GetMethodID(
|
|
||||||
resultclass, "<init>", "(Lorg/kiwix/kiwixlib/JNIKiwixSearcher;JLorg/kiwix/kiwixlib/JNIKiwixSearcher;)V");
|
|
||||||
result = env->NewObject(resultclass, ctor, obj, reinterpret_cast<jlong>(new Handle<kiwix::Result>(cresult)), obj);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_dispose(
|
|
||||||
JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
Handle<kiwix::Result>::dispose(env, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_getUrl(JNIEnv* env,
|
|
||||||
jobject obj)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return c2jni(RESULT->get_url(), env);
|
|
||||||
} catch (...) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_getTitle(JNIEnv* env,
|
|
||||||
jobject obj)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return c2jni(RESULT->get_title(), env);
|
|
||||||
} catch (...) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_getSnippet(JNIEnv* env,
|
|
||||||
jobject obj)
|
|
||||||
{
|
|
||||||
return c2jni(RESULT->get_snippet(), env);
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_getContent(JNIEnv* env,
|
|
||||||
jobject obj)
|
|
||||||
{
|
|
||||||
return c2jni(RESULT->get_content(), env);
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
kiwix_jni = custom_target('jni',
|
|
||||||
input: ['org/kiwix/kiwixlib/JNIKiwix.java',
|
|
||||||
'org/kiwix/kiwixlib/JNIKiwixReader.java',
|
|
||||||
'org/kiwix/kiwixlib/JNIKiwixSearcher.java',
|
|
||||||
'org/kiwix/kiwixlib/JNIKiwixInt.java',
|
|
||||||
'org/kiwix/kiwixlib/JNIKiwixString.java',
|
|
||||||
'org/kiwix/kiwixlib/JNIKiwixBool.java',
|
|
||||||
'org/kiwix/kiwixlib/JNIKiwixException.java',
|
|
||||||
'org/kiwix/kiwixlib/Pair.java'],
|
|
||||||
output: ['org_kiwix_kiwixlib_JNIKiwix.h',
|
|
||||||
'org_kiwix_kiwixlib_JNIKiwixReader.h',
|
|
||||||
'org_kiwix_kiwixlib_JNIKiwixSearcher.h',
|
|
||||||
'org_kiwix_kiwixlib_JNIKiwixSearcher_Result.h'],
|
|
||||||
command:['javac', '-d', '@OUTDIR@', '-h', '@OUTDIR@', '@INPUT@']
|
|
||||||
)
|
|
||||||
|
|
||||||
kiwix_sources += [
|
|
||||||
'android/kiwix.cpp',
|
|
||||||
'android/kiwixreader.cpp',
|
|
||||||
'android/kiwixsearcher.cpp',
|
|
||||||
kiwix_jni]
|
|
||||||
|
|
||||||
install_subdir('org', install_dir: 'kiwix-lib/java')
|
|
||||||
install_subdir('res', install_dir: 'kiwix-lib')
|
|
||||||
install_data('AndroidManifest.xml', install_dir: 'kiwix-lib')
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
* Copyright (C) 2017 Matthieu Gautier <mgautier@kymeria.fr>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.kiwix.kiwixlib;
|
|
||||||
|
|
||||||
import org.kiwix.kiwixlib.JNIKiwixException;
|
|
||||||
import org.kiwix.kiwixlib.JNIKiwixString;
|
|
||||||
import org.kiwix.kiwixlib.JNIKiwixInt;
|
|
||||||
import org.kiwix.kiwixlib.JNIKiwixSearcher;
|
|
||||||
import org.kiwix.kiwixlib.Pair;
|
|
||||||
|
|
||||||
public class JNIKiwixReader
|
|
||||||
{
|
|
||||||
public native String getMainPage();
|
|
||||||
|
|
||||||
public native String getTitle();
|
|
||||||
|
|
||||||
public native String getId();
|
|
||||||
|
|
||||||
public native String getLanguage();
|
|
||||||
|
|
||||||
public native String getMimeType(String url);
|
|
||||||
|
|
||||||
public native byte[] getContent(String url,
|
|
||||||
JNIKiwixString title,
|
|
||||||
JNIKiwixString mimeType,
|
|
||||||
JNIKiwixInt size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getContentPart.
|
|
||||||
*
|
|
||||||
* Get only a part of the content of the article.
|
|
||||||
* Return a byte array of `len` size starting from offset `offset`.
|
|
||||||
* Set `size` to the number of bytes read
|
|
||||||
* (`len` if everything is ok, 0 in case of error).
|
|
||||||
* If `len` == 0, no bytes are read but `size` is set to the total size of the
|
|
||||||
* article.
|
|
||||||
*/
|
|
||||||
public native byte[] getContentPart(String url,
|
|
||||||
int offest,
|
|
||||||
int len,
|
|
||||||
JNIKiwixInt size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getDirectAccessInformation.
|
|
||||||
*
|
|
||||||
* Return information giving where the content is located in the zim file.
|
|
||||||
*
|
|
||||||
* Some contents (binary content) are stored uncompressed in the zim file.
|
|
||||||
* Knowing this information, it could be interesting to directly open
|
|
||||||
* the zim file (or zim part) and directly read the content from it (and so
|
|
||||||
* bypassing the libzim).
|
|
||||||
*
|
|
||||||
* Return a `Pair` (filename, offset) where the content is located.
|
|
||||||
*
|
|
||||||
* If the content cannot be directly accessed (content is compressed or zim
|
|
||||||
* file is cut in the middle of the content), the filename is an empty string
|
|
||||||
* and offset is zero.
|
|
||||||
*/
|
|
||||||
public native Pair getDirectAccessInformation(String url);
|
|
||||||
|
|
||||||
public native boolean searchSuggestions(String prefix, int count);
|
|
||||||
|
|
||||||
public native boolean getNextSuggestion(JNIKiwixString title);
|
|
||||||
|
|
||||||
public native boolean getPageUrlFromTitle(String title, JNIKiwixString url);
|
|
||||||
|
|
||||||
public native String getDescription();
|
|
||||||
|
|
||||||
public native String getDate();
|
|
||||||
|
|
||||||
public native String getFavicon();
|
|
||||||
|
|
||||||
public native String getCreator();
|
|
||||||
|
|
||||||
public native String getPublisher();
|
|
||||||
|
|
||||||
public native String getName();
|
|
||||||
|
|
||||||
public native int getFileSize();
|
|
||||||
|
|
||||||
public native int getArticleCount();
|
|
||||||
|
|
||||||
public native int getMediaCount();
|
|
||||||
|
|
||||||
public native boolean getRandomPage(JNIKiwixString url);
|
|
||||||
|
|
||||||
public JNIKiwixSearcher search(String query, int count)
|
|
||||||
{
|
|
||||||
JNIKiwixSearcher searcher = new JNIKiwixSearcher();
|
|
||||||
searcher.addKiwixReader(this);
|
|
||||||
searcher.search(query, count);
|
|
||||||
return searcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JNIKiwixReader(String filename) throws JNIKiwixException
|
|
||||||
{
|
|
||||||
nativeHandle = getNativeReader(filename);
|
|
||||||
if (nativeHandle == 0) {
|
|
||||||
throw new JNIKiwixException("Cannot open zimfile "+filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public JNIKiwixReader() {
|
|
||||||
|
|
||||||
}
|
|
||||||
public native void dispose();
|
|
||||||
|
|
||||||
private native long getNativeReader(String filename);
|
|
||||||
private long nativeHandle;
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
* Copyright (C) 2017 Matthieu Gautier <mgautier@kymeria.fr>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
package org.kiwix.kiwixlib;
|
|
||||||
|
|
||||||
import org.kiwix.kiwixlib.JNIKiwixReader;
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
public class JNIKiwixSearcher
|
|
||||||
{
|
|
||||||
public class Result
|
|
||||||
{
|
|
||||||
private long nativeHandle;
|
|
||||||
private JNIKiwixSearcher searcher;
|
|
||||||
public Result(long handle, JNIKiwixSearcher _searcher)
|
|
||||||
{
|
|
||||||
nativeHandle = handle;
|
|
||||||
searcher = _searcher;
|
|
||||||
}
|
|
||||||
public native String getUrl();
|
|
||||||
public native String getTitle();
|
|
||||||
public native String getContent();
|
|
||||||
public native String getSnippet();
|
|
||||||
public native void dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public JNIKiwixSearcher()
|
|
||||||
{
|
|
||||||
nativeHandle = getNativeHandle();
|
|
||||||
usedReaders = new Vector();
|
|
||||||
}
|
|
||||||
public native void dispose();
|
|
||||||
|
|
||||||
private native long getNativeHandle();
|
|
||||||
private long nativeHandle;
|
|
||||||
private Vector usedReaders;
|
|
||||||
|
|
||||||
public native void addReader(JNIKiwixReader reader);
|
|
||||||
public void addKiwixReader(JNIKiwixReader reader)
|
|
||||||
{
|
|
||||||
addReader(reader);
|
|
||||||
usedReaders.addElement(reader);
|
|
||||||
};
|
|
||||||
|
|
||||||
public native void search(String query, int count);
|
|
||||||
|
|
||||||
public native Result getNextResult();
|
|
||||||
public native boolean hasMoreResult();
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.kiwix.kiwixlib;
|
|
||||||
|
|
||||||
public class JNIKiwixString
|
|
||||||
{
|
|
||||||
public String value;
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Matthieu Gautier <mgautier@kymeria.fr>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.kiwix.kiwixlib;
|
|
||||||
|
|
||||||
public class Pair
|
|
||||||
{
|
|
||||||
public String filename;
|
|
||||||
public int offset;
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<resources>
|
|
||||||
<string name="app_name">Kiwix Lib</string>
|
|
||||||
</resources>
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
* Copyright (C) 2017 Matthieu Gautier <mgautier@kymeria.fr>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef _ANDROID_JNI_UTILS_H
|
|
||||||
#define _ANDROID_JNI_UTILS_H
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
extern pthread_mutex_t globalLock;
|
|
||||||
|
|
||||||
inline jfieldID getHandleField(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
jclass c = env->GetObjectClass(obj);
|
|
||||||
// J is the type signature for long:
|
|
||||||
return env->GetFieldID(c, "nativeHandle", "J");
|
|
||||||
}
|
|
||||||
|
|
||||||
class Lock
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
pthread_mutex_t* lock;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Lock() : lock(&globalLock) { pthread_mutex_lock(lock); }
|
|
||||||
Lock(const Lock&) = delete;
|
|
||||||
Lock& operator=(const Lock&) = delete;
|
|
||||||
Lock(Lock&& other) : lock(&globalLock) { other.lock = nullptr; }
|
|
||||||
virtual ~Lock()
|
|
||||||
{
|
|
||||||
if (lock) {
|
|
||||||
pthread_mutex_unlock(lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
class LockedHandle;
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
class Handle
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
T* h;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Handle(T* h) : h(h){};
|
|
||||||
|
|
||||||
// No destructor. This must and will be handled by dispose method.
|
|
||||||
|
|
||||||
static LockedHandle<T> getHandle(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
jlong handle = env->GetLongField(obj, getHandleField(env, obj));
|
|
||||||
return LockedHandle<T>(reinterpret_cast<Handle<T>*>(handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dispose(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
auto lHandle = getHandle(env, obj);
|
|
||||||
auto handle = lHandle.h;
|
|
||||||
delete handle->h;
|
|
||||||
delete handle;
|
|
||||||
}
|
|
||||||
friend class LockedHandle<T>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
struct LockedHandle : public Lock {
|
|
||||||
Handle<T>* h;
|
|
||||||
LockedHandle(Handle<T>* h) : h(h) {}
|
|
||||||
T* operator->() { return h->h; }
|
|
||||||
T* operator*() { return h->h; }
|
|
||||||
operator bool() const { return (h->h != nullptr); }
|
|
||||||
};
|
|
||||||
|
|
||||||
/* c2jni type conversion functions */
|
|
||||||
inline jboolean c2jni(const bool& val) { return val ? JNI_TRUE : JNI_FALSE; }
|
|
||||||
inline jstring c2jni(const std::string& val, JNIEnv* env)
|
|
||||||
{
|
|
||||||
return env->NewStringUTF(val.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline jint c2jni(const int val) { return (jint)val; }
|
|
||||||
inline jint c2jni(const unsigned val) { return (unsigned)val; }
|
|
||||||
/* jni2c type conversion functions */
|
|
||||||
inline bool jni2c(const jboolean& val) { return val == JNI_TRUE; }
|
|
||||||
inline std::string jni2c(const jstring& val, JNIEnv* env)
|
|
||||||
{
|
|
||||||
const char* chars = env->GetStringUTFChars(val, 0);
|
|
||||||
std::string ret(chars);
|
|
||||||
env->ReleaseStringUTFChars(val, chars);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int jni2c(const jint val) { return (int)val; }
|
|
||||||
/* Method to deal with variable passed by reference */
|
|
||||||
inline void setStringObjValue(const std::string& value,
|
|
||||||
const jobject obj,
|
|
||||||
JNIEnv* env)
|
|
||||||
{
|
|
||||||
jclass objClass = env->GetObjectClass(obj);
|
|
||||||
jfieldID objFid = env->GetFieldID(objClass, "value", "Ljava/lang/String;");
|
|
||||||
env->SetObjectField(obj, objFid, c2jni(value, env));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void setIntObjValue(const int value, const jobject obj, JNIEnv* env)
|
|
||||||
{
|
|
||||||
jclass objClass = env->GetObjectClass(obj);
|
|
||||||
jfieldID objFid = env->GetFieldID(objClass, "value", "I");
|
|
||||||
env->SetIntField(obj, objFid, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void setBoolObjValue(const bool value, const jobject obj, JNIEnv* env)
|
|
||||||
{
|
|
||||||
jclass objClass = env->GetObjectClass(obj);
|
|
||||||
jfieldID objFid = env->GetFieldID(objClass, "value", "Z");
|
|
||||||
env->SetIntField(obj, objFid, c2jni(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void setPairObjValue(const std::string& filename, const int offset,
|
|
||||||
const jobject obj, JNIEnv* env)
|
|
||||||
{
|
|
||||||
jclass objClass = env->GetObjectClass(obj);
|
|
||||||
jfieldID filenameFid = env->GetFieldID(objClass, "filename", "Ljava/lang/String;");
|
|
||||||
env->SetObjectField(obj, filenameFid, c2jni(filename, env));
|
|
||||||
jfieldID offsetFid = env->GetFieldID(objClass, "offset", "I");
|
|
||||||
env->SetIntField(obj, offsetFid, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // _ANDROID_JNI_UTILS_H
|
|
||||||
+141
-51
@@ -2,23 +2,40 @@
|
|||||||
|
|
||||||
#include "aria2.h"
|
#include "aria2.h"
|
||||||
#include "xmlrpc.h"
|
#include "xmlrpc.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <common/otherTools.h>
|
#include "tools.h"
|
||||||
#include <common/pathTools.h>
|
#include "tools/pathTools.h"
|
||||||
#include <downloader.h> // For AriaError
|
#include "tools/stringTools.h"
|
||||||
|
#include "tools/otherTools.h"
|
||||||
|
#include "downloader.h" // For AriaError
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# define ARIA2_CMD "aria2c.exe"
|
||||||
|
#else
|
||||||
|
# define ARIA2_CMD "aria2c"
|
||||||
|
# include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define LOG_ARIA_ERROR() \
|
||||||
|
{ \
|
||||||
|
std::cerr << "ERROR: aria2 RPC request failed. (" << res << ")." << std::endl; \
|
||||||
|
std::cerr << (curlErrorBuffer[0] ? curlErrorBuffer : curl_easy_strerror(res)) << std::endl; \
|
||||||
|
}
|
||||||
|
|
||||||
namespace kiwix {
|
namespace kiwix {
|
||||||
|
|
||||||
Aria2::Aria2():
|
Aria2::Aria2():
|
||||||
mp_aria(nullptr),
|
mp_aria(nullptr),
|
||||||
m_port(42042),
|
m_port(42042),
|
||||||
m_secret("kiwixariarpc"),
|
m_secret(getNewRpcSecret())
|
||||||
mp_curl(nullptr),
|
|
||||||
m_lock(PTHREAD_MUTEX_INITIALIZER)
|
|
||||||
{
|
{
|
||||||
m_downloadDir = getDataDirectory();
|
m_downloadDir = getDataDirectory();
|
||||||
|
makeDirectory(m_downloadDir);
|
||||||
std::vector<const char*> callCmd;
|
std::vector<const char*> callCmd;
|
||||||
|
|
||||||
std::string rpc_port = "--rpc-listen-port=" + to_string(m_port);
|
std::string rpc_port = "--rpc-listen-port=" + to_string(m_port);
|
||||||
@@ -36,16 +53,22 @@ Aria2::Aria2():
|
|||||||
std::string rpc_secret = "--rpc-secret=" + m_secret;
|
std::string rpc_secret = "--rpc-secret=" + m_secret;
|
||||||
m_secret = "token:"+m_secret;
|
m_secret = "token:"+m_secret;
|
||||||
|
|
||||||
#ifdef _WIN32
|
std::string aria2cmd = appendToDirectory(
|
||||||
callCmd.push_back("aria2c.exe");
|
removeLastPathElement(getExecutablePath(true)),
|
||||||
#else
|
ARIA2_CMD);
|
||||||
callCmd.push_back("aria2c");
|
if (fileExists(aria2cmd)) {
|
||||||
#endif
|
// A local aria2c exe exists (packaged with kiwix-desktop), use it.
|
||||||
|
callCmd.push_back(aria2cmd.c_str());
|
||||||
|
} else {
|
||||||
|
// Try to use a potential installed aria2c.
|
||||||
|
callCmd.push_back(ARIA2_CMD);
|
||||||
|
}
|
||||||
|
callCmd.push_back("--follow-metalink=mem");
|
||||||
callCmd.push_back("--enable-rpc");
|
callCmd.push_back("--enable-rpc");
|
||||||
callCmd.push_back(rpc_secret.c_str());
|
callCmd.push_back(rpc_secret.c_str());
|
||||||
callCmd.push_back(rpc_port.c_str());
|
callCmd.push_back(rpc_port.c_str());
|
||||||
callCmd.push_back(download_dir.c_str());
|
callCmd.push_back(download_dir.c_str());
|
||||||
if (fileExists(session_file)) {
|
if (fileReadable(session_file)) {
|
||||||
callCmd.push_back(inputFile.c_str());
|
callCmd.push_back(inputFile.c_str());
|
||||||
}
|
}
|
||||||
callCmd.push_back(session.c_str());
|
callCmd.push_back(session.c_str());
|
||||||
@@ -61,31 +84,37 @@ Aria2::Aria2():
|
|||||||
callCmd.push_back("--max-concurrent-downloads=42");
|
callCmd.push_back("--max-concurrent-downloads=42");
|
||||||
callCmd.push_back("--rpc-max-request-size=6M");
|
callCmd.push_back("--rpc-max-request-size=6M");
|
||||||
callCmd.push_back("--file-allocation=none");
|
callCmd.push_back("--file-allocation=none");
|
||||||
|
std::string launchCmd;
|
||||||
|
for (auto &cmd : callCmd) {
|
||||||
|
launchCmd.append(cmd).append(" ");
|
||||||
|
}
|
||||||
mp_aria = Subprocess::run(callCmd);
|
mp_aria = Subprocess::run(callCmd);
|
||||||
mp_curl = curl_easy_init();
|
|
||||||
curl_easy_setopt(mp_curl, CURLOPT_URL, "http://localhost/rpc");
|
CURL* p_curl = curl_easy_init();
|
||||||
curl_easy_setopt(mp_curl, CURLOPT_PORT, m_port);
|
char curlErrorBuffer[CURL_ERROR_SIZE];
|
||||||
curl_easy_setopt(mp_curl, CURLOPT_POST, 1L);
|
|
||||||
|
curl_easy_setopt(p_curl, CURLOPT_URL, "http://localhost/rpc");
|
||||||
|
curl_easy_setopt(p_curl, CURLOPT_PORT, m_port);
|
||||||
|
curl_easy_setopt(p_curl, CURLOPT_POST, 1L);
|
||||||
|
curl_easy_setopt(p_curl, CURLOPT_ERRORBUFFER, curlErrorBuffer);
|
||||||
|
|
||||||
int watchdog = 50;
|
int watchdog = 50;
|
||||||
while(--watchdog) {
|
while(--watchdog) {
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(100));
|
sleep(10);
|
||||||
auto res = curl_easy_perform(mp_curl);
|
curlErrorBuffer[0] = 0;
|
||||||
|
auto res = curl_easy_perform(p_curl);
|
||||||
if (res == CURLE_OK) {
|
if (res == CURLE_OK) {
|
||||||
break;
|
break;
|
||||||
|
} else if (watchdog == 1) {
|
||||||
|
LOG_ARIA_ERROR();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
curl_easy_cleanup(p_curl);
|
||||||
if (!watchdog) {
|
if (!watchdog) {
|
||||||
curl_easy_cleanup(mp_curl);
|
throw std::runtime_error("Cannot connect to aria2c rpc. Aria2c launch cmd : " + launchCmd);
|
||||||
throw std::runtime_error("Cannot connect to aria2c rpc");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Aria2::~Aria2()
|
|
||||||
{
|
|
||||||
curl_easy_cleanup(mp_curl);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Aria2::close()
|
void Aria2::close()
|
||||||
{
|
{
|
||||||
saveSession();
|
saveSession();
|
||||||
@@ -94,47 +123,61 @@ void Aria2::close()
|
|||||||
|
|
||||||
size_t write_callback_to_iss(char* ptr, size_t size, size_t nmemb, void* userdata)
|
size_t write_callback_to_iss(char* ptr, size_t size, size_t nmemb, void* userdata)
|
||||||
{
|
{
|
||||||
auto str = static_cast<std::stringstream*>(userdata);
|
auto outStream = static_cast<std::stringstream*>(userdata);
|
||||||
str->write(ptr, nmemb);
|
outStream->write(ptr, nmemb);
|
||||||
return nmemb;
|
return nmemb;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Aria2::doRequest(const MethodCall& methodCall)
|
std::string Aria2::doRequest(const MethodCall& methodCall)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&m_lock);
|
|
||||||
auto requestContent = methodCall.toString();
|
auto requestContent = methodCall.toString();
|
||||||
std::stringstream stringstream;
|
std::stringstream outStream;
|
||||||
CURLcode res;
|
CURLcode res;
|
||||||
curl_easy_setopt(mp_curl, CURLOPT_POSTFIELDSIZE, requestContent.size());
|
long response_code;
|
||||||
curl_easy_setopt(mp_curl, CURLOPT_POSTFIELDS, requestContent.c_str());
|
char curlErrorBuffer[CURL_ERROR_SIZE];
|
||||||
curl_easy_setopt(mp_curl, CURLOPT_WRITEFUNCTION, &write_callback_to_iss);
|
CURL* p_curl = curl_easy_init();
|
||||||
curl_easy_setopt(mp_curl, CURLOPT_WRITEDATA, &stringstream);
|
curl_easy_setopt(p_curl, CURLOPT_URL, "http://localhost/rpc");
|
||||||
res = curl_easy_perform(mp_curl);
|
curl_easy_setopt(p_curl, CURLOPT_PORT, m_port);
|
||||||
if (res == CURLE_OK) {
|
curl_easy_setopt(p_curl, CURLOPT_POST, 1L);
|
||||||
long response_code;
|
curl_easy_setopt(p_curl, CURLOPT_ERRORBUFFER, curlErrorBuffer);
|
||||||
curl_easy_getinfo(mp_curl, CURLINFO_RESPONSE_CODE, &response_code);
|
curl_easy_setopt(p_curl, CURLOPT_POSTFIELDSIZE, requestContent.size());
|
||||||
pthread_mutex_unlock(&m_lock);
|
curl_easy_setopt(p_curl, CURLOPT_POSTFIELDS, requestContent.c_str());
|
||||||
if (response_code != 200) {
|
curl_easy_setopt(p_curl, CURLOPT_WRITEFUNCTION, &write_callback_to_iss);
|
||||||
throw std::runtime_error("Invalid return code from aria");
|
curl_easy_setopt(p_curl, CURLOPT_WRITEDATA, &outStream);
|
||||||
}
|
curlErrorBuffer[0] = 0;
|
||||||
auto responseContent = stringstream.str();
|
res = curl_easy_perform(p_curl);
|
||||||
MethodResponse response(responseContent);
|
if (res != CURLE_OK) {
|
||||||
if (response.isFault()) {
|
LOG_ARIA_ERROR();
|
||||||
throw AriaError(response.getFault().getFaultString());
|
curl_easy_cleanup(p_curl);
|
||||||
}
|
throw std::runtime_error("Cannot perform request");
|
||||||
return responseContent;
|
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&m_lock);
|
curl_easy_getinfo(p_curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||||
throw std::runtime_error("Cannot perform request");
|
curl_easy_cleanup(p_curl);
|
||||||
|
|
||||||
|
auto responseContent = outStream.str();
|
||||||
|
if (response_code != 200) {
|
||||||
|
std::cerr << "ERROR: Invalid return code (" << response_code << ") from aria :" << std::endl;
|
||||||
|
std::cerr << responseContent << std::endl;
|
||||||
|
throw std::runtime_error("Invalid return code from aria");
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodResponse response(responseContent);
|
||||||
|
if (response.isFault()) {
|
||||||
|
throw AriaError(response.getFault().getFaultString());
|
||||||
|
}
|
||||||
|
return responseContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Aria2::addUri(const std::vector<std::string>& uris)
|
std::string Aria2::addUri(const std::vector<std::string>& uris, const std::vector<std::pair<std::string, std::string>>& options)
|
||||||
{
|
{
|
||||||
MethodCall methodCall("aria2.addUri", m_secret);
|
MethodCall methodCall("aria2.addUri", m_secret);
|
||||||
auto uriParams = methodCall.newParamValue().getArray();
|
auto uriParams = methodCall.newParamValue().getArray();
|
||||||
for (auto& uri : uris) {
|
for (auto& uri : uris) {
|
||||||
uriParams.addValue().set(uri);
|
uriParams.addValue().set(uri);
|
||||||
}
|
}
|
||||||
|
for (auto& option : options) {
|
||||||
|
methodCall.newParamValue().getStruct().addMember(option.first).getValue().set(option.second);
|
||||||
|
}
|
||||||
auto ret = doRequest(methodCall);
|
auto ret = doRequest(methodCall);
|
||||||
MethodResponse response(ret);
|
MethodResponse response(ret);
|
||||||
return response.getParamValue(0).getAsS();
|
return response.getParamValue(0).getAsS();
|
||||||
@@ -153,12 +196,18 @@ std::string Aria2::tellStatus(const std::string& gid, const std::vector<std::str
|
|||||||
return doRequest(methodCall);
|
return doRequest(methodCall);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Aria2::getNewRpcSecret()
|
||||||
|
{
|
||||||
|
std::string uuid = gen_uuid("");
|
||||||
|
uuid.erase(std::remove(uuid.begin(), uuid.end(), '-'));
|
||||||
|
return uuid.substr(0, 9);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> Aria2::tellActive()
|
std::vector<std::string> Aria2::tellActive()
|
||||||
{
|
{
|
||||||
MethodCall methodCall("aria2.tellActive", m_secret);
|
MethodCall methodCall("aria2.tellActive", m_secret);
|
||||||
auto statusArray = methodCall.newParamValue().getArray();
|
auto statusArray = methodCall.newParamValue().getArray();
|
||||||
statusArray.addValue().set(std::string("gid"));
|
statusArray.addValue().set(std::string("gid"));
|
||||||
statusArray.addValue().set(std::string("following"));
|
|
||||||
auto responseContent = doRequest(methodCall);
|
auto responseContent = doRequest(methodCall);
|
||||||
MethodResponse response(responseContent);
|
MethodResponse response(responseContent);
|
||||||
std::vector<std::string> activeGID;
|
std::vector<std::string> activeGID;
|
||||||
@@ -173,6 +222,27 @@ std::vector<std::string> Aria2::tellActive()
|
|||||||
return activeGID;
|
return activeGID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> Aria2::tellWaiting()
|
||||||
|
{
|
||||||
|
MethodCall methodCall("aria2.tellWaiting", m_secret);
|
||||||
|
methodCall.newParamValue().set(0);
|
||||||
|
methodCall.newParamValue().set(99); // max number of downloads to be returned, don't know how to set this properly assumed that there will not be more than 99 paused downloads.
|
||||||
|
auto statusArray = methodCall.newParamValue().getArray();
|
||||||
|
statusArray.addValue().set(std::string("gid"));
|
||||||
|
auto responseContent = doRequest(methodCall);
|
||||||
|
MethodResponse response(responseContent);
|
||||||
|
std::vector<std::string> waitingGID;
|
||||||
|
int index = 0;
|
||||||
|
while(true) {
|
||||||
|
try {
|
||||||
|
auto structNode = response.getParamValue(0).getArray().getValue(index++).getStruct();
|
||||||
|
auto gidNode = structNode.getMember("gid");
|
||||||
|
waitingGID.push_back(gidNode.getValue().getAsS());
|
||||||
|
} catch (InvalidRPCNode& e) { break; }
|
||||||
|
}
|
||||||
|
return waitingGID;
|
||||||
|
}
|
||||||
|
|
||||||
void Aria2::saveSession()
|
void Aria2::saveSession()
|
||||||
{
|
{
|
||||||
MethodCall methodCall("aria2.saveSession", m_secret);
|
MethodCall methodCall("aria2.saveSession", m_secret);
|
||||||
@@ -186,5 +256,25 @@ void Aria2::shutdown()
|
|||||||
doRequest(methodCall);
|
doRequest(methodCall);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Aria2::pause(const std::string& gid)
|
||||||
|
{
|
||||||
|
MethodCall methodCall("aria2.pause", m_secret);
|
||||||
|
methodCall.newParamValue().set(gid);
|
||||||
|
doRequest(methodCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aria2::unpause(const std::string& gid)
|
||||||
|
{
|
||||||
|
MethodCall methodCall("aria2.unpause", m_secret);
|
||||||
|
methodCall.newParamValue().set(gid);
|
||||||
|
doRequest(methodCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aria2::remove(const std::string& gid)
|
||||||
|
{
|
||||||
|
MethodCall methodCall("aria2.remove", m_secret);
|
||||||
|
methodCall.newParamValue().set(gid);
|
||||||
|
doRequest(methodCall);
|
||||||
|
}
|
||||||
|
|
||||||
} // end namespace kiwix
|
} // end namespace kiwix
|
||||||
+7
-6
@@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
namespace kiwix {
|
namespace kiwix {
|
||||||
|
|
||||||
@@ -24,21 +23,23 @@ class Aria2
|
|||||||
int m_port;
|
int m_port;
|
||||||
std::string m_secret;
|
std::string m_secret;
|
||||||
std::string m_downloadDir;
|
std::string m_downloadDir;
|
||||||
CURL* mp_curl;
|
|
||||||
pthread_mutex_t m_lock;
|
|
||||||
|
|
||||||
std::string doRequest(const MethodCall& methodCall);
|
std::string doRequest(const MethodCall& methodCall);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Aria2();
|
Aria2();
|
||||||
virtual ~Aria2();
|
virtual ~Aria2() = default;
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
std::string addUri(const std::vector<std::string>& uri);
|
std::string addUri(const std::vector<std::string>& uri, const std::vector<std::pair<std::string, std::string>>& options = {});
|
||||||
std::string tellStatus(const std::string& gid, const std::vector<std::string>& statusKey);
|
std::string tellStatus(const std::string& gid, const std::vector<std::string>& statusKey);
|
||||||
|
static std::string getNewRpcSecret();
|
||||||
std::vector<std::string> tellActive();
|
std::vector<std::string> tellActive();
|
||||||
|
std::vector<std::string> tellWaiting();
|
||||||
void saveSession();
|
void saveSession();
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
void pause(const std::string& gid);
|
||||||
|
void unpause(const std::string& gid);
|
||||||
|
void remove(const std::string& gid);
|
||||||
};
|
};
|
||||||
|
|
||||||
}; //end namespace kiwix
|
}; //end namespace kiwix
|
||||||
|
|||||||
+165
-78
@@ -18,78 +18,79 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "book.h"
|
#include "book.h"
|
||||||
#include "reader.h"
|
|
||||||
|
|
||||||
#include "common/base64.h"
|
#include "tools.h"
|
||||||
#include "common/regexTools.h"
|
#include "tools/base64.h"
|
||||||
#include "common/networkTools.h"
|
#include "tools/regexTools.h"
|
||||||
|
#include "tools/networkTools.h"
|
||||||
|
#include "tools/otherTools.h"
|
||||||
|
#include "tools/stringTools.h"
|
||||||
|
#include "tools/pathTools.h"
|
||||||
|
#include "tools/archiveTools.h"
|
||||||
|
|
||||||
|
#include <zim/archive.h>
|
||||||
|
#include <zim/item.h>
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
|
|
||||||
namespace kiwix
|
namespace kiwix
|
||||||
{
|
{
|
||||||
/* Constructor */
|
/* Constructor */
|
||||||
Book::Book() : m_readOnly(false)
|
Book::Book() :
|
||||||
|
m_pathValid(false),
|
||||||
|
m_readOnly(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Destructor */
|
/* Destructor */
|
||||||
Book::~Book()
|
Book::~Book()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Book::Illustrations Book::getIllustrations() const
|
||||||
|
{
|
||||||
|
return m_illustrations;
|
||||||
|
}
|
||||||
|
|
||||||
bool Book::update(const kiwix::Book& other)
|
bool Book::update(const kiwix::Book& other)
|
||||||
{
|
{
|
||||||
if (m_readOnly)
|
if (m_readOnly)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_readOnly = other.m_readOnly;
|
if (m_id != other.m_id)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (m_path.empty()) {
|
*this = other;
|
||||||
m_path = other.m_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_url.empty()) {
|
|
||||||
m_url = other.m_url;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_tags.empty()) {
|
|
||||||
m_tags = other.m_tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_name.empty()) {
|
|
||||||
m_name = other.m_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_indexPath.empty()) {
|
|
||||||
m_indexPath = other.m_indexPath;
|
|
||||||
m_indexType = other.m_indexType;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_faviconMimeType.empty()) {
|
|
||||||
m_favicon = other.m_favicon;
|
|
||||||
m_faviconMimeType = other.m_faviconMimeType;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Book::update(const kiwix::Reader& reader)
|
void Book::update(const zim::Archive& archive) {
|
||||||
{
|
m_path = archive.getFilename();
|
||||||
m_path = reader.getZimFilePath();
|
m_pathValid = true;
|
||||||
m_id = reader.getId();
|
m_id = std::string(archive.getUuid());
|
||||||
m_description = reader.getDescription();
|
m_title = getArchiveTitle(archive);
|
||||||
m_language = reader.getLanguage();
|
m_description = getMetaDescription(archive);
|
||||||
m_date = reader.getDate();
|
m_language = getMetaLanguage(archive);
|
||||||
m_creator = reader.getCreator();
|
m_creator = getMetaCreator(archive);
|
||||||
m_publisher = reader.getPublisher();
|
m_publisher = getMetaPublisher(archive);
|
||||||
m_title = reader.getTitle();
|
m_date = getMetaDate(archive);
|
||||||
m_name = reader.getName();
|
m_name = getMetaName(archive);
|
||||||
m_tags = reader.getTags();
|
m_flavour = getMetaFlavour(archive);
|
||||||
m_origId = reader.getOrigId();
|
m_tags = getMetaTags(archive);
|
||||||
m_articleCount = reader.getArticleCount();
|
m_category = getCategoryFromTags();
|
||||||
m_mediaCount = reader.getMediaCount();
|
m_articleCount = archive.getArticleCount();
|
||||||
m_size = reader.getFileSize() << 10;
|
m_mediaCount = archive.getMediaCount();
|
||||||
|
m_size = static_cast<uint64_t>(getArchiveFileSize(archive)) << 10;
|
||||||
|
|
||||||
reader.getFavicon(m_favicon, m_faviconMimeType);
|
m_illustrations.clear();
|
||||||
|
for ( const auto illustrationSize : archive.getIllustrationSizes() ) {
|
||||||
|
const auto illustration = std::make_shared<Illustration>();
|
||||||
|
const zim::Item illustrationItem = archive.getIllustrationItem(illustrationSize);
|
||||||
|
illustration->width = illustration->height = illustrationSize;
|
||||||
|
illustration->mimeType = illustrationItem.getMimetype();
|
||||||
|
illustration->data = illustrationItem.getData();
|
||||||
|
// NOTE: illustration->url is left uninitialized
|
||||||
|
m_illustrations.push_back(illustration);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ATTR(name) node.attribute(name).value()
|
#define ATTR(name) node.attribute(name).value()
|
||||||
@@ -101,36 +102,46 @@ void Book::updateFromXml(const pugi::xml_node& node, const std::string& baseDir)
|
|||||||
path = computeAbsolutePath(baseDir, path);
|
path = computeAbsolutePath(baseDir, path);
|
||||||
}
|
}
|
||||||
m_path = path;
|
m_path = path;
|
||||||
path = ATTR("indexPath");
|
m_pathValid = fileReadable(path);
|
||||||
if (!path.empty()) {
|
|
||||||
if (isRelativePath(path)) {
|
|
||||||
path = computeAbsolutePath(baseDir, path);
|
|
||||||
}
|
|
||||||
m_indexPath = path;
|
|
||||||
m_indexType = XAPIAN;
|
|
||||||
}
|
|
||||||
m_title = ATTR("title");
|
m_title = ATTR("title");
|
||||||
m_name = ATTR("name");
|
|
||||||
m_tags = ATTR("tags");
|
|
||||||
m_description = ATTR("description");
|
m_description = ATTR("description");
|
||||||
m_language = ATTR("language");
|
m_language = ATTR("language");
|
||||||
m_date = ATTR("date");
|
|
||||||
m_creator = ATTR("creator");
|
m_creator = ATTR("creator");
|
||||||
m_publisher = ATTR("publisher");
|
m_publisher = ATTR("publisher");
|
||||||
|
m_date = ATTR("date");
|
||||||
m_url = ATTR("url");
|
m_url = ATTR("url");
|
||||||
|
m_name = ATTR("name");
|
||||||
|
m_flavour = ATTR("flavour");
|
||||||
|
m_tags = ATTR("tags");
|
||||||
m_origId = ATTR("origId");
|
m_origId = ATTR("origId");
|
||||||
m_articleCount = strtoull(ATTR("articleCount"), 0, 0);
|
m_articleCount = strtoull(ATTR("articleCount"), 0, 0);
|
||||||
m_mediaCount = strtoull(ATTR("mediaCount"), 0, 0);
|
m_mediaCount = strtoull(ATTR("mediaCount"), 0, 0);
|
||||||
m_size = strtoull(ATTR("size"), 0, 0) << 10;
|
m_size = strtoull(ATTR("size"), 0, 0) << 10;
|
||||||
m_favicon = base64_decode(ATTR("favicon"));
|
std::string favicon_mimetype = ATTR("faviconMimeType");
|
||||||
m_faviconMimeType = ATTR("faviconMimeType");
|
if (! favicon_mimetype.empty()) {
|
||||||
|
const auto favicon = std::make_shared<Illustration>();
|
||||||
|
favicon->data = base64_decode(ATTR("favicon"));
|
||||||
|
favicon->mimeType = favicon_mimetype;
|
||||||
|
favicon->url = ATTR("faviconUrl");
|
||||||
|
m_illustrations.assign(1, favicon);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
m_downloadId = ATTR("downloadId");
|
m_downloadId = ATTR("downloadId");
|
||||||
} catch(...) {}
|
} catch(...) {}
|
||||||
|
const auto catattr = node.attribute("category");
|
||||||
|
m_category = catattr.empty() ? getCategoryFromTags() : catattr.value();
|
||||||
}
|
}
|
||||||
#undef ATTR
|
#undef ATTR
|
||||||
|
|
||||||
|
|
||||||
|
static std::string fromOpdsDate(const std::string& date)
|
||||||
|
{
|
||||||
|
//The opds date use the standard <YYYY>-<MM>-<DD>T<HH>:<mm>:<SS>Z
|
||||||
|
//and we want <YYYY>-<MM>-<DD>. That's easy, let's take the first 10 char
|
||||||
|
return date.substr(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#define VALUE(name) node.child(name).child_value()
|
#define VALUE(name) node.child(name).child_value()
|
||||||
void Book::updateFromOpds(const pugi::xml_node& node, const std::string& urlHost)
|
void Book::updateFromOpds(const pugi::xml_node& node, const std::string& urlHost)
|
||||||
{
|
{
|
||||||
@@ -138,11 +149,22 @@ void Book::updateFromOpds(const pugi::xml_node& node, const std::string& urlHost
|
|||||||
if (!m_id.compare(0, 9, "urn:uuid:")) {
|
if (!m_id.compare(0, 9, "urn:uuid:")) {
|
||||||
m_id.erase(0, 9);
|
m_id.erase(0, 9);
|
||||||
}
|
}
|
||||||
|
// No path on opds.
|
||||||
m_title = VALUE("title");
|
m_title = VALUE("title");
|
||||||
m_description = VALUE("description");
|
m_description = VALUE("summary");
|
||||||
m_language = VALUE("language");
|
m_language = VALUE("language");
|
||||||
m_date = VALUE("updated");
|
|
||||||
m_creator = node.child("author").child("name").child_value();
|
m_creator = node.child("author").child("name").child_value();
|
||||||
|
m_publisher = node.child("publisher").child("name").child_value();
|
||||||
|
const std::string dcIssuedDate = VALUE("dc:issued");
|
||||||
|
m_date = dcIssuedDate.empty() ? VALUE("updated") : dcIssuedDate;
|
||||||
|
m_date = fromOpdsDate(m_date);
|
||||||
|
m_name = VALUE("name");
|
||||||
|
m_flavour = VALUE("flavour");
|
||||||
|
m_tags = VALUE("tags");
|
||||||
|
const auto catnode = node.child("category");
|
||||||
|
m_category = catnode.empty() ? getCategoryFromTags() : catnode.child_value();
|
||||||
|
m_articleCount = strtoull(VALUE("articleCount"), 0, 0);
|
||||||
|
m_mediaCount = strtoull(VALUE("mediaCount"), 0, 0);
|
||||||
for(auto linkNode = node.child("link"); linkNode;
|
for(auto linkNode = node.child("link"); linkNode;
|
||||||
linkNode = linkNode.next_sibling("link")) {
|
linkNode = linkNode.next_sibling("link")) {
|
||||||
std::string rel = linkNode.attribute("rel").value();
|
std::string rel = linkNode.attribute("rel").value();
|
||||||
@@ -152,19 +174,22 @@ void Book::updateFromOpds(const pugi::xml_node& node, const std::string& urlHost
|
|||||||
m_size = strtoull(linkNode.attribute("length").value(), 0, 0);
|
m_size = strtoull(linkNode.attribute("length").value(), 0, 0);
|
||||||
}
|
}
|
||||||
if (rel == "http://opds-spec.org/image/thumbnail") {
|
if (rel == "http://opds-spec.org/image/thumbnail") {
|
||||||
m_faviconUrl = urlHost + linkNode.attribute("href").value();
|
const auto favicon = std::make_shared<Illustration>();
|
||||||
m_faviconMimeType = linkNode.attribute("type").value();
|
favicon->data.clear();
|
||||||
|
favicon->url = urlHost + linkNode.attribute("href").value();
|
||||||
|
favicon->mimeType = linkNode.attribute("type").value();
|
||||||
|
m_illustrations.assign(1, favicon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#undef VALUE
|
#undef VALUE
|
||||||
|
|
||||||
std::string Book::getHumanReadableIdFromPath()
|
std::string Book::getHumanReadableIdFromPath() const
|
||||||
{
|
{
|
||||||
std::string id = m_path;
|
std::string id = m_path;
|
||||||
if (!id.empty()) {
|
if (!id.empty()) {
|
||||||
kiwix::removeAccents(id);
|
id = kiwix::removeAccents(id);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
id = replaceRegex(id, "", "^.*\\\\");
|
id = replaceRegex(id, "", "^.*\\\\");
|
||||||
@@ -186,22 +211,84 @@ void Book::setPath(const std::string& path)
|
|||||||
: path;
|
: path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Book::setIndexPath(const std::string& indexPath)
|
const Book::Illustration Book::missingDefaultIllustration;
|
||||||
|
|
||||||
|
std::shared_ptr<const Book::Illustration> Book::getIllustration(unsigned int size) const
|
||||||
{
|
{
|
||||||
m_indexPath = isRelativePath(indexPath)
|
for ( const auto& ilPtr : m_illustrations ) {
|
||||||
? computeAbsolutePath(getCurrentDirectory(), indexPath)
|
if (ilPtr->width == size && ilPtr->height == size) {
|
||||||
: indexPath;
|
return ilPtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Cannot find illustration");
|
||||||
|
}
|
||||||
|
|
||||||
|
const Book::Illustration& Book::getDefaultIllustration() const
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return *getIllustration(48);
|
||||||
|
} catch (...) {
|
||||||
|
return missingDefaultIllustration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Book::Illustration::getData() const
|
||||||
|
{
|
||||||
|
if (data.empty() && !url.empty()) {
|
||||||
|
const std::lock_guard<std::mutex> l(mutex);
|
||||||
|
if ( data.empty() ) {
|
||||||
|
try {
|
||||||
|
data = download(url);
|
||||||
|
} catch(...) {
|
||||||
|
std::cerr << "Cannot download favicon from " << url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& Book::getFavicon() const {
|
const std::string& Book::getFavicon() const {
|
||||||
if (m_favicon.empty() && !m_faviconUrl.empty()) {
|
return getDefaultIllustration().getData();
|
||||||
try {
|
}
|
||||||
m_favicon = download(m_faviconUrl);
|
|
||||||
} catch(...) {
|
const std::string& Book::getFaviconUrl() const
|
||||||
std::cerr << "Cannot download favicon from " << m_faviconUrl;
|
{
|
||||||
}
|
return getDefaultIllustration().url;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Book::getFaviconMimeType() const
|
||||||
|
{
|
||||||
|
return getDefaultIllustration().mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Book::getTagStr(const std::string& tagName) const {
|
||||||
|
return getTagValueFromTagList(convertTags(m_tags), tagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Book::getTagBool(const std::string& tagName) const {
|
||||||
|
return convertStrToBool(getTagStr(tagName));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Book::getCategory() const
|
||||||
|
{
|
||||||
|
return m_category;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Book::getCategoryFromTags() const
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return getTagStr("category");
|
||||||
}
|
}
|
||||||
return m_favicon;
|
catch ( const std::out_of_range& )
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> Book::getLanguages() const
|
||||||
|
{
|
||||||
|
return kiwix::split(m_language, ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Matthieu Gautier <mgautier@kymeria.fr>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bookmark.h"
|
||||||
|
|
||||||
|
#include <pugixml.hpp>
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
|
{
|
||||||
|
/* Constructor */
|
||||||
|
Bookmark::Bookmark()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Destructor */
|
||||||
|
Bookmark::~Bookmark()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bookmark::updateFromXml(const pugi::xml_node& node)
|
||||||
|
{
|
||||||
|
auto bookNode = node.child("book");
|
||||||
|
m_bookId = bookNode.child("id").child_value();
|
||||||
|
m_bookTitle = bookNode.child("title").child_value();
|
||||||
|
m_language = bookNode.child("language").child_value();
|
||||||
|
m_date = bookNode.child("date").child_value();
|
||||||
|
m_title = node.child("title").child_value();
|
||||||
|
m_url = node.child("url").child_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,300 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2014 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <common/otherTools.h>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
std::map<std::string, std::string> codeisomapping {
|
|
||||||
{ "af", "afg" },
|
|
||||||
{ "ax", "ala" },
|
|
||||||
{ "al", "alb" },
|
|
||||||
{ "dz", "dza" },
|
|
||||||
{ "as", "asm" },
|
|
||||||
{ "ad", "and" },
|
|
||||||
{ "ao", "ago" },
|
|
||||||
{ "ai", "aia" },
|
|
||||||
{ "aq", "ata" },
|
|
||||||
{ "ag", "atg" },
|
|
||||||
{ "ar", "arg" },
|
|
||||||
{ "am", "arm" },
|
|
||||||
{ "aw", "abw" },
|
|
||||||
{ "au", "aus" },
|
|
||||||
{ "at", "aut" },
|
|
||||||
{ "az", "aze" },
|
|
||||||
{ "bs", "bhs" },
|
|
||||||
{ "bh", "bhr" },
|
|
||||||
{ "bd", "bgd" },
|
|
||||||
{ "bb", "brb" },
|
|
||||||
{ "by", "blr" },
|
|
||||||
{ "be", "bel" },
|
|
||||||
{ "bz", "blz" },
|
|
||||||
{ "bj", "ben" },
|
|
||||||
{ "bm", "bmu" },
|
|
||||||
{ "bt", "btn" },
|
|
||||||
{ "bo", "bol" },
|
|
||||||
{ "ba", "bih" },
|
|
||||||
{ "bw", "bwa" },
|
|
||||||
{ "bv", "bvt" },
|
|
||||||
{ "br", "bra" },
|
|
||||||
{ "vg", "vgb" },
|
|
||||||
{ "io", "iot" },
|
|
||||||
{ "bn", "brn" },
|
|
||||||
{ "bg", "bgr" },
|
|
||||||
{ "bf", "bfa" },
|
|
||||||
{ "bi", "bdi" },
|
|
||||||
{ "kh", "khm" },
|
|
||||||
{ "cm", "cmr" },
|
|
||||||
{ "ca", "can" },
|
|
||||||
{ "cv", "cpv" },
|
|
||||||
{ "ky", "cym" },
|
|
||||||
{ "cf", "caf" },
|
|
||||||
{ "td", "tcd" },
|
|
||||||
{ "cl", "chl" },
|
|
||||||
{ "cn", "chn" },
|
|
||||||
{ "hk", "hkg" },
|
|
||||||
{ "mo", "mac" },
|
|
||||||
{ "cx", "cxr" },
|
|
||||||
{ "cc", "cck" },
|
|
||||||
{ "co", "col" },
|
|
||||||
{ "km", "com" },
|
|
||||||
{ "cg", "cog" },
|
|
||||||
{ "cd", "cod" },
|
|
||||||
{ "ck", "cok" },
|
|
||||||
{ "cr", "cri" },
|
|
||||||
{ "ci", "civ" },
|
|
||||||
{ "hr", "hrv" },
|
|
||||||
{ "cu", "cub" },
|
|
||||||
{ "cy", "cyp" },
|
|
||||||
{ "cz", "cze" },
|
|
||||||
{ "dk", "dnk" },
|
|
||||||
{ "dj", "dji" },
|
|
||||||
{ "dm", "dma" },
|
|
||||||
{ "do", "dom" },
|
|
||||||
{ "ec", "ecu" },
|
|
||||||
{ "eg", "egy" },
|
|
||||||
{ "sv", "slv" },
|
|
||||||
{ "gq", "gnq" },
|
|
||||||
{ "er", "eri" },
|
|
||||||
{ "ee", "est" },
|
|
||||||
{ "et", "eth" },
|
|
||||||
{ "fk", "flk" },
|
|
||||||
{ "fo", "fro" },
|
|
||||||
{ "fj", "fji" },
|
|
||||||
{ "fi", "fin" },
|
|
||||||
{ "fr", "fra" },
|
|
||||||
{ "gf", "guf" },
|
|
||||||
{ "pf", "pyf" },
|
|
||||||
{ "tf", "atf" },
|
|
||||||
{ "ga", "gab" },
|
|
||||||
{ "gm", "gmb" },
|
|
||||||
{ "ge", "geo" },
|
|
||||||
{ "de", "deu" },
|
|
||||||
{ "gh", "gha" },
|
|
||||||
{ "gi", "gib" },
|
|
||||||
{ "gr", "grc" },
|
|
||||||
{ "gl", "grl" },
|
|
||||||
{ "gd", "grd" },
|
|
||||||
{ "gp", "glp" },
|
|
||||||
{ "gu", "gum" },
|
|
||||||
{ "gt", "gtm" },
|
|
||||||
{ "gg", "ggy" },
|
|
||||||
{ "gn", "gin" },
|
|
||||||
{ "gw", "gnb" },
|
|
||||||
{ "gy", "guy" },
|
|
||||||
{ "ht", "hti" },
|
|
||||||
{ "hm", "hmd" },
|
|
||||||
{ "va", "vat" },
|
|
||||||
{ "hn", "hnd" },
|
|
||||||
{ "hu", "hun" },
|
|
||||||
{ "is", "isl" },
|
|
||||||
{ "in", "ind" },
|
|
||||||
{ "id", "idn" },
|
|
||||||
{ "ir", "irn" },
|
|
||||||
{ "iq", "irq" },
|
|
||||||
{ "ie", "irl" },
|
|
||||||
{ "im", "imn" },
|
|
||||||
{ "il", "isr" },
|
|
||||||
{ "it", "ita" },
|
|
||||||
{ "jm", "jam" },
|
|
||||||
{ "jp", "jpn" },
|
|
||||||
{ "je", "jey" },
|
|
||||||
{ "jo", "jor" },
|
|
||||||
{ "kz", "kaz" },
|
|
||||||
{ "ke", "ken" },
|
|
||||||
{ "ki", "kir" },
|
|
||||||
{ "kp", "prk" },
|
|
||||||
{ "kr", "kor" },
|
|
||||||
{ "kw", "kwt" },
|
|
||||||
{ "kg", "kgz" },
|
|
||||||
{ "la", "lao" },
|
|
||||||
{ "lv", "lva" },
|
|
||||||
{ "lb", "lbn" },
|
|
||||||
{ "ls", "lso" },
|
|
||||||
{ "lr", "lbr" },
|
|
||||||
{ "ly", "lby" },
|
|
||||||
{ "li", "lie" },
|
|
||||||
{ "lt", "ltu" },
|
|
||||||
{ "lu", "lux" },
|
|
||||||
{ "mk", "mkd" },
|
|
||||||
{ "mg", "mdg" },
|
|
||||||
{ "mw", "mwi" },
|
|
||||||
{ "my", "mys" },
|
|
||||||
{ "mv", "mdv" },
|
|
||||||
{ "ml", "mli" },
|
|
||||||
{ "mt", "mlt" },
|
|
||||||
{ "mh", "mhl" },
|
|
||||||
{ "mq", "mtq" },
|
|
||||||
{ "mr", "mrt" },
|
|
||||||
{ "mu", "mus" },
|
|
||||||
{ "yt", "myt" },
|
|
||||||
{ "mx", "mex" },
|
|
||||||
{ "fm", "fsm" },
|
|
||||||
{ "md", "mda" },
|
|
||||||
{ "mc", "mco" },
|
|
||||||
{ "mn", "mng" },
|
|
||||||
{ "me", "mne" },
|
|
||||||
{ "ms", "msr" },
|
|
||||||
{ "ma", "mar" },
|
|
||||||
{ "mz", "moz" },
|
|
||||||
{ "mm", "mmr" },
|
|
||||||
{ "na", "nam" },
|
|
||||||
{ "nr", "nru" },
|
|
||||||
{ "np", "npl" },
|
|
||||||
{ "nl", "nld" },
|
|
||||||
{ "an", "ant" },
|
|
||||||
{ "nc", "ncl" },
|
|
||||||
{ "nz", "nzl" },
|
|
||||||
{ "ni", "nic" },
|
|
||||||
{ "ne", "ner" },
|
|
||||||
{ "ng", "nga" },
|
|
||||||
{ "nu", "niu" },
|
|
||||||
{ "nf", "nfk" },
|
|
||||||
{ "mp", "mnp" },
|
|
||||||
{ "no", "nor" },
|
|
||||||
{ "om", "omn" },
|
|
||||||
{ "pk", "pak" },
|
|
||||||
{ "pw", "plw" },
|
|
||||||
{ "ps", "pse" },
|
|
||||||
{ "pa", "pan" },
|
|
||||||
{ "pg", "png" },
|
|
||||||
{ "py", "pry" },
|
|
||||||
{ "pe", "per" },
|
|
||||||
{ "ph", "phl" },
|
|
||||||
{ "pn", "pcn" },
|
|
||||||
{ "pl", "pol" },
|
|
||||||
{ "pt", "prt" },
|
|
||||||
{ "pr", "pri" },
|
|
||||||
{ "qa", "qat" },
|
|
||||||
{ "re", "reu" },
|
|
||||||
{ "ro", "rou" },
|
|
||||||
{ "ru", "rus" },
|
|
||||||
{ "rw", "rwa" },
|
|
||||||
{ "bl", "blm" },
|
|
||||||
{ "sh", "shn" },
|
|
||||||
{ "kn", "kna" },
|
|
||||||
{ "lc", "lca" },
|
|
||||||
{ "mf", "maf" },
|
|
||||||
{ "pm", "spm" },
|
|
||||||
{ "vc", "vct" },
|
|
||||||
{ "ws", "wsm" },
|
|
||||||
{ "sm", "smr" },
|
|
||||||
{ "st", "stp" },
|
|
||||||
{ "sa", "sau" },
|
|
||||||
{ "sn", "sen" },
|
|
||||||
{ "rs", "srb" },
|
|
||||||
{ "sc", "syc" },
|
|
||||||
{ "sl", "sle" },
|
|
||||||
{ "sg", "sgp" },
|
|
||||||
{ "sk", "svk" },
|
|
||||||
{ "si", "svn" },
|
|
||||||
{ "sb", "slb" },
|
|
||||||
{ "so", "som" },
|
|
||||||
{ "za", "zaf" },
|
|
||||||
{ "gs", "sgs" },
|
|
||||||
{ "ss", "ssd" },
|
|
||||||
{ "es", "esp" },
|
|
||||||
{ "lk", "lka" },
|
|
||||||
{ "sd", "sdn" },
|
|
||||||
{ "sr", "sur" },
|
|
||||||
{ "sj", "sjm" },
|
|
||||||
{ "sz", "swz" },
|
|
||||||
{ "se", "swe" },
|
|
||||||
{ "ch", "che" },
|
|
||||||
{ "sy", "syr" },
|
|
||||||
{ "tw", "twn" },
|
|
||||||
{ "tj", "tjk" },
|
|
||||||
{ "tz", "tza" },
|
|
||||||
{ "th", "tha" },
|
|
||||||
{ "tl", "tls" },
|
|
||||||
{ "tg", "tgo" },
|
|
||||||
{ "tk", "tkl" },
|
|
||||||
{ "to", "ton" },
|
|
||||||
{ "tt", "tto" },
|
|
||||||
{ "tn", "tun" },
|
|
||||||
{ "tr", "tur" },
|
|
||||||
{ "tm", "tkm" },
|
|
||||||
{ "tc", "tca" },
|
|
||||||
{ "tv", "tuv" },
|
|
||||||
{ "ug", "uga" },
|
|
||||||
{ "ua", "ukr" },
|
|
||||||
{ "ae", "are" },
|
|
||||||
{ "gb", "gbr" },
|
|
||||||
{ "us", "usa" },
|
|
||||||
{ "um", "umi" },
|
|
||||||
{ "uy", "ury" },
|
|
||||||
{ "uz", "uzb" },
|
|
||||||
{ "vu", "vut" },
|
|
||||||
{ "ve", "ven" },
|
|
||||||
{ "vn", "vnm" },
|
|
||||||
{ "vi", "vir" },
|
|
||||||
{ "wf", "wlf" },
|
|
||||||
{ "eh", "esh" },
|
|
||||||
{ "ye", "yem" },
|
|
||||||
{ "zm", "zmb" },
|
|
||||||
{ "zw", "zwe" }
|
|
||||||
};
|
|
||||||
|
|
||||||
void kiwix::sleep(unsigned int milliseconds)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
Sleep(milliseconds);
|
|
||||||
#else
|
|
||||||
usleep(1000 * milliseconds);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct XmlStringWriter: pugi::xml_writer
|
|
||||||
{
|
|
||||||
std::string result;
|
|
||||||
virtual void write(const void* data, size_t size){
|
|
||||||
result.append(static_cast<const char*>(data), size);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string kiwix::nodeToString(pugi::xml_node node)
|
|
||||||
{
|
|
||||||
XmlStringWriter writer;
|
|
||||||
node.print(writer, " ");
|
|
||||||
return writer.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string kiwix::converta2toa3(const std::string& a2code){
|
|
||||||
return codeisomapping.at(a2code);
|
|
||||||
}
|
|
||||||
@@ -1,338 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011-2014 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <common/pathTools.h>
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <limits.h>
|
|
||||||
#include <mach-o/dyld.h>
|
|
||||||
#elif _WIN32
|
|
||||||
#include <direct.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#include "shlwapi.h"
|
|
||||||
#define getcwd _getcwd // stupid MSFT "deprecation" warning
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#else
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define SEPARATOR "\\"
|
|
||||||
#else
|
|
||||||
#define SEPARATOR "/"
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#ifndef PATH_MAX
|
|
||||||
#define PATH_MAX 1024
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool isRelativePath(const string& path)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
return path.empty() || path.substr(1, 2) == ":\\" ? false : true;
|
|
||||||
#else
|
|
||||||
return path.empty() || path.substr(0, 1) == "/" ? false : true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
string computeRelativePath(const string path, const string absolutePath)
|
|
||||||
{
|
|
||||||
std::vector<std::string> pathParts = kiwix::split(path, SEPARATOR);
|
|
||||||
std::vector<std::string> absolutePathParts
|
|
||||||
= kiwix::split(absolutePath, SEPARATOR);
|
|
||||||
|
|
||||||
unsigned int commonCount = 0;
|
|
||||||
while (commonCount < pathParts.size()
|
|
||||||
&& commonCount < absolutePathParts.size()
|
|
||||||
&& pathParts[commonCount] == absolutePathParts[commonCount]) {
|
|
||||||
if (!pathParts[commonCount].empty()) {
|
|
||||||
commonCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string relativePath;
|
|
||||||
#ifdef _WIN32
|
|
||||||
/* On Windows you have a token more because the root is represented
|
|
||||||
by a letter */
|
|
||||||
if (commonCount == 0) {
|
|
||||||
relativePath = "../";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (unsigned int i = commonCount; i < pathParts.size(); i++) {
|
|
||||||
relativePath += "../";
|
|
||||||
}
|
|
||||||
for (unsigned int i = commonCount; i < absolutePathParts.size(); i++) {
|
|
||||||
relativePath += absolutePathParts[i];
|
|
||||||
relativePath += i + 1 < absolutePathParts.size() ? "/" : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return relativePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Warning: the relative path must be with slashes */
|
|
||||||
string computeAbsolutePath(const string path, const string relativePath)
|
|
||||||
{
|
|
||||||
string absolutePath;
|
|
||||||
|
|
||||||
if (path.empty()) {
|
|
||||||
char* path = NULL;
|
|
||||||
size_t size = 0;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
path = _getcwd(path, size);
|
|
||||||
#else
|
|
||||||
path = getcwd(path, size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
absolutePath = string(path) + SEPARATOR;
|
|
||||||
} else {
|
|
||||||
absolutePath = path.substr(path.length() - 1, 1) == SEPARATOR
|
|
||||||
? path
|
|
||||||
: path + SEPARATOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if _WIN32
|
|
||||||
char* cRelativePath = _strdup(relativePath.c_str());
|
|
||||||
#else
|
|
||||||
char* cRelativePath = strdup(relativePath.c_str());
|
|
||||||
#endif
|
|
||||||
char* token = strtok(cRelativePath, "/");
|
|
||||||
|
|
||||||
while (token != NULL) {
|
|
||||||
if (string(token) == "..") {
|
|
||||||
absolutePath = removeLastPathElement(absolutePath, true, false);
|
|
||||||
token = strtok(NULL, "/");
|
|
||||||
} else if (strcmp(token, ".") && strcmp(token, "")) {
|
|
||||||
absolutePath += string(token);
|
|
||||||
token = strtok(NULL, "/");
|
|
||||||
if (token != NULL) {
|
|
||||||
absolutePath += SEPARATOR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
token = strtok(NULL, "/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return absolutePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
string removeLastPathElement(const string path,
|
|
||||||
const bool removePreSeparator,
|
|
||||||
const bool removePostSeparator)
|
|
||||||
{
|
|
||||||
string newPath = path;
|
|
||||||
size_t offset = newPath.find_last_of(SEPARATOR);
|
|
||||||
if (removePreSeparator &&
|
|
||||||
#ifndef _WIN32
|
|
||||||
offset != newPath.find_first_of(SEPARATOR) &&
|
|
||||||
#endif
|
|
||||||
offset == newPath.length() - 1) {
|
|
||||||
newPath = newPath.substr(0, offset);
|
|
||||||
offset = newPath.find_last_of(SEPARATOR);
|
|
||||||
}
|
|
||||||
newPath = removePostSeparator ? newPath.substr(0, offset)
|
|
||||||
: newPath.substr(0, offset + 1);
|
|
||||||
return newPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
string appendToDirectory(const string& directoryPath, const string& filename)
|
|
||||||
{
|
|
||||||
string newPath = directoryPath + SEPARATOR + filename;
|
|
||||||
return newPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
string getLastPathElement(const string& path)
|
|
||||||
{
|
|
||||||
return path.substr(path.find_last_of(SEPARATOR) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int getFileSize(const string& path)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
struct _stat filestatus;
|
|
||||||
_stat(path.c_str(), &filestatus);
|
|
||||||
#else
|
|
||||||
struct stat filestatus;
|
|
||||||
stat(path.c_str(), &filestatus);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return filestatus.st_size / 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
string getFileSizeAsString(const string& path)
|
|
||||||
{
|
|
||||||
ostringstream convert;
|
|
||||||
convert << getFileSize(path);
|
|
||||||
return convert.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
string getFileContent(const string& path)
|
|
||||||
{
|
|
||||||
std::ifstream f(path, std::ios::in|std::ios::ate);
|
|
||||||
std::string content;
|
|
||||||
if (f.is_open()) {
|
|
||||||
auto size = f.tellg();
|
|
||||||
content.reserve(size);
|
|
||||||
f.seekg(0, std::ios::beg);
|
|
||||||
content.assign((std::istreambuf_iterator<char>(f)),
|
|
||||||
std::istreambuf_iterator<char>());
|
|
||||||
}
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fileExists(const string& path)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
return PathFileExists(path.c_str());
|
|
||||||
#else
|
|
||||||
bool flag = false;
|
|
||||||
fstream fin;
|
|
||||||
fin.open(path.c_str(), ios::in);
|
|
||||||
if (fin.is_open()) {
|
|
||||||
flag = true;
|
|
||||||
}
|
|
||||||
fin.close();
|
|
||||||
return flag;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool makeDirectory(const string& path)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
int status = _mkdir(path.c_str());
|
|
||||||
#else
|
|
||||||
int status = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
|
||||||
#endif
|
|
||||||
return status == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
string makeTmpDirectory()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
char cbase[MAX_PATH+1];
|
|
||||||
int base_len = GetTempPath(MAX_PATH+1, cbase);
|
|
||||||
UUID uuid;
|
|
||||||
UuidCreate(&uuid);
|
|
||||||
char* dir_name;
|
|
||||||
UuidToString(&uuid, reinterpret_cast<unsigned char**>(&dir_name));
|
|
||||||
string dir(cbase, base_len);
|
|
||||||
dir += dir_name;
|
|
||||||
_mkdir(dir.c_str());
|
|
||||||
RpcStringFree(reinterpret_cast<unsigned char**>(&dir_name));
|
|
||||||
#else
|
|
||||||
string base = "/tmp";
|
|
||||||
auto _template = base + "/kiwix-lib_XXXXXX";
|
|
||||||
char* _template_array = new char[_template.size()+1];
|
|
||||||
memcpy(_template_array, _template.c_str(), _template.size());
|
|
||||||
string dir = mkdtemp(_template_array);
|
|
||||||
delete[] _template_array;
|
|
||||||
#endif
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to create a link and if does not work then make a copy */
|
|
||||||
bool copyFile(const string& sourcePath, const string& destPath)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
#ifndef _WIN32
|
|
||||||
if (link(sourcePath.c_str(), destPath.c_str()) != 0) {
|
|
||||||
#endif
|
|
||||||
std::ifstream infile(sourcePath.c_str(), std::ios_base::binary);
|
|
||||||
std::ofstream outfile(destPath.c_str(), std::ios_base::binary);
|
|
||||||
outfile << infile.rdbuf();
|
|
||||||
#ifndef _WIN32
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} catch (exception& e) {
|
|
||||||
cerr << e.what() << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
string getExecutablePath()
|
|
||||||
{
|
|
||||||
char binRootPath[PATH_MAX];
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
GetModuleFileName(NULL, binRootPath, PATH_MAX);
|
|
||||||
return std::string(binRootPath);
|
|
||||||
#elif __APPLE__
|
|
||||||
uint32_t max = (uint32_t)PATH_MAX;
|
|
||||||
_NSGetExecutablePath(binRootPath, &max);
|
|
||||||
return std::string(binRootPath);
|
|
||||||
#else
|
|
||||||
ssize_t size = readlink("/proc/self/exe", binRootPath, PATH_MAX);
|
|
||||||
if (size != -1) {
|
|
||||||
return std::string(binRootPath, size);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool writeTextFile(const string& path, const string& content)
|
|
||||||
{
|
|
||||||
std::ofstream file;
|
|
||||||
file.open(path.c_str());
|
|
||||||
file << content;
|
|
||||||
file.close();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
string getCurrentDirectory()
|
|
||||||
{
|
|
||||||
char* a_cwd = getcwd(NULL, 0);
|
|
||||||
string s_cwd(a_cwd);
|
|
||||||
free(a_cwd);
|
|
||||||
return s_cwd;
|
|
||||||
}
|
|
||||||
|
|
||||||
string getDataDirectory()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
char* cDataDir = ::getenv("APPDATA");
|
|
||||||
#else
|
|
||||||
char* cDataDir = ::getenv("KIWIX_DATA_DIR");
|
|
||||||
#endif
|
|
||||||
std::string dataDir = cDataDir==nullptr ? "" : cDataDir;
|
|
||||||
if (!dataDir.empty())
|
|
||||||
return dataDir;
|
|
||||||
#ifdef _WIN32
|
|
||||||
cDataDir = ::getenv("USERPROFILE");
|
|
||||||
dataDir = cDataDir==nullptr ? getCurrentDirectory() : cDataDir;
|
|
||||||
#else
|
|
||||||
cDataDir = ::getenv("XDG_DATA_HOME");
|
|
||||||
dataDir = cDataDir==nullptr ? "" : cDataDir;
|
|
||||||
if (dataDir.empty()) {
|
|
||||||
cDataDir = ::getenv("HOME");
|
|
||||||
dataDir = cDataDir==nullptr ? getCurrentDirectory() : cDataDir;
|
|
||||||
dataDir = appendToDirectory(dataDir, ".local");
|
|
||||||
dataDir = appendToDirectory(dataDir, "share");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return appendToDirectory(dataDir, "kiwix");
|
|
||||||
}
|
|
||||||
+1
-2
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
#mesondefine VERSION
|
#mesondefine LIBKIWIX_VERSION
|
||||||
|
|
||||||
#mesondefine ENABLE_CTPP2
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013 Renaud Gaudin <reg@kiwix.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <ctpp2/CTPP2VMStringLoader.hpp>
|
|
||||||
|
|
||||||
namespace CTPP // C++ Template Engine
|
|
||||||
{
|
|
||||||
|
|
||||||
//
|
|
||||||
// Convert byte order
|
|
||||||
//
|
|
||||||
static void ConvertExecutable(VMExecutable * oCore)
|
|
||||||
{
|
|
||||||
// Code entry point
|
|
||||||
oCore -> entry_point = Swap32(oCore -> entry_point);
|
|
||||||
// Offset of code segment
|
|
||||||
oCore -> code_offset = Swap32(oCore -> code_offset);
|
|
||||||
|
|
||||||
// Code segment size
|
|
||||||
oCore -> code_size = Swap32(oCore -> code_size);
|
|
||||||
|
|
||||||
// Offset of static text segment
|
|
||||||
oCore -> syscalls_offset = Swap32(oCore -> syscalls_offset);
|
|
||||||
// Static text segment size
|
|
||||||
oCore -> syscalls_data_size = Swap32(oCore -> syscalls_data_size);
|
|
||||||
|
|
||||||
// Offset of static text index segment
|
|
||||||
oCore -> syscalls_index_offset = Swap32(oCore -> syscalls_index_offset);
|
|
||||||
// Static text index segment size
|
|
||||||
oCore -> syscalls_index_size = Swap32(oCore -> syscalls_index_size);
|
|
||||||
|
|
||||||
// Offset of static data segment
|
|
||||||
oCore -> static_data_offset = Swap32(oCore -> static_data_offset);
|
|
||||||
|
|
||||||
// Static data segment size
|
|
||||||
oCore -> static_data_data_size = Swap32(oCore -> static_data_data_size);
|
|
||||||
|
|
||||||
// Offset of static text segment
|
|
||||||
oCore -> static_text_offset = Swap32(oCore -> static_text_offset);
|
|
||||||
// Static text segment size
|
|
||||||
oCore -> static_text_data_size = Swap32(oCore -> static_text_data_size);
|
|
||||||
|
|
||||||
// Offset of static text index segment
|
|
||||||
oCore -> static_text_index_offset = Swap32(oCore -> static_text_index_offset);
|
|
||||||
// Static text index segment size
|
|
||||||
oCore -> static_text_index_size = Swap32(oCore -> static_text_index_size);
|
|
||||||
|
|
||||||
// Version 2.2+
|
|
||||||
// Offset of static data bit index
|
|
||||||
oCore -> static_data_bit_index_offset = Swap32(oCore -> static_data_bit_index_offset);
|
|
||||||
/// Offset of static data bit index
|
|
||||||
oCore -> static_data_bit_index_size = Swap32(oCore -> static_data_bit_index_size);
|
|
||||||
|
|
||||||
// Platform
|
|
||||||
oCore -> platform = Swap64(oCore -> platform);
|
|
||||||
|
|
||||||
// Ugly-jolly hack!
|
|
||||||
// ... dereferencing type-punned pointer will break strict-aliasing rules ...
|
|
||||||
UINT_64 iTMP;
|
|
||||||
memcpy(&iTMP, &(oCore -> ieee754double), sizeof(UINT_64));
|
|
||||||
iTMP = Swap64(iTMP);
|
|
||||||
memcpy(&(oCore -> ieee754double), &iTMP, sizeof(UINT_64));
|
|
||||||
|
|
||||||
// Cyclic Redundancy Check
|
|
||||||
oCore -> crc = 0;
|
|
||||||
|
|
||||||
// Convert data structures
|
|
||||||
|
|
||||||
// Convert code segment
|
|
||||||
VMInstruction * pInstructions = const_cast<VMInstruction *>(VMExecutable::GetCodeSeg(oCore));
|
|
||||||
UINT_32 iI = 0;
|
|
||||||
UINT_32 iSteps = oCore -> code_size / sizeof(VMInstruction);
|
|
||||||
for(iI = 0; iI < iSteps; ++iI)
|
|
||||||
{
|
|
||||||
pInstructions -> instruction = Swap32(pInstructions -> instruction);
|
|
||||||
pInstructions -> argument = Swap32(pInstructions -> argument);
|
|
||||||
pInstructions -> reserved = Swap64(pInstructions -> reserved);
|
|
||||||
++pInstructions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert syscalls index
|
|
||||||
TextDataIndex * pTextIndex = const_cast<TextDataIndex *>(VMExecutable::GetSyscallsIndexSeg(oCore));
|
|
||||||
iSteps = oCore -> syscalls_index_size / sizeof(TextDataIndex);
|
|
||||||
for(iI = 0; iI < iSteps; ++iI)
|
|
||||||
{
|
|
||||||
pTextIndex -> offset = Swap32(pTextIndex -> offset);
|
|
||||||
pTextIndex -> length = Swap32(pTextIndex -> length);
|
|
||||||
++pTextIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert static text index
|
|
||||||
pTextIndex = const_cast<TextDataIndex *>(VMExecutable::GetStaticTextIndexSeg(oCore));
|
|
||||||
iSteps = oCore -> static_text_index_size / sizeof(TextDataIndex);
|
|
||||||
for(iI = 0; iI < iSteps; ++iI)
|
|
||||||
{
|
|
||||||
pTextIndex -> offset = Swap32(pTextIndex -> offset);
|
|
||||||
pTextIndex -> length = Swap32(pTextIndex -> length);
|
|
||||||
++pTextIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert static data
|
|
||||||
StaticDataVar * pStaticDataVar = const_cast<StaticDataVar *>(VMExecutable::GetStaticDataSeg(oCore));
|
|
||||||
iSteps = oCore -> static_data_data_size / sizeof(StaticDataVar);
|
|
||||||
for(iI = 0; iI < iSteps; ++iI)
|
|
||||||
{
|
|
||||||
(*pStaticDataVar).i_data = Swap64((*pStaticDataVar).i_data);
|
|
||||||
++pStaticDataVar;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Constructor
|
|
||||||
//
|
|
||||||
VMStringLoader::VMStringLoader(CCHAR_P rawContent, size_t rawContentSize)
|
|
||||||
{
|
|
||||||
oCore = (VMExecutable *)malloc(rawContentSize + 1);
|
|
||||||
memcpy(oCore, rawContent, rawContentSize);
|
|
||||||
|
|
||||||
if (oCore -> magic[0] == 'C' &&
|
|
||||||
oCore -> magic[1] == 'T' &&
|
|
||||||
oCore -> magic[2] == 'P' &&
|
|
||||||
oCore -> magic[3] == 'P')
|
|
||||||
{
|
|
||||||
// Check version
|
|
||||||
if (oCore -> version[0] >= 1)
|
|
||||||
{
|
|
||||||
// Platform-dependent data (byte order)
|
|
||||||
if (oCore -> platform == 0x4142434445464748ull)
|
|
||||||
{
|
|
||||||
#ifdef _DEBUG
|
|
||||||
fprintf(stderr, "Big/Little Endian conversion: Nothing to do\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Nothing to do, only check crc
|
|
||||||
UINT_32 iCRC = oCore -> crc;
|
|
||||||
oCore -> crc = 0;
|
|
||||||
|
|
||||||
// Calculate CRC of file
|
|
||||||
// KELSON: next line used to refer to oStat.st_size
|
|
||||||
// changed it to rawContentSize
|
|
||||||
if (iCRC != crc32((UCCHAR_P)oCore, rawContentSize))
|
|
||||||
{
|
|
||||||
free(oCore);
|
|
||||||
throw CTPPLogicError("CRC checksum invalid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Platform-dependent data (byte order)
|
|
||||||
else if (oCore -> platform == 0x4847464544434241ull)
|
|
||||||
{
|
|
||||||
// Need to reconvert data
|
|
||||||
#ifdef _DEBUG
|
|
||||||
fprintf(stderr, "Big/Little Endian conversion: Need to reconvert core\n");
|
|
||||||
#endif
|
|
||||||
ConvertExecutable(oCore);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
free(oCore);
|
|
||||||
throw CTPPLogicError("Conversion of middle-end architecture does not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check IEEE 754 format
|
|
||||||
if (oCore -> ieee754double != 15839800103804824402926068484019465486336.0)
|
|
||||||
{
|
|
||||||
free(oCore);
|
|
||||||
throw CTPPLogicError("IEEE 754 format is broken, cannot convert file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pVMMemoryCore = new VMMemoryCore(oCore);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
free(oCore);
|
|
||||||
throw CTPPLogicError("Not an CTPP bytecode file.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Get ready-to-run program
|
|
||||||
//
|
|
||||||
const VMMemoryCore * VMStringLoader::GetCore() const { return pVMMemoryCore; }
|
|
||||||
|
|
||||||
//
|
|
||||||
// A destructor
|
|
||||||
//
|
|
||||||
VMStringLoader::~VMStringLoader() throw()
|
|
||||||
{
|
|
||||||
delete pVMMemoryCore;
|
|
||||||
free(oCore);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace CTPP
|
|
||||||
// End.
|
|
||||||
+78
-21
@@ -18,7 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "downloader.h"
|
#include "downloader.h"
|
||||||
#include "common/pathTools.h"
|
#include "tools/pathTools.h"
|
||||||
|
#include "tools/stringTools.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
|
|
||||||
#include "aria2.h"
|
#include "aria2.h"
|
||||||
#include "xmlrpc.h"
|
#include "xmlrpc.h"
|
||||||
#include "common/otherTools.h"
|
#include "tools/otherTools.h"
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
|
|
||||||
namespace kiwix
|
namespace kiwix
|
||||||
@@ -36,6 +37,8 @@ namespace kiwix
|
|||||||
|
|
||||||
void Download::updateStatus(bool follow)
|
void Download::updateStatus(bool follow)
|
||||||
{
|
{
|
||||||
|
if (m_status == Download::K_REMOVED)
|
||||||
|
return;
|
||||||
static std::vector<std::string> statusKey = {"status", "files", "totalLength",
|
static std::vector<std::string> statusKey = {"status", "files", "totalLength",
|
||||||
"completedLength", "followedBy",
|
"completedLength", "followedBy",
|
||||||
"downloadSpeed", "verifiedLength"};
|
"downloadSpeed", "verifiedLength"};
|
||||||
@@ -72,12 +75,12 @@ void Download::updateStatus(bool follow)
|
|||||||
} catch (InvalidRPCNode& e) { }
|
} catch (InvalidRPCNode& e) { }
|
||||||
}
|
}
|
||||||
m_status = status;
|
m_status = status;
|
||||||
m_totalLength = structNode.getMember("totalLength").getValue().getAsI();
|
m_totalLength = extractFromString<uint64_t>(structNode.getMember("totalLength").getValue().getAsS());
|
||||||
m_completedLength = structNode.getMember("completedLength").getValue().getAsI();
|
m_completedLength = extractFromString<uint64_t>(structNode.getMember("completedLength").getValue().getAsS());
|
||||||
m_downloadSpeed = structNode.getMember("downloadSpeed").getValue().getAsI();
|
m_downloadSpeed = extractFromString<uint64_t>(structNode.getMember("downloadSpeed").getValue().getAsS());
|
||||||
try {
|
try {
|
||||||
auto verifiedLengthValue = structNode.getMember("verifiedLength").getValue();
|
auto verifiedLengthValue = structNode.getMember("verifiedLength").getValue();
|
||||||
m_verifiedLength = verifiedLengthValue.getAsI();
|
m_verifiedLength = extractFromString<uint64_t>(verifiedLengthValue.getAsS());
|
||||||
} catch (InvalidRPCNode& e) { m_verifiedLength = 0; }
|
} catch (InvalidRPCNode& e) { m_verifiedLength = 0; }
|
||||||
auto filesMember = structNode.getMember("files");
|
auto filesMember = structNode.getMember("files");
|
||||||
auto fileStruct = filesMember.getValue().getArray().getValue(0).getStruct();
|
auto fileStruct = filesMember.getValue().getArray().getValue(0).getStruct();
|
||||||
@@ -93,17 +96,57 @@ void Download::updateStatus(bool follow)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Download::resumeDownload()
|
||||||
|
{
|
||||||
|
if (!m_followedBy.empty())
|
||||||
|
mp_aria->unpause(m_followedBy);
|
||||||
|
else
|
||||||
|
mp_aria->unpause(m_did);
|
||||||
|
updateStatus(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Download::pauseDownload()
|
||||||
|
{
|
||||||
|
if (!m_followedBy.empty())
|
||||||
|
mp_aria->pause(m_followedBy);
|
||||||
|
else
|
||||||
|
mp_aria->pause(m_did);
|
||||||
|
updateStatus(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Download::cancelDownload()
|
||||||
|
{
|
||||||
|
if (!m_followedBy.empty())
|
||||||
|
mp_aria->remove(m_followedBy);
|
||||||
|
else
|
||||||
|
mp_aria->remove(m_did);
|
||||||
|
m_status = Download::K_REMOVED;
|
||||||
|
}
|
||||||
|
|
||||||
/* Constructor */
|
/* Constructor */
|
||||||
Downloader::Downloader() :
|
Downloader::Downloader() :
|
||||||
mp_aria(new Aria2())
|
mp_aria(new Aria2())
|
||||||
{
|
{
|
||||||
for (auto gid : mp_aria->tellActive()) {
|
try {
|
||||||
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
|
for (auto gid : mp_aria->tellWaiting()) {
|
||||||
m_knownDownloads[gid]->updateStatus();
|
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
|
||||||
|
m_knownDownloads[gid]->updateStatus(false);
|
||||||
|
}
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
std::cerr << "aria2 tellWaiting failed : " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
for (auto gid : mp_aria->tellActive()) {
|
||||||
|
if( m_knownDownloads.find(gid) == m_knownDownloads.end()) {
|
||||||
|
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
|
||||||
|
m_knownDownloads[gid]->updateStatus(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
std::cerr << "aria2 tellActive failed : " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Destructor */
|
/* Destructor */
|
||||||
Downloader::~Downloader()
|
Downloader::~Downloader()
|
||||||
{
|
{
|
||||||
@@ -114,7 +157,8 @@ void Downloader::close()
|
|||||||
mp_aria->close();
|
mp_aria->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Downloader::getDownloadIds() {
|
std::vector<std::string> Downloader::getDownloadIds() const {
|
||||||
|
std::unique_lock<std::mutex> lock(m_lock);
|
||||||
std::vector<std::string> ret;
|
std::vector<std::string> ret;
|
||||||
for(auto& p:m_knownDownloads) {
|
for(auto& p:m_knownDownloads) {
|
||||||
ret.push_back(p.first);
|
ret.push_back(p.first);
|
||||||
@@ -122,33 +166,46 @@ std::vector<std::string> Downloader::getDownloadIds() {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Download* Downloader::startDownload(const std::string& uri)
|
std::shared_ptr<Download> Downloader::startDownload(const std::string& uri, const std::vector<std::pair<std::string, std::string>>& options)
|
||||||
{
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_lock);
|
||||||
for (auto& p: m_knownDownloads) {
|
for (auto& p: m_knownDownloads) {
|
||||||
auto& d = p.second;
|
auto& d = p.second;
|
||||||
auto& uris = d->getUris();
|
auto& uris = d->getUris();
|
||||||
if (std::find(uris.begin(), uris.end(), uri) != uris.end())
|
if (std::find(uris.begin(), uris.end(), uri) != uris.end())
|
||||||
return d.get();
|
return d;
|
||||||
}
|
}
|
||||||
std::vector<std::string> uris = {uri};
|
std::vector<std::string> uris = {uri};
|
||||||
auto gid = mp_aria->addUri(uris);
|
auto gid = mp_aria->addUri(uris, options);
|
||||||
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
|
m_knownDownloads[gid] = std::make_shared<Download>(mp_aria, gid);
|
||||||
return m_knownDownloads[gid].get();
|
return m_knownDownloads[gid];
|
||||||
}
|
}
|
||||||
|
|
||||||
Download* Downloader::getDownload(const std::string& did)
|
std::shared_ptr<Download> Downloader::getDownload(const std::string& did)
|
||||||
{
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_lock);
|
||||||
try {
|
try {
|
||||||
return m_knownDownloads.at(did).get();
|
return m_knownDownloads.at(did);
|
||||||
} catch(exception& e) {
|
} catch(std::exception& e) {
|
||||||
|
for (auto gid : mp_aria->tellWaiting()) {
|
||||||
|
if (gid == did) {
|
||||||
|
m_knownDownloads[gid] = std::make_shared<Download>(mp_aria, gid);
|
||||||
|
return m_knownDownloads[gid];
|
||||||
|
}
|
||||||
|
}
|
||||||
for (auto gid : mp_aria->tellActive()) {
|
for (auto gid : mp_aria->tellActive()) {
|
||||||
if (gid == did) {
|
if (gid == did) {
|
||||||
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
|
m_knownDownloads[gid] = std::make_shared<Download>(mp_aria, gid);
|
||||||
return m_knownDownloads[gid].get();
|
return m_knownDownloads[gid];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t Downloader::getNbDownload() const {
|
||||||
|
std::unique_lock<std::mutex> lock(m_lock);
|
||||||
|
return m_knownDownloads.size();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
-138
@@ -1,138 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "reader.h"
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include <zim/search.h>
|
|
||||||
|
|
||||||
namespace kiwix
|
|
||||||
{
|
|
||||||
|
|
||||||
Entry::Entry(zim::Article article)
|
|
||||||
: article(article)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RETURN_IF_INVALID(WHAT) if(!good()) { return (WHAT); }
|
|
||||||
|
|
||||||
std::string Entry::getPath() const
|
|
||||||
{
|
|
||||||
RETURN_IF_INVALID("");
|
|
||||||
return article.getLongUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Entry::getTitle() const
|
|
||||||
{
|
|
||||||
RETURN_IF_INVALID("");
|
|
||||||
return article.getTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Entry::getContent() const
|
|
||||||
{
|
|
||||||
RETURN_IF_INVALID("");
|
|
||||||
return article.getData();
|
|
||||||
}
|
|
||||||
|
|
||||||
zim::Blob Entry::getBlob(offset_type offset) const
|
|
||||||
{
|
|
||||||
RETURN_IF_INVALID(zim::Blob());
|
|
||||||
return article.getData(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
zim::Blob Entry::getBlob(offset_type offset, size_type size) const
|
|
||||||
{
|
|
||||||
RETURN_IF_INVALID(zim::Blob());
|
|
||||||
return article.getData(offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<std::string, offset_type> Entry::getDirectAccessInfo() const
|
|
||||||
{
|
|
||||||
RETURN_IF_INVALID(std::make_pair("", 0));
|
|
||||||
return article.getDirectAccessInformation();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_type Entry::getSize() const
|
|
||||||
{
|
|
||||||
RETURN_IF_INVALID(0);
|
|
||||||
return article.getArticleSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Entry::getMimetype() const
|
|
||||||
{
|
|
||||||
RETURN_IF_INVALID("");
|
|
||||||
try {
|
|
||||||
return article.getMimeType();
|
|
||||||
} catch (exception& e) {
|
|
||||||
return "application/octet-stream";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Entry::isRedirect() const
|
|
||||||
{
|
|
||||||
RETURN_IF_INVALID(false);
|
|
||||||
return article.isRedirect();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Entry::isLinkTarget() const
|
|
||||||
{
|
|
||||||
RETURN_IF_INVALID(false);
|
|
||||||
return article.isLinktarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Entry::isDeleted() const
|
|
||||||
{
|
|
||||||
RETURN_IF_INVALID(false);
|
|
||||||
return article.isDeleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry Entry::getRedirectEntry() const
|
|
||||||
{
|
|
||||||
RETURN_IF_INVALID(Entry());
|
|
||||||
if ( !article.isRedirect() ) {
|
|
||||||
throw NoEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto targeted_article = article.getRedirectArticle();
|
|
||||||
if ( !targeted_article.good()) {
|
|
||||||
throw NoEntry();
|
|
||||||
}
|
|
||||||
return targeted_article;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry Entry::getFinalEntry() const
|
|
||||||
{
|
|
||||||
RETURN_IF_INVALID(Entry());
|
|
||||||
if (final_article.good()) {
|
|
||||||
return final_article;
|
|
||||||
}
|
|
||||||
|
|
||||||
int loopCounter = 42;
|
|
||||||
final_article = article;
|
|
||||||
while (final_article.isRedirect() && loopCounter--) {
|
|
||||||
final_article = final_article.getRedirectArticle();
|
|
||||||
if ( !final_article.good()) {
|
|
||||||
throw NoEntry();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return final_article;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
#include "html_dumper.h"
|
||||||
|
#include "libkiwix-resources.h"
|
||||||
|
#include "tools/otherTools.h"
|
||||||
|
#include "tools.h"
|
||||||
|
#include "tools/regexTools.h"
|
||||||
|
#include "server/i18n.h"
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Constructor */
|
||||||
|
HTMLDumper::HTMLDumper(const Library* library, const NameMapper* nameMapper)
|
||||||
|
: LibraryDumper(library, nameMapper)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
/* Destructor */
|
||||||
|
HTMLDumper::~HTMLDumper()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string humanFriendlyTitle(std::string title)
|
||||||
|
{
|
||||||
|
std::string humanFriendlyString = replaceRegex(title, "_", " ");
|
||||||
|
humanFriendlyString[0] = toupper(humanFriendlyString[0]);
|
||||||
|
return humanFriendlyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
kainjow::mustache::list getTagList(std::string tags)
|
||||||
|
{
|
||||||
|
const auto tagsList = kiwix::split(tags, ";", true, false);
|
||||||
|
kainjow::mustache::list finalTagList;
|
||||||
|
for (auto tag : tagsList) {
|
||||||
|
if (tag[0] != '_')
|
||||||
|
finalTagList.push_back(kainjow::mustache::object{
|
||||||
|
{"tag", tag}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return finalTagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // unnamed namespace
|
||||||
|
|
||||||
|
std::string HTMLDumper::dumpPlainHTML(kiwix::Filter filter) const
|
||||||
|
{
|
||||||
|
kainjow::mustache::list booksData;
|
||||||
|
const auto filteredBooks = library->filter(filter);
|
||||||
|
const auto searchQuery = filter.getQuery();
|
||||||
|
auto languages = getLanguageData();
|
||||||
|
auto categories = getCategoryData();
|
||||||
|
|
||||||
|
for (auto &category : categories) {
|
||||||
|
const auto categoryName = category.get("name")->string_value();
|
||||||
|
if (categoryName == filter.getCategory()) {
|
||||||
|
category["selected"] = true;
|
||||||
|
}
|
||||||
|
category["hf_name"] = humanFriendlyTitle(categoryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &language : languages) {
|
||||||
|
if (language.get("lang_code")->string_value() == filter.getLang()) {
|
||||||
|
language["selected"] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( const auto& bookId : filteredBooks ) {
|
||||||
|
const auto bookObj = library->getBookById(bookId);
|
||||||
|
const auto bookTitle = bookObj.getTitle();
|
||||||
|
std::string contentId = "";
|
||||||
|
try {
|
||||||
|
contentId = urlEncode(nameMapper->getNameForId(bookId));
|
||||||
|
} catch (...) {}
|
||||||
|
const auto bookDescription = bookObj.getDescription();
|
||||||
|
const auto langCode = bookObj.getCommaSeparatedLanguages();
|
||||||
|
const auto bookIconUrl = rootLocation + "/catalog/v2/illustration/" + bookId + "/?size=48";
|
||||||
|
const auto tags = bookObj.getTags();
|
||||||
|
const auto downloadAvailable = (bookObj.getUrl() != "");
|
||||||
|
std::string faviconAttr = "style=background-image:url(" + bookIconUrl + ")";
|
||||||
|
|
||||||
|
booksData.push_back(kainjow::mustache::object{
|
||||||
|
{"id", contentId},
|
||||||
|
{"title", bookTitle},
|
||||||
|
{"description", bookDescription},
|
||||||
|
{"langCode", langCode},
|
||||||
|
{"faviconAttr", faviconAttr},
|
||||||
|
{"tagList", getTagList(tags)},
|
||||||
|
{"downloadAvailable", downloadAvailable}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getTranslation = i18n::GetTranslatedStringWithMsgId(m_userLang);
|
||||||
|
|
||||||
|
const auto translations = kainjow::mustache::object{
|
||||||
|
getTranslation("search"),
|
||||||
|
getTranslation("download"),
|
||||||
|
getTranslation("count-of-matching-books", {{"COUNT", to_string(filteredBooks.size())}}),
|
||||||
|
getTranslation("book-filtering-all-categories"),
|
||||||
|
getTranslation("book-filtering-all-languages"),
|
||||||
|
getTranslation("powered-by-kiwix-html"),
|
||||||
|
getTranslation("welcome-to-kiwix-server"),
|
||||||
|
getTranslation("preview-book"),
|
||||||
|
getTranslation("welcome-page-overzealous-filter", {{"URL", "?lang="}})
|
||||||
|
};
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
RESOURCE::templates::no_js_library_page_html,
|
||||||
|
kainjow::mustache::object{
|
||||||
|
{"root", rootLocation},
|
||||||
|
{"books", booksData },
|
||||||
|
{"searchQuery", searchQuery},
|
||||||
|
{"languages", languages},
|
||||||
|
{"categories", categories},
|
||||||
|
{"noResults", filteredBooks.size() == 0},
|
||||||
|
{"translations", translations}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace kiwix
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
#include "kiwixserve.h"
|
||||||
|
#include "subprocess.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# define KIWIXSERVE_CMD "kiwix-serve.exe"
|
||||||
|
# include <windows.h>
|
||||||
|
#else
|
||||||
|
# define KIWIXSERVE_CMD "kiwix-serve"
|
||||||
|
# include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "tools.h"
|
||||||
|
#include "tools/pathTools.h"
|
||||||
|
#include "tools/stringTools.h"
|
||||||
|
|
||||||
|
namespace kiwix {
|
||||||
|
|
||||||
|
KiwixServe::KiwixServe(const std::string& libraryPath, int port)
|
||||||
|
: m_port(port),
|
||||||
|
m_libraryPath(libraryPath)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
KiwixServe::~KiwixServe()
|
||||||
|
{
|
||||||
|
shutDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KiwixServe::run()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
int pid = GetCurrentProcessId();
|
||||||
|
#else
|
||||||
|
pid_t pid = getpid();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::vector<const char*> callCmd;
|
||||||
|
std::string kiwixServeCmd = appendToDirectory(
|
||||||
|
removeLastPathElement(getExecutablePath(true)),
|
||||||
|
KIWIXSERVE_CMD);
|
||||||
|
if (fileExists(kiwixServeCmd)) {
|
||||||
|
// A local kiwix-serve exe exists (packaged with kiwix-desktop), use it.
|
||||||
|
callCmd.push_back(kiwixServeCmd.c_str());
|
||||||
|
} else {
|
||||||
|
// Try to use a potential installed kiwix-serve.
|
||||||
|
callCmd.push_back(KIWIXSERVE_CMD);
|
||||||
|
}
|
||||||
|
std::string attachProcessOpt = "-a" + to_string(pid);
|
||||||
|
std::string portOpt = "-p" + to_string(m_port);
|
||||||
|
callCmd.push_back(attachProcessOpt.c_str());
|
||||||
|
callCmd.push_back(portOpt.c_str());
|
||||||
|
callCmd.push_back("-l");
|
||||||
|
callCmd.push_back(m_libraryPath.c_str());
|
||||||
|
mp_kiwixServe = Subprocess::run(callCmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KiwixServe::shutDown()
|
||||||
|
{
|
||||||
|
if (mp_kiwixServe)
|
||||||
|
mp_kiwixServe->kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KiwixServe::isRunning()
|
||||||
|
{
|
||||||
|
if (mp_kiwixServe) {
|
||||||
|
return (mp_kiwixServe->isRunning());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KiwixServe::setPort(int port)
|
||||||
|
{
|
||||||
|
if (port >= 1 && port <= 65535) {
|
||||||
|
m_port = port;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return m_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+800
-222
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,120 @@
|
|||||||
|
#include "library_dumper.h"
|
||||||
|
#include "tools/stringTools.h"
|
||||||
|
#include "tools/otherTools.h"
|
||||||
|
#include "tools.h"
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
|
{
|
||||||
|
/* Constructor */
|
||||||
|
LibraryDumper::LibraryDumper(const Library* library, const NameMapper* nameMapper)
|
||||||
|
: library(library),
|
||||||
|
nameMapper(nameMapper)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
/* Destructor */
|
||||||
|
LibraryDumper::~LibraryDumper()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryDumper::setOpenSearchInfo(int totalResults, int startIndex, int count)
|
||||||
|
{
|
||||||
|
m_totalResults = totalResults;
|
||||||
|
m_startIndex = startIndex,
|
||||||
|
m_count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::map<std::string, std::string> iso639_3 = {
|
||||||
|
{"atj", "atikamekw"},
|
||||||
|
{"azb", "آذربایجان دیلی"},
|
||||||
|
{"bcl", "central bikol"},
|
||||||
|
{"bgs", "tagabawa"},
|
||||||
|
{"bxr", "буряад хэлэн"},
|
||||||
|
{"cbk", "chavacano"},
|
||||||
|
{"cdo", "閩東語"},
|
||||||
|
{"dag", "Dagbani"},
|
||||||
|
{"diq", "dimli"},
|
||||||
|
{"dty", "डोटेली"},
|
||||||
|
{"eml", "emiliân-rumagnōl"},
|
||||||
|
{"fbs", "српскохрватски"},
|
||||||
|
{"hbs", "srpskohrvatski"},
|
||||||
|
{"ido", "ido"},
|
||||||
|
{"kbp", "kabɩyɛ"},
|
||||||
|
{"kld", "Gamilaraay"},
|
||||||
|
{"lbe", "лакку маз"},
|
||||||
|
{"lbj", "ལ་དྭགས་སྐད་"},
|
||||||
|
{"map", "Austronesian"},
|
||||||
|
{"mhr", "марий йылме"},
|
||||||
|
{"mnw", "ဘာသာမန်"},
|
||||||
|
{"myn", "mayan"},
|
||||||
|
{"nah", "nahuatl"},
|
||||||
|
{"nai", "north American Indian"},
|
||||||
|
{"nds", "plattdütsch"},
|
||||||
|
{"nrm", "bhasa narom"},
|
||||||
|
{"olo", "livvi"},
|
||||||
|
{"pih", "Pitcairn-Norfolk"},
|
||||||
|
{"pnb", "Western Panjabi"},
|
||||||
|
{"rmr", "Caló"},
|
||||||
|
{"rmy", "romani shib"},
|
||||||
|
{"roa", "romance languages"},
|
||||||
|
{"twi", "twi"},
|
||||||
|
};
|
||||||
|
|
||||||
|
std::once_flag fillLanguagesFlag;
|
||||||
|
|
||||||
|
void fillLanguagesMap()
|
||||||
|
{
|
||||||
|
for (auto icuLangPtr = icu::Locale::getISOLanguages(); *icuLangPtr != NULL; ++icuLangPtr) {
|
||||||
|
const ICULanguageInfo lang(*icuLangPtr);
|
||||||
|
iso639_3.insert({lang.iso3Code(), lang.selfName()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getLanguageSelfName(const std::string& lang) {
|
||||||
|
const auto itr = iso639_3.find(lang);
|
||||||
|
if (itr != iso639_3.end()) {
|
||||||
|
return itr->second;
|
||||||
|
}
|
||||||
|
return lang;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // unnamed namespace
|
||||||
|
|
||||||
|
kainjow::mustache::list LibraryDumper::getCategoryData() const
|
||||||
|
{
|
||||||
|
const auto now = gen_date_str();
|
||||||
|
kainjow::mustache::list categoryData;
|
||||||
|
for ( const auto& category : library->getBooksCategories() ) {
|
||||||
|
const auto urlencodedCategoryName = urlEncode(category);
|
||||||
|
categoryData.push_back(kainjow::mustache::object{
|
||||||
|
{"name", category},
|
||||||
|
{"urlencoded_name", urlencodedCategoryName},
|
||||||
|
{"updated", now},
|
||||||
|
{"id", gen_uuid(libraryId + "/categories/" + urlencodedCategoryName)}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return categoryData;
|
||||||
|
}
|
||||||
|
|
||||||
|
kainjow::mustache::list LibraryDumper::getLanguageData() const
|
||||||
|
{
|
||||||
|
const auto now = gen_date_str();
|
||||||
|
kainjow::mustache::list languageData;
|
||||||
|
std::call_once(fillLanguagesFlag, fillLanguagesMap);
|
||||||
|
for ( const auto& langAndBookCount : library->getBooksLanguagesWithCounts() ) {
|
||||||
|
const std::string languageCode = langAndBookCount.first;
|
||||||
|
const int bookCount = langAndBookCount.second;
|
||||||
|
const auto languageSelfName = getLanguageSelfName(languageCode);
|
||||||
|
languageData.push_back(kainjow::mustache::object{
|
||||||
|
{"lang_code", languageCode},
|
||||||
|
{"lang_self_name", languageSelfName},
|
||||||
|
{"book_count", to_string(bookCount)},
|
||||||
|
{"updated", now},
|
||||||
|
{"id", gen_uuid(libraryId + "/languages/" + languageCode)}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return languageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace kiwix
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Matthieu Gautier <mgautier@kymeria.fr>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libxml_dumper.h"
|
||||||
|
#include "book.h"
|
||||||
|
|
||||||
|
#include "tools.h"
|
||||||
|
#include "tools/base64.h"
|
||||||
|
#include "tools/stringTools.h"
|
||||||
|
#include "tools/otherTools.h"
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
|
{
|
||||||
|
/* Constructor */
|
||||||
|
LibXMLDumper::LibXMLDumper(const Library* library)
|
||||||
|
: library(library)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
/* Destructor */
|
||||||
|
LibXMLDumper::~LibXMLDumper()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ADD_ATTRIBUTE(node, name, value) { (node).append_attribute((name)) = (value).c_str(); }
|
||||||
|
#define ADD_ATTR_NOT_EMPTY(node, name, value) { if (!(value).empty()) ADD_ATTRIBUTE(node, name, value); }
|
||||||
|
|
||||||
|
void LibXMLDumper::handleBook(Book book, pugi::xml_node root_node) {
|
||||||
|
if (book.readOnly())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto entry_node = root_node.append_child("book");
|
||||||
|
ADD_ATTRIBUTE(entry_node, "id", book.getId());
|
||||||
|
|
||||||
|
if (!book.getPath().empty()) {
|
||||||
|
ADD_ATTRIBUTE(entry_node, "path", computeRelativePath(baseDir, book.getPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (book.getOrigId().empty()) {
|
||||||
|
ADD_ATTR_NOT_EMPTY(entry_node, "title", book.getTitle());
|
||||||
|
ADD_ATTR_NOT_EMPTY(entry_node, "description", book.getDescription());
|
||||||
|
ADD_ATTR_NOT_EMPTY(entry_node, "language", book.getCommaSeparatedLanguages());
|
||||||
|
ADD_ATTR_NOT_EMPTY(entry_node, "creator", book.getCreator());
|
||||||
|
ADD_ATTR_NOT_EMPTY(entry_node, "publisher", book.getPublisher());
|
||||||
|
ADD_ATTR_NOT_EMPTY(entry_node, "name", book.getName());
|
||||||
|
ADD_ATTR_NOT_EMPTY(entry_node, "flavour", book.getFlavour());
|
||||||
|
ADD_ATTR_NOT_EMPTY(entry_node, "tags", book.getTags());
|
||||||
|
try {
|
||||||
|
auto defaultIllustration = book.getIllustration(48);
|
||||||
|
ADD_ATTR_NOT_EMPTY(entry_node, "faviconMimeType", defaultIllustration->mimeType);
|
||||||
|
ADD_ATTR_NOT_EMPTY(entry_node, "faviconUrl", defaultIllustration->url);
|
||||||
|
if (!defaultIllustration->getData().empty())
|
||||||
|
ADD_ATTRIBUTE(entry_node, "favicon", base64_encode(defaultIllustration->getData()));
|
||||||
|
} catch(...) {}
|
||||||
|
} else {
|
||||||
|
ADD_ATTRIBUTE(entry_node, "origId", book.getOrigId());
|
||||||
|
}
|
||||||
|
|
||||||
|
ADD_ATTR_NOT_EMPTY(entry_node, "date", book.getDate());
|
||||||
|
ADD_ATTR_NOT_EMPTY(entry_node, "url", book.getUrl());
|
||||||
|
|
||||||
|
if (book.getArticleCount())
|
||||||
|
ADD_ATTRIBUTE(entry_node, "articleCount", to_string(book.getArticleCount()));
|
||||||
|
|
||||||
|
if (book.getMediaCount())
|
||||||
|
ADD_ATTRIBUTE(entry_node, "mediaCount", to_string(book.getMediaCount()));
|
||||||
|
|
||||||
|
if (book.getSize())
|
||||||
|
ADD_ATTRIBUTE(entry_node, "size", to_string(book.getSize()>>10));
|
||||||
|
|
||||||
|
ADD_ATTR_NOT_EMPTY(entry_node, "downloadId", book.getDownloadId());
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ADD_TEXT_ENTRY(node, child, value) (node).append_child((child)).append_child(pugi::node_pcdata).set_value((value).c_str())
|
||||||
|
|
||||||
|
void LibXMLDumper::handleBookmark(Bookmark bookmark, pugi::xml_node root_node) {
|
||||||
|
|
||||||
|
auto entry_node = root_node.append_child("bookmark");
|
||||||
|
auto book_node = entry_node.append_child("book");
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto book = library->getBookByIdThreadSafe(bookmark.getBookId());
|
||||||
|
ADD_TEXT_ENTRY(book_node, "id", book.getId());
|
||||||
|
ADD_TEXT_ENTRY(book_node, "title", book.getTitle());
|
||||||
|
ADD_TEXT_ENTRY(book_node, "language", book.getCommaSeparatedLanguages());
|
||||||
|
ADD_TEXT_ENTRY(book_node, "date", book.getDate());
|
||||||
|
} catch (...) {
|
||||||
|
ADD_TEXT_ENTRY(book_node, "id", bookmark.getBookId());
|
||||||
|
ADD_TEXT_ENTRY(book_node, "title", bookmark.getBookTitle());
|
||||||
|
ADD_TEXT_ENTRY(book_node, "language", bookmark.getLanguage());
|
||||||
|
ADD_TEXT_ENTRY(book_node, "date", bookmark.getDate());
|
||||||
|
}
|
||||||
|
ADD_TEXT_ENTRY(entry_node, "title", bookmark.getTitle());
|
||||||
|
ADD_TEXT_ENTRY(entry_node, "url", bookmark.getUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string LibXMLDumper::dumpLibXMLContent(const std::vector<std::string>& bookIds)
|
||||||
|
{
|
||||||
|
pugi::xml_document doc;
|
||||||
|
|
||||||
|
/* Add the library node */
|
||||||
|
pugi::xml_node libraryNode = doc.append_child("library");
|
||||||
|
|
||||||
|
libraryNode.append_attribute("version") = KIWIX_LIBRARY_VERSION;
|
||||||
|
|
||||||
|
if (library) {
|
||||||
|
for (auto& bookId: bookIds) {
|
||||||
|
handleBook(library->getBookById(bookId), libraryNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodeToString(libraryNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LibXMLDumper::dumpLibXMLBookmark()
|
||||||
|
{
|
||||||
|
pugi::xml_document doc;
|
||||||
|
|
||||||
|
/* Add the library node */
|
||||||
|
pugi::xml_node bookmarksNode = doc.append_child("bookmarks");
|
||||||
|
|
||||||
|
if (library) {
|
||||||
|
for (auto& bookmark: library->getBookmarks()) {
|
||||||
|
handleBookmark(bookmark, bookmarksNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodeToString(bookmarksNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+149
-42
@@ -19,35 +19,92 @@
|
|||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
|
#include "tools.h"
|
||||||
|
#include "tools/pathTools.h"
|
||||||
|
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
|
|
||||||
namespace kiwix
|
namespace kiwix
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
struct NoDelete
|
||||||
|
{
|
||||||
|
template<class T> void operator()(T*) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // unnamed namespace
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// LibraryManipulator
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
LibraryManipulator::LibraryManipulator(Library* library)
|
||||||
|
: library(*library)
|
||||||
|
{}
|
||||||
|
|
||||||
|
LibraryManipulator::~LibraryManipulator()
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool LibraryManipulator::addBookToLibrary(const Book& book)
|
||||||
|
{
|
||||||
|
const auto ret = library.addBook(book);
|
||||||
|
if ( ret ) {
|
||||||
|
bookWasAddedToLibrary(book);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryManipulator::addBookmarkToLibrary(const Bookmark& bookmark)
|
||||||
|
{
|
||||||
|
library.addBookmark(bookmark);
|
||||||
|
bookmarkWasAddedToLibrary(bookmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t LibraryManipulator::removeBooksNotUpdatedSince(Library::Revision rev)
|
||||||
|
{
|
||||||
|
const auto n = library.removeBooksNotUpdatedSince(rev);
|
||||||
|
if ( n != 0 ) {
|
||||||
|
booksWereRemovedFromLibrary();
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryManipulator::bookWasAddedToLibrary(const Book& book)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryManipulator::bookmarkWasAddedToLibrary(const Bookmark& bookmark)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryManipulator::booksWereRemovedFromLibrary()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Manager
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/* Constructor */
|
/* Constructor */
|
||||||
Manager::Manager(LibraryManipulator* manipulator):
|
Manager::Manager(LibraryManipulator* manipulator):
|
||||||
writableLibraryPath(""),
|
writableLibraryPath(""),
|
||||||
manipulator(manipulator),
|
manipulator(manipulator, NoDelete())
|
||||||
mustDeleteManipulator(false)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Manager::Manager(Library* library) :
|
Manager::Manager(Library* library) :
|
||||||
writableLibraryPath(""),
|
writableLibraryPath(""),
|
||||||
manipulator(new DefaultLibraryManipulator(library)),
|
manipulator(new LibraryManipulator(library))
|
||||||
mustDeleteManipulator(true)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Destructor */
|
|
||||||
Manager::~Manager()
|
|
||||||
{
|
|
||||||
if (mustDeleteManipulator) {
|
|
||||||
delete manipulator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool Manager::parseXmlDom(const pugi::xml_document& doc,
|
bool Manager::parseXmlDom(const pugi::xml_document& doc,
|
||||||
const bool readOnly,
|
bool readOnly,
|
||||||
const std::string& libraryPath)
|
const std::string& libraryPath,
|
||||||
|
bool trustLibrary)
|
||||||
{
|
{
|
||||||
pugi::xml_node libraryNode = doc.child("library");
|
pugi::xml_node libraryNode = doc.child("library");
|
||||||
|
|
||||||
@@ -59,14 +116,10 @@ bool Manager::parseXmlDom(const pugi::xml_document& doc,
|
|||||||
|
|
||||||
book.setReadOnly(readOnly);
|
book.setReadOnly(readOnly);
|
||||||
book.updateFromXml(bookNode,
|
book.updateFromXml(bookNode,
|
||||||
removeLastPathElement(libraryPath, true, false));
|
removeLastPathElement(libraryPath));
|
||||||
|
|
||||||
/* Update the book properties with the new importer */
|
if (!trustLibrary && !book.getPath().empty()) {
|
||||||
if (libraryVersion.empty()
|
this->readBookFromPath(book.getPath(), &book);
|
||||||
|| atoi(libraryVersion.c_str()) <= atoi(KIWIX_LIBRARY_VERSION)) {
|
|
||||||
if (!book.getPath().empty()) {
|
|
||||||
this->readBookFromPath(book.getPath(), &book);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
manipulator->addBookToLibrary(book);
|
manipulator->addBookToLibrary(book);
|
||||||
}
|
}
|
||||||
@@ -75,15 +128,16 @@ bool Manager::parseXmlDom(const pugi::xml_document& doc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Manager::readXml(const std::string& xml,
|
bool Manager::readXml(const std::string& xml,
|
||||||
const bool readOnly,
|
bool readOnly,
|
||||||
const std::string& libraryPath)
|
const std::string& libraryPath,
|
||||||
|
bool trustLibrary)
|
||||||
{
|
{
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
pugi::xml_parse_result result
|
pugi::xml_parse_result result
|
||||||
= doc.load_buffer_inplace((void*)xml.data(), xml.size());
|
= doc.load_buffer((void*)xml.data(), xml.size());
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
this->parseXmlDom(doc, readOnly, libraryPath);
|
this->parseXmlDom(doc, readOnly, libraryPath, trustLibrary);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -95,6 +149,15 @@ bool Manager::parseOpdsDom(const pugi::xml_document& doc, const std::string& url
|
|||||||
{
|
{
|
||||||
pugi::xml_node libraryNode = doc.child("feed");
|
pugi::xml_node libraryNode = doc.child("feed");
|
||||||
|
|
||||||
|
try {
|
||||||
|
m_totalBooks = strtoull(libraryNode.child("totalResults").child_value(), 0, 0);
|
||||||
|
m_startIndex = strtoull(libraryNode.child("startIndex").child_value(), 0, 0);
|
||||||
|
m_itemsPerPage = strtoull(libraryNode.child("itemsPerPage").child_value(), 0, 0);
|
||||||
|
m_hasSearchResult = true;
|
||||||
|
} catch(...) {
|
||||||
|
m_hasSearchResult = false;
|
||||||
|
}
|
||||||
|
|
||||||
for (pugi::xml_node entryNode = libraryNode.child("entry"); entryNode;
|
for (pugi::xml_node entryNode = libraryNode.child("entry"); entryNode;
|
||||||
entryNode = entryNode.next_sibling("entry")) {
|
entryNode = entryNode.next_sibling("entry")) {
|
||||||
kiwix::Book book;
|
kiwix::Book book;
|
||||||
@@ -115,7 +178,7 @@ bool Manager::readOpds(const std::string& content, const std::string& urlHost)
|
|||||||
{
|
{
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
pugi::xml_parse_result result
|
pugi::xml_parse_result result
|
||||||
= doc.load_buffer_inplace((void*)content.data(), content.size());
|
= doc.load_buffer((void*)content.data(), content.size());
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
this->parseOpdsDom(doc, urlHost);
|
this->parseOpdsDom(doc, urlHost);
|
||||||
@@ -125,21 +188,22 @@ bool Manager::readOpds(const std::string& content, const std::string& urlHost)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Manager::readFile(const std::string& path, const bool readOnly)
|
bool Manager::readFile(
|
||||||
{
|
const std::string& path,
|
||||||
return this->readFile(path, path, readOnly);
|
bool readOnly,
|
||||||
}
|
bool trustLibrary)
|
||||||
|
|
||||||
bool Manager::readFile(const std::string& nativePath,
|
|
||||||
const std::string& UTF8Path,
|
|
||||||
const bool readOnly)
|
|
||||||
{
|
{
|
||||||
bool retVal = true;
|
bool retVal = true;
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
pugi::xml_parse_result result = doc.load_file(nativePath.c_str());
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
pugi::xml_parse_result result = doc.load_file(Utf8ToWide(path).c_str());
|
||||||
|
#else
|
||||||
|
pugi::xml_parse_result result = doc.load_file(path.c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
this->parseXmlDom(doc, readOnly, UTF8Path);
|
this->parseXmlDom(doc, readOnly, path, trustLibrary);
|
||||||
} else {
|
} else {
|
||||||
retVal = false;
|
retVal = false;
|
||||||
}
|
}
|
||||||
@@ -148,7 +212,7 @@ bool Manager::readFile(const std::string& nativePath,
|
|||||||
* able to know where to save the library if new content are
|
* able to know where to save the library if new content are
|
||||||
* available */
|
* available */
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
this->writableLibraryPath = UTF8Path;
|
this->writableLibraryPath = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
@@ -165,16 +229,16 @@ std::string Manager::addBookFromPathAndGetId(const std::string& pathToOpen,
|
|||||||
kiwix::Book book;
|
kiwix::Book book;
|
||||||
|
|
||||||
if (this->readBookFromPath(pathToOpen, &book)) {
|
if (this->readBookFromPath(pathToOpen, &book)) {
|
||||||
if (pathToSave != pathToOpen) {
|
if (!pathToSave.empty() && pathToSave != pathToOpen) {
|
||||||
book.setPath(isRelativePath(pathToSave)
|
book.setPath(isRelativePath(pathToSave)
|
||||||
? computeAbsolutePath(
|
? computeAbsolutePath(
|
||||||
removeLastPathElement(writableLibraryPath, true, false),
|
removeLastPathElement(writableLibraryPath),
|
||||||
pathToSave)
|
pathToSave)
|
||||||
: pathToSave);
|
: pathToSave);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!checkMetaData
|
if (!checkMetaData
|
||||||
|| (checkMetaData && !book.getTitle().empty() && !book.getLanguage().empty()
|
|| (!book.getTitle().empty() && !book.getLanguages().empty()
|
||||||
&& !book.getDate().empty())) {
|
&& !book.getDate().empty())) {
|
||||||
book.setUrl(url);
|
book.setUrl(url);
|
||||||
manipulator->addBookToLibrary(book);
|
manipulator->addBookToLibrary(book);
|
||||||
@@ -199,12 +263,15 @@ bool Manager::addBookFromPath(const std::string& pathToOpen,
|
|||||||
|
|
||||||
bool Manager::readBookFromPath(const std::string& path, kiwix::Book* book)
|
bool Manager::readBookFromPath(const std::string& path, kiwix::Book* book)
|
||||||
{
|
{
|
||||||
|
std::string tmp_path = path;
|
||||||
|
if (isRelativePath(path)) {
|
||||||
|
tmp_path = computeAbsolutePath(getCurrentDirectory(), path);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
kiwix::Reader reader(path);
|
zim::Archive archive(tmp_path);
|
||||||
book->update(reader);
|
book->update(archive);
|
||||||
book->setPathValid(true);
|
book->setPathValid(true);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::cerr << "Invalid " << path << " : " << e.what() << std::endl;
|
|
||||||
book->setPathValid(false);
|
book->setPathValid(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -212,4 +279,44 @@ bool Manager::readBookFromPath(const std::string& path, kiwix::Book* book)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Manager::readBookmarkFile(const std::string& path)
|
||||||
|
{
|
||||||
|
pugi::xml_document doc;
|
||||||
|
pugi::xml_parse_result result = doc.load_file(path.c_str());
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pugi::xml_node libraryNode = doc.child("bookmarks");
|
||||||
|
|
||||||
|
for (pugi::xml_node node = libraryNode.child("bookmark"); node;
|
||||||
|
node = node.next_sibling("bookmark")) {
|
||||||
|
kiwix::Bookmark bookmark;
|
||||||
|
|
||||||
|
bookmark.updateFromXml(node);
|
||||||
|
|
||||||
|
manipulator->addBookmarkToLibrary(bookmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::reload(const Paths& paths)
|
||||||
|
{
|
||||||
|
const auto libRevision = manipulator->getLibrary().getRevision();
|
||||||
|
for (std::string path : paths) {
|
||||||
|
if (!path.empty()) {
|
||||||
|
if ( kiwix::isRelativePath(path) )
|
||||||
|
path = kiwix::computeAbsolutePath(kiwix::getCurrentDirectory(), path);
|
||||||
|
|
||||||
|
if (!readFile(path, false, true)) {
|
||||||
|
throw std::runtime_error("Failed to load the XML library file '" + path + "'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
manipulator->removeBooksNotUpdatedSince(libRevision);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+29
-29
@@ -1,24 +1,38 @@
|
|||||||
kiwix_sources = [
|
kiwix_sources = [
|
||||||
'book.cpp',
|
'book.cpp',
|
||||||
|
'bookmark.cpp',
|
||||||
'library.cpp',
|
'library.cpp',
|
||||||
'manager.cpp',
|
'manager.cpp',
|
||||||
|
'libxml_dumper.cpp',
|
||||||
'opds_dumper.cpp',
|
'opds_dumper.cpp',
|
||||||
|
'html_dumper.cpp',
|
||||||
|
'library_dumper.cpp',
|
||||||
'downloader.cpp',
|
'downloader.cpp',
|
||||||
'reader.cpp',
|
'server.cpp',
|
||||||
'entry.cpp',
|
'search_renderer.cpp',
|
||||||
'searcher.cpp',
|
|
||||||
'subprocess.cpp',
|
'subprocess.cpp',
|
||||||
'aria2.cpp',
|
'aria2.cpp',
|
||||||
'common/base64.cpp',
|
'tools/base64.cpp',
|
||||||
'common/pathTools.cpp',
|
'tools/pathTools.cpp',
|
||||||
'common/regexTools.cpp',
|
'tools/regexTools.cpp',
|
||||||
'common/stringTools.cpp',
|
'tools/stringTools.cpp',
|
||||||
'common/networkTools.cpp',
|
'tools/networkTools.cpp',
|
||||||
'common/otherTools.cpp',
|
'tools/otherTools.cpp',
|
||||||
'xapian/htmlparse.cc',
|
'tools/archiveTools.cpp',
|
||||||
'xapian/myhtmlparse.cc'
|
'kiwixserve.cpp',
|
||||||
|
'name_mapper.cpp',
|
||||||
|
'server/byte_range.cpp',
|
||||||
|
'server/etag.cpp',
|
||||||
|
'server/request_context.cpp',
|
||||||
|
'server/response.cpp',
|
||||||
|
'server/internalServer.cpp',
|
||||||
|
'server/internalServer_catalog_v2.cpp',
|
||||||
|
'server/i18n.cpp',
|
||||||
|
'opds_catalog.cpp',
|
||||||
|
'version.cpp'
|
||||||
]
|
]
|
||||||
kiwix_sources += lib_resources
|
kiwix_sources += lib_resources
|
||||||
|
kiwix_sources += i18n_resources
|
||||||
|
|
||||||
if host_machine.system() == 'windows'
|
if host_machine.system() == 'windows'
|
||||||
kiwix_sources += 'subprocess_windows.cpp'
|
kiwix_sources += 'subprocess_windows.cpp'
|
||||||
@@ -26,32 +40,18 @@ else
|
|||||||
kiwix_sources += 'subprocess_unix.cpp'
|
kiwix_sources += 'subprocess_unix.cpp'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if xapian_dep.found()
|
install_dir = get_option('libdir')
|
||||||
kiwix_sources += ['xapianSearcher.cpp']
|
|
||||||
endif
|
|
||||||
|
|
||||||
if get_option('android')
|
|
||||||
subdir('android')
|
|
||||||
install_dir = 'kiwix-lib/jniLibs/' + meson.get_cross_property('android_abi')
|
|
||||||
else
|
|
||||||
install_dir = get_option('libdir')
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
if has_ctpp2_dep
|
|
||||||
kiwix_sources += ['ctpp2/CTPP2VMStringLoader.cpp']
|
|
||||||
endif
|
|
||||||
|
|
||||||
config_h = configure_file(output : 'kiwix_config.h',
|
config_h = configure_file(output : 'kiwix_config.h',
|
||||||
configuration : conf,
|
configuration : conf,
|
||||||
input : 'config.h.in')
|
input : 'config.h.in')
|
||||||
install_headers(config_h, subdir:'kiwix')
|
install_headers(config_h, subdir:'kiwix')
|
||||||
|
|
||||||
kiwixlib = library('kiwix',
|
libkiwix = library('kiwix',
|
||||||
kiwix_sources,
|
kiwix_sources,
|
||||||
include_directories : inc,
|
include_directories : inc,
|
||||||
dependencies : all_deps,
|
dependencies : all_deps,
|
||||||
|
link_args: extra_libs,
|
||||||
version: meson.project_version(),
|
version: meson.project_version(),
|
||||||
install: true,
|
install: true,
|
||||||
install_dir: install_dir,
|
install_dir: install_dir)
|
||||||
install_rpath: '$ORIGIN')
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Emmanuel Engelhart <kelson@kiwix.org>
|
* Copyright 2020 Emmanuel Engelhart <kelson@kiwix.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,9 +17,8 @@
|
|||||||
* MA 02110-1301, USA.
|
* MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.kiwix.kiwixlib;
|
#include <microhttpd.h>
|
||||||
|
|
||||||
public class JNIKiwixInt
|
#if MHD_VERSION < 0x00097002
|
||||||
{
|
typedef int MHD_Result;
|
||||||
public int value;
|
#endif
|
||||||
}
|
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Matthieu Gautier <mgautier@kymeria.fr>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "name_mapper.h"
|
||||||
|
#include "library.h"
|
||||||
|
#include "tools/regexTools.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace kiwix {
|
||||||
|
|
||||||
|
HumanReadableNameMapper::HumanReadableNameMapper(kiwix::Library& library, bool withAlias) {
|
||||||
|
for (auto& bookId: library.filter(kiwix::Filter().local(true).valid(true))) {
|
||||||
|
auto& currentBook = library.getBookById(bookId);
|
||||||
|
auto bookName = currentBook.getHumanReadableIdFromPath();
|
||||||
|
m_idToName[bookId] = bookName;
|
||||||
|
m_nameToId[bookName] = bookId;
|
||||||
|
|
||||||
|
if (!withAlias)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto aliasName = replaceRegex(bookName, "", "_[[:digit:]]{4}-[[:digit:]]{2}$");
|
||||||
|
if (aliasName == bookName) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (m_nameToId.find(aliasName) == m_nameToId.end()) {
|
||||||
|
m_nameToId[aliasName] = bookId;
|
||||||
|
} else {
|
||||||
|
auto alreadyPresentPath = library.getBookById(m_nameToId[aliasName]).getPath();
|
||||||
|
std::cerr << "Path collision: " << alreadyPresentPath
|
||||||
|
<< " and " << currentBook.getPath()
|
||||||
|
<< " can't share the same URL path '" << aliasName << "'."
|
||||||
|
<< " Therefore, only " << alreadyPresentPath
|
||||||
|
<< " will be served." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string HumanReadableNameMapper::getNameForId(const std::string& id) const {
|
||||||
|
return m_idToName.at(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string HumanReadableNameMapper::getIdForName(const std::string& name) const {
|
||||||
|
return m_nameToId.at(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UpdatableNameMapper
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
UpdatableNameMapper::UpdatableNameMapper(Library& lib, bool withAlias)
|
||||||
|
: library(lib)
|
||||||
|
, withAlias(withAlias)
|
||||||
|
{
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdatableNameMapper::update()
|
||||||
|
{
|
||||||
|
const auto newNameMapper = new HumanReadableNameMapper(library, withAlias);
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
nameMapper.reset(newNameMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdatableNameMapper::NameMapperHandle
|
||||||
|
UpdatableNameMapper::currentNameMapper() const
|
||||||
|
{
|
||||||
|
// Return a copy of the handle to the current NameMapper object. It will
|
||||||
|
// ensure that the object survives any call to UpdatableNameMapper::update()
|
||||||
|
// made before the completion of any pending operation on that object.
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
return nameMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string UpdatableNameMapper::getNameForId(const std::string& id) const
|
||||||
|
{
|
||||||
|
// Ensure that the current nameMapper object survives a concurrent call
|
||||||
|
// to UpdatableNameMapper::update()
|
||||||
|
return currentNameMapper()->getNameForId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string UpdatableNameMapper::getIdForName(const std::string& name) const
|
||||||
|
{
|
||||||
|
// Ensure that the current nameMapper object survives a concurrent call
|
||||||
|
// to UpdatableNameMapper::update()
|
||||||
|
return currentNameMapper()->getIdForName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Veloman Yunkan <veloman.yunkan@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "opds_catalog.h"
|
||||||
|
#include "tools/stringTools.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const char opdsSearchEndpoint[] = "/catalog/v2/entries";
|
||||||
|
|
||||||
|
enum Separator { AMP };
|
||||||
|
|
||||||
|
std::ostringstream& operator<<(std::ostringstream& oss, Separator sep)
|
||||||
|
{
|
||||||
|
if ( oss.tellp() > 0 )
|
||||||
|
oss << "&";
|
||||||
|
return oss;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string buildSearchString(const Filter& f)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
if ( f.hasQuery() )
|
||||||
|
oss << AMP << "q=" << urlEncode(f.getQuery());
|
||||||
|
|
||||||
|
if ( f.hasCategory() )
|
||||||
|
oss << AMP << "category=" << urlEncode(f.getCategory());
|
||||||
|
|
||||||
|
if ( f.hasLang() )
|
||||||
|
oss << AMP << "lang=" << urlEncode(f.getLang());
|
||||||
|
|
||||||
|
if ( f.hasName() )
|
||||||
|
oss << AMP << "name=" << urlEncode(f.getName());
|
||||||
|
|
||||||
|
if ( !f.getAcceptTags().empty() )
|
||||||
|
oss << AMP << "tag=" << urlEncode(join(f.getAcceptTags(), ";"));
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // unnamed namespace
|
||||||
|
|
||||||
|
std::string getSearchUrl(const Filter& f)
|
||||||
|
{
|
||||||
|
const std::string searchString = buildSearchString(f);
|
||||||
|
|
||||||
|
if ( searchString.empty() )
|
||||||
|
return opdsSearchEndpoint;
|
||||||
|
else
|
||||||
|
return opdsSearchEndpoint + ("?" + searchString);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace kiwix
|
||||||
+159
-88
@@ -20,13 +20,18 @@
|
|||||||
#include "opds_dumper.h"
|
#include "opds_dumper.h"
|
||||||
#include "book.h"
|
#include "book.h"
|
||||||
|
|
||||||
#include <common/otherTools.h>
|
#include "libkiwix-resources.h"
|
||||||
|
#include <mustache.hpp>
|
||||||
|
|
||||||
|
#include "tools/stringTools.h"
|
||||||
|
#include "tools/otherTools.h"
|
||||||
|
|
||||||
namespace kiwix
|
namespace kiwix
|
||||||
{
|
{
|
||||||
|
|
||||||
/* Constructor */
|
/* Constructor */
|
||||||
OPDSDumper::OPDSDumper(Library* library)
|
OPDSDumper::OPDSDumper(const Library* library, const NameMapper* nameMapper)
|
||||||
: library(library)
|
: LibraryDumper(library, nameMapper)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
/* Destructor */
|
/* Destructor */
|
||||||
@@ -34,104 +39,170 @@ OPDSDumper::~OPDSDumper()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string gen_date_str()
|
namespace
|
||||||
{
|
{
|
||||||
auto now = time(0);
|
|
||||||
auto tm = localtime(&now);
|
|
||||||
|
|
||||||
std::stringstream is;
|
const std::string XML_HEADER(R"(<?xml version="1.0" encoding="UTF-8"?>)");
|
||||||
is << std::setw(2) << std::setfill('0')
|
|
||||||
<< 1900+tm->tm_year << "-"
|
typedef kainjow::mustache::data MustacheData;
|
||||||
<< std::setw(2) << std::setfill('0') << tm->tm_mon << "-"
|
typedef kainjow::mustache::list BooksData;
|
||||||
<< std::setw(2) << std::setfill('0') << tm->tm_mday << "T"
|
typedef kainjow::mustache::list IllustrationInfo;
|
||||||
<< std::setw(2) << std::setfill('0') << tm->tm_hour << ":"
|
|
||||||
<< std::setw(2) << std::setfill('0') << tm->tm_min << ":"
|
IllustrationInfo getBookIllustrationInfo(const Book& book)
|
||||||
<< std::setw(2) << std::setfill('0') << tm->tm_sec << "Z";
|
{
|
||||||
return is.str();
|
kainjow::mustache::list illustrations;
|
||||||
|
if ( book.isPathValid() ) {
|
||||||
|
for ( const auto& illustration : book.getIllustrations() ) {
|
||||||
|
// For now, we are handling only sizexsize@1 illustration.
|
||||||
|
// So we can simply pass one size to mustache.
|
||||||
|
illustrations.push_back(kainjow::mustache::object{
|
||||||
|
{"icon_size", to_string(illustration->width)},
|
||||||
|
{"icon_mimetype", illustration->mimeType}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return illustrations;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OPDSDumper::setOpenSearchInfo(int totalResults, int startIndex, int count)
|
std::string fullEntryXML(const Book& book, const std::string& rootLocation, const std::string& contentId)
|
||||||
{
|
{
|
||||||
m_totalResults = totalResults;
|
const auto bookDate = book.getDate() + "T00:00:00Z";
|
||||||
m_startIndex = startIndex,
|
const kainjow::mustache::object data{
|
||||||
m_count = count;
|
{"root", rootLocation},
|
||||||
m_isSearchResult = true;
|
{"id", book.getId()},
|
||||||
|
{"name", book.getName()},
|
||||||
|
{"title", book.getTitle()},
|
||||||
|
{"description", book.getDescription()},
|
||||||
|
{"language", book.getCommaSeparatedLanguages()},
|
||||||
|
{"content_id", urlEncode(contentId)},
|
||||||
|
{"updated", bookDate}, // XXX: this should be the entry update datetime
|
||||||
|
{"book_date", bookDate},
|
||||||
|
{"category", book.getCategory()},
|
||||||
|
{"flavour", book.getFlavour()},
|
||||||
|
{"tags", book.getTags()},
|
||||||
|
{"article_count", to_string(book.getArticleCount())},
|
||||||
|
{"media_count", to_string(book.getMediaCount())},
|
||||||
|
{"author_name", book.getCreator()},
|
||||||
|
{"publisher_name", book.getPublisher()},
|
||||||
|
{"url", onlyAsNonEmptyMustacheValue(book.getUrl())},
|
||||||
|
{"size", to_string(book.getSize())},
|
||||||
|
{"icons", getBookIllustrationInfo(book)},
|
||||||
|
};
|
||||||
|
return render_template(RESOURCE::templates::catalog_v2_entry_xml, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ADD_TEXT_ENTRY(node, child, value) (node).append_child((child)).append_child(pugi::node_pcdata).set_value((value).c_str())
|
std::string partialEntryXML(const Book& book, const std::string& rootLocation)
|
||||||
|
{
|
||||||
pugi::xml_node OPDSDumper::handleBook(Book book, pugi::xml_node root_node) {
|
const auto bookDate = book.getDate() + "T00:00:00Z";
|
||||||
auto entry_node = root_node.append_child("entry");
|
const kainjow::mustache::object data{
|
||||||
ADD_TEXT_ENTRY(entry_node, "title", book.getTitle());
|
{"root", rootLocation},
|
||||||
ADD_TEXT_ENTRY(entry_node, "id", "urn:uuid:"+book.getId());
|
{"endpoint_root", rootLocation + "/catalog/v2"},
|
||||||
ADD_TEXT_ENTRY(entry_node, "icon", rootLocation + "/meta?name=favicon&content=" + book.getHumanReadableIdFromPath());
|
{"id", book.getId()},
|
||||||
ADD_TEXT_ENTRY(entry_node, "updated", date);
|
{"title", book.getTitle()},
|
||||||
ADD_TEXT_ENTRY(entry_node, "summary", book.getDescription());
|
{"updated", bookDate}, // XXX: this should be the entry update datetime
|
||||||
|
};
|
||||||
auto content_node = entry_node.append_child("link");
|
const auto xmlTemplate = RESOURCE::templates::catalog_v2_partial_entry_xml;
|
||||||
content_node.append_attribute("type") = "text/html";
|
return render_template(xmlTemplate, data);
|
||||||
content_node.append_attribute("href") = (rootLocation + "/" + book.getHumanReadableIdFromPath()).c_str();
|
|
||||||
|
|
||||||
auto author_node = entry_node.append_child("author");
|
|
||||||
ADD_TEXT_ENTRY(author_node, "name", book.getCreator());
|
|
||||||
|
|
||||||
if (! book.getUrl().empty()) {
|
|
||||||
auto acquisition_link = entry_node.append_child("link");
|
|
||||||
acquisition_link.append_attribute("rel") = "http://opds-spec.org/acquisition/open-access";
|
|
||||||
acquisition_link.append_attribute("type") = "application/x-zim";
|
|
||||||
acquisition_link.append_attribute("href") = book.getUrl().c_str();
|
|
||||||
acquisition_link.append_attribute("length") = to_string(book.getSize()).c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! book.getFaviconMimeType().empty() ) {
|
|
||||||
auto image_link = entry_node.append_child("link");
|
|
||||||
image_link.append_attribute("rel") = "http://opds-spec.org/image/thumbnail";
|
|
||||||
image_link.append_attribute("type") = book.getFaviconMimeType().c_str();
|
|
||||||
image_link.append_attribute("href") = (rootLocation + "/meta?name=favicon&content=" + book.getHumanReadableIdFromPath()).c_str();
|
|
||||||
}
|
|
||||||
return entry_node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string OPDSDumper::dumpOPDSFeed(const std::vector<std::string>& bookIds)
|
BooksData getBooksData(const Library* library, const NameMapper* nameMapper, const std::vector<std::string>& bookIds, const std::string& rootLocation, bool partial)
|
||||||
{
|
{
|
||||||
date = gen_date_str();
|
BooksData booksData;
|
||||||
pugi::xml_document doc;
|
for ( const auto& bookId : bookIds ) {
|
||||||
|
try {
|
||||||
auto root_node = doc.append_child("feed");
|
const Book book = library->getBookByIdThreadSafe(bookId);
|
||||||
root_node.append_attribute("xmlns") = "http://www.w3.org/2005/Atom";
|
const std::string contentId = nameMapper->getNameForId(bookId);
|
||||||
root_node.append_attribute("xmlns:opds") = "http://opds-spec.org/2010/catalog";
|
const auto entryXML = partial
|
||||||
|
? partialEntryXML(book, rootLocation)
|
||||||
ADD_TEXT_ENTRY(root_node, "id", id);
|
: fullEntryXML(book, rootLocation, contentId);
|
||||||
|
booksData.push_back(kainjow::mustache::object{ {"entry", entryXML} });
|
||||||
ADD_TEXT_ENTRY(root_node, "title", title);
|
} catch ( const std::out_of_range& ) {
|
||||||
ADD_TEXT_ENTRY(root_node, "updated", date);
|
// the book was removed from the library since its id was obtained
|
||||||
|
// ignore it
|
||||||
if (m_isSearchResult) {
|
|
||||||
ADD_TEXT_ENTRY(root_node, "totalResults", to_string(m_totalResults));
|
|
||||||
ADD_TEXT_ENTRY(root_node, "startIndex", to_string(m_startIndex));
|
|
||||||
ADD_TEXT_ENTRY(root_node, "itemsPerPage", to_string(m_count));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto self_link_node = root_node.append_child("link");
|
|
||||||
self_link_node.append_attribute("rel") = "self";
|
|
||||||
self_link_node.append_attribute("href") = "";
|
|
||||||
self_link_node.append_attribute("type") = "application/atom+xml";
|
|
||||||
|
|
||||||
|
|
||||||
if (!searchDescriptionUrl.empty() ) {
|
|
||||||
auto search_link = root_node.append_child("link");
|
|
||||||
search_link.append_attribute("rel") = "search";
|
|
||||||
search_link.append_attribute("type") = "application/opensearchdescription+xml";
|
|
||||||
search_link.append_attribute("href") = searchDescriptionUrl.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (library) {
|
|
||||||
for (auto& bookId: bookIds) {
|
|
||||||
handleBook(library->getBookById(bookId), root_node);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeToString(root_node);
|
return booksData;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // unnamed namespace
|
||||||
|
|
||||||
|
string OPDSDumper::dumpOPDSFeed(const std::vector<std::string>& bookIds, const std::string& query) const
|
||||||
|
{
|
||||||
|
const auto booksData = getBooksData(library, nameMapper, bookIds, rootLocation, false);
|
||||||
|
const kainjow::mustache::object template_data{
|
||||||
|
{"date", gen_date_str()},
|
||||||
|
{"root", rootLocation},
|
||||||
|
{"feed_id", gen_uuid(libraryId + "/catalog/search?"+query)},
|
||||||
|
{"filter", onlyAsNonEmptyMustacheValue(query)},
|
||||||
|
{"totalResults", to_string(m_totalResults)},
|
||||||
|
{"startIndex", to_string(m_startIndex)},
|
||||||
|
{"itemsPerPage", to_string(m_count)},
|
||||||
|
{"books", booksData }
|
||||||
|
};
|
||||||
|
|
||||||
|
return render_template(RESOURCE::templates::catalog_entries_xml, template_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
string OPDSDumper::dumpOPDSFeedV2(const std::vector<std::string>& bookIds, const std::string& query, bool partial) const
|
||||||
|
{
|
||||||
|
const auto endpointRoot = rootLocation + "/catalog/v2";
|
||||||
|
const auto booksData = getBooksData(library, nameMapper, bookIds, rootLocation, partial);
|
||||||
|
|
||||||
|
const char* const endpoint = partial ? "/partial_entries" : "/entries";
|
||||||
|
const kainjow::mustache::object template_data{
|
||||||
|
{"date", gen_date_str()},
|
||||||
|
{"endpoint_root", endpointRoot},
|
||||||
|
{"feed_id", gen_uuid(libraryId + endpoint + "?" + query)},
|
||||||
|
{"filter", onlyAsNonEmptyMustacheValue(query)},
|
||||||
|
{"query", query.empty() ? "" : "?" + query},
|
||||||
|
{"totalResults", to_string(m_totalResults)},
|
||||||
|
{"startIndex", to_string(m_startIndex)},
|
||||||
|
{"itemsPerPage", to_string(m_count)},
|
||||||
|
{"books", booksData },
|
||||||
|
{"dump_partial_entries", MustacheData(partial)}
|
||||||
|
};
|
||||||
|
|
||||||
|
return render_template(RESOURCE::templates::catalog_v2_entries_xml, template_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OPDSDumper::dumpOPDSCompleteEntry(const std::string& bookId) const
|
||||||
|
{
|
||||||
|
const auto book = library->getBookById(bookId);
|
||||||
|
const std::string contentId = nameMapper->getNameForId(bookId);
|
||||||
|
return XML_HEADER
|
||||||
|
+ "\n"
|
||||||
|
+ fullEntryXML(book, rootLocation, contentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OPDSDumper::categoriesOPDSFeed() const
|
||||||
|
{
|
||||||
|
const auto now = gen_date_str();
|
||||||
|
kainjow::mustache::list categoryData = getCategoryData();
|
||||||
|
return render_template(
|
||||||
|
RESOURCE::templates::catalog_v2_categories_xml,
|
||||||
|
kainjow::mustache::object{
|
||||||
|
{"date", now},
|
||||||
|
{"endpoint_root", rootLocation + "/catalog/v2"},
|
||||||
|
{"feed_id", gen_uuid(libraryId + "/categories")},
|
||||||
|
{"categories", categoryData }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OPDSDumper::languagesOPDSFeed() const
|
||||||
|
{
|
||||||
|
const auto now = gen_date_str();
|
||||||
|
kainjow::mustache::list languageData = getLanguageData();
|
||||||
|
return render_template(
|
||||||
|
RESOURCE::templates::catalog_v2_languages_xml,
|
||||||
|
kainjow::mustache::object{
|
||||||
|
{"date", now},
|
||||||
|
{"endpoint_root", rootLocation + "/catalog/v2"},
|
||||||
|
{"feed_id", gen_uuid(libraryId + "/languages")},
|
||||||
|
{"languages", languageData }
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
-862
@@ -1,862 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "reader.h"
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include <zim/search.h>
|
|
||||||
|
|
||||||
inline char hi(char v)
|
|
||||||
{
|
|
||||||
char hex[] = "0123456789abcdef";
|
|
||||||
return hex[(v >> 4) & 0xf];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline char lo(char v)
|
|
||||||
{
|
|
||||||
char hex[] = "0123456789abcdef";
|
|
||||||
return hex[v & 0xf];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string hexUUID(std::string in)
|
|
||||||
{
|
|
||||||
std::ostringstream out;
|
|
||||||
for (unsigned n = 0; n < 4; ++n) {
|
|
||||||
out << hi(in[n]) << lo(in[n]);
|
|
||||||
}
|
|
||||||
out << '-';
|
|
||||||
for (unsigned n = 4; n < 6; ++n) {
|
|
||||||
out << hi(in[n]) << lo(in[n]);
|
|
||||||
}
|
|
||||||
out << '-';
|
|
||||||
for (unsigned n = 6; n < 8; ++n) {
|
|
||||||
out << hi(in[n]) << lo(in[n]);
|
|
||||||
}
|
|
||||||
out << '-';
|
|
||||||
for (unsigned n = 8; n < 10; ++n) {
|
|
||||||
out << hi(in[n]) << lo(in[n]);
|
|
||||||
}
|
|
||||||
out << '-';
|
|
||||||
for (unsigned n = 10; n < 16; ++n) {
|
|
||||||
out << hi(in[n]) << lo(in[n]);
|
|
||||||
}
|
|
||||||
std::string op = out.str();
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace kiwix
|
|
||||||
{
|
|
||||||
/* Constructor */
|
|
||||||
Reader::Reader(const string zimFilePath) : zimFileHandler(NULL)
|
|
||||||
{
|
|
||||||
string tmpZimFilePath = zimFilePath;
|
|
||||||
|
|
||||||
/* Remove potential trailing zimaa */
|
|
||||||
size_t found = tmpZimFilePath.rfind("zimaa");
|
|
||||||
if (found != string::npos && tmpZimFilePath.size() > 5
|
|
||||||
&& found == tmpZimFilePath.size() - 5) {
|
|
||||||
tmpZimFilePath.resize(tmpZimFilePath.size() - 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->zimFileHandler = new zim::File(tmpZimFilePath);
|
|
||||||
|
|
||||||
if (this->zimFileHandler != NULL) {
|
|
||||||
this->firstArticleOffset
|
|
||||||
= this->zimFileHandler->getNamespaceBeginOffset('A');
|
|
||||||
this->lastArticleOffset = this->zimFileHandler->getNamespaceEndOffset('A');
|
|
||||||
this->nsACount = this->zimFileHandler->getNamespaceCount('A');
|
|
||||||
this->nsICount = this->zimFileHandler->getNamespaceCount('I');
|
|
||||||
this->zimFilePath = zimFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialize random seed: */
|
|
||||||
srand(time(NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Destructor */
|
|
||||||
Reader::~Reader()
|
|
||||||
{
|
|
||||||
if (this->zimFileHandler != NULL) {
|
|
||||||
delete this->zimFileHandler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
zim::File* Reader::getZimFileHandler() const
|
|
||||||
{
|
|
||||||
return this->zimFileHandler;
|
|
||||||
}
|
|
||||||
std::map<const std::string, unsigned int> Reader::parseCounterMetadata() const
|
|
||||||
{
|
|
||||||
std::map<const std::string, unsigned int> counters;
|
|
||||||
string mimeType, item, counterString;
|
|
||||||
unsigned int counter;
|
|
||||||
|
|
||||||
zim::Article article = this->zimFileHandler->getArticle('M', "Counter");
|
|
||||||
|
|
||||||
if (article.good()) {
|
|
||||||
stringstream ssContent(article.getData());
|
|
||||||
|
|
||||||
while (getline(ssContent, item, ';')) {
|
|
||||||
stringstream ssItem(item);
|
|
||||||
getline(ssItem, mimeType, '=');
|
|
||||||
getline(ssItem, counterString, '=');
|
|
||||||
if (!counterString.empty() && !mimeType.empty()) {
|
|
||||||
sscanf(counterString.c_str(), "%u", &counter);
|
|
||||||
counters.insert(pair<string, int>(mimeType, counter));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return counters;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the count of articles which can be indexed/displayed */
|
|
||||||
unsigned int Reader::getArticleCount() const
|
|
||||||
{
|
|
||||||
std::map<const std::string, unsigned int> counterMap
|
|
||||||
= this->parseCounterMetadata();
|
|
||||||
unsigned int counter = 0;
|
|
||||||
|
|
||||||
if (counterMap.empty()) {
|
|
||||||
counter = this->nsACount;
|
|
||||||
} else {
|
|
||||||
auto it = counterMap.find("text/html");
|
|
||||||
if (it != counterMap.end()) {
|
|
||||||
counter = it->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the count of medias content in the ZIM file */
|
|
||||||
unsigned int Reader::getMediaCount() const
|
|
||||||
{
|
|
||||||
std::map<const std::string, unsigned int> counterMap
|
|
||||||
= this->parseCounterMetadata();
|
|
||||||
unsigned int counter = 0;
|
|
||||||
|
|
||||||
if (counterMap.empty()) {
|
|
||||||
counter = this->nsICount;
|
|
||||||
} else {
|
|
||||||
auto it = counterMap.find("image/jpeg");
|
|
||||||
if (it != counterMap.end()) {
|
|
||||||
counter += it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
it = counterMap.find("image/gif");
|
|
||||||
if (it != counterMap.end()) {
|
|
||||||
counter += it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
it = counterMap.find("image/png");
|
|
||||||
if (it != counterMap.end()) {
|
|
||||||
counter += it->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the total of all items of a ZIM file, redirects included */
|
|
||||||
unsigned int Reader::getGlobalCount() const
|
|
||||||
{
|
|
||||||
return this->zimFileHandler->getCountArticles();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the UID of the ZIM file */
|
|
||||||
string Reader::getId() const
|
|
||||||
{
|
|
||||||
std::ostringstream s;
|
|
||||||
s << this->zimFileHandler->getFileheader().getUuid();
|
|
||||||
return s.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return a page url from a title */
|
|
||||||
bool Reader::getPageUrlFromTitle(const string& title, string& url) const
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
auto entry = getEntryFromTitle(title);
|
|
||||||
entry = entry.getFinalEntry();
|
|
||||||
url = entry.getPath();
|
|
||||||
return true;
|
|
||||||
} catch (NoEntry& e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return an URL from a title */
|
|
||||||
string Reader::getRandomPageUrl() const
|
|
||||||
{
|
|
||||||
return getRandomPage().getPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry Reader::getRandomPage() const
|
|
||||||
{
|
|
||||||
if (!this->zimFileHandler) {
|
|
||||||
throw NoEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
zim::Article article;
|
|
||||||
std::string mainPagePath = this->getMainPage().getPath();
|
|
||||||
int watchdog = 42;
|
|
||||||
|
|
||||||
do {
|
|
||||||
auto idx = this->firstArticleOffset
|
|
||||||
+ (zim::size_type)((double)rand() / ((double)RAND_MAX + 1)
|
|
||||||
* this->nsACount);
|
|
||||||
article = zimFileHandler->getArticle(idx);
|
|
||||||
if (!watchdog--) {
|
|
||||||
throw NoEntry();
|
|
||||||
}
|
|
||||||
} while (!article.good() && article.getLongUrl() == mainPagePath);
|
|
||||||
|
|
||||||
return article;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the welcome page URL */
|
|
||||||
string Reader::getMainPageUrl() const
|
|
||||||
{
|
|
||||||
return getMainPage().getPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry Reader::getMainPage() const
|
|
||||||
{
|
|
||||||
if (!this->zimFileHandler) {
|
|
||||||
throw NoEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
string url = "";
|
|
||||||
|
|
||||||
zim::Article article;
|
|
||||||
if (this->zimFileHandler->getFileheader().hasMainPage())
|
|
||||||
{
|
|
||||||
article = zimFileHandler->getArticle(
|
|
||||||
this->zimFileHandler->getFileheader().getMainPage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!article.good())
|
|
||||||
{
|
|
||||||
return getFirstPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
return article;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Reader::getFavicon(string& content, string& mimeType) const
|
|
||||||
{
|
|
||||||
static const char* const paths[] = {"-/favicon.png", "I/favicon.png", "I/favicon", "-/favicon"};
|
|
||||||
|
|
||||||
for (auto &path: paths) {
|
|
||||||
try {
|
|
||||||
auto entry = getEntryFromPath(path);
|
|
||||||
entry = entry.getFinalEntry();
|
|
||||||
content = entry.getContent();
|
|
||||||
mimeType = entry.getMimetype();
|
|
||||||
return true;
|
|
||||||
} catch(NoEntry& e) {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Reader::getZimFilePath() const
|
|
||||||
{
|
|
||||||
return this->zimFilePath;
|
|
||||||
}
|
|
||||||
/* Return a metatag value */
|
|
||||||
bool Reader::getMetatag(const string& name, string& value) const
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
auto entry = getEntryFromPath("M/"+name);
|
|
||||||
value = entry.getContent();
|
|
||||||
return true;
|
|
||||||
} catch(NoEntry& e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string Reader::getTitle() const
|
|
||||||
{
|
|
||||||
string value;
|
|
||||||
this->getMetatag("Title", value);
|
|
||||||
if (value.empty()) {
|
|
||||||
value = getLastPathElement(zimFileHandler->getFilename());
|
|
||||||
std::replace(value.begin(), value.end(), '_', ' ');
|
|
||||||
size_t pos = value.find(".zim");
|
|
||||||
value = value.substr(0, pos);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Reader::getName() const
|
|
||||||
{
|
|
||||||
string value;
|
|
||||||
this->getMetatag("Name", value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Reader::getTags() const
|
|
||||||
{
|
|
||||||
string value;
|
|
||||||
this->getMetatag("Tags", value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Reader::getDescription() const
|
|
||||||
{
|
|
||||||
string value;
|
|
||||||
this->getMetatag("Description", value);
|
|
||||||
|
|
||||||
/* Mediawiki Collection tends to use the "Subtitle" name */
|
|
||||||
if (value.empty()) {
|
|
||||||
this->getMetatag("Subtitle", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Reader::getLanguage() const
|
|
||||||
{
|
|
||||||
string value;
|
|
||||||
this->getMetatag("Language", value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Reader::getDate() const
|
|
||||||
{
|
|
||||||
string value;
|
|
||||||
this->getMetatag("Date", value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Reader::getCreator() const
|
|
||||||
{
|
|
||||||
string value;
|
|
||||||
this->getMetatag("Creator", value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Reader::getPublisher() const
|
|
||||||
{
|
|
||||||
string value;
|
|
||||||
this->getMetatag("Publisher", value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Reader::getOrigId() const
|
|
||||||
{
|
|
||||||
string value;
|
|
||||||
this->getMetatag("startfileuid", value);
|
|
||||||
if (value.empty()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
std::string id = value;
|
|
||||||
std::string origID;
|
|
||||||
std::string temp = "";
|
|
||||||
unsigned int k = 0;
|
|
||||||
char tempArray[16] = "";
|
|
||||||
for (unsigned int i = 0; i < id.size(); i++) {
|
|
||||||
if (id[i] == '\n') {
|
|
||||||
tempArray[k] = atoi(temp.c_str());
|
|
||||||
temp = "";
|
|
||||||
k++;
|
|
||||||
} else {
|
|
||||||
temp += id[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
origID = hexUUID(tempArray);
|
|
||||||
return origID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the first page URL */
|
|
||||||
string Reader::getFirstPageUrl() const
|
|
||||||
{
|
|
||||||
return getFirstPage().getPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry Reader::getFirstPage() const
|
|
||||||
{
|
|
||||||
if (!this->zimFileHandler) {
|
|
||||||
throw NoEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto firstPageOffset = zimFileHandler->getNamespaceBeginOffset('A');
|
|
||||||
auto article = zimFileHandler->getArticle(firstPageOffset);
|
|
||||||
|
|
||||||
if (! article.good()) {
|
|
||||||
throw NoEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
return article;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _parseUrl(const string& url, char* ns, string& title)
|
|
||||||
{
|
|
||||||
/* Offset to visit the url */
|
|
||||||
unsigned int urlLength = url.size();
|
|
||||||
unsigned int offset = 0;
|
|
||||||
|
|
||||||
/* Ignore the first '/' */
|
|
||||||
if (url[offset] == '/')
|
|
||||||
offset++;
|
|
||||||
|
|
||||||
if (url[offset] == '/' || offset >= urlLength)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Get namespace */
|
|
||||||
*ns = url[offset++];
|
|
||||||
|
|
||||||
if (url[offset] != '/' || offset >= urlLength)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
offset++;
|
|
||||||
|
|
||||||
if ( offset >= urlLength)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Get content title */
|
|
||||||
title = url.substr(offset, urlLength - offset);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Reader::parseUrl(const string& url, char* ns, string& title) const
|
|
||||||
{
|
|
||||||
return _parseUrl(url, ns, title);
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry Reader::getEntryFromPath(const std::string& path) const
|
|
||||||
{
|
|
||||||
char ns = 0;
|
|
||||||
std::string short_url;
|
|
||||||
|
|
||||||
if (!this->zimFileHandler) {
|
|
||||||
throw NoEntry();
|
|
||||||
}
|
|
||||||
_parseUrl(path, &ns, short_url);
|
|
||||||
|
|
||||||
if (short_url.empty() && ns == 0) {
|
|
||||||
return getMainPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto article = zimFileHandler->getArticle(ns, short_url);
|
|
||||||
if (!article.good()) {
|
|
||||||
throw NoEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
return article;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry Reader::getEntryFromEncodedPath(const std::string& path) const
|
|
||||||
{
|
|
||||||
return getEntryFromPath(urlDecode(path, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry Reader::getEntryFromTitle(const std::string& title) const
|
|
||||||
{
|
|
||||||
if (!this->zimFileHandler) {
|
|
||||||
throw NoEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto article = this->zimFileHandler->getArticleByTitle('A', title);
|
|
||||||
if (!article.good()) {
|
|
||||||
throw NoEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
return article;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return article by url */
|
|
||||||
bool Reader::getArticleObjectByDecodedUrl(const string& url,
|
|
||||||
zim::Article& article) const
|
|
||||||
{
|
|
||||||
if (this->zimFileHandler == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse the url */
|
|
||||||
char ns = 0;
|
|
||||||
string urlStr;
|
|
||||||
_parseUrl(url, &ns, urlStr);
|
|
||||||
|
|
||||||
/* Main page */
|
|
||||||
if (urlStr.empty() && ns == 0) {
|
|
||||||
_parseUrl(this->getMainPage().getPath(), &ns, urlStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Extract the content from the zim file */
|
|
||||||
article = zimFileHandler->getArticle(ns, urlStr);
|
|
||||||
return article.good();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the mimeType without the content */
|
|
||||||
bool Reader::getMimeTypeByUrl(const string& url, string& mimeType) const
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
auto entry = getEntryFromPath(url);
|
|
||||||
mimeType = entry.getMimetype();
|
|
||||||
return true;
|
|
||||||
} catch (NoEntry& e) {
|
|
||||||
mimeType = "";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_content_by_decoded_url(const Reader& reader,
|
|
||||||
const string& url,
|
|
||||||
string& content,
|
|
||||||
string& title,
|
|
||||||
unsigned int& contentLength,
|
|
||||||
string& contentType,
|
|
||||||
string& baseUrl)
|
|
||||||
{
|
|
||||||
content = "";
|
|
||||||
contentType = "";
|
|
||||||
contentLength = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
auto entry = reader.getEntryFromPath(url);
|
|
||||||
entry = entry.getFinalEntry();
|
|
||||||
baseUrl = entry.getPath();
|
|
||||||
contentType = entry.getMimetype();
|
|
||||||
content = entry.getContent();
|
|
||||||
contentLength = entry.getSize();
|
|
||||||
title = entry.getTitle();
|
|
||||||
|
|
||||||
/* Try to set a stub HTML header/footer if necesssary */
|
|
||||||
if (contentType.find("text/html") != string::npos
|
|
||||||
&& content.find("<body") == std::string::npos
|
|
||||||
&& content.find("<BODY") == std::string::npos) {
|
|
||||||
content = "<html><head><title>" + title +
|
|
||||||
"</title><meta http-equiv=\"Content-Type\" content=\"text/html; "
|
|
||||||
"charset=utf-8\" /></head><body>" +
|
|
||||||
content + "</body></html>";
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (NoEntry& e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Get a content from a zim file */
|
|
||||||
bool Reader::getContentByUrl(const string& url,
|
|
||||||
string& content,
|
|
||||||
string& title,
|
|
||||||
unsigned int& contentLength,
|
|
||||||
string& contentType) const
|
|
||||||
{
|
|
||||||
std::string stubRedirectUrl;
|
|
||||||
return get_content_by_decoded_url(*this,
|
|
||||||
kiwix::urlDecode(url),
|
|
||||||
content,
|
|
||||||
title,
|
|
||||||
contentLength,
|
|
||||||
contentType,
|
|
||||||
stubRedirectUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Reader::getContentByEncodedUrl(const string& url,
|
|
||||||
string& content,
|
|
||||||
string& title,
|
|
||||||
unsigned int& contentLength,
|
|
||||||
string& contentType,
|
|
||||||
string& baseUrl) const
|
|
||||||
{
|
|
||||||
return get_content_by_decoded_url(*this,
|
|
||||||
kiwix::urlDecode(url),
|
|
||||||
content,
|
|
||||||
title,
|
|
||||||
contentLength,
|
|
||||||
contentType,
|
|
||||||
baseUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Reader::getContentByEncodedUrl(const string& url,
|
|
||||||
string& content,
|
|
||||||
string& title,
|
|
||||||
unsigned int& contentLength,
|
|
||||||
string& contentType) const
|
|
||||||
{
|
|
||||||
std::string stubRedirectUrl;
|
|
||||||
return get_content_by_decoded_url(*this,
|
|
||||||
kiwix::urlDecode(url),
|
|
||||||
content,
|
|
||||||
title,
|
|
||||||
contentLength,
|
|
||||||
contentType,
|
|
||||||
stubRedirectUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Reader::getContentByDecodedUrl(const string& url,
|
|
||||||
string& content,
|
|
||||||
string& title,
|
|
||||||
unsigned int& contentLength,
|
|
||||||
string& contentType) const
|
|
||||||
{
|
|
||||||
std::string stubRedirectUrl;
|
|
||||||
return get_content_by_decoded_url(*this,
|
|
||||||
url,
|
|
||||||
content,
|
|
||||||
title,
|
|
||||||
contentLength,
|
|
||||||
contentType,
|
|
||||||
stubRedirectUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Reader::getContentByDecodedUrl(const string& url,
|
|
||||||
string& content,
|
|
||||||
string& title,
|
|
||||||
unsigned int& contentLength,
|
|
||||||
string& contentType,
|
|
||||||
string& baseUrl) const
|
|
||||||
{
|
|
||||||
return get_content_by_decoded_url(*this,
|
|
||||||
url,
|
|
||||||
content,
|
|
||||||
title,
|
|
||||||
contentLength,
|
|
||||||
contentType,
|
|
||||||
baseUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if an article exists */
|
|
||||||
bool Reader::urlExists(const string& url) const
|
|
||||||
{
|
|
||||||
return pathExists(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Reader::pathExists(const string& path) const
|
|
||||||
{
|
|
||||||
if (!zimFileHandler)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
char ns = 0;
|
|
||||||
string titleStr;
|
|
||||||
_parseUrl(path, &ns, titleStr);
|
|
||||||
zim::File::const_iterator findItr = zimFileHandler->find(ns, titleStr);
|
|
||||||
return findItr != zimFileHandler->end() && findItr->getUrl() == titleStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Does the ZIM file has a fulltext index */
|
|
||||||
bool Reader::hasFulltextIndex() const
|
|
||||||
{
|
|
||||||
if (!zimFileHandler || zimFileHandler->is_multiPart() )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ( pathExists("Z//fulltextIndex/xapian")
|
|
||||||
|| pathExists("X/fulltext/xapian"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search titles by prefix */
|
|
||||||
bool Reader::searchSuggestions(const string& prefix,
|
|
||||||
unsigned int suggestionsCount,
|
|
||||||
const bool reset)
|
|
||||||
{
|
|
||||||
bool retVal = false;
|
|
||||||
zim::File::const_iterator articleItr;
|
|
||||||
|
|
||||||
/* Reset the suggestions otherwise check if the suggestions number is less
|
|
||||||
* than the suggestionsCount */
|
|
||||||
if (reset) {
|
|
||||||
this->suggestions.clear();
|
|
||||||
this->suggestionsOffset = this->suggestions.begin();
|
|
||||||
} else {
|
|
||||||
if (this->suggestions.size() > suggestionsCount) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return if no prefix */
|
|
||||||
if (prefix.size() == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (articleItr = zimFileHandler->findByTitle('A', prefix);
|
|
||||||
articleItr != zimFileHandler->end()
|
|
||||||
&& articleItr->getTitle().compare(0, prefix.size(), prefix) == 0
|
|
||||||
&& this->suggestions.size() < suggestionsCount;
|
|
||||||
++articleItr) {
|
|
||||||
/* Extract the interesting part of article title & url */
|
|
||||||
std::string normalizedArticleTitle
|
|
||||||
= kiwix::normalize(articleItr->getTitle());
|
|
||||||
std::string articleFinalUrl = "/A/" + articleItr->getUrl();
|
|
||||||
if (articleItr->isRedirect()) {
|
|
||||||
zim::Article article = *articleItr;
|
|
||||||
unsigned int loopCounter = 0;
|
|
||||||
while (article.isRedirect() && loopCounter++ < 42) {
|
|
||||||
article = article.getRedirectArticle();
|
|
||||||
}
|
|
||||||
articleFinalUrl = "/A/" + article.getUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Go through all already found suggestions and skip if this
|
|
||||||
article is already in the suggestions list (with an other
|
|
||||||
title) */
|
|
||||||
bool insert = true;
|
|
||||||
std::vector<std::vector<std::string>>::iterator suggestionItr;
|
|
||||||
for (suggestionItr = this->suggestions.begin();
|
|
||||||
suggestionItr != this->suggestions.end();
|
|
||||||
suggestionItr++) {
|
|
||||||
int result = normalizedArticleTitle.compare((*suggestionItr)[2]);
|
|
||||||
if (result == 0 && articleFinalUrl.compare((*suggestionItr)[1]) == 0) {
|
|
||||||
insert = false;
|
|
||||||
break;
|
|
||||||
} else if (result < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Insert if possible */
|
|
||||||
if (insert) {
|
|
||||||
std::vector<std::string> suggestion;
|
|
||||||
suggestion.push_back(articleItr->getTitle());
|
|
||||||
suggestion.push_back(articleFinalUrl);
|
|
||||||
suggestion.push_back(normalizedArticleTitle);
|
|
||||||
this->suggestions.insert(suggestionItr, suggestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Suggestions where found */
|
|
||||||
retVal = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the cursor to the begining */
|
|
||||||
this->suggestionsOffset = this->suggestions.begin();
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> Reader::getTitleVariants(
|
|
||||||
const std::string& title) const
|
|
||||||
{
|
|
||||||
std::vector<std::string> variants;
|
|
||||||
variants.push_back(title);
|
|
||||||
variants.push_back(kiwix::ucFirst(title));
|
|
||||||
variants.push_back(kiwix::lcFirst(title));
|
|
||||||
variants.push_back(kiwix::toTitle(title));
|
|
||||||
return variants;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try also a few variations of the prefix to have better results */
|
|
||||||
bool Reader::searchSuggestionsSmart(const string& prefix,
|
|
||||||
unsigned int suggestionsCount)
|
|
||||||
{
|
|
||||||
std::vector<std::string> variants = this->getTitleVariants(prefix);
|
|
||||||
bool retVal;
|
|
||||||
|
|
||||||
this->suggestions.clear();
|
|
||||||
this->suggestionsOffset = this->suggestions.begin();
|
|
||||||
/* Try to search in the title using fulltext search database */
|
|
||||||
const zim::Search* suggestionSearch
|
|
||||||
= this->getZimFileHandler()->suggestions(prefix, 0, suggestionsCount);
|
|
||||||
|
|
||||||
if (suggestionSearch->get_matches_estimated()) {
|
|
||||||
for (auto current = suggestionSearch->begin();
|
|
||||||
current != suggestionSearch->end();
|
|
||||||
current++) {
|
|
||||||
std::vector<std::string> suggestion;
|
|
||||||
suggestion.push_back(current->getTitle());
|
|
||||||
suggestion.push_back("/A/" + current->getUrl());
|
|
||||||
suggestion.push_back(kiwix::normalize(current->getTitle()));
|
|
||||||
this->suggestions.push_back(suggestion);
|
|
||||||
}
|
|
||||||
this->suggestionsOffset = this->suggestions.begin();
|
|
||||||
retVal = true;
|
|
||||||
} else {
|
|
||||||
for (std::vector<std::string>::iterator variantsItr = variants.begin();
|
|
||||||
variantsItr != variants.end();
|
|
||||||
variantsItr++) {
|
|
||||||
retVal = this->searchSuggestions(*variantsItr, suggestionsCount, false)
|
|
||||||
|| retVal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get next suggestion */
|
|
||||||
bool Reader::getNextSuggestion(string& title)
|
|
||||||
{
|
|
||||||
if (this->suggestionsOffset != this->suggestions.end()) {
|
|
||||||
/* title */
|
|
||||||
title = (*(this->suggestionsOffset))[0];
|
|
||||||
|
|
||||||
/* increment the cursor for the next call */
|
|
||||||
this->suggestionsOffset++;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Reader::getNextSuggestion(string& title, string& url)
|
|
||||||
{
|
|
||||||
if (this->suggestionsOffset != this->suggestions.end()) {
|
|
||||||
/* title */
|
|
||||||
title = (*(this->suggestionsOffset))[0];
|
|
||||||
url = (*(this->suggestionsOffset))[1];
|
|
||||||
|
|
||||||
/* increment the cursor for the next call */
|
|
||||||
this->suggestionsOffset++;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if the file has as checksum */
|
|
||||||
bool Reader::canCheckIntegrity() const
|
|
||||||
{
|
|
||||||
return this->zimFileHandler->getChecksum() != "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return true if corrupted, false otherwise */
|
|
||||||
bool Reader::isCorrupted() const
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
if (this->zimFileHandler->verify() == true) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (exception& e) {
|
|
||||||
cerr << e.what() << endl;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the file size, works also for splitted files */
|
|
||||||
unsigned int Reader::getFileSize() const
|
|
||||||
{
|
|
||||||
zim::File* file = this->getZimFileHandler();
|
|
||||||
zim::offset_type size = 0;
|
|
||||||
|
|
||||||
if (file != NULL) {
|
|
||||||
size = file->getFilesize();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (size / 1024);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Emmanuel Engelhart <kelson@kiwix.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "search_renderer.h"
|
||||||
|
#include "library.h"
|
||||||
|
#include "name_mapper.h"
|
||||||
|
|
||||||
|
#include "tools/archiveTools.h"
|
||||||
|
|
||||||
|
#include <zim/search.h>
|
||||||
|
|
||||||
|
#include <mustache.hpp>
|
||||||
|
#include "libkiwix-resources.h"
|
||||||
|
#include "tools/stringTools.h"
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Constructor */
|
||||||
|
SearchRenderer::SearchRenderer(zim::SearchResultSet srs, NameMapper* mapper,
|
||||||
|
unsigned int start, unsigned int estimatedResultCount)
|
||||||
|
: SearchRenderer(srs, mapper, nullptr, start, estimatedResultCount)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SearchRenderer::SearchRenderer(zim::SearchResultSet srs, NameMapper* mapper, Library* library,
|
||||||
|
unsigned int start, unsigned int estimatedResultCount)
|
||||||
|
: m_srs(srs),
|
||||||
|
mp_nameMapper(mapper),
|
||||||
|
mp_library(library),
|
||||||
|
protocolPrefix("zim://"),
|
||||||
|
searchProtocolPrefix("search://"),
|
||||||
|
estimatedResultCount(estimatedResultCount),
|
||||||
|
resultStart(start)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/* Destructor */
|
||||||
|
SearchRenderer::~SearchRenderer() = default;
|
||||||
|
|
||||||
|
void SearchRenderer::setSearchPattern(const std::string& pattern)
|
||||||
|
{
|
||||||
|
searchPattern = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchRenderer::setSearchBookQuery(const std::string& bookQuery)
|
||||||
|
{
|
||||||
|
searchBookQuery = bookQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchRenderer::setProtocolPrefix(const std::string& prefix)
|
||||||
|
{
|
||||||
|
this->protocolPrefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchRenderer::setSearchProtocolPrefix(const std::string& prefix)
|
||||||
|
{
|
||||||
|
this->searchProtocolPrefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string extractValueFromQuery(const std::string& query, const std::string& key) {
|
||||||
|
const std::string p = key + "=";
|
||||||
|
const size_t i = query.find(p);
|
||||||
|
if (i == std::string::npos) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::string r = query.substr(i + p.size());
|
||||||
|
return r.substr(0, r.find("&"));
|
||||||
|
}
|
||||||
|
|
||||||
|
kainjow::mustache::data buildQueryData
|
||||||
|
(
|
||||||
|
const std::string& searchProtocolPrefix,
|
||||||
|
const std::string& pattern,
|
||||||
|
const std::string& bookQuery
|
||||||
|
) {
|
||||||
|
kainjow::mustache::data query;
|
||||||
|
query.set("pattern", kiwix::encodeDiples(pattern));
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << searchProtocolPrefix << "?pattern=" << urlEncode(pattern);
|
||||||
|
ss << "&" << bookQuery;
|
||||||
|
query.set("unpaginatedQuery", ss.str());
|
||||||
|
auto lang = extractValueFromQuery(bookQuery, "books.filter.lang");
|
||||||
|
if(!lang.empty()) {
|
||||||
|
query.set("lang", lang);
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
kainjow::mustache::data buildPagination(
|
||||||
|
unsigned int pageLength,
|
||||||
|
unsigned int resultsCount,
|
||||||
|
unsigned int resultsStart
|
||||||
|
)
|
||||||
|
{
|
||||||
|
assert(pageLength!=0);
|
||||||
|
kainjow::mustache::data pagination;
|
||||||
|
kainjow::mustache::data pages{kainjow::mustache::data::type::list};
|
||||||
|
|
||||||
|
if (resultsCount == 0) {
|
||||||
|
// Easy case
|
||||||
|
pagination.set("itemsPerPage", to_string(pageLength));
|
||||||
|
pagination.set("hasPages", false);
|
||||||
|
pagination.set("pages", pages);
|
||||||
|
return pagination;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First we want to display pages starting at a multiple of `pageLength`
|
||||||
|
// so, let's calculate the start index of the current page.
|
||||||
|
auto currentPage = resultsStart/pageLength;
|
||||||
|
auto lastPage = ((resultsCount-1)/pageLength);
|
||||||
|
auto lastPageStart = lastPage*pageLength;
|
||||||
|
auto nbPages = lastPage + 1;
|
||||||
|
|
||||||
|
auto firstPageGenerated = currentPage > 4 ? currentPage-4 : 0;
|
||||||
|
auto lastPageGenerated = std::min(currentPage+4, lastPage);
|
||||||
|
|
||||||
|
if (nbPages != 1) {
|
||||||
|
if (firstPageGenerated!=0) {
|
||||||
|
kainjow::mustache::data page;
|
||||||
|
page.set("label", "◀");
|
||||||
|
page.set("start", to_string(0));
|
||||||
|
page.set("current", false);
|
||||||
|
pages.push_back(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i=firstPageGenerated; i<=lastPageGenerated; i++) {
|
||||||
|
kainjow::mustache::data page;
|
||||||
|
page.set("label", to_string(i+1));
|
||||||
|
page.set("start", to_string(i*pageLength));
|
||||||
|
page.set("current", bool(i == currentPage));
|
||||||
|
pages.push_back(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastPageGenerated!=lastPage) {
|
||||||
|
kainjow::mustache::data page;
|
||||||
|
page.set("label", "▶");
|
||||||
|
page.set("start", to_string(lastPageStart));
|
||||||
|
page.set("current", false);
|
||||||
|
pages.push_back(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pagination.set("itemsPerPage", to_string(pageLength));
|
||||||
|
pagination.set("hasPages", firstPageGenerated < lastPageGenerated);
|
||||||
|
pagination.set("pages", pages);
|
||||||
|
return pagination;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SearchRenderer::renderTemplate(const std::string& tmpl_str)
|
||||||
|
{
|
||||||
|
const std::string absPathPrefix = protocolPrefix;
|
||||||
|
// Build the results list
|
||||||
|
kainjow::mustache::data items{kainjow::mustache::data::type::list};
|
||||||
|
for (auto it = m_srs.begin(); it != m_srs.end(); it++) {
|
||||||
|
kainjow::mustache::data result;
|
||||||
|
const std::string zim_id(it.getZimId());
|
||||||
|
const auto path = mp_nameMapper->getNameForId(zim_id) + "/" + it.getPath();
|
||||||
|
result.set("title", it.getTitle());
|
||||||
|
result.set("absolutePath", absPathPrefix + urlEncode(path));
|
||||||
|
result.set("snippet", it.getSnippet());
|
||||||
|
if (mp_library) {
|
||||||
|
result.set("bookTitle", mp_library->getBookById(zim_id).getTitle());
|
||||||
|
}
|
||||||
|
if (it.getWordCount() >= 0) {
|
||||||
|
result.set("wordCount", kiwix::beautifyInteger(it.getWordCount()));
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push_back(result);
|
||||||
|
}
|
||||||
|
kainjow::mustache::data results;
|
||||||
|
results.set("items", items);
|
||||||
|
results.set("count", kiwix::beautifyInteger(estimatedResultCount));
|
||||||
|
results.set("hasResults", estimatedResultCount != 0);
|
||||||
|
results.set("start", kiwix::beautifyInteger(resultStart));
|
||||||
|
results.set("end", kiwix::beautifyInteger(std::min(resultStart+pageLength-1, estimatedResultCount)));
|
||||||
|
|
||||||
|
// pagination
|
||||||
|
auto pagination = buildPagination(
|
||||||
|
pageLength,
|
||||||
|
estimatedResultCount,
|
||||||
|
resultStart
|
||||||
|
);
|
||||||
|
|
||||||
|
kainjow::mustache::data query = buildQueryData(
|
||||||
|
searchProtocolPrefix,
|
||||||
|
searchPattern,
|
||||||
|
searchBookQuery
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
kainjow::mustache::data allData;
|
||||||
|
allData.set("searchProtocolPrefix", searchProtocolPrefix);
|
||||||
|
allData.set("results", results);
|
||||||
|
allData.set("pagination", pagination);
|
||||||
|
allData.set("query", query);
|
||||||
|
|
||||||
|
kainjow::mustache::mustache tmpl(tmpl_str);
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
tmpl.render(allData, [&ss](const std::string& str) { ss << str; });
|
||||||
|
if (!tmpl.is_valid()) {
|
||||||
|
throw std::runtime_error("Error while rendering search results: " + tmpl.error_message());
|
||||||
|
}
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SearchRenderer::getHtml()
|
||||||
|
{
|
||||||
|
return renderTemplate(RESOURCE::templates::search_result_html);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SearchRenderer::getXml()
|
||||||
|
{
|
||||||
|
return renderTemplate(RESOURCE::templates::search_result_xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,465 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 Emmanuel Engelhart <kelson@kiwix.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include "searcher.h"
|
|
||||||
#include "reader.h"
|
|
||||||
#include "xapianSearcher.h"
|
|
||||||
|
|
||||||
#include <zim/search.h>
|
|
||||||
|
|
||||||
#ifdef ENABLE_CTPP2
|
|
||||||
#include <ctpp2/CDT.hpp>
|
|
||||||
#include <ctpp2/CTPP2FileLogger.hpp>
|
|
||||||
#include <ctpp2/CTPP2SimpleVM.hpp>
|
|
||||||
#include "ctpp2/CTPP2VMStringLoader.hpp"
|
|
||||||
#include "kiwixlib-resources.h"
|
|
||||||
|
|
||||||
using namespace CTPP;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MAX_SEARCH_LEN 140
|
|
||||||
|
|
||||||
namespace kiwix
|
|
||||||
{
|
|
||||||
class _Result : public Result
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
_Result(zim::Search::iterator& iterator);
|
|
||||||
virtual ~_Result(){};
|
|
||||||
|
|
||||||
virtual std::string get_url();
|
|
||||||
virtual std::string get_title();
|
|
||||||
virtual int get_score();
|
|
||||||
virtual std::string get_snippet();
|
|
||||||
virtual std::string get_content();
|
|
||||||
virtual int get_wordCount();
|
|
||||||
virtual int get_size();
|
|
||||||
virtual int get_readerIndex();
|
|
||||||
|
|
||||||
private:
|
|
||||||
zim::Search::iterator iterator;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SearcherInternal {
|
|
||||||
const zim::Search* _search;
|
|
||||||
XapianSearcher* _xapianSearcher;
|
|
||||||
zim::Search::iterator current_iterator;
|
|
||||||
|
|
||||||
SearcherInternal() : _search(NULL), _xapianSearcher(NULL) {}
|
|
||||||
~SearcherInternal()
|
|
||||||
{
|
|
||||||
if (_search != NULL) {
|
|
||||||
delete _search;
|
|
||||||
}
|
|
||||||
if (_xapianSearcher != NULL) {
|
|
||||||
delete _xapianSearcher;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Constructor */
|
|
||||||
Searcher::Searcher(const string& xapianDirectoryPath,
|
|
||||||
Reader* reader,
|
|
||||||
const string& humanReadableName)
|
|
||||||
: internal(new SearcherInternal()),
|
|
||||||
searchPattern(""),
|
|
||||||
protocolPrefix("zim://"),
|
|
||||||
searchProtocolPrefix("search://?"),
|
|
||||||
resultCountPerPage(0),
|
|
||||||
estimatedResultCount(0),
|
|
||||||
resultStart(0),
|
|
||||||
resultEnd(0),
|
|
||||||
contentHumanReadableId(humanReadableName)
|
|
||||||
{
|
|
||||||
loadICUExternalTables();
|
|
||||||
if (!reader || !reader->hasFulltextIndex()) {
|
|
||||||
internal->_xapianSearcher = new XapianSearcher(xapianDirectoryPath, reader);
|
|
||||||
}
|
|
||||||
this->humanReaderNames.push_back(humanReadableName);
|
|
||||||
}
|
|
||||||
|
|
||||||
Searcher::Searcher(const std::string& humanReadableName)
|
|
||||||
: internal(new SearcherInternal()),
|
|
||||||
searchPattern(""),
|
|
||||||
protocolPrefix("zim://"),
|
|
||||||
searchProtocolPrefix("search://?"),
|
|
||||||
resultCountPerPage(0),
|
|
||||||
estimatedResultCount(0),
|
|
||||||
resultStart(0),
|
|
||||||
resultEnd(0),
|
|
||||||
contentHumanReadableId(humanReadableName)
|
|
||||||
{
|
|
||||||
loadICUExternalTables();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Destructor */
|
|
||||||
Searcher::~Searcher()
|
|
||||||
{
|
|
||||||
delete internal;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Searcher::add_reader(Reader* reader, const std::string& humanReadableName)
|
|
||||||
{
|
|
||||||
if (!reader->hasFulltextIndex()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this->readers.push_back(reader);
|
|
||||||
this->humanReaderNames.push_back(humanReadableName);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search strings in the database */
|
|
||||||
void Searcher::search(std::string& search,
|
|
||||||
unsigned int resultStart,
|
|
||||||
unsigned int resultEnd,
|
|
||||||
const bool verbose)
|
|
||||||
{
|
|
||||||
this->reset();
|
|
||||||
|
|
||||||
if (verbose == true) {
|
|
||||||
cout << "Performing query `" << search << "'" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If resultEnd & resultStart inverted */
|
|
||||||
if (resultStart > resultEnd) {
|
|
||||||
resultEnd += resultStart;
|
|
||||||
resultStart = resultEnd - resultStart;
|
|
||||||
resultEnd -= resultStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to find results */
|
|
||||||
if (resultStart != resultEnd) {
|
|
||||||
/* Avoid big researches */
|
|
||||||
this->resultCountPerPage = resultEnd - resultStart;
|
|
||||||
if (this->resultCountPerPage > MAX_SEARCH_LEN) {
|
|
||||||
resultEnd = resultStart + MAX_SEARCH_LEN;
|
|
||||||
this->resultCountPerPage = MAX_SEARCH_LEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perform the search */
|
|
||||||
this->searchPattern = search;
|
|
||||||
this->resultStart = resultStart;
|
|
||||||
this->resultEnd = resultEnd;
|
|
||||||
string unaccentedSearch = removeAccents(search);
|
|
||||||
if (internal->_xapianSearcher) {
|
|
||||||
internal->_xapianSearcher->searchInIndex(
|
|
||||||
unaccentedSearch, resultStart, resultEnd, verbose);
|
|
||||||
this->estimatedResultCount
|
|
||||||
= internal->_xapianSearcher->results.get_matches_estimated();
|
|
||||||
} else {
|
|
||||||
std::vector<const zim::File*> zims;
|
|
||||||
for (auto current = this->readers.begin(); current != this->readers.end();
|
|
||||||
current++) {
|
|
||||||
if ( (*current)->hasFulltextIndex() ) {
|
|
||||||
zims.push_back((*current)->getZimFileHandler());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zim::Search* search = new zim::Search(zims);
|
|
||||||
search->set_query(unaccentedSearch);
|
|
||||||
search->set_range(resultStart, resultEnd);
|
|
||||||
internal->_search = search;
|
|
||||||
internal->current_iterator = internal->_search->begin();
|
|
||||||
this->estimatedResultCount = internal->_search->get_matches_estimated();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Searcher::geo_search(float latitude, float longitude, float distance,
|
|
||||||
unsigned int resultStart,
|
|
||||||
unsigned int resultEnd,
|
|
||||||
const bool verbose)
|
|
||||||
{
|
|
||||||
this->reset();
|
|
||||||
|
|
||||||
if (verbose == true) {
|
|
||||||
cout << "Performing geo query `" << distance << "&(" << latitude << ";" << longitude << ")'" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If resultEnd & resultStart inverted */
|
|
||||||
if (resultStart > resultEnd) {
|
|
||||||
resultEnd += resultStart;
|
|
||||||
resultStart = resultEnd - resultStart;
|
|
||||||
resultEnd -= resultStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to find results */
|
|
||||||
if (resultStart == resultEnd) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (internal->_xapianSearcher) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Avoid big researches */
|
|
||||||
this->resultCountPerPage = resultEnd - resultStart;
|
|
||||||
if (this->resultCountPerPage > MAX_SEARCH_LEN) {
|
|
||||||
resultEnd = resultStart + MAX_SEARCH_LEN;
|
|
||||||
this->resultCountPerPage = MAX_SEARCH_LEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perform the search */
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << "Articles located less than " << distance << " meters of " << latitude << ";" << longitude;
|
|
||||||
this->searchPattern = oss.str();
|
|
||||||
this->resultStart = resultStart;
|
|
||||||
this->resultEnd = resultEnd;
|
|
||||||
|
|
||||||
std::vector<const zim::File*> zims;
|
|
||||||
for (auto current = this->readers.begin(); current != this->readers.end();
|
|
||||||
current++) {
|
|
||||||
zims.push_back((*current)->getZimFileHandler());
|
|
||||||
}
|
|
||||||
zim::Search* search = new zim::Search(zims);
|
|
||||||
search->set_query("");
|
|
||||||
search->set_georange(latitude, longitude, distance);
|
|
||||||
search->set_range(resultStart, resultEnd);
|
|
||||||
internal->_search = search;
|
|
||||||
internal->current_iterator = internal->_search->begin();
|
|
||||||
this->estimatedResultCount = internal->_search->get_matches_estimated();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Searcher::restart_search()
|
|
||||||
{
|
|
||||||
if (internal->_xapianSearcher) {
|
|
||||||
internal->_xapianSearcher->restart_search();
|
|
||||||
} else if (internal->_search) {
|
|
||||||
internal->current_iterator = internal->_search->begin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result* Searcher::getNextResult()
|
|
||||||
{
|
|
||||||
if (internal->_xapianSearcher) {
|
|
||||||
return internal->_xapianSearcher->getNextResult();
|
|
||||||
} else if (internal->_search &&
|
|
||||||
internal->current_iterator != internal->_search->end()) {
|
|
||||||
Result* result = new _Result(internal->current_iterator);
|
|
||||||
internal->current_iterator++;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset the results */
|
|
||||||
void Searcher::reset()
|
|
||||||
{
|
|
||||||
this->estimatedResultCount = 0;
|
|
||||||
this->searchPattern = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Searcher::suggestions(std::string& search, const bool verbose)
|
|
||||||
{
|
|
||||||
this->reset();
|
|
||||||
|
|
||||||
if (verbose == true) {
|
|
||||||
cout << "Performing suggestion query `" << search << "`" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->searchPattern = search;
|
|
||||||
this->resultStart = 0;
|
|
||||||
this->resultEnd = 10;
|
|
||||||
string unaccentedSearch = removeAccents(search);
|
|
||||||
|
|
||||||
if (internal->_xapianSearcher) {
|
|
||||||
/* [TODO] Suggestion on a external database ?
|
|
||||||
* We do not support that. */
|
|
||||||
this->estimatedResultCount = 0;
|
|
||||||
} else {
|
|
||||||
std::vector<const zim::File*> zims;
|
|
||||||
for (auto current = this->readers.begin(); current != this->readers.end();
|
|
||||||
current++) {
|
|
||||||
zims.push_back((*current)->getZimFileHandler());
|
|
||||||
}
|
|
||||||
zim::Search* search = new zim::Search(zims);
|
|
||||||
search->set_query(unaccentedSearch);
|
|
||||||
search->set_range(resultStart, resultEnd);
|
|
||||||
search->set_suggestion_mode(true);
|
|
||||||
internal->_search = search;
|
|
||||||
internal->current_iterator = internal->_search->begin();
|
|
||||||
this->estimatedResultCount = internal->_search->get_matches_estimated();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the result count estimation */
|
|
||||||
unsigned int Searcher::getEstimatedResultCount()
|
|
||||||
{
|
|
||||||
return this->estimatedResultCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Searcher::setProtocolPrefix(const std::string prefix)
|
|
||||||
{
|
|
||||||
this->protocolPrefix = prefix;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Searcher::setSearchProtocolPrefix(const std::string prefix)
|
|
||||||
{
|
|
||||||
this->searchProtocolPrefix = prefix;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_Result::_Result(zim::Search::iterator& iterator)
|
|
||||||
: iterator(iterator)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string _Result::get_url()
|
|
||||||
{
|
|
||||||
return iterator.get_url();
|
|
||||||
}
|
|
||||||
std::string _Result::get_title()
|
|
||||||
{
|
|
||||||
return iterator.get_title();
|
|
||||||
}
|
|
||||||
int _Result::get_score()
|
|
||||||
{
|
|
||||||
return iterator.get_score();
|
|
||||||
}
|
|
||||||
std::string _Result::get_snippet()
|
|
||||||
{
|
|
||||||
return iterator.get_snippet();
|
|
||||||
}
|
|
||||||
std::string _Result::get_content()
|
|
||||||
{
|
|
||||||
if (iterator->good()) {
|
|
||||||
return iterator->getData();
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
int _Result::get_size()
|
|
||||||
{
|
|
||||||
return iterator.get_size();
|
|
||||||
}
|
|
||||||
int _Result::get_wordCount()
|
|
||||||
{
|
|
||||||
return iterator.get_wordCount();
|
|
||||||
}
|
|
||||||
int _Result::get_readerIndex()
|
|
||||||
{
|
|
||||||
return iterator.get_fileIndex();
|
|
||||||
}
|
|
||||||
#ifdef ENABLE_CTPP2
|
|
||||||
|
|
||||||
string Searcher::getHtml()
|
|
||||||
{
|
|
||||||
SimpleVM oSimpleVM(
|
|
||||||
1024, //iIMaxFunctions (default value)
|
|
||||||
4096, //iIMaxArgStackSize (default value)
|
|
||||||
4096, //iIMaxCodeStackSize (default value)
|
|
||||||
10240 * 2 //iIMaxSteps (default*2)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Fill data
|
|
||||||
CDT oData;
|
|
||||||
CDT resultsCDT(CDT::ARRAY_VAL);
|
|
||||||
|
|
||||||
this->restart_search();
|
|
||||||
Result* p_result = NULL;
|
|
||||||
while ((p_result = this->getNextResult())) {
|
|
||||||
CDT result;
|
|
||||||
result["title"] = p_result->get_title();
|
|
||||||
result["url"] = p_result->get_url();
|
|
||||||
result["snippet"] = p_result->get_snippet();
|
|
||||||
result["contentId"] = humanReaderNames[p_result->get_readerIndex()];
|
|
||||||
|
|
||||||
if (p_result->get_size() >= 0) {
|
|
||||||
result["size"] = kiwix::beautifyInteger(p_result->get_size());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_result->get_wordCount() >= 0) {
|
|
||||||
result["wordCount"] = kiwix::beautifyInteger(p_result->get_wordCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
resultsCDT.PushBack(result);
|
|
||||||
delete p_result;
|
|
||||||
}
|
|
||||||
this->restart_search();
|
|
||||||
oData["results"] = resultsCDT;
|
|
||||||
|
|
||||||
// pages
|
|
||||||
CDT pagesCDT(CDT::ARRAY_VAL);
|
|
||||||
|
|
||||||
unsigned int pageStart
|
|
||||||
= this->resultStart / this->resultCountPerPage >= 5
|
|
||||||
? this->resultStart / this->resultCountPerPage - 4
|
|
||||||
: 0;
|
|
||||||
unsigned int pageCount
|
|
||||||
= this->estimatedResultCount / this->resultCountPerPage + 1 - pageStart;
|
|
||||||
|
|
||||||
if (pageCount > 10) {
|
|
||||||
pageCount = 10;
|
|
||||||
} else if (pageCount == 1) {
|
|
||||||
pageCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int i = pageStart; i < pageStart + pageCount; i++) {
|
|
||||||
CDT page;
|
|
||||||
page["label"] = i + 1;
|
|
||||||
page["start"] = i * this->resultCountPerPage;
|
|
||||||
page["end"] = (i + 1) * this->resultCountPerPage;
|
|
||||||
|
|
||||||
if (i * this->resultCountPerPage == this->resultStart) {
|
|
||||||
page["selected"] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pagesCDT.PushBack(page);
|
|
||||||
}
|
|
||||||
oData["pages"] = pagesCDT;
|
|
||||||
|
|
||||||
oData["count"] = kiwix::beautifyInteger(this->estimatedResultCount);
|
|
||||||
oData["searchPattern"] = kiwix::encodeDiples(this->searchPattern);
|
|
||||||
oData["searchPatternEncoded"] = urlEncode(this->searchPattern);
|
|
||||||
oData["resultStart"] = this->resultStart + 1;
|
|
||||||
oData["resultEnd"] = (this->resultEnd > this->estimatedResultCount
|
|
||||||
? this->estimatedResultCount
|
|
||||||
: this->resultEnd);
|
|
||||||
oData["resultRange"] = this->resultCountPerPage;
|
|
||||||
oData["resultLastPageStart"]
|
|
||||||
= this->estimatedResultCount > this->resultCountPerPage
|
|
||||||
? std::round(this->estimatedResultCount / this->resultCountPerPage) * this->resultCountPerPage
|
|
||||||
: 0;
|
|
||||||
oData["protocolPrefix"] = this->protocolPrefix;
|
|
||||||
oData["searchProtocolPrefix"] = this->searchProtocolPrefix;
|
|
||||||
oData["contentId"] = this->contentHumanReadableId;
|
|
||||||
|
|
||||||
std::string template_ct2 = RESOURCE::results_ct2;
|
|
||||||
VMStringLoader oLoader(template_ct2.c_str(), template_ct2.size());
|
|
||||||
|
|
||||||
FileLogger oLogger(stderr);
|
|
||||||
|
|
||||||
// DEBUG only (write output to stdout)
|
|
||||||
// oSimpleVM.Run(oData, oLoader, stdout, oLogger);
|
|
||||||
|
|
||||||
std::string sResult;
|
|
||||||
oSimpleVM.Run(oData, oLoader, sResult, oLogger);
|
|
||||||
|
|
||||||
return sResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Matthieu Gautier <mgautier@kymeria.fr>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "server.h"
|
||||||
|
|
||||||
|
#include "library.h"
|
||||||
|
#include "name_mapper.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <zim/item.h>
|
||||||
|
#include "server/internalServer.h"
|
||||||
|
|
||||||
|
namespace kiwix {
|
||||||
|
|
||||||
|
Server::Server(Library* library, NameMapper* nameMapper) :
|
||||||
|
mp_library(library),
|
||||||
|
mp_nameMapper(nameMapper),
|
||||||
|
mp_server(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Server::~Server() = default;
|
||||||
|
|
||||||
|
bool Server::start() {
|
||||||
|
mp_server.reset(new InternalServer(
|
||||||
|
mp_library,
|
||||||
|
mp_nameMapper,
|
||||||
|
m_addr,
|
||||||
|
m_port,
|
||||||
|
m_root,
|
||||||
|
m_nbThreads,
|
||||||
|
m_multizimSearchLimit,
|
||||||
|
m_verbose,
|
||||||
|
m_withTaskbar,
|
||||||
|
m_withLibraryButton,
|
||||||
|
m_blockExternalLinks,
|
||||||
|
m_indexTemplateString,
|
||||||
|
m_ipConnectionLimit));
|
||||||
|
return mp_server->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::stop() {
|
||||||
|
if (mp_server) {
|
||||||
|
mp_server->stop();
|
||||||
|
mp_server.reset(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::setRoot(const std::string& root)
|
||||||
|
{
|
||||||
|
m_root = root;
|
||||||
|
if (m_root[0] != '/') {
|
||||||
|
m_root = "/" + m_root;
|
||||||
|
}
|
||||||
|
if (m_root.back() == '/') {
|
||||||
|
m_root.erase(m_root.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Server::getPort()
|
||||||
|
{
|
||||||
|
return mp_server->getPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Server::getAddress()
|
||||||
|
{
|
||||||
|
return mp_server->getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Veloman Yunkan <veloman.yunkan@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "byte_range.h"
|
||||||
|
|
||||||
|
#include "tools/stringTools.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace kiwix {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
ByteRange parseByteRange(const std::string& rangeStr)
|
||||||
|
{
|
||||||
|
std::istringstream iss(rangeStr);
|
||||||
|
|
||||||
|
int64_t start, end = INT64_MAX;
|
||||||
|
if (iss >> start) {
|
||||||
|
if ( start < 0 ) {
|
||||||
|
if ( iss.eof() )
|
||||||
|
return ByteRange(-start);
|
||||||
|
} else {
|
||||||
|
char c;
|
||||||
|
if (iss >> c && c=='-') {
|
||||||
|
iss >> end; // if this fails, end is not modified, which is OK
|
||||||
|
if (iss.eof() && start <= end)
|
||||||
|
return ByteRange(ByteRange::PARSED, start, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ByteRange(ByteRange::INVALID, 0, INT64_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // unnamed namespace
|
||||||
|
|
||||||
|
ByteRange::ByteRange()
|
||||||
|
: kind_(NONE)
|
||||||
|
, first_(0)
|
||||||
|
, last_(INT64_MAX)
|
||||||
|
{}
|
||||||
|
|
||||||
|
ByteRange::ByteRange(Kind kind, int64_t first, int64_t last)
|
||||||
|
: kind_(kind)
|
||||||
|
, first_(first)
|
||||||
|
, last_(last)
|
||||||
|
{
|
||||||
|
assert(kind != NONE);
|
||||||
|
assert(first >= 0);
|
||||||
|
assert(last >= first || (first == 0 && last == -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteRange::ByteRange(int64_t suffix_length)
|
||||||
|
: kind_(PARSED)
|
||||||
|
, first_(-suffix_length)
|
||||||
|
, last_(INT64_MAX)
|
||||||
|
{
|
||||||
|
assert(suffix_length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t ByteRange::first() const
|
||||||
|
{
|
||||||
|
assert(kind_ > PARSED);
|
||||||
|
return first_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t ByteRange::last() const
|
||||||
|
{
|
||||||
|
assert(kind_ > PARSED);
|
||||||
|
return last_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t ByteRange::length() const
|
||||||
|
{
|
||||||
|
assert(kind_ > PARSED);
|
||||||
|
return last_ + 1 - first_;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteRange ByteRange::parse(const std::string& rangeStr)
|
||||||
|
{
|
||||||
|
const std::string byteUnitSpec("bytes=");
|
||||||
|
if ( ! kiwix::startsWith(rangeStr, byteUnitSpec) )
|
||||||
|
return ByteRange(INVALID, 0, INT64_MAX);
|
||||||
|
|
||||||
|
return parseByteRange(rangeStr.substr(byteUnitSpec.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteRange ByteRange::resolve(int64_t contentSize) const
|
||||||
|
{
|
||||||
|
if ( kind() == NONE )
|
||||||
|
return ByteRange(RESOLVED_FULL_CONTENT, 0, contentSize-1);
|
||||||
|
|
||||||
|
if ( kind() == INVALID )
|
||||||
|
return ByteRange(RESOLVED_UNSATISFIABLE, 0, contentSize-1);
|
||||||
|
|
||||||
|
const int64_t resolved_first = first_ < 0
|
||||||
|
? std::max(int64_t(0), contentSize + first_)
|
||||||
|
: first_;
|
||||||
|
|
||||||
|
const int64_t resolved_last = std::min(contentSize-1, last_);
|
||||||
|
|
||||||
|
if ( resolved_first > resolved_last )
|
||||||
|
return ByteRange(RESOLVED_UNSATISFIABLE, 0, contentSize-1);
|
||||||
|
|
||||||
|
return ByteRange(RESOLVED_PARTIAL_CONTENT, resolved_first, resolved_last);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace kiwix
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Veloman Yunkan <veloman.yunkan@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef KIWIXLIB_SERVER_BYTE_RANGE_H
|
||||||
|
#define KIWIXLIB_SERVER_BYTE_RANGE_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace kiwix {
|
||||||
|
|
||||||
|
class ByteRange
|
||||||
|
{
|
||||||
|
public: // types
|
||||||
|
// ByteRange is parsed in a request, then it must be resolved (taking
|
||||||
|
// into account the actual size of the requested resource) before
|
||||||
|
// being applied in the response.
|
||||||
|
// The Kind enum represents possible states in such a lifecycle.
|
||||||
|
enum Kind {
|
||||||
|
// The request is not a range request (no Range header)
|
||||||
|
NONE,
|
||||||
|
|
||||||
|
// The value of the Range header is not a valid continuous
|
||||||
|
// range. Note that a valid (according to RFC7233) sequence of multiple
|
||||||
|
// byte ranges is considered invalid in the current implementation
|
||||||
|
// (i.e. only single-range partial requests are supported).
|
||||||
|
INVALID,
|
||||||
|
|
||||||
|
// This byte-range has been successfully parsed from the request
|
||||||
|
PARSED,
|
||||||
|
|
||||||
|
// This is a response to a regular (non-range) request
|
||||||
|
RESOLVED_FULL_CONTENT,
|
||||||
|
|
||||||
|
// The range request is invalid or unsatisfiable
|
||||||
|
RESOLVED_UNSATISFIABLE,
|
||||||
|
|
||||||
|
// This is a response to a (satisfiable) range request
|
||||||
|
RESOLVED_PARTIAL_CONTENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
public: // functions
|
||||||
|
// Constructs a ByteRange object of NONE kind
|
||||||
|
ByteRange();
|
||||||
|
|
||||||
|
// Constructs a ByteRange object of the given kind (except NONE)
|
||||||
|
ByteRange(Kind kind, int64_t first, int64_t last);
|
||||||
|
|
||||||
|
// Constructs a ByteRange object of PARSED kind corresponding to a
|
||||||
|
// range request of the form "Range: bytes=-suffix_length"
|
||||||
|
explicit ByteRange(int64_t suffix_length);
|
||||||
|
|
||||||
|
Kind kind() const { return kind_; }
|
||||||
|
int64_t first() const;
|
||||||
|
int64_t last() const;
|
||||||
|
int64_t length() const;
|
||||||
|
|
||||||
|
static ByteRange parse(const std::string& rangeStr);
|
||||||
|
ByteRange resolve(int64_t contentSize) const;
|
||||||
|
|
||||||
|
private: // data
|
||||||
|
Kind kind_;
|
||||||
|
int64_t first_;
|
||||||
|
int64_t last_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace kiwix
|
||||||
|
|
||||||
|
#endif //KIWIXLIB_SERVER_BYTE_RANGE_H
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Veloman Yunkan <veloman.yunkan@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "etag.h"
|
||||||
|
|
||||||
|
#include "tools/stringTools.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace kiwix {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Characters in the options part of the ETag could in principle be picked up
|
||||||
|
// from the latin alphabet in natural order (the character corresponding to
|
||||||
|
// ETag::Option opt would be 'a'+opt; that would somewhat simplify the code in
|
||||||
|
// this file). However it is better to have some mnemonics in the option names,
|
||||||
|
// hence below variable: all_options[opt] corresponds to the character going
|
||||||
|
// into the ETag for ETag::Option opt.
|
||||||
|
// IMPORTANT: The characters in all_options must come in sorted order (so that
|
||||||
|
// IMPORTANT: isValidOptionsString() works correctly).
|
||||||
|
const char all_options[] = "Zz";
|
||||||
|
|
||||||
|
static_assert(ETag::OPTION_COUNT == sizeof(all_options) - 1, "");
|
||||||
|
|
||||||
|
bool isValidETagBody(const std::string& s)
|
||||||
|
{
|
||||||
|
return !s.empty() && s.find_first_of("\"/") == std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSubsequenceOf(const std::string& s, const std::string& sortedString)
|
||||||
|
{
|
||||||
|
std::string::size_type i = 0;
|
||||||
|
for ( const char c : s )
|
||||||
|
{
|
||||||
|
const std::string::size_type j = sortedString.find(c, i);
|
||||||
|
if ( j == std::string::npos )
|
||||||
|
return false;
|
||||||
|
i = j+1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValidOptionsString(const std::string& s)
|
||||||
|
{
|
||||||
|
return isSubsequenceOf(s, all_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
void ETag::set_option(Option opt)
|
||||||
|
{
|
||||||
|
if ( ! get_option(opt) )
|
||||||
|
{
|
||||||
|
m_options.push_back(all_options[opt]);
|
||||||
|
std::sort(m_options.begin(), m_options.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ETag::get_option(Option opt) const
|
||||||
|
{
|
||||||
|
return m_options.find(all_options[opt]) != std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ETag::get_etag() const
|
||||||
|
{
|
||||||
|
if ( m_body.empty() )
|
||||||
|
return std::string();
|
||||||
|
|
||||||
|
return "\"" + m_body + "/" + m_options + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
ETag::ETag(const std::string& body, const std::string& options)
|
||||||
|
{
|
||||||
|
if ( isValidETagBody(body) && isValidOptionsString(options) )
|
||||||
|
{
|
||||||
|
m_body = body;
|
||||||
|
m_options = options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ETag ETag::parse(std::string s)
|
||||||
|
{
|
||||||
|
if ( kiwix::startsWith("W/", s) )
|
||||||
|
s = s.substr(2);
|
||||||
|
|
||||||
|
if ( s.front() != '"' || s.back() != '"' )
|
||||||
|
return ETag();
|
||||||
|
|
||||||
|
s = s.substr(1, s.size()-2);
|
||||||
|
|
||||||
|
const std::string::size_type i = s.find('/');
|
||||||
|
if ( i == std::string::npos )
|
||||||
|
return ETag();
|
||||||
|
|
||||||
|
return ETag(s.substr(0, i), s.substr(i+1));
|
||||||
|
}
|
||||||
|
|
||||||
|
ETag ETag::match(const std::string& etags, const std::string& body)
|
||||||
|
{
|
||||||
|
std::istringstream ss(etags);
|
||||||
|
std::string etag_str;
|
||||||
|
while ( ss >> etag_str )
|
||||||
|
{
|
||||||
|
if ( etag_str.back() == ',' )
|
||||||
|
etag_str.pop_back();
|
||||||
|
|
||||||
|
const ETag etag = parse(etag_str);
|
||||||
|
if ( etag && etag.m_body == body )
|
||||||
|
return etag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ETag();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace kiwix
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Veloman Yunkan <veloman.yunkan@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef KIWIXLIB_SERVER_ETAG_H
|
||||||
|
#define KIWIXLIB_SERVER_ETAG_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace kiwix {
|
||||||
|
|
||||||
|
// The ETag string used by Kiwix server (more precisely, its value inside the
|
||||||
|
// double quotes) consists of two parts:
|
||||||
|
//
|
||||||
|
// 1. Body - A string uniquely identifying the object or state from which
|
||||||
|
// the resource has been obtained.
|
||||||
|
//
|
||||||
|
// 2. Options - Zero or more characters encoding the type of the ETag and/or
|
||||||
|
// the values of some of the headers of the response
|
||||||
|
//
|
||||||
|
// The two parts are separated with a slash (/) symbol (which is always present,
|
||||||
|
// even when the the options part is empty). Neither portion of a Kiwix ETag
|
||||||
|
// may contain the slash symbol.
|
||||||
|
// Examples of valid Kiwix server ETags (including the double quotes):
|
||||||
|
//
|
||||||
|
// "abcdefghijklmn/"
|
||||||
|
// "1234567890/z"
|
||||||
|
// "6f1d19d0-633f-087b-fb55-7ac324ff9baf/Zz"
|
||||||
|
//
|
||||||
|
// The options part of the Kiwix ETag allows to correctly set the required
|
||||||
|
// headers when responding to a conditional If-None-Match request with a 304
|
||||||
|
// (Not Modified) response without following the full code path that would
|
||||||
|
// discover the necessary options.
|
||||||
|
|
||||||
|
class ETag
|
||||||
|
{
|
||||||
|
public: // types
|
||||||
|
enum Option {
|
||||||
|
ZIM_CONTENT,
|
||||||
|
COMPRESSED_CONTENT,
|
||||||
|
OPTION_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
public: // functions
|
||||||
|
ETag() {}
|
||||||
|
|
||||||
|
void set_body(const std::string& s) { m_body = s; }
|
||||||
|
void set_option(Option opt);
|
||||||
|
|
||||||
|
explicit operator bool() const { return !m_body.empty(); }
|
||||||
|
|
||||||
|
bool get_option(Option opt) const;
|
||||||
|
std::string get_etag() const;
|
||||||
|
|
||||||
|
|
||||||
|
static ETag match(const std::string& etags, const std::string& server_id);
|
||||||
|
|
||||||
|
private: // functions
|
||||||
|
ETag(const std::string& serverId, const std::string& options);
|
||||||
|
|
||||||
|
static ETag parse(std::string s);
|
||||||
|
|
||||||
|
private: // data
|
||||||
|
std::string m_body;
|
||||||
|
std::string m_options;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace kiwix
|
||||||
|
|
||||||
|
#endif // KIWIXLIB_SERVER_ETAG_H
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 Veloman Yunkan <veloman.yunkan@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "i18n.h"
|
||||||
|
|
||||||
|
#include "tools/otherTools.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace kiwix
|
||||||
|
{
|
||||||
|
|
||||||
|
const char* I18nStringTable::get(const std::string& key) const
|
||||||
|
{
|
||||||
|
const I18nString* const begin = entries;
|
||||||
|
const I18nString* const end = begin + entryCount;
|
||||||
|
const I18nString* found = std::lower_bound(begin, end, key,
|
||||||
|
[](const I18nString& a, const std::string& k) {
|
||||||
|
return a.key < k;
|
||||||
|
});
|
||||||
|
return (found == end || found->key != key) ? nullptr : found->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace i18n
|
||||||
|
{
|
||||||
|
// this data is generated by the i18n resource compiler
|
||||||
|
extern const I18nStringTable stringTables[];
|
||||||
|
extern const size_t langCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
class I18nStringDB
|
||||||
|
{
|
||||||
|
public: // functions
|
||||||
|
I18nStringDB() {
|
||||||
|
for ( size_t i = 0; i < kiwix::i18n::langCount; ++i ) {
|
||||||
|
const auto& t = kiwix::i18n::stringTables[i];
|
||||||
|
lang2TableMap[t.lang] = &t;
|
||||||
|
}
|
||||||
|
enStrings = lang2TableMap.at("en");
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string get(const std::string& lang, const std::string& key) const {
|
||||||
|
const char* s = getStringsFor(lang)->get(key);
|
||||||
|
if ( s == nullptr ) {
|
||||||
|
s = enStrings->get(key);
|
||||||
|
if ( s == nullptr ) {
|
||||||
|
throw std::runtime_error("Invalid message id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getStringCount(const std::string& lang) const {
|
||||||
|
try {
|
||||||
|
return lang2TableMap.at(lang)->entryCount;
|
||||||
|
} catch(const std::out_of_range&) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private: // functions
|
||||||
|
const I18nStringTable* getStringsFor(const std::string& lang) const {
|
||||||
|
try {
|
||||||
|
return lang2TableMap.at(lang);
|
||||||
|
} catch(const std::out_of_range&) {
|
||||||
|
return enStrings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private: // data
|
||||||
|
std::map<std::string, const I18nStringTable*> lang2TableMap;
|
||||||
|
const I18nStringTable* enStrings;
|
||||||
|
};
|
||||||
|
|
||||||
|
const I18nStringDB& getStringDb()
|
||||||
|
{
|
||||||
|
static const I18nStringDB stringDb;
|
||||||
|
return stringDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // unnamed namespace
|
||||||
|
|
||||||
|
std::string getTranslatedString(const std::string& lang, const std::string& key)
|
||||||
|
{
|
||||||
|
return getStringDb().get(lang, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace i18n
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string expandParameterizedString(const std::string& lang,
|
||||||
|
const std::string& key,
|
||||||
|
const Parameters& params)
|
||||||
|
{
|
||||||
|
const std::string tmpl = getTranslatedString(lang, key);
|
||||||
|
return render_template(tmpl, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace i18n
|
||||||
|
|
||||||
|
std::string ParameterizedMessage::getText(const std::string& lang) const
|
||||||
|
{
|
||||||
|
return i18n::expandParameterizedString(lang, msgId, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
LangPreference parseSingleLanguagePreference(const std::string& s)
|
||||||
|
{
|
||||||
|
const size_t langStart = s.find_first_not_of(" \t\n");
|
||||||
|
if ( langStart == std::string::npos ) {
|
||||||
|
return {"", 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t langEnd = s.find(';', langStart);
|
||||||
|
if ( langEnd == std::string::npos ) {
|
||||||
|
return {s.substr(langStart), 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string lang = s.substr(langStart, langEnd - langStart);
|
||||||
|
// We don't care about langEnd == langStart which will result in an empty
|
||||||
|
// language name - it will be dismissed by parseUserLanguagePreferences()
|
||||||
|
|
||||||
|
float q = 1.0;
|
||||||
|
int nCharsScanned;
|
||||||
|
if ( 1 == sscanf(s.c_str() + langEnd + 1, "q=%f%n", &q, &nCharsScanned)
|
||||||
|
&& langEnd + 1 + nCharsScanned == s.size() ) {
|
||||||
|
return {lang, q};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {"", 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // unnamed namespace
|
||||||
|
|
||||||
|
UserLangPreferences parseUserLanguagePreferences(const std::string& s)
|
||||||
|
{
|
||||||
|
UserLangPreferences result;
|
||||||
|
std::istringstream iss(s);
|
||||||
|
std::string singleLangPrefStr;
|
||||||
|
while ( std::getline(iss, singleLangPrefStr, ',') )
|
||||||
|
{
|
||||||
|
const auto langPref = parseSingleLanguagePreference(singleLangPrefStr);
|
||||||
|
if ( !langPref.lang.empty() && langPref.preference > 0 ) {
|
||||||
|
result.push_back(langPref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string selectMostSuitableLanguage(const UserLangPreferences& prefs)
|
||||||
|
{
|
||||||
|
if ( prefs.empty() ) {
|
||||||
|
return "en";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string bestLangSoFar("en");
|
||||||
|
float bestScoreSoFar = 0;
|
||||||
|
const auto& stringDb = getStringDb();
|
||||||
|
for ( const auto& entry : prefs ) {
|
||||||
|
const float score = entry.preference * stringDb.getStringCount(entry.lang);
|
||||||
|
if ( score > bestScoreSoFar ) {
|
||||||
|
bestScoreSoFar = score;
|
||||||
|
bestLangSoFar = entry.lang;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestLangSoFar;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace kiwix
|
||||||
Loaded 100 of 597 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user