Compare commits

...
173 Commits
Author SHA1 Message Date
Matthieu Gautier 8fc42558d3 New version 9.1.1 2020-04-17 17:36:11 +02:00
Matthieu Gautier 5d98a6964e Merge pull request #343 from kiwix/fix_datadir_windows
Fix datadir windows
2020-04-15 10:18:51 +02:00
Matthieu Gautier 9d8bf8ddcb Create the dataDirectory before returning its path. 2020-04-15 08:24:55 +02:00
Matthieu Gautier 4c8aad0e68 Do not use std::fstream has it doesn't support wchar path.
This is surprising, but C++11 fstream doesn't have a constructor
that take wchar as path.
So, on windows, we cannot open a stream on a path containing non ascii
char. VC++ provide an extension for that, but it is not standard and
g++ mingwin doesn't provide it.

So move all our write/read tools function to the plain old c versions,
using _wopen to open wide path on windows.
2020-04-14 18:13:35 +02:00
Matthieu Gautier eb6f0f710c Correctly detetect the dataDir on windows.
We must use the wide version of the getenv to correctly handle the case
we have accents in the user directory.

This also change the default dataDirectory on windows from $APPDATA to
$APPDATA/kiwix.
2020-04-14 12:12:34 +02:00
Matthieu Gautier d9d2a702ef Merge pull request #342 from kiwix/fix_iterator
Adapt to new libzim api.
2020-04-13 16:18:57 +02:00
Matthieu Gautier cbf5bd57a8 Adapt to new libzim api.
It is not possible to create a iterator without argument anymore.
2020-04-13 16:06:17 +02:00
Matthieu Gautier 2bf6b04726 Update minimum version of libzim. 2020-04-08 18:03:18 +02:00
Matthieu Gautier 12a0660342 New version 9.1.0 2020-04-08 15:38:25 +02:00
Matthieu Gautier ab478ed4cf Merge pull request #337 from kiwix/fix_book_articlecount
Fix book articlecount
2020-04-07 12:46:56 +02:00
Matthieu Gautier 533541cf45 Write the articleCount and mediaCount metadata in the OPDS stream.
This is not standard OPDS. But clients need this information.
2020-04-07 12:22:44 +02:00
Matthieu Gautier bab0fd3d4f Correctly initialize members' value of book.
Fix #333
2020-04-07 12:21:36 +02:00
Kelson 375792a686 Merge pull request #335 from kiwix/fix_taskbar
attach taskbar to <head> instead of <head>\n
2020-04-03 16:56:55 +02:00
renaud gaudin 7155c788e2 attach taskbar to <head> instead of <head>\n
Fixed a regression introduced in block-external-links feature.

For cleaner source, the taskbar (and the block-external JS file) were both
attached to `<head>\n`.
Unfortunately, this isn't safe enough as some ZIM files might have all kinds of HTML
syntax. Sotoki for instance have no CR after head, rendering the attachment impossible.

Note: realizing this method is somehow fragile as any HTML content with extra attribute
on the `<head>` tag or without a `<head>` tag would break the taskbar and the block external feature.
2020-04-03 16:53:43 +02:00
Kelson 3ac926b39b Merge pull request #331 from kiwix/readme-mustache
add required info to README
2020-04-03 16:41:37 +02:00
renaud gaudin ea3444e2fe add required info to README
Mustache 4 was release in late october 2019 and is not compatible with kiwix-lib as is.
Replacement in taskbar is messed-up and breaks suggestions.
Until this is fixed/adapted, inform users to use version 3.
2020-04-03 16:38:16 +02:00
Kelson 083c06d170 Merge pull request #336 from kiwix/better-ci-event
Run Github actions on any push (not only in PR)
2020-04-03 16:37:01 +02:00
Kelson 286fbba9f6 Run Github actions on any push (not only in PR) 2020-04-03 16:32:49 +02:00
Kelson 778603e1bf Use Github Actions CI badge (instead of Travis CI) 2020-04-03 16:30:42 +02:00
Kelson 411d4b0779 Merge pull request #334 from kiwix/fix-external
fix external link blocking for nested elems in link
2020-04-01 20:05:24 +02:00
renaud gaudin bce0e8c37c fix external link blocking for nested elems in link
if a link contains nested elements like `<a href="http://somewhere"><strong>goto</strong></a>`
then the link is trigger by the `<strong />` element which has no `href` attribute.
We were thus releasing the event in this case, resulting in legitimate external links
not blocked.

We are now looking for the closest `<a />` parent (might be self) to retrieve the `href`
attribute and capture if necessary.
2020-04-01 17:50:54 +00:00
Matthieu Gautier 507002d34e Merge pull request #330 from kiwix/block-external-links
Add external links blocking in serve
2020-03-30 16:57:08 +02:00
renaud gaudin 34a8144f51 cleaned-up external link blocker page
- removed useless JS/CSS
- set specific title
2020-03-30 14:42:51 +00:00
renaud gaudin 4709a42f4f disable external links blocking on 500 handler 2020-03-30 14:42:37 +00:00
renaud gaudin d04d9bf7f3 Unblock external link in catch page in JS code
Instead of disabling the blocking for the handler, the JS code detects it is
displaying the handler and allows external links to go through
2020-03-27 12:26:22 +00:00
renaud gaudin 412f0d9c61 moved blockExternalLink outside of taskbar
- `setBlockExternalLinks()` on server
- zero-dependency JS code
- JS script added in `inject_externallinks_blocker()`
- changed URL to `/catch/external?source=<source>`
2020-03-27 11:25:39 +00:00
renaud gaudin 0ad8bf45fc Add external links blocking in serve
In many use cases, it is not wanted to have user accidentaly click on external links
and leave the served ZIM content.
This could be because the result is unpredictible (reader not implementing this properly)
or because the serve user knows there's no backup internet connexion or because there is
an induced cost behind external links that doesn't affect served content.

using a new flag (`blockExternalLinks`) on `Response`/`setTaskBar`, a piece of JS code
is injected into the taskbar code.
This code adds a JS handler on all link click events and verifies the destination.
If the destination appears to be an external link (1), the link target is changed to
a specific URL:

```
/external?source=<original_uri>
```

(1) external is a link that's not on the same origin and starts with either `http:` `https:` or `//`.

Server implements a new handler on `/external` that displays a new page (`captured_external.html`)
which returns a generic message explaining the situation and offering to click on the link
again should the user really want to.
This is done by specifically asking `set_taskbar` to not block external requests on that page.

This approach allows integrators using a reverse proxy to handle that endpoint differently (rebrand it)

1. `Server` now has an `m_blockExternalLinks` defaulting to `false`
1. `Server.setTaskbar` is extended to support an additional bool to set the variable.
1. `Response` now has an `m_blockExternalLinks`
1. `Response` constr expects an additional bool for `blockExternalLinks`.
1. `Response.set_taskbar` is extended to support an additional bool to set the variable.
1. JNI/Java Wrapper reflects the extensions.
1. New resource file `templates/block_external.js` (included in head_part). Should it be in skin?
1. New resource file `templates/captured_external.html` for `handle_captured_external()`
1. Added a comment on `head_part.html` to help with JS insertion at the right place
1. `introduce_taskbar()` conditionnaly inserts the JS inside the taskbar
2020-03-26 12:06:36 +00:00
Matthieu Gautier 3ab3ffe3ea Merge pull request #329 from kiwix/getBookByPath
Get book by path
2020-03-06 12:12:43 +01:00
Matthieu Gautier 064d5f3fa6 Make the search argument constant. 2020-03-06 12:08:05 +01:00
Matthieu Gautier 46626a3f98 Add the method get bookByPath in library. 2020-03-06 12:08:05 +01:00
Matthieu Gautier 90ba27fbab Merge pull request #328 from kiwix/java_article_size
[JAVA] Add a method to get the size of an article.
2020-03-04 17:10:18 +01:00
Matthieu Gautier 76c293e403 [JAVA] Add a method to get the size of an article.
Fix #327
2020-03-04 16:40:03 +01:00
Matthieu Gautier 78e57c1a51 New version 9.0.1 2020-02-21 14:58:04 +01:00
Matthieu Gautier 7c49dc6af9 Merge pull request #326 from kiwix/fix_pair_size
[JAVA] Use a long to store the offset of a article in the zim file.
2020-02-19 14:58:43 +01:00
Matthieu Gautier 2e60a088ab [JAVA] Use a long to store the offset of a article in the zim file.
Fixes kiwix/kiwix-android#1769
2020-02-19 14:27:51 +01:00
Matthieu Gautier 9ab0f825f4 Merge pull request #325 from kiwix/new_version
New version 9.0.0
2020-02-12 14:54:12 +01:00
Matthieu Gautier 411ca28598 New version 9.0.0 2020-02-12 14:48:03 +01:00
Matthieu Gautier cc2b5e63ca Merge pull request #323 from kiwix/fix_wrapper
Fix wrapper
2020-02-12 10:47:29 +01:00
Matthieu Gautier ea29557a33 [Java] Add a wrapper on method to update book from another book or reader. 2020-02-11 17:44:14 +01:00
Matthieu Gautier b53f531f2b Fix typo getTagStr in the wrapper 2020-02-10 17:25:31 +01:00
Matthieu Gautier bd74ebed1e Merge pull request #320 from kiwix/path_win
Fix launching command with path containing spaces.
2020-02-07 13:15:24 +01:00
Matthieu Gautier 1632e9c55b Fix launching command with path containing spaces.
We must correctly quote path with space on windows.
This is needed as we can't launch command using a array of string on
windows but by giving only one string using space as separator.

Fix kiwix/kiwix-desktop#268
2020-02-06 11:46:33 +01:00
Kelson 6dd6a5dd80 Github Kiwix Sponsoring page link 2020-02-01 17:44:42 +01:00
Kelson b65b90d78c Merge pull request #319 from kiwix/fix-missing-stdexcept-include
Include stdexcept to fix GCC v10 compilation
2020-02-01 17:20:53 +01:00
Kelson 6a975994cc Include stdexcept to fix GCC v10 compilation 2020-02-01 13:52:27 +01:00
Matthieu Gautier 3ae596783d Name size filtering (#315)
Name size filtering
2020-01-30 19:13:55 +01:00
Matthieu Gautier ce6e956434 [OPDS] Add the url argument to filter by size and name.
Fix kiwix/kiwix-tools#231
2020-01-30 19:02:33 +01:00
Matthieu Gautier f560a1f815 Be able to filter the books by name. 2020-01-30 19:02:33 +01:00
Matthieu Gautier d14ba0c2e8 Merge pull request #314 from kiwix/trust_library
Trust the library.xml information by default.
2020-01-30 19:02:13 +01:00
Matthieu Gautier 34257cfc1f Trust the library.xml information by default.
Do not try to read the zim file and update the book when parsing a
library.xml.
Needed by kiwix/kiwix-tools#319
2020-01-30 18:22:07 +01:00
Matthieu Gautier 8f990feabb Merge pull request #316 from kiwix/tagging_system
Tagging system
2020-01-30 18:21:28 +01:00
Matthieu Gautier fff2524ee2 Fix opensearch description. 2020-01-30 17:48:56 +01:00
Matthieu Gautier a756e7f8f3 Add flavour attribute to book.
Fix #259
Fix kiwix/kiwix-tools#316
2020-01-30 17:48:56 +01:00
Matthieu Gautier bc257d2d6d Add method to get value of tag from a book.
Only the `getTagStr` method is available on android because we need a
proper exception handling on wrapping side.

Fix #298
2020-01-30 17:48:56 +01:00
Matthieu Gautier 7275f9b8e3 Move function to convert and use tags inside otherTools. 2020-01-30 17:48:56 +01:00
Matthieu Gautier b63827196c Small fixes (#313)
Small fixes
2020-01-30 17:23:30 +01:00
Matthieu Gautier 2881face70 Add missing setting of attribute. 2020-01-30 15:42:54 +01:00
Matthieu Gautier 77ba09c310 Reorder setting and dumper of book attribute.
No real change. Reordering setting and dumping of attribute in the same
order (mostly) they are declared in book.h make it easier to detect missing
attribute.
2020-01-30 15:42:54 +01:00
Matthieu Gautier 49aa0fbb9f Use a macro to write the filters. 2020-01-30 15:42:54 +01:00
Matthieu Gautier 7846b45bef Fix opds filtering by tag. 2020-01-30 15:42:54 +01:00
Matthieu Gautier 7e26f3502d Fix opds dumper/parser.
Add missing attributes.
2020-01-30 15:42:54 +01:00
Matthieu Gautier 47a866400c Add parameter options to start a download with aria2 (#310)
Add parameter options to start a download with aria2
2020-01-30 15:42:40 +01:00
luddens 4b6c26bd0b add parameter option to start a download with aria2
Downloader::startDownload has a new parameter option which is a vector of
pair that represents the options that can be set for adding a uri with aria2
with the function Aria2::addUri.

Aria2::addUri uses this parameter to set the struct of parameters for the
aria2 command
2020-01-29 15:38:09 +01:00
Matthieu Gautier 94c47383a9 Remove useless Aria2c error handling functions (#306)
Remove useless Aria2c error handling functions
2020-01-28 17:36:54 +01:00
luddens 6bcecc2677 remove useless error handling functions
Some of the changes from https://github.com/kiwix/kiwix-lib/pull/292
that doesn't work properly are removed
2020-01-28 16:58:55 +01:00
Matthieu Gautier e20c3520dc Android manager (#311)
Android manager
2020-01-28 15:48:38 +01:00
Matthieu Gautier 24fb1868d7 Fix typo in metadata. 2020-01-28 12:08:18 +01:00
Matthieu Gautier 82afb804e1 Add a small java test on the kiwix-lib.
Will the compilation should be made by meson.
It seems it is not possible to specify a existing jar
to link with. Use a custom script for now.
2020-01-28 12:08:18 +01:00
Matthieu Gautier 0951546356 Create the jar library when creating the java wrapper. 2020-01-28 12:08:18 +01:00
Matthieu Gautier f09c739c1f Be able to create a wrapper for java.
Android is a specific wrapper.
Java is another one.
2020-01-28 12:08:18 +01:00
Matthieu Gautier df9ddd5451 Use correct mutex on android and java. 2020-01-28 12:08:18 +01:00
Matthieu Gautier fe513951d3 Use a macro to print error log.
This allow use to compile the JNI wrapper not for
android.
2020-01-28 12:08:18 +01:00
Matthieu Gautier 7f0d509a88 Add the filter functionality. 2020-01-28 12:08:18 +01:00
Matthieu Gautier 54f671b2f1 Add some methods to get information from the library.
Else, the library is useless.
2020-01-28 12:08:18 +01:00
Matthieu Gautier c2c89c6c86 Rename the JNIKiwixLibrary class to Library.
This mainly use the "new memory system". No need to call dispose function.
Rename the class to Library to conform with the naming semantics
(JNIKiwix* use old memory system)
2020-01-28 12:08:18 +01:00
Matthieu Gautier 75652d0e9f WIP Add a wrapper around the kiwix::manager class.
The JNIKiwixManager is used to manage (insertion of book in) the library.

It is created, as needed, using an existing Library as input.
It is then used to add books, parse library.xml or opds content.
Then it can be destruct (and must be) with the `dispose` method.

```java
library = JNILibrary(...);
manager = JNIManager(library);
manager.parseOpds(opdscontent);
manager.dispose();
// library contains the books declared in the opds content.
// Use the library methods to get the books' info.
```
2020-01-28 12:08:18 +01:00
Matthieu Gautier 6535dc2e38 Add a wrapper for the Book class. 2020-01-28 12:08:18 +01:00
Matthieu Gautier 6b2f768c8f Use template function c2jni to convert c++ type to jni. 2020-01-28 12:08:18 +01:00
Matthieu Gautier 6099c3113f Readd code coverage (#312)
Readd code coverage
2020-01-28 10:53:36 +01:00
Matthieu Gautier 21d8a6952f Readd code coverage 2020-01-27 18:05:12 +01:00
Matthieu Gautier b84f4e03ea update book even if the members aren't empty (#302)
update book even if the members aren't empty
2020-01-27 17:58:41 +01:00
luddens ff21a095cb update book even if the members aren't empty
remove the conditions to always update the book
2020-01-27 17:50:44 +01:00
Matthieu Gautier fa091a19c6 Merge pull request #309 from kiwix/github-action
Use github actions to run the CI.
2020-01-21 18:18:56 +01:00
Matthieu Gautier e3ba9fa5cc Use github actions to run the CI. 2020-01-21 18:10:33 +01:00
Matthieu Gautier 560f67522f Do not build tests if we are cross-compiling (#308)
Do not build tests if we are cross-compiling
2020-01-21 11:11:22 +01:00
Matthieu Gautier 11118efd84 Do not build tests if we are cross-compiling 2020-01-21 10:58:43 +01:00
Matthieu Gautier 9c2bc6affc Fix tests. (#307)
Fix tests.
2020-01-20 17:12:58 +01:00
Matthieu Gautier 4fe31a20e3 Fix tests.
As we use the main library of gtest (if already installed)
we don't need to (and must not) define a main function for the tests.

Does the same (use main library) if we use the subproject.
2020-01-20 17:00:18 +01:00
Matthieu Gautier 0f99c1ad20 Win library path (#305)
Win library path
2020-01-13 17:12:16 +01:00
Matthieu Gautier 91db055d86 Remove function to read file using a native path.
All path must be utf8. This is already the case in all our project.
(If this not the case, this is a bug)

So we don't need to have a version with a native and utf8 path.
2020-01-13 16:59:58 +01:00
Matthieu Gautier 5540149e2b Correctly open the library path on windows.
We need to convert the path to wstring on Windows to handle directory
with accented characters.

Fix kiwix-desktop#269
2020-01-13 16:54:09 +01:00
Matthieu Gautier 7c7e351d34 Add missing function's declarations to convert path for windows. 2020-01-13 16:51:48 +01:00
Matthieu Gautier c5051b343e Catalog empty search (#304)
Catalog empty search
2020-01-13 14:09:33 +01:00
Matthieu Gautier 02a0d592f9 Install python3 in Travis MacOS
For some reason, `homebrew install gcovr` now install python3 as
dependency.
This is a dependency since a long time, I don't know why it was not
installing it before.

But the installation fails because it cannot link correctly python3
because python2 is already installed.

Then meson, when it tries to run the conversion script, fails because
the script cannot find python3.

The solution I've found is to unlink python2 and relink python3 to have
a correct installation of python3.
2020-01-13 13:29:18 +01:00
Matthieu Gautier 071e9e3fec Correctly filter the catalog when we don't what to filter.
Set the different filter's fields only when we are requested to filter
them. Else, we ends to requests that some fields are empty.

If the request has no argument, we raise an exception (catched) and so
we don't set the corresponding field in the filter.

Fix #303
2020-01-07 17:29:43 +01:00
Matthieu Gautier 4a01303438 Correctly set the id of the opds stream.
Set the id of the stream *after* the uuid is generated.
2020-01-07 17:29:29 +01:00
Kelson 8095a87bf1 Bump-up version to 8.2.2 and update Changelog 2019-12-08 12:12:39 +01:00
Kelson bb55527508 Merge pull request #301 from kiwix/remove-trailing-spaces
Remove trailing spaces
2019-12-08 12:07:21 +01:00
Kelson b7c5e5f339 Remove trailing spaces 2019-12-08 11:52:16 +01:00
Kelson 316deeb485 Merge pull request #300 from kiwix/exec-perm
Add execution permission to a few scripts
2019-12-08 10:03:57 +01:00
Kelson 721d981825 Add execution permission to a few scripts 2019-12-07 14:00:22 +01:00
Kelson 2244074f3c travis-ci.org -> travis-ci.com 2019-12-05 08:10:56 +01:00
Kelson 0a1e01eb2b Update install_deps.sh 2019-11-29 17:28:25 +01:00
Kelson abd2fa3bf3 Bump-up to 8.2.1 in build.gradle 2019-11-27 19:15:24 +01:00
Kelson ea3349f37c Merge pull request #297 from kiwix/reintroduce-taskbar
Reintroduce taskbar
2019-11-26 08:55:19 -05:00
Kelson e3c6ca0d1b Bump-up version to 8.2.1 2019-11-26 11:54:39 +01:00
Kelson 52e165cf78 Reintroduce kiwix-serve taskbar 2019-11-26 11:54:00 +01:00
Kelson 3b7c805183 Bump-up version to 8.2.0 2019-11-20 13:07:28 +01:00
Kelson 9c4867a95a Update Changelog 2019-11-20 13:06:24 +01:00
Kelson 223f7ee78a Add default Github configuration 2019-11-20 06:12:08 +01:00
Kelson 20690bd5f5 Merge pull request #294 from kiwix/remove-absolute-url-support
Remove absolute internal URL support
2019-11-08 15:29:40 +01:00
Emmanuel Engelhart de7b7c34b5 Remove absolute internal URL support 2019-11-07 18:05:58 +01:00
Kelson f0ac66aea1 Merge pull request #292 from kiwix/aria2-comment
More error handling for aria2 cmd
2019-11-01 15:40:51 +01:00
luddens 20a2c78733 add get aria2 launch cmd method 2019-11-01 15:27:21 +01:00
luddens 9850be7267 add Curl error message 2019-11-01 15:27:21 +01:00
luddens 0dd996c6a3 add try catch around aria2 first commands 2019-11-01 15:27:21 +01:00
Kelson 2500cc8e63 Merge pull request #288 from kiwix/get-bookmarks-api
Add a parameter to getBookmarks fct to get valid bookmarks only
2019-10-31 14:38:50 +01:00
luddens bd6797143c add getBookmarks test 2019-10-31 15:01:52 +02:00
luddens c9a15c9961 Add a parameter to getBookmarks fct to get valid bookmarks only
The default value of this parameter is false, in this case all the bookmarks
are returned, otherwise only those who are related to books of the library.
2019-10-31 14:05:21 +02:00
Kelson f1d55f8e86 Merge pull request #290 from kiwix/set-port-kiwixserve
add setPort() method
2019-10-28 16:41:19 +01:00
luddens a2c2955f41 add kiwixserve unit test 2019-10-28 16:00:26 +01:00
luddens 9975e0b369 add setPort() method 2019-10-28 15:56:49 +01:00
Kelson efe1c2dea3 Merge pull request #289 from Aditya-Sood/kiwix-android/issue/765
Fixes kiwix-android issue#765
2019-10-03 11:25:18 +01:00
Aditya-Sood 2af9ba4eab Readd original getNextSuggestion() 2019-10-01 13:30:35 +05:30
Aditya-Sood c007373b46 Re-add comment 2019-10-01 13:30:34 +05:30
Aditya-Sood e1acf9acff Code & local repository cleanup 2019-10-01 13:30:34 +05:30
Aditya-Sood daaadf3e1c Comment out previous definitions 2019-10-01 13:30:34 +05:30
Aditya-Sood 74bd482335 Preliminary work 2019-10-01 13:30:34 +05:30
Matthieu Gautier 2aebffb27c New version 8.1.0 2019-09-26 15:56:30 +02:00
Kelson da247b3242 Merge pull request #287 from kiwix/codecov-badge
Add Codecov badge to README
2019-09-26 13:40:57 +03:00
Kelson f85ec9ea6f Add Codecov badge to README 2019-09-26 12:37:57 +02:00
Matthieu Gautier b4fac9d0df Remove test directory from codecoverage (#286)
Remove test directory from codecoverage
2019-09-26 12:14:03 +02:00
Matthieu Gautier 4f2ede80e5 Remove test directory from codecoverage 2019-09-26 12:00:33 +02:00
Matthieu Gautier c2ecb9d126 Add code coverage in travis CI. (#285)
Add code coverage in travis CI.
2019-09-26 11:58:49 +02:00
Matthieu Gautier 5883dba0ef Add code coverage in travis CI.
Fix #284
2019-09-26 11:34:35 +02:00
Matthieu Gautier 7ad6aedd66 Convert path get from windows environment to utf8. (#283)
Convert path get from windows environment to utf8.
2019-09-25 18:13:59 +02:00
Matthieu Gautier 67170709bb Convert path get from windows environment to utf8.
Fix kiwix/kiwix-desktop#203
2019-09-25 18:07:42 +02:00
Matthieu Gautier dfd16155af Add missing implementation of android's getArticleCount and getM… (#282)
Add missing implementation of android's getArticleCount and getMediaCount.
2019-09-24 11:57:00 +02:00
Matthieu Gautier 0db06d98a8 Add missing implementation of android's getArticleCount and getMediaCount.
Fix #281
2019-09-24 11:47:05 +02:00
Matthieu Gautier 742156d366 [API Break] Fix pathTools (and a bit stringTools). (#280)
[API Break] Fix pathTools (and a bit stringTools).
2019-09-24 10:58:51 +02:00
Matthieu Gautier 598dd3c175 [API Break] Fix pathTools (and a bit stringTools).
Api changes :
 - removeLastPathElement do not takes extra arguments
   `removePreSeparator` and `removePostSeparator`.
   This is not needed as path do not need special tailing separator.
 - Only one function `split`. Arguments can be implicitly convert to
   string. No need for overloading functions to explicitly cast them.
 - `split` function takes another argument `trimEmpty`. If true, empty
   element are removed.

Path manipulation now almost pass trough a vector<string> to store each
path's part.

Most of the complex works is now made in the normalizeParts function.
2019-09-19 18:16:06 +02:00
Matthieu Gautier 49c0c5ff47 Fix join (#279)
Fix join
2019-09-17 16:28:52 +02:00
Matthieu Gautier 65ebc7fe7f New version 8.0.1 2019-09-17 16:22:28 +02:00
Matthieu Gautier 2f4636e2df Fix stringTools join function. 2019-09-17 16:22:28 +02:00
Kelson f4e9148b1d Small fix of OSes names in the README 2019-09-17 12:20:17 +02:00
Matthieu Gautier 891666b8c4 new version 8.0.0 2019-09-17 11:47:18 +02:00
Matthieu Gautier 57a2b98e7a [ABI Break] Correctly detect the executable path in appimage. (#277)
[ABI Break] Correctly detect the executable path in appimage.
2019-09-17 11:41:15 +02:00
Matthieu Gautier 9b4419f3fc [ABI Break] Correctly detect the executable path in appimage.
There are two executable path :
- The user one (the appimage path)
- The real one (in the appimage archive)

When we search of `library.xml` we need the user one.
But when we search of `aria2c` or `kiwix-serve` we need the real one.

Fix kiwix/kiwix-desktop#256
2019-09-17 11:23:16 +02:00
Matthieu Gautier 15d5b4ed58 Metadata (#276)
Metadata
2019-09-17 11:21:19 +02:00
Matthieu Gautier 2f91149da3 Update .gitignore. 2019-09-17 10:38:16 +02:00
Matthieu Gautier 6ee174b546 Add a method to get the value of a specific tag.
Fix #258
2019-09-17 10:37:53 +02:00
Matthieu Gautier 2a6772b76d [API Change] Convert tags to the new convention.
Use the new convention describe here : https://wiki.openzim.org/wiki/Tags
2019-09-17 10:30:24 +02:00
Matthieu Gautier 660d5d7fb7 [API Change] Rename getMatatag to getMetadata. 2019-09-16 10:36:04 +02:00
Matthieu Gautier 157c1c939c Add a string tool to join a list of strings together. 2019-09-16 09:42:10 +02:00
Matthieu Gautier bd91e89785 Add missing method to get the zim metadata.
According to https://wiki.openzim.org/wiki/Metadata
2019-09-12 15:33:07 +02:00
Matthieu Gautier 1245d4e467 Use a macro to get the content of the metadata. 2019-09-12 15:26:53 +02:00
Matthieu Gautier 420be55bfa Reorder methods to get metadata.
Use the same order than https://wiki.openzim.org/wiki/Metadata
2019-09-12 15:24:17 +02:00
Matthieu Gautier 651cb9165c Win kiwix serve (#274)
Win kiwix serve
2019-09-11 15:25:41 +02:00
Matthieu Gautier 49046248fd New version 7.0.0 2019-09-11 14:04:21 +02:00
Matthieu Gautier e42e061d45 Add a way to specify a library to use with kiwix-serve.
If kiwix-desktop use a `library.xml` in the same directory than the
executable, we need to use it instead of the default one.

Instead of detect again the `library.xml` to use, let `kiwix-desktop` set
the library to use.

This also fix a issue when `/` is not a valid path separator in windows.
2019-09-11 14:04:21 +02:00
Kelson d90774450d Slightly bigger top-padding for kiwix-serve 2019-09-11 13:50:33 +02:00
Matthieu Gautier 9e36c876f5 New version 6.0.4 2019-09-10 15:54:15 +02:00
Matthieu Gautier 1a4c434e3c Correctly cast double to int. (#273)
Correctly cast double to int.
2019-09-10 15:09:09 +02:00
Matthieu Gautier 3294508d87 Correctly cast double to int.
Ms cl compiler complains about the implicit conversion.
2019-09-10 14:10:40 +02:00
Matthieu Gautier 351e573bce Correctly detect the executable path if we use a AppImage. (#272)
Correctly detect the executable path if we use a AppImage.
2019-09-09 18:37:37 +02:00
Matthieu Gautier a32363e6a2 Correctly detect the executable path if we use a AppImage.
AppImage works by decompressing the "program" in a temporary directory.
So the executable path is not the path of the AppImage file.

By using the environment variables set by appimage we can find the correct
"path" of the executable.

Fix kiwix/kiwix-desktop#46
2019-09-09 18:27:53 +02:00
Matthieu Gautier 56f8b7a876 Fix search (#271)
Fix search
2019-09-09 14:59:35 +02:00
Matthieu Gautier 87dc145dc7 Correctly set searcher information even if resultStart equals resultEnd. 2019-09-09 14:43:51 +02:00
Matthieu Gautier a13244dc0e Rename hasResult to hasResults 2019-09-09 14:43:51 +02:00
Matthieu Gautier 78dbd66522 [HTML Rendering] Do not render page navigation buttons if only one page. 2019-09-09 14:43:51 +02:00
Matthieu Gautier fdc291b7c2 [HTML Rendering] Do not do division by zero.
We must correctly handle the case if resultStart is equal to resultEnd.
2019-09-09 14:43:51 +02:00
Kelson d372cea146 Merge pull request #270 from kiwix/aur-badges
Add AUR badge
2019-09-08 17:06:49 +02:00
Kelson e1fcd12e48 Add AUR badge 2019-09-08 17:05:44 +02:00
101 changed files with 2785 additions and 815 deletions

No files matched your search

+12
View File
@@ -0,0 +1,12 @@
codecov:
notify:
require_ci_to_pass: yes
coverage:
status:
project:
default:
threshold: 1%
ignore:
- "test"
+12
View File
@@ -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/
+27
View File
@@ -0,0 +1,27 @@
# Configuration for Move Issues - https://github.com/dessant/move-issues
# Delete the command comment when it contains no other content
deleteCommand: true
# Close the source issue after moving
closeSourceIssue: true
# Lock the source issue after moving
lockSourceIssue: false
# Mention issue and comment authors
mentionAuthors: true
# Preserve mentions in the issue content
keepContentMentions: true
# Move labels that also exist on the target repository
moveLabels: true
# Set custom aliases for targets
# aliases:
# r: repo
# or: owner/repo
# Repository to extend settings from
# _extends: repo
+15
View File
@@ -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.
+152
View File
@@ -0,0 +1,152 @@
name: CI
on: [push]
jobs:
Macos:
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v1
- name: Setup python 3.5
uses: actions/setup-python@v1
with:
python-version: '3.5'
- name: Install packages
uses: mstksg/get-package@v1
with:
brew: gcovr pkg-config ninja
- name: Install python modules
run: pip3 install meson==0.49.2 pytest
- name: Install deps
shell: bash
run: |
ARCHIVE_NAME=deps2_osx_native_dyn_kiwix-lib.tar.xz
wget -O- http://tmp.kiwix.org/ci/${ARCHIVE_NAME} | tar -xJ -C $HOME
- name: Compile
shell: bash
run: |
export PKG_CONFIG_PATH=$HOME/BUILD_native_dyn/INSTALL/lib/pkgconfig
export CPPFLAGS="-I$HOME/BUILD_native_dyn/INSTALL/include"
meson . build --default-library=shared -Db_coverage=true
cd build
ninja
- name: Test
shell: bash
run: |
export LD_LIBRARY_PATH=$HOME/BUILD_native_dyn/INSTALL/lib:$HOME/BUILD_native_dyn/INSTALL/lib64
cd build
meson test --verbose
ninja coverage
env:
SKIP_BIG_MEMORY_TEST: 1
- name: Publish coverage
shell: bash
run: |
curl https://codecov.io/bash -o codecov.sh
bash codecov.sh -n osx_native_dyn -Z
rm codecov.sh
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
Linux:
strategy:
fail-fast: false
matrix:
target:
- native_static
- native_dyn
- android_arm
- android_arm64
- win32_static
- win32_dyn
include:
- target: native_static
image_variant: xenial
lib_postfix: '/x86_64-linux-gnu'
- target: native_dyn
image_variant: xenial
lib_postfix: '/x86_64-linux-gnu'
- target: android_arm
image_variant: xenial
lib_postfix: '/x86_64-linux-gnu'
- target: android_arm64
image_variant: xenial
lib_postfix: '/x86_64-linux-gnu'
- target: win32_static
image_variant: f31
lib_postfix: '64'
- target: win32_dyn
image_variant: f31
lib_postfix: '64'
env:
HOME: /home/runner
runs-on: ubuntu-latest
container:
image: "kiwix/kiwix-build_ci:${{matrix.image_variant}}-26"
steps:
- name: Extract branch name
shell: bash
run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
id: extract_branch
- name: Checkout code
shell: python
run: |
from subprocess import check_call
from os import environ
command = [
'git', 'clone',
'https://github.com/${{github.repository}}',
'--depth=1',
'--branch', '${{steps.extract_branch.outputs.branch}}'
]
check_call(command, cwd=environ['HOME'])
- name: Install deps
shell: bash
run: |
ARCHIVE_NAME=deps2_${OS_NAME}_${{matrix.target}}_kiwix-lib.tar.xz
wget -O- http://tmp.kiwix.org/ci/${ARCHIVE_NAME} | tar -xJ -C /home/runner
- name: Compile
shell: bash
run: |
meson --version
if [[ "${{matrix.target}}" =~ .*_dyn ]]; then
MESON_OPTION="--default-library=shared"
else
MESON_OPTION="--default-library=static"
fi
if [[ "${{matrix.target}}" =~ native_.* ]]; then
MESON_OPTION="$MESON_OPTION -Db_coverage=true"
else
MESON_OPTION="$MESON_OPTION --cross-file $HOME/BUILD_${{matrix.target}}/meson_cross_file.txt"
fi
if [[ "${{matrix.target}}" =~ android_.* ]]; then
MESON_OPTION="$MESON_OPTION -Dandroid=true"
fi
cd $HOME/kiwix-lib
meson . build ${MESON_OPTION}
cd build
ninja
env:
PKG_CONFIG_PATH: "/home/runner/BUILD_${{matrix.target}}/INSTALL/lib/pkgconfig:/home/runner/BUILD_${{matrix.target}}/INSTALL/lib${{matrix.lib_postfix}}/pkgconfig"
CPPFLAGS: "-I/home/runner/BUILD_${{matrix.target}}/INSTALL/include"
- name: Test
if: startsWith(matrix.target, 'native_')
shell: bash
run: |
cd $HOME/kiwix-lib/build
meson test --verbose
ninja coverage
env:
LD_LIBRARY_PATH: "/home/runner/BUILD_${{matrix.target}}/INSTALL/lib:/home/runner/BUILD_${{matrix.target}}/INSTALL/lib${{matrix.lib_postfix}}"
SKIP_BIG_MEMORY_TEST: 1
- name: Publish coverage
shell: bash
run: |
cd $HOME/kiwix-lib
curl https://codecov.io/bash -o codecov.sh
bash codecov.sh -n "${OS_NAME}_${{matrix.target}}" -Z
rm codecov.sh
if: startsWith(matrix.target, 'native_')
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+2
View File
@@ -1,2 +1,4 @@
.idea/ .idea/
*.swp *.swp
subprojects/googletest-release*
*.class
-37
View File
@@ -1,37 +0,0 @@
language: cpp
dist: xenial
sudo: true
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.5
- 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
+2 -2
View File
@@ -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
+96
View File
@@ -1,3 +1,99 @@
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 kiwix-lib 6.0.3
=============== ===============
+5 -3
View File
@@ -3,11 +3,13 @@ Kiwix library
The Kiwix library provides the [Kiwix](https://kiwix.org) software The Kiwix library provides the [Kiwix](https://kiwix.org) software
suite core. It contains the code shared by all Kiwix ports (Windows, suite core. It contains the code shared by all Kiwix ports (Windows,
Linux, OSX, Android, ...). GNU/Linux, macOS, Android, iOS, ...).
[![Download](https://api.bintray.com/packages/kiwix/kiwix/kiwixlib/images/download.svg)](https://bintray.com/kiwix/kiwix/kiwixlib/_latestVersion) [![Download](https://api.bintray.com/packages/kiwix/kiwix/kiwixlib/images/download.svg)](https://bintray.com/kiwix/kiwix/kiwixlib/_latestVersion)
[![Build Status](https://travis-ci.org/kiwix/kiwix-lib.svg?branch=master)](https://travis-ci.org/kiwix/kiwix-lib) [![AUR version](https://img.shields.io/aur/version/kiwix-lib)](https://aur.archlinux.org/packages/kiwix-lib/)
[![Build Status](https://github.com/kiwix/kiwix-lib/workflows/CI/badge.svg?query=branch%3Amaster)](https://github.com/kiwix/kiwix-lib/actions?query=branch%3Amaster)
[![CodeFactor](https://www.codefactor.io/repository/github/kiwix/kiwix-lib/badge)](https://www.codefactor.io/repository/github/kiwix/kiwix-lib) [![CodeFactor](https://www.codefactor.io/repository/github/kiwix/kiwix-lib/badge)](https://www.codefactor.io/repository/github/kiwix/kiwix-lib)
[![Codecov](https://codecov.io/gh/kiwix/kiwix-lib/branch/master/graph/badge.svg)](https://codecov.io/gh/kiwix/kiwix-lib)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
Disclaimer Disclaimer
@@ -39,7 +41,7 @@ libraries need to be available:
* [Aria2](https://aria2.github.io/) (package `aria2` on Ubuntu) * [Aria2](https://aria2.github.io/) (package `aria2` on Ubuntu)
* [Mustache](https://github.com/kainjow/Mustache) (Just copy the * [Mustache](https://github.com/kainjow/Mustache) (Just copy the
header `mustache.hpp` somewhere it can be found by the compiler and/or header `mustache.hpp` somewhere it can be found by the compiler and/or
set CPPFLAGS with correct `-I` option) set CPPFLAGS with correct `-I` option). Use Mustache version 3 only.
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
Vendored Regular → Executable
View File
File mode changed.
Vendored Regular → Executable
View File
File mode changed.
@@ -26,7 +26,7 @@ task writePom {
project { project {
groupId 'org.kiwix.kiwixlib' groupId 'org.kiwix.kiwixlib'
artifactId 'kiwixlib' artifactId 'kiwixlib'
version '6.0.3' + (System.env.KIWIXLIB_BUILDVERSION == null ? '' : '-'+System.env.KIWIXLIB_BUILDVERSION) version '9.1.1' + (System.env.KIWIXLIB_BUILDVERSION == null ? '' : '-'+System.env.KIWIXLIB_BUILDVERSION)
packaging 'aar' packaging 'aar'
name 'kiwixlib' name 'kiwixlib'
url 'https://github.com/kiwix/kiwix-lib' url 'https://github.com/kiwix/kiwix-lib'
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="kiwix.org.kiwixlib"> package="org.kiwix.kiwixlib">
<application <application
android:allowBackup="true" android:allowBackup="true"
+10 -5
View File
@@ -60,6 +60,9 @@ class Book
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; }
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; }
@@ -81,6 +84,7 @@ 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; }
@@ -94,7 +98,7 @@ class Book
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_title; std::string m_title;
std::string m_description; std::string m_description;
std::string m_language; std::string m_language;
@@ -103,12 +107,13 @@ class Book
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; mutable std::string m_favicon;
std::string m_faviconUrl; std::string m_faviconUrl;
std::string m_faviconMimeType; std::string m_faviconMimeType;
+2 -1
View File
@@ -25,6 +25,7 @@
#include <map> #include <map>
#include <pthread.h> #include <pthread.h>
#include <memory> #include <memory>
#include <stdexcept>
namespace kiwix namespace kiwix
{ {
@@ -92,7 +93,7 @@ class Downloader
void close(); void close();
Download* startDownload(const std::string& uri); Download* startDownload(const std::string& uri, const std::vector<std::pair<std::string, std::string>>& options = {});
Download* getDownload(const std::string& did); Download* getDownload(const std::string& did);
size_t getNbDownload() { return m_knownDownloads.size(); } size_t getNbDownload() { return m_knownDownloads.size(); }
+8 -8
View File
@@ -64,14 +64,14 @@ class Entry
* @return the path of the entry. * @return the path of the entry.
*/ */
std::string getPath() const; std::string getPath() const;
/** /**
* Get the title of the entry. * Get the title of the entry.
* *
* @return the title of the entry. * @return the title of the entry.
*/ */
std::string getTitle() const; std::string getTitle() const;
/** /**
* Get the content of the entry. * Get the content of the entry.
* *
@@ -81,7 +81,7 @@ class Entry
* @return the content of the entry. * @return the content of the entry.
*/ */
std::string getContent() const; std::string getContent() const;
/** /**
* Get the blob of the entry. * Get the blob of the entry.
* *
@@ -91,7 +91,7 @@ class Entry
* @return the blob of the entry. * @return the blob of the entry.
*/ */
zim::Blob getBlob(offset_type offset = 0) const; zim::Blob getBlob(offset_type offset = 0) const;
/** /**
* Get the blob of the entry. * Get the blob of the entry.
* *
@@ -102,7 +102,7 @@ class Entry
* @return the blob of the entry. * @return the blob of the entry.
*/ */
zim::Blob getBlob(offset_type offset, size_type size) const; zim::Blob getBlob(offset_type offset, size_type size) const;
/** /**
* Get the info for direct access to the content of the entry. * Get the info for direct access to the content of the entry.
* *
@@ -118,7 +118,7 @@ class Entry
* Return <"",0> if is not possible to read directly. * Return <"",0> if is not possible to read directly.
*/ */
std::pair<std::string, offset_type> getDirectAccessInfo() const; std::pair<std::string, offset_type> getDirectAccessInfo() const;
/** /**
* Get the size of the entry. * Get the size of the entry.
* *
@@ -132,8 +132,8 @@ class Entry
* @return the mime_type of the entry. * @return the mime_type of the entry.
*/ */
std::string getMimetype() const; std::string getMimetype() const;
/** /**
* Get if the entry is a redirect entry. * Get if the entry is a redirect entry.
* *
+4 -1
View File
@@ -2,6 +2,7 @@
#define KIWIXLIB_KIWIX_SERVE_H_ #define KIWIXLIB_KIWIX_SERVE_H_
#include <memory> #include <memory>
#include <string>
class Subprocess; class Subprocess;
namespace kiwix { namespace kiwix {
@@ -9,17 +10,19 @@ namespace kiwix {
class KiwixServe class KiwixServe
{ {
public: public:
KiwixServe(int port = 8181); KiwixServe(const std::string& libraryPath, int port = 8181);
~KiwixServe(); ~KiwixServe();
void run(); void run();
void shutDown(); void shutDown();
bool isRunning(); bool isRunning();
int getPort() { return m_port; } int getPort() { return m_port; }
int setPort(int port);
private: private:
std::unique_ptr<Subprocess> mp_kiwixServe; std::unique_ptr<Subprocess> mp_kiwixServe;
int m_port; int m_port;
std::string m_libraryPath;
}; };
}; //end namespace kiwix }; //end namespace kiwix
+4 -1
View File
@@ -57,6 +57,7 @@ class Filter {
std::string _creator; std::string _creator;
size_t _maxSize; size_t _maxSize;
std::string _query; std::string _query;
std::string _name;
public: public:
Filter(); Filter();
@@ -100,6 +101,7 @@ class Filter {
Filter& creator(std::string creator); Filter& creator(std::string creator);
Filter& maxSize(size_t size); Filter& maxSize(size_t size);
Filter& query(std::string query); Filter& query(std::string query);
Filter& name(std::string name);
bool accept(const Book& book) const; bool accept(const Book& book) const;
}; };
@@ -147,6 +149,7 @@ class Library
bool removeBookmark(const std::string& zimId, const std::string& url); bool removeBookmark(const std::string& zimId, const std::string& url);
Book& getBookById(const std::string& id); Book& getBookById(const std::string& id);
Book& getBookByPath(const std::string& path);
std::shared_ptr<Reader> getReaderById(const std::string& id); std::shared_ptr<Reader> getReaderById(const std::string& id);
/** /**
@@ -208,7 +211,7 @@ class Library
* *
* @return A list of bookmarks * @return A list of bookmarks
*/ */
const std::vector<kiwix::Bookmark>& getBookmarks() { return m_bookmarks; } const std::vector<kiwix::Bookmark> getBookmarks(bool onlyValidBookmarks = true);
/** /**
* Get all book ids of the books in the library. * Get all book ids of the books in the library.
+7 -19
View File
@@ -69,26 +69,12 @@ class Manager
/** /**
* 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.
* @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.
*
* @param nativePath The path of the `library.xml`
* @param UTF8Path The utf8 version (?) of the path. Also the path where the
* library will be writen i readOnly is False.
* @param readOnly Set if the libray path could be overwritten latter with
* updated content.
* @return True if file has been properly parsed.
*/
bool readFile(const std::string& nativePath,
const std::string& UTF8Path,
const bool readOnly = true);
/** /**
* Load a library content store in the string. * Load a library content store in the string.
@@ -101,7 +87,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.
@@ -168,8 +155,9 @@ class Manager
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);
+100 -35
View File
@@ -158,31 +158,7 @@ class Reader
* @param[out] value The value will be set to the content 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. * @return True if it was possible to get the content of the metadata.
*/ */
bool getMetatag(const string& name, string& value) const; bool getMetadata(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. * Get the name of the zim file.
@@ -192,18 +168,13 @@ class Reader
string getName() const; string getName() const;
/** /**
* Get the tags of the zim file. * Get the title of the zim file.
* *
* @return The tags of the zim file as specified in the zim metadata. * @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 getTags() const; string getTitle() 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. * Get the creator of the zim file.
@@ -219,6 +190,100 @@ class Reader
*/ */
string getPublisher() const; string getPublisher() 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 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 long description of the zim file.
*
* @return The long description of the zim file as specifed in the zim metadata.
*/
string getLongDescription() 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 license of the zim file.
*
* @return The license of the zim file as specified in the zim metadata.
*/
string getLicense() const;
/**
* Get the tags of the zim file.
*
* @param original If true, return the original tags as specified in the zim metadata.
* Else, try to convert it to the new 'normalized' format.
* @return The tags of the zim file.
*/
string getTags(bool original=false) const;
/**
* Get the value (as a string) of a specific tag.
*
* According to https://wiki.openzim.org/wiki/Tags
*
* @return The value of the specified tag.
* @throw std::out_of_range if the specified tag is not found.
*/
string getTagStr(const std::string& tagName) const;
/**
* Get the boolean value of a specific tag.
*
* According to https://wiki.openzim.org/wiki/Tags
*
* @return The boolean value of the specified tag.
* @throw std::out_of_range if the specified tag is not found.
* std::domain_error if the value of the tag cannot be convert to bool.
*/
bool getTagBool(const std::string& tagName) const;
/**
* Get the relations of the zim file.
*
* @return The relation of the zim file as specified in the zim metadata.
*/
string getRelation() const;
/**
* Get the flavour of the zim file.
*
* @return The flavour of the zim file as specified in the zim metadata.
*/
string getFlavour() const;
/**
* Get the source of the zim file.
*
* @return The source of the zim file as specified in the zim metadata.
*/
string getSource() const;
/**
* Get the scraper of the zim file.
*
* @return The scraper of the zim file as specified in the zim metadata.
*/
string getScraper() const;
/** /**
* Get the origId of the zim file. * Get the origId of the zim file.
* *
+1 -1
View File
@@ -89,7 +89,7 @@ class Searcher
* @param resultEnd the end 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. * @param verbose print some info on stdout if true.
*/ */
void search(std::string& search, void search(const std::string& search,
unsigned int resultStart, unsigned int resultStart,
unsigned int resultEnd, unsigned int resultEnd,
const bool verbose = false); const bool verbose = false);
+4 -1
View File
@@ -56,7 +56,9 @@ namespace kiwix
void setNbThreads(int threads) { m_nbThreads = threads; } void setNbThreads(int threads) { m_nbThreads = threads; }
void setVerbose(bool verbose) { m_verbose = verbose; } void setVerbose(bool verbose) { m_verbose = verbose; }
void setTaskbar(bool withTaskbar, bool withLibraryButton) void setTaskbar(bool withTaskbar, bool withLibraryButton)
{ m_withTaskbar = withTaskbar; m_withLibraryButton = withLibraryButton; } { m_withTaskbar = withTaskbar; m_withLibraryButton = withLibraryButton; }
void setBlockExternalLinks(bool blockExternalLinks)
{ m_blockExternalLinks = blockExternalLinks; }
protected: protected:
Library* mp_library; Library* mp_library;
@@ -68,6 +70,7 @@ namespace kiwix
bool m_verbose = false; bool m_verbose = false;
bool m_withTaskbar = true; bool m_withTaskbar = true;
bool m_withLibraryButton = true; bool m_withLibraryButton = true;
bool m_blockExternalLinks = false;
std::unique_ptr<InternalServer> mp_server; std::unique_ptr<InternalServer> mp_server;
}; };
} }
+13 -3
View File
@@ -21,6 +21,7 @@
#define KIWIX_OTHERTOOLS_H #define KIWIX_OTHERTOOLS_H
#include <string> #include <string>
#include <vector>
namespace pugi { namespace pugi {
class xml_node; class xml_node;
@@ -28,9 +29,18 @@ namespace pugi {
namespace kiwix namespace kiwix
{ {
void sleep(unsigned int milliseconds); void sleep(unsigned int milliseconds);
std::string nodeToString(const pugi::xml_node& node); std::string nodeToString(const pugi::xml_node& node);
std::string converta2toa3(const std::string& a2code); std::string converta2toa3(const std::string& a2code);
/*
* Convert all format tag string to new format
*/
std::vector<std::string> convertTags(const std::string& tags_str);
std::string getTagValueFromTagList(const std::vector<std::string>& tagList,
const std::string& tagName);
bool convertStrToBool(const std::string& value);
} }
#endif #endif
+6 -4
View File
@@ -22,12 +22,14 @@
#include <string> #include <string>
#ifdef _WIN32
std::string WideToUtf8(const std::wstring& wstr);
std::wstring Utf8ToWide(const std::string& str);
#endif
bool isRelativePath(const std::string& path); bool isRelativePath(const std::string& path);
std::string computeAbsolutePath(const std::string& path, const std::string& relativePath); std::string computeAbsolutePath(const std::string& path, const std::string& relativePath);
std::string computeRelativePath(const std::string& path, const std::string& absolutePath); std::string computeRelativePath(const std::string& path, const std::string& absolutePath);
std::string removeLastPathElement(const std::string& path, std::string removeLastPathElement(const std::string& path);
const bool removePreSeparator = false,
const bool removePostSeparator = false);
std::string appendToDirectory(const std::string& directoryPath, const std::string& filename); std::string appendToDirectory(const std::string& directoryPath, const std::string& filename);
unsigned int getFileSize(const std::string& path); unsigned int getFileSize(const std::string& path);
@@ -38,7 +40,7 @@ bool makeDirectory(const std::string& path);
std::string makeTmpDirectory(); std::string makeTmpDirectory();
bool copyFile(const std::string& sourcePath, const std::string& destPath); bool copyFile(const std::string& sourcePath, const std::string& destPath);
std::string getLastPathElement(const std::string& path); std::string getLastPathElement(const std::string& path);
std::string getExecutablePath(); std::string getExecutablePath(bool realPathOnly = false);
std::string getCurrentDirectory(); std::string getCurrentDirectory();
std::string getDataDirectory(); std::string getDataDirectory();
bool writeTextFile(const std::string& path, const std::string& content); bool writeTextFile(const std::string& path, const std::string& content);
+2 -4
View File
@@ -43,10 +43,8 @@ void loadICUExternalTables();
std::string urlEncode(const std::string& value, bool encodeReserved = false); std::string urlEncode(const std::string& value, bool encodeReserved = false);
std::string urlDecode(const std::string& value, bool component = false); std::string urlDecode(const std::string& value, bool component = false);
std::vector<std::string> split(const std::string&, const std::string&); std::vector<std::string> split(const std::string&, const std::string&, bool trimEmpty = true);
std::vector<std::string> split(const char*, const char*); std::string join(const std::vector<std::string>& list, const std::string& sep);
std::vector<std::string> split(const std::string&, const char*);
std::vector<std::string> split(const char*, const std::string&);
std::string ucAll(const std::string& word); std::string ucAll(const std::string& word);
std::string lcAll(const std::string& word); std::string lcAll(const std::string& word);
+10 -4
View File
@@ -1,20 +1,26 @@
project('kiwix-lib', 'cpp', project('kiwix-lib', 'cpp',
version : '6.0.3', # Also change this in android-kiwix-lib-publisher/kiwixLibAndroid/build.gradle version : '9.1.1', # Also change this in android-kiwix-lib-publisher/kiwixLibAndroid/build.gradle
license : 'GPL', license : 'GPL',
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')
static_deps = get_option('android') or get_option('default_library') == 'static' wrapper = get_option('wrapper')
if get_option('android')
static_deps = 'android' in wrapper or 'java' in wrapper or get_option('default_library') == 'static'
if 'android' in wrapper
extra_libs = ['-llog'] extra_libs = ['-llog']
else else
extra_libs = [] extra_libs = []
endif endif
if 'java' in wrapper
add_languages('java')
endif
thread_dep = dependency('threads') thread_dep = dependency('threads')
libicu_dep = dependency('icu-i18n', static:static_deps) libicu_dep = dependency('icu-i18n', static:static_deps)
libzim_dep = dependency('libzim', version : '>=5.0.0', static:static_deps) libzim_dep = dependency('libzim', version : '>=6.1.1', 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) microhttpd_dep = dependency('libmicrohttpd', static:static_deps)
+2 -2
View File
@@ -1,2 +1,2 @@
option('android', type : 'boolean', value : false, option('wrapper', type:'array', choices:['java', 'android'], value:[],
description : 'Do we make a kiwix-lib for android') description: 'The wrapper to generate.')
+6 -6
View File
@@ -81,12 +81,12 @@ 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,
@@ -95,11 +95,11 @@ class Resource:
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 automaically generated. Do not modify it.
@@ -113,7 +113,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);
@@ -137,7 +137,7 @@ def gen_c_file(resources, basename):
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.
-72
View File
@@ -1,72 +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 <android/log.h>
#include "org_kiwix_kiwixlib_JNIKiwixLibrary.h"
#include "library.h"
#include "reader.h"
#include "utils.h"
/* Kiwix Reader JNI functions */
JNIEXPORT jlong JNICALL Java_org_kiwix_kiwixlib_JNIKiwixLibrary_getNativeLibrary(
JNIEnv* env, jobject obj)
{
__android_log_print(ANDROID_LOG_INFO, "kiwix", "Attempting to create library");
Lock l;
try {
kiwix::Library* library = new kiwix::Library();
return reinterpret_cast<jlong>(new Handle<kiwix::Library>(library));
} catch (std::exception& e) {
__android_log_print(ANDROID_LOG_WARN, "kiwix", "Error creating ZIM library");
__android_log_print(ANDROID_LOG_WARN, "kiwix", e.what());
return 0;
}
}
JNIEXPORT void JNICALL
Java_org_kiwix_kiwixlib_JNIKiwixLibrary_dispose(JNIEnv* env, jobject obj)
{
Handle<kiwix::Library>::dispose(env, obj);
}
#define LIBRARY (Handle<kiwix::Library>::getHandle(env, obj))
/* Kiwix library functions */
JNIEXPORT jboolean JNICALL
Java_org_kiwix_kiwixlib_JNIKiwixLibrary_addBook(JNIEnv* env, jobject obj, jstring path)
{
std::string cPath = jni2c(path, env);
bool ret;
try {
kiwix::Reader reader(cPath);
kiwix::Book book;
book.update(reader);
ret = LIBRARY->addBook(book);
} catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to add the book");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
ret = false;
}
return ret;
}
-32
View File
@@ -1,32 +0,0 @@
kiwix_jni = custom_target('jni',
input: ['org/kiwix/kiwixlib/JNIICU.java',
'org/kiwix/kiwixlib/JNIKiwixReader.java',
'org/kiwix/kiwixlib/JNIKiwixLibrary.java',
'org/kiwix/kiwixlib/JNIKiwixSearcher.java',
'org/kiwix/kiwixlib/JNIKiwixServer.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_JNIKiwixLibrary.h',
'org_kiwix_kiwixlib_JNIKiwixServer.h',
'org_kiwix_kiwixlib_JNIKiwixSearcher.h',
'org_kiwix_kiwixlib_JNIKiwixSearcher_Result.h'],
command:['javac', '-d', '@OUTDIR@', '-h', '@OUTDIR@', '@INPUT@']
)
kiwix_sources += [
'android/kiwixicu.cpp',
'android/kiwixreader.cpp',
'android/kiwixlibrary.cpp',
'android/kiwixsearcher.cpp',
'android/kiwixserver.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')
+21 -3
View File
@@ -49,7 +49,7 @@ Aria2::Aria2():
m_secret = "token:"+m_secret; m_secret = "token:"+m_secret;
std::string aria2cmd = appendToDirectory( std::string aria2cmd = appendToDirectory(
removeLastPathElement(getExecutablePath(), true, true), removeLastPathElement(getExecutablePath(true)),
ARIA2_CMD); ARIA2_CMD);
if (fileExists(aria2cmd)) { if (fileExists(aria2cmd)) {
// A local aria2c exe exists (packaged with kiwix-desktop), use it. // A local aria2c exe exists (packaged with kiwix-desktop), use it.
@@ -78,23 +78,38 @@ 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(); mp_curl = curl_easy_init();
char errbuf[CURL_ERROR_SIZE];
curl_easy_setopt(mp_curl, CURLOPT_URL, "http://localhost/rpc"); curl_easy_setopt(mp_curl, CURLOPT_URL, "http://localhost/rpc");
curl_easy_setopt(mp_curl, CURLOPT_PORT, m_port); curl_easy_setopt(mp_curl, CURLOPT_PORT, m_port);
curl_easy_setopt(mp_curl, CURLOPT_POST, 1L); curl_easy_setopt(mp_curl, CURLOPT_POST, 1L);
curl_easy_setopt(mp_curl, CURLOPT_ERRORBUFFER, errbuf);
int watchdog = 50; int watchdog = 50;
while(--watchdog) { while(--watchdog) {
sleep(10); sleep(10);
errbuf[0] = 0;
auto res = curl_easy_perform(mp_curl); auto res = curl_easy_perform(mp_curl);
if (res == CURLE_OK) { if (res == CURLE_OK) {
break; break;
} else if (watchdog == 1) {
std::cerr <<" curl_easy_perform() failed." << std::endl;
fprintf(stderr, "\nlibcurl: (%d) ", res);
if (errbuf[0] != 0) {
std::cerr << errbuf << std::endl;
} else {
std::cerr << curl_easy_strerror(res) << std::endl;
}
} }
} }
if (!watchdog) { if (!watchdog) {
curl_easy_cleanup(mp_curl); curl_easy_cleanup(mp_curl);
throw std::runtime_error("Cannot connect to aria2c rpc"); throw std::runtime_error("Cannot connect to aria2c rpc. Aria2c launch cmd : " + launchCmd);
} }
} }
@@ -145,13 +160,16 @@ std::string Aria2::doRequest(const MethodCall& methodCall)
throw std::runtime_error("Cannot perform request"); throw std::runtime_error("Cannot perform request");
} }
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();
+1 -1
View File
@@ -34,7 +34,7 @@ class Aria2
virtual ~Aria2(); virtual ~Aria2();
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);
std::vector<std::string> tellActive(); std::vector<std::string> tellActive();
std::vector<std::string> tellWaiting(); std::vector<std::string> tellWaiting();
+50 -27
View File
@@ -23,6 +23,7 @@
#include "tools/base64.h" #include "tools/base64.h"
#include "tools/regexTools.h" #include "tools/regexTools.h"
#include "tools/networkTools.h" #include "tools/networkTools.h"
#include "tools/otherTools.h"
#include <pugixml.hpp> #include <pugixml.hpp>
@@ -44,43 +45,48 @@ bool Book::update(const kiwix::Book& other)
if (m_readOnly) if (m_readOnly)
return false; return false;
if (m_id != other.m_id)
return false;
m_readOnly = other.m_readOnly; m_readOnly = other.m_readOnly;
m_path = other.m_path;
m_pathValid = other.m_pathValid;
m_title = other.m_title;
m_description = other.m_description;
m_language = other.m_language;
m_creator = other.m_creator;
m_publisher = other.m_publisher;
m_date = other.m_date;
m_url = other.m_url;
m_name = other.m_name;
m_flavour = other.m_flavour;
m_tags = other.m_tags;
m_origId = other.m_origId;
m_articleCount = other.m_articleCount;
m_mediaCount = other.m_mediaCount;
m_size = other.m_size;
m_favicon = other.m_favicon;
m_faviconMimeType = other.m_faviconMimeType;
m_faviconUrl = other.m_faviconUrl;
if (m_path.empty()) { m_downloadId = other.m_downloadId;
m_path = other.m_path;
m_pathValid = other.m_pathValid;
}
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_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 kiwix::Reader& reader)
{ {
m_path = reader.getZimFilePath(); m_path = reader.getZimFilePath();
m_pathValid = true;
m_id = reader.getId(); m_id = reader.getId();
m_title = reader.getTitle();
m_description = reader.getDescription(); m_description = reader.getDescription();
m_language = reader.getLanguage(); m_language = reader.getLanguage();
m_date = reader.getDate();
m_creator = reader.getCreator(); m_creator = reader.getCreator();
m_publisher = reader.getPublisher(); m_publisher = reader.getPublisher();
m_title = reader.getTitle(); m_date = reader.getDate();
m_name = reader.getName(); m_name = reader.getName();
m_flavour = reader.getFlavour();
m_tags = reader.getTags(); m_tags = reader.getTags();
m_origId = reader.getOrigId(); m_origId = reader.getOrigId();
m_articleCount = reader.getArticleCount(); m_articleCount = reader.getArticleCount();
@@ -100,21 +106,24 @@ 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;
m_pathValid = fileExists(path);
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")); m_favicon = base64_decode(ATTR("favicon"));
m_faviconMimeType = ATTR("faviconMimeType"); m_faviconMimeType = ATTR("faviconMimeType");
m_faviconUrl = ATTR("faviconUrl");
try { try {
m_downloadId = ATTR("downloadId"); m_downloadId = ATTR("downloadId");
} catch(...) {} } catch(...) {}
@@ -137,12 +146,18 @@ 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 = fromOpdsDate(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();
m_date = fromOpdsDate(VALUE("updated"));
m_name = VALUE("name");
m_flavour = VALUE("flavour");
m_tags = VALUE("tags"); m_tags = VALUE("tags");
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();
@@ -197,4 +212,12 @@ const std::string& Book::getFavicon() const {
return m_favicon; return m_favicon;
} }
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));
}
} }
+16 -9
View File
@@ -127,17 +127,24 @@ void Download::cancelDownload()
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->tellActive()) {
m_knownDownloads[gid]->updateStatus(); m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
m_knownDownloads[gid]->updateStatus();
}
} catch (std::exception& e) {
std::cerr << "aria2 tellActive failed : " << e.what();
} }
for (auto gid : mp_aria->tellWaiting()) { 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();
}
} catch (std::exception& e) {
std::cerr << "aria2 tellWaiting failed : " << e.what();
} }
} }
/* Destructor */ /* Destructor */
Downloader::~Downloader() Downloader::~Downloader()
{ {
@@ -156,7 +163,7 @@ std::vector<std::string> Downloader::getDownloadIds() {
return ret; return ret;
} }
Download* Downloader::startDownload(const std::string& uri) Download* Downloader::startDownload(const std::string& uri, const std::vector<std::pair<std::string, std::string>>& options)
{ {
for (auto& p: m_knownDownloads) { for (auto& p: m_knownDownloads) {
auto& d = p.second; auto& d = p.second;
@@ -165,7 +172,7 @@ Download* Downloader::startDownload(const std::string& uri)
return d.get(); return d.get();
} }
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::unique_ptr<Download>(new Download(mp_aria, gid));
return m_knownDownloads[gid].get(); return m_knownDownloads[gid].get();
} }
+15 -4
View File
@@ -14,7 +14,9 @@
namespace kiwix { namespace kiwix {
KiwixServe::KiwixServe(int port) : m_port(port) KiwixServe::KiwixServe(const std::string& libraryPath, int port)
: m_port(port),
m_libraryPath(libraryPath)
{ {
} }
@@ -34,7 +36,7 @@ void KiwixServe::run()
std::vector<const char*> callCmd; std::vector<const char*> callCmd;
std::string kiwixServeCmd = appendToDirectory( std::string kiwixServeCmd = appendToDirectory(
removeLastPathElement(getExecutablePath(), true, true), removeLastPathElement(getExecutablePath(true)),
KIWIXSERVE_CMD); KIWIXSERVE_CMD);
if (fileExists(kiwixServeCmd)) { if (fileExists(kiwixServeCmd)) {
// A local kiwix-serve exe exists (packaged with kiwix-desktop), use it. // A local kiwix-serve exe exists (packaged with kiwix-desktop), use it.
@@ -43,13 +45,12 @@ void KiwixServe::run()
// Try to use a potential installed kiwix-serve. // Try to use a potential installed kiwix-serve.
callCmd.push_back(KIWIXSERVE_CMD); callCmd.push_back(KIWIXSERVE_CMD);
} }
std::string libraryPath = getDataDirectory() + "/library.xml";
std::string attachProcessOpt = "-a" + to_string(pid); std::string attachProcessOpt = "-a" + to_string(pid);
std::string portOpt = "-p" + to_string(m_port); std::string portOpt = "-p" + to_string(m_port);
callCmd.push_back(attachProcessOpt.c_str()); callCmd.push_back(attachProcessOpt.c_str());
callCmd.push_back(portOpt.c_str()); callCmd.push_back(portOpt.c_str());
callCmd.push_back("-l"); callCmd.push_back("-l");
callCmd.push_back(libraryPath.c_str()); callCmd.push_back(m_libraryPath.c_str());
mp_kiwixServe = Subprocess::run(callCmd); mp_kiwixServe = Subprocess::run(callCmd);
} }
@@ -67,4 +68,14 @@ bool KiwixServe::isRunning()
return false; return false;
} }
int KiwixServe::setPort(int port)
{
if (port >= 1 && port <= 65535) {
m_port = port;
} else {
return -1;
}
return m_port;
}
} }
+52 -25
View File
@@ -85,6 +85,18 @@ Book& Library::getBookById(const std::string& id)
return m_books.at(id); return m_books.at(id);
} }
Book& Library::getBookByPath(const std::string& path)
{
for(auto& it: m_books) {
auto& book = it.second;
if (book.getPath() == path)
return book;
}
std::ostringstream ss;
ss << "No book with path " << path << " in the library." << std::endl;
throw std::out_of_range(ss.str());
}
std::shared_ptr<Reader> Library::getReaderById(const std::string& id) std::shared_ptr<Reader> Library::getReaderById(const std::string& id)
{ {
try { try {
@@ -115,7 +127,7 @@ unsigned int Library::getBookCount(const bool localBooks,
bool Library::writeToFile(const std::string& path) bool Library::writeToFile(const std::string& path)
{ {
auto baseDir = removeLastPathElement(path, true, false); auto baseDir = removeLastPathElement(path);
LibXMLDumper dumper(this); LibXMLDumper dumper(this);
dumper.setBaseDir(baseDir); dumper.setBaseDir(baseDir);
return writeTextFile(path, dumper.dumpLibXMLContent(getBooksIds())); return writeTextFile(path, dumper.dumpLibXMLContent(getBooksIds()));
@@ -184,6 +196,21 @@ std::vector<std::string> Library::getBooksPublishers()
return booksPublishers; return booksPublishers;
} }
const std::vector<kiwix::Bookmark> Library::getBookmarks(bool onlyValidBookmarks)
{
if (!onlyValidBookmarks) {
return m_bookmarks;
}
std::vector<kiwix::Bookmark> validBookmarks;
auto booksId = getBooksIds();
for(auto& bookmark:m_bookmarks) {
if (std::find(booksId.begin(), booksId.end(), bookmark.getBookId()) != booksId.end()) {
validBookmarks.push_back(bookmark);
}
}
return validBookmarks;
}
std::vector<std::string> Library::getBooksIds() std::vector<std::string> Library::getBooksIds()
{ {
std::vector<std::string> bookIds; std::vector<std::string> bookIds;
@@ -363,6 +390,7 @@ enum filterTypes {
_CREATOR = FLAG(10), _CREATOR = FLAG(10),
MAXSIZE = FLAG(11), MAXSIZE = FLAG(11),
QUERY = FLAG(12), QUERY = FLAG(12),
NAME = FLAG(13),
}; };
Filter& Filter::local(bool accept) Filter& Filter::local(bool accept)
@@ -450,24 +478,35 @@ Filter& Filter::query(std::string query)
return *this; return *this;
} }
Filter& Filter::name(std::string name)
{
_name = name;
activeFilters |= NAME;
return *this;
}
#define ACTIVE(X) (activeFilters & (X)) #define ACTIVE(X) (activeFilters & (X))
#define FILTER(TAG, TEST) if (ACTIVE(TAG) && !(TEST)) { return false; }
bool Filter::accept(const Book& book) const bool Filter::accept(const Book& book) const
{ {
auto local = !book.getPath().empty(); auto local = !book.getPath().empty();
if (ACTIVE(_LOCAL) && !local) FILTER(_LOCAL, local)
return false; FILTER(_NOLOCAL, !local)
if (ACTIVE(_NOLOCAL) && local)
return false;
auto valid = book.isPathValid(); auto valid = book.isPathValid();
if (ACTIVE(_VALID) && !valid) FILTER(_VALID, valid)
return false; FILTER(_NOVALID, !valid)
if (ACTIVE(_NOVALID) && valid)
return false;
auto remote = !book.getUrl().empty(); auto remote = !book.getUrl().empty();
if (ACTIVE(_REMOTE) && !remote) FILTER(_REMOTE, remote)
return false; FILTER(_NOREMOTE, !remote)
if (ACTIVE(_NOREMOTE) && remote)
return false; FILTER(MAXSIZE, book.getSize() <= _maxSize)
FILTER(LANG, book.getLanguage() == _lang)
FILTER(_PUBLISHER, book.getPublisher() == _publisher)
FILTER(_CREATOR, book.getCreator() == _creator)
FILTER(NAME, book.getName() == _name)
if (ACTIVE(ACCEPTTAGS)) { if (ACTIVE(ACCEPTTAGS)) {
if (!_acceptTags.empty()) { if (!_acceptTags.empty()) {
auto vBookTags = split(book.getTags(), ";"); auto vBookTags = split(book.getTags(), ";");
@@ -490,18 +529,6 @@ bool Filter::accept(const Book& book) const
} }
} }
} }
if (ACTIVE(MAXSIZE) && book.getSize() > _maxSize)
return false;
if (ACTIVE(LANG) && book.getLanguage() != _lang)
return false;
if (ACTIVE(_PUBLISHER) && book.getPublisher() != _publisher)
return false;
if (ACTIVE(_CREATOR) && book.getCreator() != _creator)
return false;
if ( ACTIVE(QUERY) if ( ACTIVE(QUERY)
&& !(matchRegex(book.getTitle(), "\\Q" + _query + "\\E") && !(matchRegex(book.getTitle(), "\\Q" + _query + "\\E")
|| matchRegex(book.getDescription(), "\\Q" + _query + "\\E"))) || matchRegex(book.getDescription(), "\\Q" + _query + "\\E")))
+4 -2
View File
@@ -53,13 +53,15 @@ void LibXMLDumper::handleBook(Book book, pugi::xml_node root_node) {
if (book.getOrigId().empty()) { if (book.getOrigId().empty()) {
ADD_ATTR_NOT_EMPTY(entry_node, "title", book.getTitle()); ADD_ATTR_NOT_EMPTY(entry_node, "title", book.getTitle());
ADD_ATTR_NOT_EMPTY(entry_node, "name", book.getName());
ADD_ATTR_NOT_EMPTY(entry_node, "tags", book.getTags());
ADD_ATTR_NOT_EMPTY(entry_node, "description", book.getDescription()); ADD_ATTR_NOT_EMPTY(entry_node, "description", book.getDescription());
ADD_ATTR_NOT_EMPTY(entry_node, "language", book.getLanguage()); ADD_ATTR_NOT_EMPTY(entry_node, "language", book.getLanguage());
ADD_ATTR_NOT_EMPTY(entry_node, "creator", book.getCreator()); ADD_ATTR_NOT_EMPTY(entry_node, "creator", book.getCreator());
ADD_ATTR_NOT_EMPTY(entry_node, "publisher", book.getPublisher()); 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());
ADD_ATTR_NOT_EMPTY(entry_node, "faviconMimeType", book.getFaviconMimeType()); ADD_ATTR_NOT_EMPTY(entry_node, "faviconMimeType", book.getFaviconMimeType());
ADD_ATTR_NOT_EMPTY(entry_node, "faviconUrl", book.getFaviconUrl());
if (!book.getFavicon().empty()) if (!book.getFavicon().empty())
ADD_ATTRIBUTE(entry_node, "favicon", base64_encode(book.getFavicon())); ADD_ATTRIBUTE(entry_node, "favicon", base64_encode(book.getFavicon()));
} else { } else {
+25 -24
View File
@@ -19,6 +19,8 @@
#include "manager.h" #include "manager.h"
#include "tools/pathTools.h"
#include <pugixml.hpp> #include <pugixml.hpp>
namespace kiwix namespace kiwix
@@ -46,8 +48,9 @@ Manager::~Manager()
} }
} }
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 +62,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 +74,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_inplace((void*)xml.data(), xml.size());
if (result) { if (result) {
this->parseXmlDom(doc, readOnly, libraryPath); this->parseXmlDom(doc, readOnly, libraryPath, trustLibrary);
} }
return true; return true;
@@ -134,21 +134,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;
} }
@@ -157,7 +158,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;
@@ -177,7 +178,7 @@ std::string Manager::addBookFromPathAndGetId(const std::string& pathToOpen,
if (pathToSave != pathToOpen) { if (pathToSave != pathToOpen) {
book.setPath(isRelativePath(pathToSave) book.setPath(isRelativePath(pathToSave)
? computeAbsolutePath( ? computeAbsolutePath(
removeLastPathElement(writableLibraryPath, true, false), removeLastPathElement(writableLibraryPath),
pathToSave) pathToSave)
: pathToSave); : pathToSave);
} }
+5 -2
View File
@@ -32,13 +32,16 @@ else
kiwix_sources += 'subprocess_unix.cpp' kiwix_sources += 'subprocess_unix.cpp'
endif endif
if get_option('android') if 'android' in wrapper
subdir('android')
install_dir = 'kiwix-lib/jniLibs/' + meson.get_cross_property('android_abi') install_dir = 'kiwix-lib/jniLibs/' + meson.get_cross_property('android_abi')
else else
install_dir = get_option('libdir') install_dir = get_option('libdir')
endif endif
if 'android' in wrapper or 'java' in wrapper
subdir('wrapper/java')
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')
+11 -3
View File
@@ -70,12 +70,17 @@ void OPDSDumper::setOpenSearchInfo(int totalResults, int startIndex, int count)
pugi::xml_node OPDSDumper::handleBook(Book book, pugi::xml_node root_node) { pugi::xml_node OPDSDumper::handleBook(Book book, pugi::xml_node root_node) {
auto entry_node = root_node.append_child("entry"); auto entry_node = root_node.append_child("entry");
ADD_TEXT_ENTRY(entry_node, "title", book.getTitle());
ADD_TEXT_ENTRY(entry_node, "id", "urn:uuid:"+book.getId()); ADD_TEXT_ENTRY(entry_node, "id", "urn:uuid:"+book.getId());
ADD_TEXT_ENTRY(entry_node, "icon", rootLocation + "/meta?name=favicon&content=" + book.getHumanReadableIdFromPath()); ADD_TEXT_ENTRY(entry_node, "title", book.getTitle());
ADD_TEXT_ENTRY(entry_node, "updated", gen_date_from_yyyy_mm_dd(book.getDate()));
ADD_TEXT_ENTRY(entry_node, "summary", book.getDescription()); ADD_TEXT_ENTRY(entry_node, "summary", book.getDescription());
ADD_TEXT_ENTRY(entry_node, "language", book.getLanguage());
ADD_TEXT_ENTRY(entry_node, "updated", gen_date_from_yyyy_mm_dd(book.getDate()));
ADD_TEXT_ENTRY(entry_node, "name", book.getName());
ADD_TEXT_ENTRY(entry_node, "flavour", book.getFlavour());
ADD_TEXT_ENTRY(entry_node, "tags", book.getTags()); ADD_TEXT_ENTRY(entry_node, "tags", book.getTags());
ADD_TEXT_ENTRY(entry_node, "articleCount", to_string(book.getArticleCount()));
ADD_TEXT_ENTRY(entry_node, "mediaCount", to_string(book.getMediaCount()));
ADD_TEXT_ENTRY(entry_node, "icon", rootLocation + "/meta?name=favicon&content=" + book.getHumanReadableIdFromPath());
auto content_node = entry_node.append_child("link"); auto content_node = entry_node.append_child("link");
content_node.append_attribute("type") = "text/html"; content_node.append_attribute("type") = "text/html";
@@ -84,6 +89,9 @@ pugi::xml_node OPDSDumper::handleBook(Book book, pugi::xml_node root_node) {
auto author_node = entry_node.append_child("author"); auto author_node = entry_node.append_child("author");
ADD_TEXT_ENTRY(author_node, "name", book.getCreator()); ADD_TEXT_ENTRY(author_node, "name", book.getCreator());
auto publisher_node = entry_node.append_child("publisher");
ADD_TEXT_ENTRY(publisher_node, "name", book.getPublisher());
if (! book.getUrl().empty()) { if (! book.getUrl().empty()) {
auto acquisition_link = entry_node.append_child("link"); auto acquisition_link = entry_node.append_child("link");
acquisition_link.append_attribute("rel") = "http://opds-spec.org/acquisition/open-access"; acquisition_link.append_attribute("rel") = "http://opds-spec.org/acquisition/open-access";
+71 -30
View File
@@ -22,6 +22,8 @@
#include <zim/search.h> #include <zim/search.h>
#include "tools/otherTools.h"
inline char hi(char v) inline char hi(char v)
{ {
char hex[] = "0123456789abcdef"; char hex[] = "0123456789abcdef";
@@ -278,7 +280,7 @@ string Reader::getZimFilePath() const
return this->zimFilePath; return this->zimFilePath;
} }
/* Return a metatag value */ /* Return a metatag value */
bool Reader::getMetatag(const string& name, string& value) const bool Reader::getMetadata(const string& name, string& value) const
{ {
try { try {
auto entry = getEntryFromPath("M/"+name); auto entry = getEntryFromPath("M/"+name);
@@ -289,10 +291,17 @@ bool Reader::getMetatag(const string& name, string& value) const
} }
} }
#define METADATA(NAME) std::string v; getMetadata(NAME, v); return v;
string Reader::getName() const
{
METADATA("Name")
}
string Reader::getTitle() const string Reader::getTitle() const
{ {
string value; string value;
this->getMetatag("Title", value); this->getMetadata("Title", value);
if (value.empty()) { if (value.empty()) {
value = getLastPathElement(zimFileHandler->getFilename()); value = getLastPathElement(zimFileHandler->getFilename());
std::replace(value.begin(), value.end(), '_', ' '); std::replace(value.begin(), value.end(), '_', ' ');
@@ -302,65 +311,98 @@ string Reader::getTitle() const
return value; return value;
} }
string Reader::getName() const string Reader::getCreator() const
{ {
string value; METADATA("Creator")
this->getMetatag("Name", value);
return value;
} }
string Reader::getTags() const string Reader::getPublisher() const
{ {
string value; METADATA("Publisher")
this->getMetatag("Tags", value); }
return value;
string Reader::getDate() const
{
METADATA("Date")
} }
string Reader::getDescription() const string Reader::getDescription() const
{ {
string value; string value;
this->getMetatag("Description", value); this->getMetadata("Description", value);
/* Mediawiki Collection tends to use the "Subtitle" name */ /* Mediawiki Collection tends to use the "Subtitle" name */
if (value.empty()) { if (value.empty()) {
this->getMetatag("Subtitle", value); this->getMetadata("Subtitle", value);
} }
return value; return value;
} }
string Reader::getLongDescription() const
{
METADATA("LongDescription")
}
string Reader::getLanguage() const string Reader::getLanguage() const
{ {
string value; METADATA("Language")
this->getMetatag("Language", value);
return value;
} }
string Reader::getDate() const string Reader::getLicense() const
{ {
string value; METADATA("License")
this->getMetatag("Date", value);
return value;
} }
string Reader::getCreator() const string Reader::getTags(bool original) const
{ {
string value; string tags_str;
this->getMetatag("Creator", value); getMetadata("Tags", tags_str);
return value; if (original) {
return tags_str;
}
auto tags = convertTags(tags_str);
return join(tags, ";");
} }
string Reader::getPublisher() const
string Reader::getTagStr(const std::string& tagName) const
{ {
string value; string tags_str;
this->getMetatag("Publisher", value); getMetadata("Tags", tags_str);
return value; return getTagValueFromTagList(convertTags(tags_str), tagName);
} }
bool Reader::getTagBool(const std::string& tagName) const
{
return convertStrToBool(getTagStr(tagName));
}
string Reader::getRelation() const
{
METADATA("Relation")
}
string Reader::getFlavour() const
{
METADATA("Flavour")
}
string Reader::getSource() const
{
METADATA("Source")
}
string Reader::getScraper() const
{
METADATA("Scraper")
}
#undef METADATA
string Reader::getOrigId() const string Reader::getOrigId() const
{ {
string value; string value;
this->getMetatag("startfileuid", value); this->getMetadata("startfileuid", value);
if (value.empty()) { if (value.empty()) {
return ""; return "";
} }
@@ -672,7 +714,6 @@ bool Reader::searchSuggestions(const string& prefix,
const bool reset) const bool reset)
{ {
bool retVal = false; bool retVal = false;
zim::File::const_iterator articleItr;
/* Reset the suggestions otherwise check if the suggestions number is less /* Reset the suggestions otherwise check if the suggestions number is less
* than the suggestionsCount */ * than the suggestionsCount */
@@ -690,7 +731,7 @@ bool Reader::searchSuggestions(const string& prefix,
return false; return false;
} }
for (articleItr = zimFileHandler->findByTitle('A', prefix); for (auto articleItr = zimFileHandler->findByTitle('A', prefix);
articleItr != zimFileHandler->end() articleItr != zimFileHandler->end()
&& articleItr->getTitle().compare(0, prefix.size(), prefix) == 0 && articleItr->getTitle().compare(0, prefix.size(), prefix) == 0
&& this->suggestions.size() < suggestionsCount; && this->suggestions.size() < suggestionsCount;
+19 -18
View File
@@ -96,27 +96,29 @@ std::string SearchRenderer::getHtml()
auto resultEnd = mp_searcher->getResultEnd(); auto resultEnd = mp_searcher->getResultEnd();
auto resultCountPerPage = resultEnd - resultStart; auto resultCountPerPage = resultEnd - resultStart;
auto estimatedResultCount = mp_searcher->getEstimatedResultCount(); auto estimatedResultCount = mp_searcher->getEstimatedResultCount();
auto currentPage = 0U;
unsigned int pageStart auto pageStart = 0U;
= resultStart / resultCountPerPage >= 5 auto pageEnd = 0U;
? resultStart / resultCountPerPage - 4 auto lastPageStart = 0U;
: 0; if (resultCountPerPage) {
unsigned int pageCount currentPage = resultStart/resultCountPerPage;
= estimatedResultCount / resultCountPerPage + 1 - pageStart; pageStart = currentPage > 4 ? currentPage-4 : 0;
pageEnd = currentPage + 5;
if (pageCount > 10) { if (pageEnd > estimatedResultCount / resultCountPerPage) {
pageCount = 10; pageEnd = estimatedResultCount / resultCountPerPage;
} else if (pageCount == 1) { }
pageCount = 0; if (estimatedResultCount > resultCountPerPage) {
lastPageStart = static_cast<int>(round(estimatedResultCount/resultCountPerPage)) * resultCountPerPage;
}
} }
for (unsigned int i = pageStart; i < pageStart + pageCount; i++) { for (unsigned int i = pageStart; i < pageEnd; i++) {
kainjow::mustache::data page; kainjow::mustache::data page;
page.set("label", to_string(i + 1)); page.set("label", to_string(i + 1));
page.set("start", to_string(i * resultCountPerPage)); page.set("start", to_string(i * resultCountPerPage));
page.set("end", to_string((i + 1) * resultCountPerPage)); page.set("end", to_string((i + 1) * resultCountPerPage));
if (i * resultCountPerPage == resultStart) { if (i == currentPage) {
page.set("selected", true); page.set("selected", true);
} }
pages.push_back(page); pages.push_back(page);
@@ -128,16 +130,15 @@ std::string SearchRenderer::getHtml()
kainjow::mustache::data allData; kainjow::mustache::data allData;
allData.set("results", results); allData.set("results", results);
allData.set("pages", pages); allData.set("pages", pages);
allData.set("hasResult", estimatedResultCount != 0); allData.set("hasResults", estimatedResultCount != 0);
allData.set("hasPages", pageStart != pageEnd);
allData.set("count", kiwix::beautifyInteger(estimatedResultCount)); allData.set("count", kiwix::beautifyInteger(estimatedResultCount));
allData.set("searchPattern", kiwix::encodeDiples(this->searchPattern)); allData.set("searchPattern", kiwix::encodeDiples(this->searchPattern));
allData.set("searchPatternEncoded", urlEncode(this->searchPattern)); allData.set("searchPatternEncoded", urlEncode(this->searchPattern));
allData.set("resultStart", to_string(resultStart + 1)); allData.set("resultStart", to_string(resultStart + 1));
allData.set("resultEnd", to_string(min(resultEnd, estimatedResultCount))); allData.set("resultEnd", to_string(min(resultEnd, estimatedResultCount)));
allData.set("resultRange", to_string(resultCountPerPage)); allData.set("resultRange", to_string(resultCountPerPage));
allData.set("resultLastPageStart", to_string(estimatedResultCount > resultCountPerPage allData.set("resultLastPageStart", to_string(lastPageStart));
? round(estimatedResultCount / resultCountPerPage) * resultCountPerPage
: 0));
allData.set("lastResult", to_string(estimatedResultCount)); allData.set("lastResult", to_string(estimatedResultCount));
allData.set("protocolPrefix", this->protocolPrefix); allData.set("protocolPrefix", this->protocolPrefix);
allData.set("searchProtocolPrefix", this->searchProtocolPrefix); allData.set("searchProtocolPrefix", this->searchProtocolPrefix);
+9 -9
View File
@@ -97,7 +97,7 @@ Reader* Searcher::get_reader(int readerIndex)
} }
/* Search strings in the database */ /* Search strings in the database */
void Searcher::search(std::string& search, void Searcher::search(const std::string& search,
unsigned int resultStart, unsigned int resultStart,
unsigned int resultEnd, unsigned int resultEnd,
const bool verbose) const bool verbose)
@@ -108,12 +108,12 @@ void Searcher::search(std::string& search,
cout << "Performing query `" << search << "'" << endl; cout << "Performing query `" << search << "'" << endl;
} }
this->searchPattern = search;
this->resultStart = resultStart;
this->resultEnd = resultEnd;
/* Try to find results */ /* Try to find results */
if (resultStart != resultEnd) { if (resultStart != resultEnd) {
/* Perform the search */ /* Perform the search */
this->searchPattern = search;
this->resultStart = resultStart;
this->resultEnd = resultEnd;
string unaccentedSearch = removeAccents(search); string unaccentedSearch = removeAccents(search);
std::vector<const zim::File*> zims; std::vector<const zim::File*> zims;
for (auto current = this->readers.begin(); current != this->readers.end(); for (auto current = this->readers.begin(); current != this->readers.end();
@@ -146,11 +146,6 @@ void Searcher::geo_search(float latitude, float longitude, float distance,
cout << "Performing geo query `" << distance << "&(" << latitude << ";" << longitude << ")'" << endl; cout << "Performing geo query `" << distance << "&(" << latitude << ";" << longitude << ")'" << endl;
} }
/* Try to find results */
if (resultStart == resultEnd) {
return;
}
/* Perform the search */ /* Perform the search */
std::ostringstream oss; std::ostringstream oss;
oss << "Articles located less than " << distance << " meters of " << latitude << ";" << longitude; oss << "Articles located less than " << distance << " meters of " << latitude << ";" << longitude;
@@ -158,6 +153,11 @@ void Searcher::geo_search(float latitude, float longitude, float distance,
this->resultStart = resultStart; this->resultStart = resultStart;
this->resultEnd = resultEnd; this->resultEnd = resultEnd;
/* Try to find results */
if (resultStart == resultEnd) {
return;
}
std::vector<const zim::File*> zims; std::vector<const zim::File*> zims;
for (auto current = this->readers.begin(); current != this->readers.end(); for (auto current = this->readers.begin(); current != this->readers.end();
current++) { current++) {
+51 -35
View File
@@ -95,7 +95,8 @@ class InternalServer {
int nbThreads, int nbThreads,
bool verbose, bool verbose,
bool withTaskbar, bool withTaskbar,
bool withLibraryButton); bool withLibraryButton,
bool blockExternalLinks);
virtual ~InternalServer() = default; virtual ~InternalServer() = default;
int handlerCallback(struct MHD_Connection* connection, int handlerCallback(struct MHD_Connection* connection,
@@ -119,6 +120,7 @@ class InternalServer {
Response handle_search(const RequestContext& request); Response handle_search(const RequestContext& request);
Response handle_suggest(const RequestContext& request); Response handle_suggest(const RequestContext& request);
Response handle_random(const RequestContext& request); Response handle_random(const RequestContext& request);
Response handle_captured_external(const RequestContext& request);
Response handle_content(const RequestContext& request); Response handle_content(const RequestContext& request);
kainjow::mustache::data get_default_data(); kainjow::mustache::data get_default_data();
@@ -131,6 +133,7 @@ class InternalServer {
std::atomic_bool m_verbose; std::atomic_bool m_verbose;
bool m_withTaskbar; bool m_withTaskbar;
bool m_withLibraryButton; bool m_withLibraryButton;
bool m_blockExternalLinks;
struct MHD_Daemon* mp_daemon; struct MHD_Daemon* mp_daemon;
Library* mp_library; Library* mp_library;
@@ -157,7 +160,8 @@ bool Server::start() {
m_nbThreads, m_nbThreads,
m_verbose, m_verbose,
m_withTaskbar, m_withTaskbar,
m_withLibraryButton)); m_withLibraryButton,
m_blockExternalLinks));
return mp_server->start(); return mp_server->start();
} }
@@ -186,7 +190,8 @@ InternalServer::InternalServer(Library* library,
int nbThreads, int nbThreads,
bool verbose, bool verbose,
bool withTaskbar, bool withTaskbar,
bool withLibraryButton) : bool withLibraryButton,
bool blockExternalLinks) :
m_addr(addr), m_addr(addr),
m_port(port), m_port(port),
m_root(root), m_root(root),
@@ -194,6 +199,7 @@ InternalServer::InternalServer(Library* library,
m_verbose(verbose), m_verbose(verbose),
m_withTaskbar(withTaskbar), m_withTaskbar(withTaskbar),
m_withLibraryButton(withLibraryButton), m_withLibraryButton(withLibraryButton),
m_blockExternalLinks(blockExternalLinks),
mp_daemon(nullptr), mp_daemon(nullptr),
mp_library(library), mp_library(library),
mp_nameMapper(nameMapper ? nameMapper : &defaultNameMapper) mp_nameMapper(nameMapper ? nameMapper : &defaultNameMapper)
@@ -340,6 +346,9 @@ Response InternalServer::handle_request(const RequestContext& request)
if (request.get_url() == "/random") if (request.get_url() == "/random")
return handle_random(request); return handle_random(request);
if (request.get_url() == "/catch/external")
return handle_captured_external(request);
return handle_content(request); return handle_content(request);
} catch (std::exception& e) { } catch (std::exception& e) {
fprintf(stderr, "===== Unhandled error : %s\n", e.what()); fprintf(stderr, "===== Unhandled error : %s\n", e.what());
@@ -359,7 +368,7 @@ kainjow::mustache::data InternalServer::get_default_data()
Response InternalServer::get_default_response() Response InternalServer::get_default_response()
{ {
return Response(m_root, m_verbose.load(), m_withTaskbar, m_withLibraryButton); return Response(m_root, m_verbose.load(), m_withTaskbar, m_withLibraryButton, m_blockExternalLinks);
} }
@@ -383,7 +392,7 @@ Response InternalServer::build_500(const std::string& msg)
{ {
kainjow::mustache::data data; kainjow::mustache::data data;
data.set("error", msg); data.set("error", msg);
Response response(m_root, true, false, false); Response response(m_root, true, false, false, false);
response.set_template(RESOURCE::templates::_500_html, data); response.set_template(RESOURCE::templates::_500_html, data);
response.set_mimeType("text/html"); response.set_mimeType("text/html");
response.set_code(MHD_HTTP_INTERNAL_SERVER_ERROR); response.set_code(MHD_HTTP_INTERNAL_SERVER_ERROR);
@@ -711,6 +720,26 @@ Response InternalServer::handle_random(const RequestContext& request)
} }
} }
Response InternalServer::handle_captured_external(const RequestContext& request)
{
std::string source = "";
try {
source = kiwix::urlDecode(request.get_argument("source"));
} catch (const std::out_of_range& e) {}
if (source.empty())
return build_404(request, "");
auto data = get_default_data();
data.set("source", source);
auto response = get_default_response();
response.set_template(RESOURCE::templates::captured_external_html, data);
response.set_mimeType("text/html; charset=utf-8");
response.set_compress(true);
response.set_taskbar("", "");
return response;
}
Response InternalServer::handle_catalog(const RequestContext& request) Response InternalServer::handle_catalog(const RequestContext& request)
{ {
if (m_verbose.load()) { if (m_verbose.load()) {
@@ -742,7 +771,6 @@ Response InternalServer::handle_catalog(const RequestContext& request)
kiwix::OPDSDumper opdsDumper; kiwix::OPDSDumper opdsDumper;
opdsDumper.setRootLocation(m_root); opdsDumper.setRootLocation(m_root);
opdsDumper.setSearchDescriptionUrl("catalog/searchdescription.xml"); opdsDumper.setSearchDescriptionUrl("catalog/searchdescription.xml");
opdsDumper.setId(kiwix::to_string(uuid));
opdsDumper.setLibrary(mp_library); opdsDumper.setLibrary(mp_library);
response.set_mimeType("application/atom+xml;profile=opds-catalog;kind=acquisition; charset=utf-8"); response.set_mimeType("application/atom+xml;profile=opds-catalog;kind=acquisition; charset=utf-8");
std::vector<std::string> bookIdsToDump; std::vector<std::string> bookIdsToDump;
@@ -751,17 +779,22 @@ Response InternalServer::handle_catalog(const RequestContext& request)
uuid = zim::Uuid::generate(host); uuid = zim::Uuid::generate(host);
bookIdsToDump = mp_library->filter(kiwix::Filter().valid(true).local(true).remote(true)); bookIdsToDump = mp_library->filter(kiwix::Filter().valid(true).local(true).remote(true));
} else if (url == "search") { } else if (url == "search") {
std::string query; auto filter = kiwix::Filter().valid(true).local(true).remote(true);
std::string language; string query("<Empty query>");
std::vector<std::string> tags;
std::vector<std::string> noTags;
size_t count(10); size_t count(10);
size_t startIndex(0); size_t startIndex(0);
try { try {
query = request.get_argument("q"); query = request.get_argument("q");
filter.query(query);
} catch (const std::out_of_range&) {} } catch (const std::out_of_range&) {}
try { try {
language = request.get_argument("lang"); filter.maxSize(extractFromString<unsigned long>(request.get_argument("maxsize")));
} catch (...) {}
try {
filter.name(request.get_argument("name"));
} catch (const std::out_of_range&) {}
try {
filter.lang(request.get_argument("lang"));
} catch (const std::out_of_range&) {} } catch (const std::out_of_range&) {}
try { try {
count = extractFromString<unsigned long>(request.get_argument("count")); count = extractFromString<unsigned long>(request.get_argument("count"));
@@ -770,20 +803,14 @@ Response InternalServer::handle_catalog(const RequestContext& request)
startIndex = extractFromString<unsigned long>(request.get_argument("start")); startIndex = extractFromString<unsigned long>(request.get_argument("start"));
} catch (...) {} } catch (...) {}
try { try {
tags = kiwix::split(request.get_argument("notag"), ";"); filter.acceptTags(kiwix::split(request.get_argument("tag"), ";"));
} catch (...) {} } catch (...) {}
try { try {
noTags = kiwix::split(request.get_argument("notag"), ";"); filter.rejectTags(kiwix::split(request.get_argument("notag"), ";"));
} catch (...) {} } catch (...) {}
opdsDumper.setTitle("Search result for " + query); opdsDumper.setTitle("Search result for " + query);
uuid = zim::Uuid::generate(); uuid = zim::Uuid::generate();
bookIdsToDump = mp_library->filter( bookIdsToDump = mp_library->filter(filter);
kiwix::Filter().valid(true).local(true).remote(true)
.query(query)
.lang(language)
.acceptTags(tags)
.rejectTags(noTags)
);
auto totalResults = bookIdsToDump.size(); auto totalResults = bookIdsToDump.size();
bookIdsToDump.erase(bookIdsToDump.begin(), bookIdsToDump.begin()+startIndex); bookIdsToDump.erase(bookIdsToDump.begin(), bookIdsToDump.begin()+startIndex);
if (count>0 && bookIdsToDump.size() > count) { if (count>0 && bookIdsToDump.size() > count) {
@@ -792,6 +819,7 @@ Response InternalServer::handle_catalog(const RequestContext& request)
opdsDumper.setOpenSearchInfo(totalResults, startIndex, bookIdsToDump.size()); opdsDumper.setOpenSearchInfo(totalResults, startIndex, bookIdsToDump.size());
} }
opdsDumper.setId(kiwix::to_string(uuid));
response.set_content(opdsDumper.dumpOPDSFeed(bookIdsToDump)); response.set_content(opdsDumper.dumpOPDSFeed(bookIdsToDump));
return response; return response;
} }
@@ -870,23 +898,11 @@ Response InternalServer::handle_content(const RequestContext& request)
zim::Blob raw_content = entry.getBlob(); zim::Blob raw_content = entry.getBlob();
content = string(raw_content.data(), raw_content.size()); content = string(raw_content.data(), raw_content.size());
auto response = get_default_response(); auto response = get_default_response();
response.set_mimeType(mimeType);
/* Special rewrite URL in case of ZIM file use intern *asbolute* url like if (mimeType.find("text/html") != string::npos)
* /A/Kiwix */
if (mimeType.find("text/html") != string::npos) {
content = replaceRegex(content,
"$1$2" + m_root + "/" + bookName + "/$3/",
"(href|src)(=[\"|\']{0,1})/([A-Z|\\-])/");
content = replaceRegex(content,
"$1$2" + m_root + "/" + bookName + "/$3/",
"(@import[ ]+)([\"|\']{0,1})/([A-Z|\\-])/");
response.set_taskbar(bookName, reader->getTitle()); response.set_taskbar(bookName, reader->getTitle());
} else if (mimeType.find("text/css") != string::npos) {
content = replaceRegex(content, response.set_mimeType(mimeType);
"$1$2" + m_root + "/" + bookName + "/$3/",
"(url|URL)(\\([\"|\']{0,1})/([A-Z|\\-])/");
}
response.set_content(content); response.set_content(content);
response.set_compress(true); response.set_compress(true);
response.set_cache(true); response.set_cache(true);
+17 -2
View File
@@ -17,7 +17,7 @@
namespace kiwix { namespace kiwix {
Response::Response(const std::string& root, bool verbose, bool withTaskbar, bool withLibraryButton) Response::Response(const std::string& root, bool verbose, bool withTaskbar, bool withLibraryButton, bool blockExternalLinks)
: m_verbose(verbose), : m_verbose(verbose),
m_root(root), m_root(root),
m_content(""), m_content(""),
@@ -25,6 +25,7 @@ Response::Response(const std::string& root, bool verbose, bool withTaskbar, bool
m_returnCode(MHD_HTTP_OK), m_returnCode(MHD_HTTP_OK),
m_withTaskbar(withTaskbar), m_withTaskbar(withTaskbar),
m_withLibraryButton(withLibraryButton), m_withLibraryButton(withLibraryButton),
m_blockExternalLinks(blockExternalLinks),
m_useCache(false), m_useCache(false),
m_addTaskbar(false), m_addTaskbar(false),
m_bookName(""), m_bookName(""),
@@ -127,6 +128,17 @@ void Response::introduce_taskbar()
} }
void Response::inject_externallinks_blocker()
{
kainjow::mustache::data data;
data.set("root", m_root);
auto script_tag = render_template(RESOURCE::templates::external_blocker_part_html, data);
m_content = appendToFirstOccurence(
m_content,
"<head>",
script_tag);
}
int Response::send(const RequestContext& request, MHD_Connection* connection) int Response::send(const RequestContext& request, MHD_Connection* connection)
{ {
@@ -136,11 +148,14 @@ int Response::send(const RequestContext& request, MHD_Connection* connection)
if (m_addTaskbar) { if (m_addTaskbar) {
introduce_taskbar(); introduce_taskbar();
} }
if ( m_blockExternalLinks ) {
inject_externallinks_blocker();
}
bool shouldCompress = m_compress && request.can_compress(); bool shouldCompress = m_compress && request.can_compress();
shouldCompress &= m_mimeType.find("text/") != string::npos shouldCompress &= m_mimeType.find("text/") != string::npos
|| m_mimeType.find("application/javascript") != string::npos || m_mimeType.find("application/javascript") != string::npos
|| m_mimeType.find("application/json") != string::npos; || m_mimeType.find("application/json") != string::npos;
shouldCompress &= (m_content.size() > KIWIX_MIN_CONTENT_SIZE_TO_DEFLATE); shouldCompress &= (m_content.size() > KIWIX_MIN_CONTENT_SIZE_TO_DEFLATE);
+3 -1
View File
@@ -42,7 +42,7 @@ class RequestContext;
class Response { class Response {
public: public:
Response(const std::string& root, bool verbose, bool withTaskbar, bool withLibraryButton); Response(const std::string& root, bool verbose, bool withTaskbar, bool withLibraryButton, bool blockExternalLinks);
~Response() = default; ~Response() = default;
int send(const RequestContext& request, MHD_Connection* connection); int send(const RequestContext& request, MHD_Connection* connection);
@@ -64,6 +64,7 @@ class Response {
int getReturnCode() { return m_returnCode; } int getReturnCode() { return m_returnCode; }
void introduce_taskbar(); void introduce_taskbar();
void inject_externallinks_blocker();
private: private:
bool m_verbose; bool m_verbose;
@@ -75,6 +76,7 @@ class Response {
int m_returnCode; int m_returnCode;
bool m_withTaskbar; bool m_withTaskbar;
bool m_withLibraryButton; bool m_withLibraryButton;
bool m_blockExternalLinks;
bool m_useCache; bool m_useCache;
bool m_compress; bool m_compress;
bool m_addTaskbar; bool m_addTaskbar;
+15 -8
View File
@@ -4,6 +4,7 @@
#include <windows.h> #include <windows.h>
#include <winbase.h> #include <winbase.h>
#include <shlwapi.h>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
@@ -34,11 +35,11 @@ DWORD WINAPI WinImpl::waitForPID(void* _self)
return 0; return 0;
} }
std::unique_ptr<wchar_t[]> toWideChar(const std::string& value) std::unique_ptr<wchar_t[]> toWideChar(const std::string& value, size_t min_size = 0)
{ {
auto size = MultiByteToWideChar(CP_UTF8, 0, size_t size = MultiByteToWideChar(CP_UTF8, 0,
value.c_str(), -1, nullptr, 0); value.c_str(), -1, nullptr, 0);
auto wdata = std::unique_ptr<wchar_t[]>(new wchar_t[size]); auto wdata = std::unique_ptr<wchar_t[]>(new wchar_t[size>min_size?size:min_size]);
auto ret = MultiByteToWideChar(CP_UTF8, 0, auto ret = MultiByteToWideChar(CP_UTF8, 0,
value.c_str(), -1, wdata.get(), size); value.c_str(), -1, wdata.get(), size);
if (0 == ret) { if (0 == ret) {
@@ -46,6 +47,9 @@ std::unique_ptr<wchar_t[]> toWideChar(const std::string& value)
oss << "Cannot convert to wchar : " << GetLastError(); oss << "Cannot convert to wchar : " << GetLastError();
throw std::runtime_error(oss.str()); throw std::runtime_error(oss.str());
} }
if (size < min_size) {
memset(wdata.get() + size, 0, min_size-size);
}
return wdata; return wdata;
} }
@@ -55,14 +59,16 @@ void WinImpl::run(commandLine_t& commandLine)
STARTUPINFOW startInfo = {0}; STARTUPINFOW startInfo = {0};
PROCESS_INFORMATION procInfo; PROCESS_INFORMATION procInfo;
startInfo.cb = sizeof(startInfo); startInfo.cb = sizeof(startInfo);
std::ostringstream oss; std::wostringstream oss;
for(auto& item: commandLine) { for(auto& item: commandLine) {
oss << item << " "; auto witem = toWideChar(item, MAX_PATH);
PathQuoteSpacesW(witem.get());
oss << witem.get() << " ";
} }
auto wCommandLine = toWideChar(oss.str()); auto wCommandLine = oss.str();
if (CreateProcessW( if (CreateProcessW(
NULL, NULL,
wCommandLine.get(), const_cast<wchar_t*>(wCommandLine.c_str()),
NULL, NULL,
NULL, NULL,
false, false,
@@ -70,7 +76,8 @@ void WinImpl::run(commandLine_t& commandLine)
NULL, NULL,
NULL, NULL,
&startInfo, &startInfo,
&procInfo)) { &procInfo))
{
m_pid = procInfo.dwProcessId; m_pid = procInfo.dwProcessId;
m_handle = procInfo.hProcess; m_handle = procInfo.hProcess;
CloseHandle(procInfo.hThread); CloseHandle(procInfo.hThread);
+2 -2
View File
@@ -1,4 +1,4 @@
/* /*
base64.cpp and base64.h base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger Copyright (C) 2004-2008 René Nyffenegger
@@ -27,7 +27,7 @@
#include <tools/base64.h> #include <tools/base64.h>
#include <iostream> #include <iostream>
static const std::string base64_chars = static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz"
"0123456789+/"; "0123456789+/";
+76
View File
@@ -25,7 +25,10 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#include "tools/stringTools.h"
#include <map> #include <map>
#include <sstream>
#include <pugixml.hpp> #include <pugixml.hpp>
@@ -204,3 +207,76 @@ std::string kiwix::nodeToString(const pugi::xml_node& node)
std::string kiwix::converta2toa3(const std::string& a2code){ std::string kiwix::converta2toa3(const std::string& a2code){
return codeisomapping.at(a2code); return codeisomapping.at(a2code);
} }
std::vector<std::string> kiwix::convertTags(const std::string& tags_str)
{
auto tags = kiwix::split(tags_str, ";");
std::vector<std::string> tagsList;
bool picSeen(false), vidSeen(false), detSeen(false), indexSeen(false);
for (auto tag: tags) {
picSeen |= (tag == "nopic" || startsWith(tag, "_pictures:"));
vidSeen |= (tag == "novid" || startsWith(tag, "_videos:"));
detSeen |= (tag == "nodet" || startsWith(tag, "_details:"));
indexSeen |= kiwix::startsWith(tag, "_ftindex");
if (tag == "nopic") {
tagsList.push_back("_pictures:no");
} else if (tag == "novid") {
tagsList.push_back("_videos:no");
} else if (tag == "nodet") {
tagsList.push_back("_details:no");
} else if (tag == "_ftindex") {
tagsList.push_back("_ftindex:yes");
} else {
tagsList.push_back(tag);
}
}
if (!indexSeen) {
tagsList.push_back("_ftindex:no");
}
if (!picSeen) {
tagsList.push_back("_pictures:yes");
}
if (!vidSeen) {
tagsList.push_back("_videos:yes");
}
if (!detSeen) {
tagsList.push_back("_details:yes");
}
return tagsList;
}
std::string kiwix::getTagValueFromTagList(
const std::vector<std::string>& tagList, const std::string& tagName)
{
for (auto tag: tagList) {
if (tag[0] == '_') {
auto delimPos = tag.find(':');
if (delimPos == std::string::npos) {
// No delimiter... what to do ?
continue;
}
auto cTagName = tag.substr(1, delimPos-1);
auto cTagValue = tag.substr(delimPos+1);
if (cTagName == tagName) {
return cTagValue;
}
}
}
std::stringstream ss;
ss << tagName << " cannot be found";
throw std::out_of_range(ss.str());
}
bool kiwix::convertStrToBool(const std::string& value)
{
if (value == "yes") {
return true;
} else if (value == "no") {
return false;
}
std::stringstream ss;
ss << "Tag value '" << value << "' cannot be converted to bool.";
throw std::domain_error(ss.str());
}
+233 -133
View File
@@ -18,6 +18,7 @@
*/ */
#include "tools/pathTools.h" #include "tools/pathTools.h"
#include <stdexcept>
#ifdef __APPLE__ #ifdef __APPLE__
#include <limits.h> #include <limits.h>
@@ -39,20 +40,44 @@
#include <fstream> #include <fstream>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <algorithm>
#ifdef _WIN32 #ifdef _WIN32
const std::string SEPARATOR("\\"); # define SEPARATOR "\\"
# include <io.h>
#else #else
const std::string SEPARATOR("/"); # define SEPARATOR "/"
#include <unistd.h> # include <unistd.h>
# include <sys/stat.h>
#endif #endif
#include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
#ifndef PATH_MAX #ifndef PATH_MAX
#define PATH_MAX 1024 #define PATH_MAX 1024
#endif #endif
#ifdef _WIN32
std::string WideToUtf8(const std::wstring& wstr)
{
auto needed_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr.size(), NULL, 0, NULL, NULL);
std::string ret(needed_size, 0);
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr.size(), &ret[0], needed_size, NULL, NULL);
return ret;
}
std::wstring Utf8ToWide(const std::string& str)
{
auto needed_size = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), NULL, 0);
std::wstring ret(needed_size, 0);
MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), &ret[0], needed_size);
return ret;
}
#endif
bool isRelativePath(const std::string& path) bool isRelativePath(const std::string& path)
{ {
#ifdef _WIN32 #ifdef _WIN32
@@ -62,11 +87,65 @@ bool isRelativePath(const std::string& path)
#endif #endif
} }
std::vector<std::string> normalizeParts(std::vector<std::string> parts, bool absolute)
{
std::vector<std::string> ret;
#ifdef _WIN32
//Special case if we have a drive directory not at first.
//Starts from there.
auto it = find_if(parts.rbegin(), parts.rend(),
[](const std::string& p) ->bool
{ return p.length() == 2 && p[1] == ':'; });
if (it != parts.rend()) {
parts.erase(parts.begin(), it.base()-1);
}
#endif
size_t index = 0;
for (auto& part: parts) {
index++;
if (part == "..") {
if (absolute) {
// We try to remove as far as possible.
if (ret.size() > 1) {
ret.pop_back();
}
} else {
// We remove only if we can remove it.
// Else we add it.
if (!ret.empty() && ret.back() != "..") {
ret.pop_back();
} else {
ret.push_back("..");
}
}
continue;
}
if (part == "") {
#ifndef _WIN32
if (ret.empty() && (absolute || index<parts.size())) {
ret.push_back("");
}
#endif
continue;
}
if (part == ".") {
continue;
}
ret.push_back(part);
}
#ifndef _WIN32
if (absolute && ret.size() == 1 && ret.back() == "") {
ret.push_back("");
}
#endif
return ret;
}
std::string computeRelativePath(const std::string& path, const std::string& absolutePath) std::string computeRelativePath(const std::string& path, const std::string& absolutePath)
{ {
std::vector<std::string> pathParts = kiwix::split(path, SEPARATOR); auto pathParts = normalizeParts(kiwix::split(path, SEPARATOR, false), false);
std::vector<std::string> absolutePathParts auto absolutePathParts = kiwix::split(absolutePath, SEPARATOR, false);
= kiwix::split(absolutePath, SEPARATOR);
unsigned int commonCount = 0; unsigned int commonCount = 0;
while (commonCount < pathParts.size() while (commonCount < pathParts.size()
@@ -75,108 +154,60 @@ std::string computeRelativePath(const std::string& path, const std::string& abso
commonCount++; commonCount++;
} }
std::string relativePath; std::vector<std::string> relativeParts;
#ifdef _WIN32
/* On Windows you have a token more because the root is represented
by a letter */
if (commonCount == 0) {
relativePath = ".." + SEPARATOR;
}
#endif
for (unsigned int i = commonCount; i < pathParts.size(); i++) { for (unsigned int i = commonCount; i < pathParts.size(); i++) {
relativePath += ".." + SEPARATOR; relativeParts.push_back("..");
} }
for (unsigned int i = commonCount; i < absolutePathParts.size(); i++) { for (unsigned int i = commonCount; i < absolutePathParts.size(); i++) {
relativePath += absolutePathParts[i]; relativeParts.push_back(absolutePathParts[i]);
relativePath += i + 1 < absolutePathParts.size() ? SEPARATOR : "";
} }
return relativePath; auto ret = kiwix::join(normalizeParts(relativeParts, false), SEPARATOR);
return ret;
} }
#ifdef _WIN32
# define STRTOK strtok_s
#else
# define STRTOK strtok_r
#endif
/* Warning: the relative path must be with slashes */
std::string computeAbsolutePath(const std::string& path, const std::string& relativePath) std::string computeAbsolutePath(const std::string& path, const std::string& relativePath)
{ {
std::string absolutePath; std::string absolutePath = path;
if (path.empty()) { if (path.empty()) {
char* path = NULL; absolutePath = getCurrentDirectory();
size_t size = 0;
#ifdef _WIN32
path = _getcwd(path, size);
#else
path = getcwd(path, size);
#endif
absolutePath = std::string(path) + SEPARATOR;
} else {
absolutePath = path.substr(path.length() - 1, 1) == SEPARATOR
? path
: path + SEPARATOR;
} }
#if _WIN32 auto absoluteParts = normalizeParts(kiwix::split(absolutePath, SEPARATOR, false), true);
char* cRelativePath = _strdup(relativePath.c_str()); auto relativeParts = kiwix::split(relativePath, SEPARATOR, false);
#else
char* cRelativePath = strdup(relativePath.c_str());
#endif
char* saveptr = nullptr;
char* token = STRTOK(cRelativePath, "/", &saveptr);
while (token != NULL) { absoluteParts.insert(absoluteParts.end(), relativeParts.begin(), relativeParts.end());
if (std::string(token) == "..") { auto ret = kiwix::join(normalizeParts(absoluteParts, true), SEPARATOR);
absolutePath = removeLastPathElement(absolutePath, true, false); return ret;
token = STRTOK(NULL, "/", &saveptr);
} else if (strcmp(token, ".") && strcmp(token, "")) {
absolutePath += std::string(token);
token = STRTOK(NULL, "/", &saveptr);
if (token != NULL) {
absolutePath += SEPARATOR;
}
} else {
token = STRTOK(NULL, "/", &saveptr);
}
}
free(cRelativePath);
return absolutePath;
} }
std::string removeLastPathElement(const std::string& path, std::string removeLastPathElement(const std::string& path)
const bool removePreSeparator,
const bool removePostSeparator)
{ {
std::string newPath = path; auto parts = normalizeParts(kiwix::split(path, SEPARATOR, false), false);
size_t offset = newPath.find_last_of(SEPARATOR); if (!parts.empty()) {
if (removePreSeparator && parts.pop_back();
#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) auto ret = kiwix::join(parts, SEPARATOR);
: newPath.substr(0, offset + 1); return ret;
return newPath;
} }
std::string appendToDirectory(const std::string& directoryPath, const std::string& filename) std::string appendToDirectory(const std::string& directoryPath, const std::string& filename)
{ {
std::string newPath = directoryPath + SEPARATOR + filename; std::string newPath = directoryPath;
if (!directoryPath.empty() && directoryPath.back() != SEPARATOR[0]) {
newPath += SEPARATOR;
}
newPath += filename;
return newPath; return newPath;
} }
std::string getLastPathElement(const std::string& path) std::string getLastPathElement(const std::string& path)
{ {
return path.substr(path.find_last_of(SEPARATOR) + 1); auto parts = normalizeParts(kiwix::split(path, SEPARATOR), false);
if (parts.empty()) {
return "";
}
auto ret = parts.back();
return ret;
} }
unsigned int getFileSize(const std::string& path) unsigned int getFileSize(const std::string& path)
@@ -201,14 +232,33 @@ std::string getFileSizeAsString(const std::string& path)
std::string getFileContent(const std::string& path) std::string getFileContent(const std::string& path)
{ {
std::ifstream f(path, std::ios::in|std::ios::ate); #ifdef _WIN32
auto wpath = Utf8ToWide(path);
auto fd = _wopen(wpath.c_str(), _O_RDONLY | _O_BINARY);
#else
auto fd = open(path.c_str(), O_RDONLY);
#endif
std::string content; std::string content;
if (f.is_open()) { if (fd != -1) {
auto size = f.tellg(); #ifdef _WIN32
content.reserve(size); auto size = _lseeki64(fd, 0, SEEK_END);
f.seekg(0, std::ios::beg); #else
content.assign((std::istreambuf_iterator<char>(f)), auto size = lseek(fd, 0, SEEK_END);
std::istreambuf_iterator<char>()); #endif
content.resize(size);
#ifdef _WIN32
_lseeki64(fd, 0, SEEK_SET);
#else
lseek(fd, 0, SEEK_SET);
#endif
auto p = (char*)content.data();
while (size) {
auto readsize = size > 2048 ? 2048 : size;
readsize = ::read(fd, p, readsize);
p += readsize;
size -= readsize;
}
close(fd);
} }
return content; return content;
} }
@@ -216,7 +266,7 @@ std::string getFileContent(const std::string& path)
bool fileExists(const std::string& path) bool fileExists(const std::string& path)
{ {
#ifdef _WIN32 #ifdef _WIN32
return PathFileExists(path.c_str()); return PathFileExistsW(Utf8ToWide(path).c_str());
#else #else
bool flag = false; bool flag = false;
std::fstream fin; std::fstream fin;
@@ -232,7 +282,7 @@ bool fileExists(const std::string& path)
bool makeDirectory(const std::string& path) bool makeDirectory(const std::string& path)
{ {
#ifdef _WIN32 #ifdef _WIN32
int status = _mkdir(path.c_str()); int status = _wmkdir(Utf8ToWide(path).c_str());
#else #else
int status = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); int status = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#endif #endif
@@ -242,15 +292,15 @@ bool makeDirectory(const std::string& path)
std::string makeTmpDirectory() std::string makeTmpDirectory()
{ {
#ifdef _WIN32 #ifdef _WIN32
char cbase[MAX_PATH]; wchar_t cbase[MAX_PATH];
char ctmp[MAX_PATH]; wchar_t ctmp[MAX_PATH];
GetTempPath(MAX_PATH-14, cbase); GetTempPathW(MAX_PATH-14, cbase);
// This create a file for us, ensure it is unique. // This create a file for us, ensure it is unique.
// So we need to delete it and create the directory using the same name. // So we need to delete it and create the directory using the same name.
GetTempFileName(cbase, "kiwix", 0, ctmp); GetTempFileNameW(cbase, L"kiwix", 0, ctmp);
DeleteFile(ctmp); DeleteFileW(ctmp);
_mkdir(ctmp); _wmkdir(ctmp);
return std::string(ctmp); return WideToUtf8(ctmp);
#else #else
char _template_array[] = {"/tmp/kiwix-lib_XXXXXX"}; char _template_array[] = {"/tmp/kiwix-lib_XXXXXX"};
std::string dir = mkdtemp(_template_array); std::string dir = mkdtemp(_template_array);
@@ -261,86 +311,136 @@ std::string makeTmpDirectory()
/* Try to create a link and if does not work then make a copy */ /* Try to create a link and if does not work then make a copy */
bool copyFile(const std::string& sourcePath, const std::string& destPath) bool copyFile(const std::string& sourcePath, const std::string& destPath)
{ {
#ifdef _WIN32
return CopyFileW(Utf8ToWide(sourcePath).c_str(), Utf8ToWide(destPath).c_str(), 1);
#else
try { try {
#ifndef _WIN32
if (link(sourcePath.c_str(), destPath.c_str()) != 0) { if (link(sourcePath.c_str(), destPath.c_str()) != 0) {
#endif
std::ifstream infile(sourcePath.c_str(), std::ios_base::binary); std::ifstream infile(sourcePath.c_str(), std::ios_base::binary);
std::ofstream outfile(destPath.c_str(), std::ios_base::binary); std::ofstream outfile(destPath.c_str(), std::ios_base::binary);
outfile << infile.rdbuf(); outfile << infile.rdbuf();
#ifndef _WIN32
} }
#endif
} catch (std::exception& e) { } catch (std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
return false; return false;
} }
return true; return true;
#endif
} }
std::string getExecutablePath() std::string getExecutablePath(bool realPathOnly)
{ {
char binRootPath[PATH_MAX]; if (!realPathOnly) {
char* cAppImage = ::getenv("APPIMAGE");
if (cAppImage) {
char* cArgv0 = ::getenv("ARGV0");
char* cOwd = ::getenv("OWD");
if (cArgv0 && cOwd) {
auto ret = appendToDirectory(cOwd, cArgv0);
return ret;
}
}
}
#ifdef _WIN32 #ifdef _WIN32
GetModuleFileName(NULL, binRootPath, PATH_MAX); std::wstring binRootPath(PATH_MAX, 0);
return std::string(binRootPath); GetModuleFileNameW(NULL, &binRootPath[0], PATH_MAX);
std::string ret = WideToUtf8(binRootPath);
return ret;
#elif __APPLE__ #elif __APPLE__
char binRootPath[PATH_MAX];
uint32_t max = (uint32_t)PATH_MAX; uint32_t max = (uint32_t)PATH_MAX;
_NSGetExecutablePath(binRootPath, &max); _NSGetExecutablePath(binRootPath, &max);
return std::string(binRootPath); return std::string(binRootPath);
#else #else
char binRootPath[PATH_MAX];
ssize_t size = readlink("/proc/self/exe", binRootPath, PATH_MAX); ssize_t size = readlink("/proc/self/exe", binRootPath, PATH_MAX);
if (size != -1) { if (size != -1) {
return std::string(binRootPath, size); return std::string(binRootPath, size);
} }
#endif
return ""; return "";
#endif
} }
bool writeTextFile(const std::string& path, const std::string& content) bool writeTextFile(const std::string& path, const std::string& content)
{ {
std::ofstream file; #ifdef _WIN32
file.open(path.c_str()); auto wpath = Utf8ToWide(path);
file << content; auto fd = _wopen(wpath.c_str(), _O_WRONLY | _O_CREAT | _O_TRUNC, S_IWRITE);
file.close(); #else
auto fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
#endif
if (fd == -1)
return false;
if (write(fd, content.c_str(), content.size()) != (long)content.size()) {
close(fd);
return false;
}
close(fd);
return true; return true;
} }
std::string getCurrentDirectory() std::string getCurrentDirectory()
{ {
char* a_cwd = getcwd(NULL, 0); #ifdef _WIN32
std::string s_cwd(a_cwd); wchar_t* a_cwd = _wgetcwd(NULL, 0);
std::string ret = WideToUtf8(a_cwd);
free(a_cwd); free(a_cwd);
return s_cwd; #else
char* a_cwd = getcwd(NULL, 0);
std::string ret(a_cwd);
free(a_cwd);
#endif
return ret;
} }
std::string getDataDirectory() std::string getDataDirectory()
{ {
// Try to get the dataDir from the `KIWIX_DATA_DIR` env var
#ifdef _WIN32 #ifdef _WIN32
char* cDataDir = ::getenv("APPDATA"); wchar_t* cDataDir = ::_wgetenv(L"KIWIX_DATA_DIR");
if (cDataDir != nullptr) {
return WideToUtf8(cDataDir);
}
#else #else
char* cDataDir = ::getenv("KIWIX_DATA_DIR"); char* cDataDir = ::getenv("KIWIX_DATA_DIR");
#endif if (cDataDir != nullptr) {
std::string dataDir = cDataDir==nullptr ? "" : cDataDir; return 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 #endif
return appendToDirectory(dataDir, "kiwix");
// Compute the dataDir from the user directory.
std::string dataDir;
#ifdef _WIN32
cDataDir = ::_wgetenv(L"APPDATA");
if (cDataDir == nullptr)
cDataDir = ::_wgetenv(L"USERPROFILE");
if (cDataDir != nullptr)
dataDir = WideToUtf8(cDataDir);
#else
cDataDir = ::getenv("XDG_DATA_HOME");
if (cDataDir != nullptr) {
dataDir = cDataDir;
} else {
cDataDir = ::getenv("HOME");
if (cDataDir != nullptr) {
dataDir = cDataDir;
dataDir = appendToDirectory(dataDir, ".local");
dataDir = appendToDirectory(dataDir, "share");
}
}
#endif
if (!dataDir.empty()) {
dataDir = appendToDirectory(dataDir, "kiwix");
makeDirectory(dataDir);
return dataDir;
}
// Let's use the currentDirectory
return getCurrentDirectory();
} }
static std::map<std::string, std::string> extMimeTypes = { static std::map<std::string, std::string> extMimeTypes = {
+1
View File
@@ -25,6 +25,7 @@
#include <memory> #include <memory>
#include <map> #include <map>
#include <stdexcept>
#include <pthread.h> #include <pthread.h>
std::map<std::string, std::shared_ptr<icu::RegexPattern>> regexCache; std::map<std::string, std::shared_ptr<icu::RegexPattern>> regexCache;
+26 -20
View File
@@ -267,36 +267,42 @@ std::string kiwix::urlDecode(const std::string& value, bool component)
/* Split string in a token array */ /* Split string in a token array */
std::vector<std::string> kiwix::split(const std::string& str, std::vector<std::string> kiwix::split(const std::string& str,
const std::string& delims = " *-") const std::string& delims,
bool trimEmpty)
{ {
std::string::size_type lastPos = str.find_first_not_of(delims, 0); std::string::size_type lastPos = 0;
std::string::size_type pos = str.find_first_of(delims, lastPos); std::string::size_type pos = 0;
std::vector<std::string> tokens; std::vector<std::string> tokens;
while( (pos = str.find_first_of(delims, lastPos)) < str.length() )
while (std::string::npos != pos || std::string::npos != lastPos) { {
tokens.push_back(str.substr(lastPos, pos - lastPos)); auto token = str.substr(lastPos, pos - lastPos);
lastPos = str.find_first_not_of(delims, pos); if (!trimEmpty || !token.empty()) {
pos = str.find_first_of(delims, lastPos); tokens.push_back(token);
}
lastPos = pos + 1;
} }
auto token = str.substr(lastPos);
if (!trimEmpty || !token.empty()) {
tokens.push_back(token);
}
return tokens; return tokens;
} }
std::vector<std::string> kiwix::split(const char* lhs, const char* rhs) std::string kiwix::join(const std::vector<std::string>& list, const std::string& sep)
{ {
const std::string m1(lhs), m2(rhs); std::stringstream ss;
return split(m1, m2); bool first = true;
for (auto& s:list) {
if (!first) {
ss << sep;
}
first = false;
ss << s;
}
return ss.str();
} }
std::vector<std::string> kiwix::split(const char* lhs, const std::string& rhs)
{
return split(lhs, rhs.c_str());
}
std::vector<std::string> kiwix::split(const std::string& lhs, const char* rhs)
{
return split(lhs.c_str(), rhs);
}
std::string kiwix::ucFirst(const std::string& word) std::string kiwix::ucFirst(const std::string& word)
{ {
File renamed without changes.
+87
View File
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2020 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_Book.h"
#include "utils.h"
#include "book.h"
JNIEXPORT void JNICALL
Java_org_kiwix_kiwixlib_Book_allocate(
JNIEnv* env, jobject thisObj)
{
allocate<kiwix::Book>(env, thisObj);
}
JNIEXPORT void JNICALL
Java_org_kiwix_kiwixlib_Book_dispose(JNIEnv* env, jobject thisObj)
{
dispose<kiwix::Book>(env, thisObj);
}
#define BOOK (getPtr<kiwix::Book>(env, thisObj))
METHOD(void, Book, update__Lorg_kiwix_kiwixlib_Book_2, jobject otherBook)
{
BOOK->update(*getPtr<kiwix::Book>(env, otherBook));
}
METHOD(void, Book, update__Lorg_kiwix_kiwixlib_JNIKiwixReader_2, jobject reader)
{
BOOK->update(**Handle<kiwix::Reader>::getHandle(env, reader));
}
#define GETTER(retType, name) JNIEXPORT retType JNICALL \
Java_org_kiwix_kiwixlib_Book_##name (JNIEnv* env, jobject thisObj) \
{ \
auto cRet = BOOK->name(); \
retType ret = c2jni(cRet, env); \
return ret; \
}
GETTER(jstring, getId)
GETTER(jstring, getPath)
GETTER(jboolean, isPathValid)
GETTER(jstring, getTitle)
GETTER(jstring, getDescription)
GETTER(jstring, getLanguage)
GETTER(jstring, getCreator)
GETTER(jstring, getPublisher)
GETTER(jstring, getDate)
GETTER(jstring, getUrl)
GETTER(jstring, getName)
GETTER(jstring, getFlavour)
GETTER(jstring, getTags)
GETTER(jlong, getArticleCount)
GETTER(jlong, getMediaCount)
GETTER(jlong, getSize)
GETTER(jstring, getFavicon)
GETTER(jstring, getFaviconUrl)
GETTER(jstring, getFaviconMimeType)
METHOD(jstring, Book, getTagStr, jstring tagName) try {
auto cRet = BOOK->getTagStr(jni2c(tagName, env));
return c2jni(cRet, env);
} catch(...) {
return c2jni<std::string>("", env);
}
#undef GETTER
+63
View File
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2019-2020 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_Filter.h"
#include "library.h"
#include "utils.h"
/* Kiwix Reader JNI functions */
METHOD0(void, Filter, allocate) {
allocate<kiwix::Filter>(env, thisObj);
}
METHOD0(void, Filter, dispose) {
dispose<kiwix::Library>(env, thisObj);
}
#define FILTER (getPtr<kiwix::Filter>(env, thisObj))
#define FORWARD(name, args_type) \
METHOD(jobject, Filter, name, args_type value) { \
FILTER->name(jni2c(value, env)); \
return thisObj; \
}
#define FORWARDA(name, args_type) \
METHOD(jobject, Filter, name, jobjectArray value) { \
FILTER->name(jni2c<args_type>(value, env)); \
return thisObj; \
}
FORWARD(local, jboolean)
FORWARD(remote, jboolean)
FORWARD(valid, jboolean)
FORWARDA(acceptTags, jstring)
FORWARDA(rejectTags, jstring)
FORWARD(lang, jstring)
FORWARD(publisher, jstring)
FORWARD(creator, jstring)
FORWARD(maxSize, jlong)
FORWARD(query, jstring)
@@ -28,7 +28,11 @@
#include "utils.h" #include "utils.h"
#if __ANDROID__
pthread_mutex_t globalLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; pthread_mutex_t globalLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
#else
pthread_mutex_t globalLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
#endif
JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIICU_setDataDirectory( JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIICU_setDataDirectory(
JNIEnv* env, jclass kclass, jstring dirStr) JNIEnv* env, jclass kclass, jstring dirStr)
@@ -21,7 +21,7 @@
#include <jni.h> #include <jni.h>
#include <zim/file.h> #include <zim/file.h>
#include <android/log.h> #include <exception>
#include "org_kiwix_kiwixlib_JNIKiwixReader.h" #include "org_kiwix_kiwixlib_JNIKiwixReader.h"
#include "tools/base64.h" #include "tools/base64.h"
@@ -34,14 +34,14 @@ JNIEXPORT jlong JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getNativeReader(
{ {
std::string cPath = jni2c(filename, env); std::string cPath = jni2c(filename, env);
__android_log_print(ANDROID_LOG_INFO, "kiwix", "Attempting to create reader with: %s", cPath.c_str()); LOG("Attempting to create reader with: %s", cPath.c_str());
Lock l; Lock l;
try { try {
kiwix::Reader* reader = new kiwix::Reader(cPath); kiwix::Reader* reader = new kiwix::Reader(cPath);
return reinterpret_cast<jlong>(new Handle<kiwix::Reader>(reader)); return reinterpret_cast<jlong>(new Handle<kiwix::Reader>(reader));
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_WARN, "kiwix", "Error opening ZIM file"); LOG("Error opening ZIM file");
__android_log_print(ANDROID_LOG_WARN, "kiwix", e.what()); LOG(e.what());
return 0; return 0;
} }
} }
@@ -64,8 +64,8 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getMainPage(JNIEnv* env, jobject obj)
std::string cUrl = READER->getMainPage().getPath(); std::string cUrl = READER->getMainPage().getPath();
url = c2jni(cUrl, env); url = c2jni(cUrl, env);
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM main page"); LOG("Unable to get ZIM main page");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
url = NULL; url = NULL;
} }
return url; return url;
@@ -80,8 +80,8 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getId(JNIEnv* env, jobject obj)
std::string cId = READER->getId(); std::string cId = READER->getId();
id = c2jni(cId, env); id = c2jni(cId, env);
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM id"); LOG("Unable to get ZIM id");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
id = NULL; id = NULL;
} }
@@ -95,10 +95,10 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getFileSize(JNIEnv* env, jobject obj)
try { try {
int cSize = READER->getFileSize(); int cSize = READER->getFileSize();
size = c2jni(cSize); size = c2jni(cSize, env);
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM file size"); LOG("Unable to get ZIM file size");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
} }
return size; return size;
@@ -113,8 +113,8 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getCreator(JNIEnv* env, jobject obj)
std::string cCreator = READER->getCreator(); std::string cCreator = READER->getCreator();
creator = c2jni(cCreator, env); creator = c2jni(cCreator, env);
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM creator"); LOG("Unable to get ZIM creator");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
creator = NULL; creator = NULL;
} }
@@ -130,8 +130,8 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getPublisher(JNIEnv* env, jobject obj)
std::string cPublisher = READER->getPublisher(); std::string cPublisher = READER->getPublisher();
publisher = c2jni(cPublisher, env); publisher = c2jni(cPublisher, env);
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM publish"); LOG("Unable to get ZIM publish");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
publisher = NULL; publisher = NULL;
} }
return publisher; return publisher;
@@ -146,8 +146,8 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getName(JNIEnv* env, jobject obj)
std::string cName = READER->getName(); std::string cName = READER->getName();
name = c2jni(cName, env); name = c2jni(cName, env);
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM name"); LOG("Unable to get ZIM name");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
name = NULL; name = NULL;
} }
return name; return name;
@@ -166,8 +166,8 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getFavicon(JNIEnv* env, jobject obj)
base64_encode(cContent), base64_encode(cContent),
env); env);
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM favicon"); LOG("Unable to get ZIM favicon");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
favicon = NULL; favicon = NULL;
} }
return favicon; return favicon;
@@ -182,8 +182,8 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getDate(JNIEnv* env, jobject obj)
std::string cDate = READER->getDate(); std::string cDate = READER->getDate();
date = c2jni(cDate, env); date = c2jni(cDate, env);
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM date"); LOG("Unable to get ZIM date");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
date = NULL; date = NULL;
} }
return date; return date;
@@ -198,8 +198,8 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getLanguage(JNIEnv* env, jobject obj)
std::string cLanguage = READER->getLanguage(); std::string cLanguage = READER->getLanguage();
language = c2jni(cLanguage, env); language = c2jni(cLanguage, env);
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get ZIM language"); LOG("Unable to get ZIM language");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
language = NULL; language = NULL;
} }
@@ -217,8 +217,8 @@ JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getMimeType(
auto cMimeType = entry.getMimetype(); auto cMimeType = entry.getMimetype();
mimeType = c2jni(cMimeType, env); mimeType = c2jni(cMimeType, env);
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get mime-type for url: %s", cUrl.c_str()); LOG("Unable to get mime-type for url: %s", cUrl.c_str());
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
mimeType = NULL; mimeType = NULL;
} }
return mimeType; return mimeType;
@@ -234,7 +234,7 @@ JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_checkUrl(
entry = entry.getFinalEntry(); entry = entry.getFinalEntry();
finalUrl = c2jni(entry.getPath(), env); finalUrl = c2jni(entry.getPath(), env);
} catch (std::exception& e) { } catch (std::exception& e) {
finalUrl = c2jni("", env); finalUrl = c2jni(std::string(), env);
} }
return finalUrl; return finalUrl;
} }
@@ -268,8 +268,8 @@ JNIEXPORT jbyteArray JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getContent(
data, 0, cSize, reinterpret_cast<const jbyte*>(entry.getBlob().data())); data, 0, cSize, reinterpret_cast<const jbyte*>(entry.getBlob().data()));
} }
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get content for url: %s", cUrl.c_str()); LOG("Unable to get content for url: %s", cUrl.c_str());
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
} }
return data; return data;
@@ -284,8 +284,8 @@ JNIEXPORT jbyteArray JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getContentPa
/* Default values */ /* Default values */
/* Retrieve the content */ /* Retrieve the content */
std::string cUrl = jni2c(url, env); std::string cUrl = jni2c(url, env);
unsigned int cOffset = jni2c(offset); unsigned int cOffset = jni2c(offset, env);
unsigned int cLen = jni2c(len); unsigned int cLen = jni2c(len, env);
try { try {
auto entry = READER->getEntryFromEncodedPath(cUrl); auto entry = READER->getEntryFromEncodedPath(cUrl);
entry = entry.getFinalEntry(); entry = entry.getFinalEntry();
@@ -300,12 +300,28 @@ JNIEXPORT jbyteArray JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getContentPa
setIntObjValue(cLen, sizeObj, env); setIntObjValue(cLen, sizeObj, env);
} }
} catch (std::exception& e) { } 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); LOG("Unable to get partial content for url: %s (%u : %u)", cUrl.c_str(), cOffset, cLen);
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
} }
return data; return data;
} }
JNIEXPORT jlong JNICALL
Java_org_kiwix_kiwixlib_JNIKiwixReader_getArticleSize(
JNIEnv* env, jobject obj, jstring url)
{
std::string cUrl = jni2c(url, env);
try {
auto entry = READER->getEntryFromEncodedPath(cUrl);
entry = entry.getFinalEntry();
return c2jni(entry.getSize(), env);
} catch(std::exception& e) {
LOG("Unable to get size for url : %s", cUrl.c_str());
LOG(e.what());
}
return c2jni(0, env);
}
JNIEXPORT jobject JNICALL JNIEXPORT jobject JNICALL
Java_org_kiwix_kiwixlib_JNIKiwixReader_getDirectAccessInformation( Java_org_kiwix_kiwixlib_JNIKiwixReader_getDirectAccessInformation(
JNIEnv* env, jobject obj, jstring url) JNIEnv* env, jobject obj, jstring url)
@@ -322,8 +338,8 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getDirectAccessInformation(
auto part_info = entry.getDirectAccessInfo(); auto part_info = entry.getDirectAccessInfo();
setPairObjValue(part_info.first, part_info.second, pair, env); setPairObjValue(part_info.first, part_info.second, pair, env);
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get direct access info for url: %s", cUrl.c_str()); LOG("Unable to get direct access info for url: %s", cUrl.c_str());
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
} }
return pair; return pair;
} }
@@ -336,15 +352,15 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_searchSuggestions(JNIEnv* env,
{ {
jboolean retVal = JNI_FALSE; jboolean retVal = JNI_FALSE;
std::string cPrefix = jni2c(prefix, env); std::string cPrefix = jni2c(prefix, env);
unsigned int cCount = jni2c(count); unsigned int cCount = jni2c(count, env);
try { try {
if (READER->searchSuggestionsSmart(cPrefix, cCount)) { if (READER->searchSuggestionsSmart(cPrefix, cCount)) {
retVal = JNI_TRUE; retVal = JNI_TRUE;
} }
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_WARN, "kiwix", "Unable to get search results for pattern: %s", cPrefix.c_str()); LOG("Unable to get search results for pattern: %s", cPrefix.c_str());
__android_log_print(ANDROID_LOG_WARN, "kiwix", e.what()); LOG(e.what());
} }
return retVal; return retVal;
@@ -353,19 +369,22 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_searchSuggestions(JNIEnv* env,
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_org_kiwix_kiwixlib_JNIKiwixReader_getNextSuggestion(JNIEnv* env, Java_org_kiwix_kiwixlib_JNIKiwixReader_getNextSuggestion(JNIEnv* env,
jobject obj, jobject obj,
jobject titleObj) jobject titleObj,
jobject urlObj)
{ {
jboolean retVal = JNI_FALSE; jboolean retVal = JNI_FALSE;
std::string cTitle; std::string cTitle;
std::string cUrl;
try { try {
if (READER->getNextSuggestion(cTitle)) { if (READER->getNextSuggestion(cTitle, cUrl)) {
setStringObjValue(cTitle, titleObj, env); setStringObjValue(cTitle, titleObj, env);
setStringObjValue(cUrl, urlObj, env);
retVal = JNI_TRUE; retVal = JNI_TRUE;
} }
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_WARN, "kiwix", "Unable to get next suggestion"); LOG("Unable to get next suggestion");
__android_log_print(ANDROID_LOG_WARN, "kiwix", e.what()); LOG(e.what());
} }
return retVal; return retVal;
@@ -385,8 +404,8 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getPageUrlFromTitle(JNIEnv* env,
setStringObjValue(entry.getPath(), urlObj, env); setStringObjValue(entry.getPath(), urlObj, env);
return JNI_TRUE; return JNI_TRUE;
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_WARN, "kiwix", "Unable to get url for title %s: ", cTitle.c_str()); LOG("Unable to get url for title %s: ", cTitle.c_str());
__android_log_print(ANDROID_LOG_WARN, "kiwix", e.what()); LOG(e.what());
} }
return JNI_FALSE; return JNI_FALSE;
@@ -401,8 +420,8 @@ JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getTitle(
std::string cTitle = READER->getTitle(); std::string cTitle = READER->getTitle();
title = c2jni(cTitle, env); title = c2jni(cTitle, env);
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get zim title"); LOG("Unable to get zim title");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
title = NULL; title = NULL;
} }
return title; return title;
@@ -417,13 +436,42 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getDescription(JNIEnv* env, jobject obj)
std::string cDescription = READER->getDescription(); std::string cDescription = READER->getDescription();
description = c2jni(cDescription, env); description = c2jni(cDescription, env);
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get zim description"); LOG("Unable to get zim description");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
description = NULL; description = NULL;
} }
return description; return description;
} }
JNIEXPORT jint JNICALL
Java_org_kiwix_kiwixlib_JNIKiwixReader_getArticleCount(JNIEnv* env, jobject obj)
{
jint articleCount = 0;
try {
auto cArticleCount = READER->getArticleCount();
articleCount = c2jni(cArticleCount, env);
} catch (std::exception& e) {
LOG("Unable to get article count.");
LOG(e.what());
}
return articleCount;
}
JNIEXPORT jint JNICALL
Java_org_kiwix_kiwixlib_JNIKiwixReader_getMediaCount(JNIEnv* env, jobject obj)
{
jint mediaCount = 0;
try {
auto cMediaCount = READER->getMediaCount();
mediaCount = c2jni(cMediaCount, env);
} catch (std::exception& e) {
LOG("Unable to get media count.");
LOG(e.what());
}
return mediaCount;
}
JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getRandomPage( JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getRandomPage(
JNIEnv* env, jobject obj, jobject urlObj) JNIEnv* env, jobject obj, jobject urlObj)
{ {
@@ -435,8 +483,8 @@ JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getRandomPage(
setStringObjValue(cUrl, urlObj, env); setStringObjValue(cUrl, urlObj, env);
retVal = JNI_TRUE; retVal = JNI_TRUE;
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get random page"); LOG("Unable to get random page");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what()); LOG(e.what());
} }
return retVal; return retVal;
} }
@@ -59,7 +59,7 @@ JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwixSearcher_search(
JNIEnv* env, jobject obj, jstring query, jint count) JNIEnv* env, jobject obj, jstring query, jint count)
{ {
std::string cquery = jni2c(query, env); std::string cquery = jni2c(query, env);
unsigned int ccount = jni2c(count); unsigned int ccount = jni2c(count, env);
SEARCHER->search(cquery, 0, ccount); SEARCHER->search(cquery, 0, ccount);
} }
@@ -21,7 +21,6 @@
#include <jni.h> #include <jni.h>
#include <zim/file.h> #include <zim/file.h>
#include <android/log.h>
#include "org_kiwix_kiwixlib_JNIKiwixServer.h" #include "org_kiwix_kiwixlib_JNIKiwixServer.h"
#include "tools/base64.h" #include "tools/base64.h"
@@ -32,15 +31,15 @@
JNIEXPORT jlong JNICALL Java_org_kiwix_kiwixlib_JNIKiwixServer_getNativeServer( JNIEXPORT jlong JNICALL Java_org_kiwix_kiwixlib_JNIKiwixServer_getNativeServer(
JNIEnv* env, jobject obj, jobject jLibrary) JNIEnv* env, jobject obj, jobject jLibrary)
{ {
__android_log_print(ANDROID_LOG_INFO, "kiwix", "Attempting to create server"); LOG("Attempting to create server");
Lock l; Lock l;
try { try {
auto library = Handle<kiwix::Library>::getHandle(env, jLibrary); auto library = getPtr<kiwix::Library>(env, jLibrary);
kiwix::Server* server = new kiwix::Server(*library); kiwix::Server* server = new kiwix::Server(library);
return reinterpret_cast<jlong>(new Handle<kiwix::Server>(server)); return reinterpret_cast<jlong>(new Handle<kiwix::Server>(server));
} catch (std::exception& e) { } catch (std::exception& e) {
__android_log_print(ANDROID_LOG_WARN, "kiwix", "Error creating the server"); LOG("Error creating the server");
__android_log_print(ANDROID_LOG_WARN, "kiwix", e.what()); LOG(e.what());
return 0; return 0;
} }
} }
@@ -86,6 +85,12 @@ Java_org_kiwix_kiwixlib_JNIKiwixServer_setTaskbar(JNIEnv* env, jobject obj, jboo
SERVER->setTaskbar(withTaskbar, withLibraryButton); SERVER->setTaskbar(withTaskbar, withLibraryButton);
} }
JNIEXPORT void JNICALL
Java_org_kiwix_kiwixlib_JNIKiwixServer_setBlockExternalLinks(JNIEnv* env, jobject obj, jboolean blockExternalLinks)
{
SERVER->setBlockExternalLinks(blockExternalLinks);
}
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_org_kiwix_kiwixlib_JNIKiwixServer_start(JNIEnv* env, jobject obj) Java_org_kiwix_kiwixlib_JNIKiwixServer_start(JNIEnv* env, jobject obj)
{ {
+96
View File
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2019-2020 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_Library.h"
#include "library.h"
#include "reader.h"
#include "utils.h"
/* Kiwix Reader JNI functions */
JNIEXPORT void JNICALL
Java_org_kiwix_kiwixlib_Library_allocate(
JNIEnv* env, jobject thisObj)
{
allocate<kiwix::Library>(env, thisObj);
}
JNIEXPORT void JNICALL
Java_org_kiwix_kiwixlib_Library_dispose(JNIEnv* env, jobject thisObj)
{
dispose<kiwix::Library>(env, thisObj);
}
#define LIBRARY (getPtr<kiwix::Library>(env, thisObj))
/* Kiwix library functions */
JNIEXPORT jboolean JNICALL
Java_org_kiwix_kiwixlib_Library_addBook(
JNIEnv* env, jobject thisObj, jstring path)
{
auto cPath = jni2c(path, env);
try {
kiwix::Reader reader(cPath);
kiwix::Book book;
book.update(reader);
return LIBRARY->addBook(book);
} catch (std::exception& e) {
LOG("Unable to add the book");
LOG(e.what()); }
return false;
}
METHOD(jobject, Library, getBookById, jstring id) {
auto cId = jni2c(id, env);
auto cBook = new kiwix::Book(LIBRARY->getBookById(cId));
jclass cls = env->FindClass("org/kiwix/kiwixlib/Book");
jmethodID constructorId = env->GetMethodID(cls, "<init>", "()V");
jobject book = env->NewObject(cls, constructorId);
setPtr(env, book, cBook);
return book;
}
METHOD(jint, Library, getBookCount, jboolean localBooks, jboolean remoteBooks) {
return LIBRARY->getBookCount(localBooks, remoteBooks);
}
METHOD0(jobjectArray, Library, getBooksIds) {
return c2jni(LIBRARY->getBooksIds(), env);
}
METHOD(jobjectArray, Library, filter, jobject filterObj) {
auto filter = getPtr<kiwix::Filter>(env, filterObj);
return c2jni(LIBRARY->filter(*filter), env);
}
METHOD0(jobjectArray, Library, getBooksLanguages) {
return c2jni(LIBRARY->getBooksLanguages(), env);
}
METHOD0(jobjectArray, Library, getBooksCreators) {
return c2jni(LIBRARY->getBooksCreators(), env);
}
METHOD0(jobjectArray, Library, getBooksPublisher) {
return c2jni(LIBRARY->getBooksPublishers(), env);
}
+132
View File
@@ -0,0 +1,132 @@
/*
* Copyright (C) 2020 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 "org_kiwix_kiwixlib_Manager.h"
#include "manager.h"
#include "utils.h"
JNIEXPORT void JNICALL
Java_org_kiwix_kiwixlib_Manager_allocate(
JNIEnv* env, jobject thisObj, jobject libraryObj)
{
auto lib = getPtr<kiwix::Library>(env, libraryObj);
allocate<kiwix::Manager>(env, thisObj, lib);
}
JNIEXPORT void JNICALL
Java_org_kiwix_kiwixlib_Manager_dispose(JNIEnv* env, jobject thisObj)
{
dispose<kiwix::Manager>(env, thisObj);
}
#define MANAGER (getPtr<kiwix::Manager>(env, thisObj))
/* Kiwix manager functions */
JNIEXPORT jboolean JNICALL
Java_org_kiwix_kiwixlib_Manager_readFile(
JNIEnv* env, jobject thisObj, jstring path)
{
auto cPath = jni2c(path, env);
try {
return MANAGER->readFile(cPath);
} catch (std::exception& e) {
LOG("Unable to get readFile");
LOG(e.what());
}
return false;
}
JNIEXPORT jboolean JNICALL
Java_org_kiwix_kiwixlib_Manager_readXml(
JNIEnv* env, jobject thisObj, jstring content, jstring libraryPath)
{
auto cContent = jni2c(content, env);
auto cPath = jni2c(libraryPath, env);
try {
return MANAGER->readXml(cContent, false, cPath);
} catch (std::exception& e) {
LOG("Unable to get ZIM id");
LOG(e.what());
}
return false;
}
JNIEXPORT jboolean JNICALL
Java_org_kiwix_kiwixlib_Manager_readOpds(
JNIEnv* env, jobject thisObj, jstring content, jstring urlHost)
{
auto cContent = jni2c(content, env);
auto cUrl = jni2c(urlHost, env);
try {
return MANAGER->readOpds(cContent, cUrl);
} catch (std::exception& e) {
LOG("Unable to get ZIM id");
LOG(e.what());
}
return false;
}
JNIEXPORT jboolean JNICALL
Java_org_kiwix_kiwixlib_Manager_readBookmarkFile(
JNIEnv* env, jobject thisObj, jstring path)
{
auto cPath = jni2c(path, env);
try {
return MANAGER->readBookmarkFile(cPath);
} catch (std::exception& e) {
LOG("Unable to get ZIM id");
LOG(e.what());
}
return false;
}
JNIEXPORT jstring JNICALL
Java_org_kiwix_kiwixlib_Manager_addBookFromPath(
JNIEnv* env, jobject thisObj,
jstring pathToOpen, jstring pathToSave, jstring url, jboolean checkMetaData)
{
auto cPathToOpen = jni2c(pathToOpen, env);
auto cPathToSave = jni2c(pathToSave, env);
auto cUrl = jni2c(url, env);
jstring id = NULL;
try {
auto cId = MANAGER->addBookFromPathAndGetId(cPathToOpen, cPathToSave, cUrl, checkMetaData);
if ( !cId.empty() ) {
id = c2jni(cId, env);
}
} catch (std::exception& e) {
LOG("Unable to get ZIM file size");
LOG(e.what());
}
return id;
}
+55
View File
@@ -0,0 +1,55 @@
java_sources = files([
'org/kiwix/kiwixlib/JNIICU.java',
'org/kiwix/kiwixlib/Book.java',
'org/kiwix/kiwixlib/JNIKiwixReader.java',
'org/kiwix/kiwixlib/Library.java',
'org/kiwix/kiwixlib/Manager.java',
'org/kiwix/kiwixlib/Filter.java',
'org/kiwix/kiwixlib/JNIKiwixSearcher.java',
'org/kiwix/kiwixlib/JNIKiwixServer.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'
])
kiwix_jni = custom_target('jni',
input: java_sources,
output: ['org_kiwix_kiwixlib_JNIKiwix.h',
'org_kiwix_kiwixlib_Book.h',
'org_kiwix_kiwixlib_JNIKiwixReader.h',
'org_kiwix_kiwixlib_Library.h',
'org_kiwix_kiwixlib_Manager.h',
'org_kiwix_kiwixlib_Filter.h',
'org_kiwix_kiwixlib_JNIKiwixServer.h',
'org_kiwix_kiwixlib_JNIKiwixSearcher.h',
'org_kiwix_kiwixlib_JNIKiwixSearcher_Result.h'],
command:['javac', '-d', '@OUTDIR@', '-h', '@OUTDIR@', '@INPUT@']
)
jni_sources = files([
'kiwixicu.cpp',
'book.cpp',
'kiwixreader.cpp',
'library.cpp',
'manager.cpp',
'filter.cpp',
'kiwixsearcher.cpp',
'kiwixserver.cpp',
])
kiwix_sources += jni_sources + [kiwix_jni]
if 'java' in wrapper
kiwix_jar = jar('kiwixlib', java_sources)
#junit_jar = files('org/kiwix/testing/junit-4.13.jar')
#test_jar = jar('testing', 'org/kiwix/testing/test.java',
# link_with: [kiwix_jar, junit_jar])
#test('javatest', test_jar)
endif
install_subdir('org', install_dir: 'kiwix-lib/java', exclude_directories: ['kiwix/testing'])
install_subdir('res', install_dir: 'kiwix-lib')
install_data('AndroidManifest.xml', install_dir: 'kiwix-lib')
@@ -0,0 +1,47 @@
package org.kiwix.kiwixlib;
public class Book
{
public Book() { allocate(); }
public native void update(Book book);
public native void update(JNIKiwixReader reader);
@Override
protected void finalize() { dispose(); }
public native String getId();
public native String getPath();
public native boolean isPathValid();
public native String getTitle();
public native String getDescription();
public native String getLanguage();
public native String getCreator();
public native String getPublisher();
public native String getDate();
public native String getUrl();
public native String getName();
public native String getFlavour();
public native String getTags();
/**
* Return the value associated to the tag tagName
*
* @param tagName the tag name to search for.
* @return The value of the tag. If the tag is not found, return empty string.
*/
public native String getTagStr(String tagName);
public native long getArticleCount();
public native long getMediaCount();
public native long getSize();
public native String getFavicon();
public native String getFaviconUrl();
public native String getFaviconMimeType();
private native void allocate();
private native void dispose();
private long nativeHandle;
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2019-2020 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 Filter
{
public native Filter local(boolean accept);
public native Filter remote(boolean accept);
public native Filter valid(boolean accept);
public native Filter acceptTags(String[] tags);
public native Filter rejectTags(String[] tags);
public native Filter lang(String lang);
public native Filter publisher(String publisher);
public native Filter creator(String creator);
public native Filter maxSize(long size);
public native Filter query(String query);
public Filter() { allocate(); }
@Override
protected void finalize() { dispose(); }
private native void allocate();
private native void dispose();
private long nativeHandle;
}
@@ -82,6 +82,16 @@ public class JNIKiwixReader
int len, int len,
JNIKiwixInt size); JNIKiwixInt size);
/**
*
* Get the size of an article.
*
* @param url The url of the article.
* @return The size of the final (redirections are resolved) article (in byte).
* Return 0 if the article is not found.
*/
public native long getArticleSize(String url);
/** /**
* getDirectAccessInformation. * getDirectAccessInformation.
* *
@@ -102,7 +112,7 @@ public class JNIKiwixReader
public native boolean searchSuggestions(String prefix, int count); public native boolean searchSuggestions(String prefix, int count);
public native boolean getNextSuggestion(JNIKiwixString title); public native boolean getNextSuggestion(JNIKiwixString title, JNIKiwixString url);
public native boolean getPageUrlFromTitle(String title, JNIKiwixString url); public native boolean getPageUrlFromTitle(String title, JNIKiwixString url);
@@ -20,7 +20,7 @@
package org.kiwix.kiwixlib; package org.kiwix.kiwixlib;
import org.kiwix.kiwixlib.JNIKiwixException; import org.kiwix.kiwixlib.JNIKiwixException;
import org.kiwix.kiwixlib.JNIKiwixLibrary; import org.kiwix.kiwixlib.Library;
public class JNIKiwixServer public class JNIKiwixServer
{ {
@@ -34,15 +34,17 @@ public class JNIKiwixServer
public native void setTaskbar(boolean withTaskBar, boolean witLibraryButton); public native void setTaskbar(boolean withTaskBar, boolean witLibraryButton);
public native void setBlockExternalLinks(boolean blockExternalLinks);
public native boolean start(); public native boolean start();
public native void stop(); public native void stop();
public JNIKiwixServer(JNIKiwixLibrary library) public JNIKiwixServer(Library library)
{ {
nativeHandle = getNativeServer(library); nativeHandle = getNativeServer(library);
} }
private native long getNativeServer(JNIKiwixLibrary library); private native long getNativeServer(Library library);
private long nativeHandle; private long nativeHandle;
} }
@@ -1,6 +1,5 @@
/* /*
* Copyright (C) 2013 Emmanuel Engelhart <kelson@kiwix.org> * Copyright (C) 2019-2020 Matthieu Gautier <mgautier@kymeria.fr>
* 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
@@ -20,19 +19,31 @@
package org.kiwix.kiwixlib; package org.kiwix.kiwixlib;
import org.kiwix.kiwixlib.Book;
import org.kiwix.kiwixlib.JNIKiwixException; import org.kiwix.kiwixlib.JNIKiwixException;
public class JNIKiwixLibrary public class Library
{ {
public native boolean addBook(String path) throws JNIKiwixException; public native boolean addBook(String path) throws JNIKiwixException;
public JNIKiwixLibrary() public native Book getBookById(String id);
public native int getBookCount(boolean localBooks, boolean remoteBooks);
public native String[] getBooksIds();
public native String[] filter(Filter filter);
public native String[] getBooksLanguages();
public native String[] getBooksCreators();
public native String[] getBooksPublishers();
public Library()
{ {
nativeHandle = getNativeLibrary(); allocate();
} }
public native void dispose(); @Override
protected void finalize() { dispose(); }
private native long getNativeLibrary(); private native void allocate();
private native void dispose();
private long nativeHandle; private long nativeHandle;
} }
@@ -0,0 +1,90 @@
/*
* Copyright (C) 2020 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.Library;
public class Manager
{
/**
* Read a `library.xml` file and add books in the library.
*
* @param path The (utf8) path to the `library.xml` file.
* @return True if the file has been properly parsed.
*/
public native boolean readFile(String path);
/**
* Load a library content stored in a string (at `library.xml` format).
*
* @param content The content corresponding of the library xml.
* @param libraryPath The library path (used to resolve relative paths)
* @return True if the content has been properly parsed.
*/
public native boolean readXml(String content, String libraryPath);
/**
* Load a library content stored in a string (at OPDS stream format)
*
* @param content the content of the OPDS stream.
* @param urlHost the url of the stream (used to resolve relative url)
* @return True if the content has been properly parsed.
*/
public native boolean readOpds(String content, String urlHost);
/**
* Load a bookmark file
*
* @param path The path of the file to read.
* @return True if the content has been properly parsed
*/
public native boolean readBookmarkFile(String path);
/**
* Add a book to the library.
*
* @param pathToOpen The path of the zim file to add.
* @param pathToSave The path to store in the library in place of
* pathToOpen.
* @param url The url of the book to store in the library
* (useful for kiiwix-serve catalog)
* @param checkMetaData Tell if we check metadata before adding a book to the
* library.
* @return The id of te book if the book has been added to the library.
* Empty string if not.
*/
public native String addBookFromPath(String pathToOpen,
String pathToSave,
String url,
boolean checkMetaData);
public Manager(Library library) {
allocate(library);
_library = library;
}
private Library _library;
@Override
protected void finalize() { dispose(); }
private native void allocate(Library library);
private native void dispose();
private long nativeHandle;
}
@@ -22,5 +22,5 @@ package org.kiwix.kiwixlib;
public class Pair public class Pair
{ {
public String filename; public String filename;
public int offset; public long offset;
} }
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/bash
# This script compile the unit test to test the java wrapper.
# This is not integrated in meson because ... this is not so easy.
KIWIX_LIB_JAR=$1
if [ -z $KIWIX_LIB_JAR ]
then
echo "You must give the path to the kiwixlib.jar as first argument"
exit 1
fi
KIWIX_LIB_DIR=$2
if [ -z $KIWIX_LIB_DIR ]
then
echo "You must give the path to directory containing libkiwix.so as second argument"
exit 1
fi
TEST_SOURCE_DIR=$(dirname $(readlink -f $0))
javac -g -d . -s . -cp $TEST_SOURCE_DIR/junit-4.13.jar:$KIWIX_LIB_JAR $TEST_SOURCE_DIR/test.java
java -Djava.library.path=$KIWIX_LIB_DIR -cp $TEST_SOURCE_DIR/junit-4.13.jar:$TEST_SOURCE_DIR/hamcrest-core-1.3.jar:$KIWIX_LIB_JAR:. org.junit.runner.JUnitCore test
Binary file not shown.
@@ -0,0 +1,53 @@
import java.io.*;
import java.util.*;
import org.junit.Test;
import static org.junit.Assert.*;
import org.kiwix.kiwixlib.*;
public class test {
static {
System.loadLibrary("kiwix");
}
private static String getCatalogContent()
throws IOException
{
BufferedReader reader = new BufferedReader(new FileReader("catalog.xml"));
String line;
StringBuilder sb = new StringBuilder();
while ((line = reader.readLine()) != null)
{
sb.append(line + "\n");
}
reader.close();
return sb.toString();
}
@Test
public void testSome()
throws IOException
{
Library lib = new Library();
Manager manager = new Manager(lib);
String content = getCatalogContent();
manager.readOpds(content, "https://library.kiwix.org");
assertEquals(lib.getBookCount(true, true), 10);
String[] bookIds = lib.getBooksIds();
assertEquals(bookIds.length, 10);
Book book = lib.getBookById(bookIds[0]);
assertEquals(book.getTitle(), "Wikisource");
assertEquals(book.getTags(), "wikisource;_category:wikisource;_pictures:no;_videos:no;_details:yes;_ftindex:yes");
assertEquals(book.getFaviconUrl(), "https://library.kiwix.org/meta?name=favicon&content=wikisource_fr_all_nopic_2020-01");
assertEquals(book.getUrl(), "http://download.kiwix.org/zim/wikisource/wikisource_fr_all_nopic_2020-01.zim.meta4");
}
static
public void main(String[] args) {
Library lib = new Library();
lib.getBookCount(true, true);
}
}
File renamed without changes.
+124 -10
View File
@@ -26,9 +26,55 @@
#include <pthread.h> #include <pthread.h>
#include <string> #include <string>
#include <vector>
#include <iostream>
#if __ANDROID__
#include <android/log.h>
#define LOG(...) __android_log_print(ANDROID_LOG_ERROR, "kiwix", __VA_ARGS__)
#else
#define LOG(...)
#endif
extern pthread_mutex_t globalLock; extern pthread_mutex_t globalLock;
template<typename T>
void setPtr(JNIEnv* env, jobject thisObj, T* ptr)
{
jclass thisClass = env->GetObjectClass(thisObj);
jfieldID fieldId = env->GetFieldID(thisClass, "nativeHandle", "J");
env->SetLongField(thisObj, fieldId, reinterpret_cast<jlong>(ptr));
}
template<typename T, typename ...Args>
void allocate(JNIEnv* env, jobject thisObj, Args && ...args)
{
T* ptr = new T(std::forward<Args>(args)...);
setPtr(env, thisObj, ptr);
}
template<typename T>
T* getPtr(JNIEnv* env, jobject thisObj)
{
jclass thisClass = env->GetObjectClass(thisObj);
jfieldID fidNumber = env->GetFieldID(thisClass, "nativeHandle", "J");
return reinterpret_cast<T*>(env->GetLongField(thisObj, fidNumber));
}
template<typename T>
void dispose(JNIEnv* env, jobject thisObj)
{
delete getPtr<T>(env, thisObj);
}
#define METHOD0(retType, class, name) \
JNIEXPORT retType JNICALL Java_org_kiwix_kiwixlib_##class##_##name( \
JNIEnv* env, jobject thisObj)
#define METHOD(retType, class, name, ...) \
JNIEXPORT retType JNICALL Java_org_kiwix_kiwixlib_##class##_##name( \
JNIEnv* env, jobject thisObj, __VA_ARGS__)
inline jfieldID getHandleField(JNIEnv* env, jobject obj) inline jfieldID getHandleField(JNIEnv* env, jobject obj)
{ {
jclass c = env->GetObjectClass(obj); jclass c = env->GetObjectClass(obj);
@@ -36,6 +82,12 @@ inline jfieldID getHandleField(JNIEnv* env, jobject obj)
return env->GetFieldID(c, "nativeHandle", "J"); return env->GetFieldID(c, "nativeHandle", "J");
} }
inline jobjectArray createArray(JNIEnv* env, size_t length, const std::string& type_sig)
{
jclass c = env->FindClass(type_sig.c_str());
return env->NewObjectArray(length, c, NULL);
}
class Lock class Lock
{ {
protected: protected:
@@ -91,19 +143,67 @@ struct LockedHandle : public Lock {
T* operator->() { return h->h; } T* operator->() { return h->h; }
T* operator*() { return h->h; } T* operator*() { return h->h; }
operator bool() const { return (h->h != nullptr); } operator bool() const { return (h->h != nullptr); }
operator T*() const { return h->h; }
}; };
/* c2jni type conversion functions */ template<typename T>
inline jboolean c2jni(const bool& val) { return val ? JNI_TRUE : JNI_FALSE; } struct JType { };
template<> struct JType<bool>{ typedef jboolean type_t; };
template<> struct JType<int>{ typedef jint type_t; };
template<> struct JType<long>{ typedef jlong type_t; };
template<> struct JType<uint64_t> { typedef jlong type_t; };
template<> struct JType<uint32_t> { typedef jlong type_t; };
template<> struct JType<std::string>{ typedef jstring type_t; };
template<> struct JType<std::vector<std::string>>{ typedef jobjectArray type_t; };
template<typename T>
inline typename JType<T>::type_t c2jni(const T& val, JNIEnv* env) {
return static_cast<typename JType<T>::type_t>(val);
}
template<>
inline jboolean c2jni(const bool& val, JNIEnv* env) { return val ? JNI_TRUE : JNI_FALSE; }
template<>
inline jstring c2jni(const std::string& val, JNIEnv* env) inline jstring c2jni(const std::string& val, JNIEnv* env)
{ {
return env->NewStringUTF(val.c_str()); return env->NewStringUTF(val.c_str());
} }
inline jint c2jni(const int val) { return (jint)val; } template<>
inline jint c2jni(const unsigned val) { return (unsigned)val; } inline jobjectArray c2jni(const std::vector<std::string>& val, JNIEnv* env)
{
auto array = createArray(env, val.size(), "java/lang/String");
size_t index = 0;
for (auto& elem: val) {
auto jElem = c2jni(elem, env);
env->SetObjectArrayElement(array, index++, jElem);
}
return array;
}
template<typename T, typename U=T>
struct CType { };
template<> struct CType<jboolean>{ typedef bool type_t; };
template<> struct CType<jint>{ typedef int type_t; };
template<> struct CType<jlong>{ typedef long type_t; };
template<> struct CType<jstring>{ typedef std::string type_t; };
template<typename U>
struct CType<jobjectArray, U>{ typedef std::vector<typename CType<U>::type_t> type_t; };
/* jni2c type conversion functions */ /* jni2c type conversion functions */
inline bool jni2c(const jboolean& val) { return val == JNI_TRUE; } template<typename T, typename U=T>
inline typename CType<T>::type_t jni2c(const T& val, JNIEnv* env) {
return static_cast<typename CType<T>::type_t>(val);
}
template<>
inline bool jni2c(const jboolean& val, JNIEnv* env) { return val == JNI_TRUE; }
template<>
inline std::string jni2c(const jstring& val, JNIEnv* env) inline std::string jni2c(const jstring& val, JNIEnv* env)
{ {
const char* chars = env->GetStringUTFChars(val, 0); const char* chars = env->GetStringUTFChars(val, 0);
@@ -112,7 +212,21 @@ inline std::string jni2c(const jstring& val, JNIEnv* env)
return ret; return ret;
} }
inline int jni2c(const jint val) { return (int)val; } template<typename U>
inline typename CType<jobjectArray, U>::type_t jni2c(const jobjectArray& val, JNIEnv* env)
{
jsize length = env->GetArrayLength(val);
typename CType<jobjectArray, U>::type_t v(length);
int i;
for (i = 0; i < length; i++) {
U obj = (U) env->GetObjectArrayElement(val, i);
auto cobj = jni2c<U>(obj, env);
v.push_back(cobj);
}
return v;
}
/* Method to deal with variable passed by reference */ /* Method to deal with variable passed by reference */
inline std::string getStringObjValue(const jobject obj, JNIEnv* env) inline std::string getStringObjValue(const jobject obj, JNIEnv* env)
{ {
@@ -141,17 +255,17 @@ inline void setBoolObjValue(const bool value, const jobject obj, JNIEnv* env)
{ {
jclass objClass = env->GetObjectClass(obj); jclass objClass = env->GetObjectClass(obj);
jfieldID objFid = env->GetFieldID(objClass, "value", "Z"); jfieldID objFid = env->GetFieldID(objClass, "value", "Z");
env->SetIntField(obj, objFid, c2jni(value)); env->SetIntField(obj, objFid, c2jni(value, env));
} }
inline void setPairObjValue(const std::string& filename, const int offset, inline void setPairObjValue(const std::string& filename, const long offset,
const jobject obj, JNIEnv* env) const jobject obj, JNIEnv* env)
{ {
jclass objClass = env->GetObjectClass(obj); jclass objClass = env->GetObjectClass(obj);
jfieldID filenameFid = env->GetFieldID(objClass, "filename", "Ljava/lang/String;"); jfieldID filenameFid = env->GetFieldID(objClass, "filename", "Ljava/lang/String;");
env->SetObjectField(obj, filenameFid, c2jni(filename, env)); env->SetObjectField(obj, filenameFid, c2jni(filename, env));
jfieldID offsetFid = env->GetFieldID(objClass, "offset", "I"); jfieldID offsetFid = env->GetFieldID(objClass, "offset", "J");
env->SetIntField(obj, offsetFid, offset); env->SetLongField(obj, offsetFid, offset);
} }
#endif // _ANDROID_JNI_UTILS_H #endif // _ANDROID_JNI_UTILS_H
+3 -2
View File
@@ -2,8 +2,9 @@
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>Zim catalog search</ShortName> <ShortName>Zim catalog search</ShortName>
<Description>Search zim files in the catalog.</Description> <Description>Search zim files in the catalog.</Description>
<Url type="application/atom+xml;profile=opds-catalog" <Url type="application/atom+xml;profile=opds-catalog"
xmlns:atom="http://www.w3.org/2005/Atom" xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:k="http://kiwix.org/opensearchextension/1.0"
indexOffset="0" indexOffset="0"
template="/{{root}}/catalog/search?q={searchTerms}&lang={language}&count={count}&start={startIndex}"/> template="/{{root}}/catalog/search?q={searchTerms?}&lang={language?}&name={k:name?}&tag={k:tag?}&notag={k:notag?}&maxsize={k:maxsize?}&count={count?}&start={startIndex?}"/>
</OpenSearchDescription> </OpenSearchDescription>
+3
View File
@@ -20,6 +20,7 @@ skin/jquery-ui/jquery-ui.min.css
skin/caret.png skin/caret.png
skin/taskbar.js skin/taskbar.js
skin/taskbar.css skin/taskbar.css
skin/block_external.js
templates/search_result.html templates/search_result.html
templates/no_search_result.html templates/no_search_result.html
templates/404.html templates/404.html
@@ -28,4 +29,6 @@ templates/index.html
templates/suggestion.json templates/suggestion.json
templates/head_part.html templates/head_part.html
templates/taskbar_part.html templates/taskbar_part.html
templates/external_blocker_part.html
templates/captured_external.html
opensearchdescription.xml opensearchdescription.xml
+72
View File
@@ -0,0 +1,72 @@
var block_path = "/catch/external";
// called only on external links
function capture_event(e, target) { target.setAttribute("href", encodeURI(block_path + "?source=" + target.href)); }
// called on all link clicks. filters external and call capture_event
function on_click_event(e) {
var target = findParent("a", e.target);
if (target !== null && "href" in target) {
var href = target.href;
if (window.location.pathname.indexOf(block_path) == 0) // already in catch page
return;
if (href.indexOf(window.location.origin) == 0)
return;
if (href.substr(0, 2) == "//")
return capture_event(e, target);
if (href.substr(0, 5) == "http:")
return capture_event(e, target);
if (href.substr(0, 6) == "https:")
return capture_event(e, target);
return;
}
}
// script entrypoint (called on document ready)
function run() { live('a', 'click', on_click_event); }
// find first parent with tagname
function findParent(tagname, el) {
while (el) {
if ((el.nodeName || el.tagName).toLowerCase() === tagname.toLowerCase()) {
return el;
}
el = el.parentNode;
}
return null;
}
// matches polyfill
this.Element && function(ElementPrototype) {
ElementPrototype.matches = ElementPrototype.matches ||
ElementPrototype.matchesSelector ||
ElementPrototype.webkitMatchesSelector ||
ElementPrototype.msMatchesSelector ||
function(selector) {
var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1;
while (nodes[++i] && nodes[i] != node);
return !!nodes[i];
}
}(Element.prototype);
// helper for enabling IE 8 event bindings
function addEvent(el, type, handler) {
if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler);
}
// live binding helper using matchesSelector
function live(selector, event, callback, context) {
addEvent(context || document, event, function(e) {
var found, el = e.target || e.srcElement;
while (el && el.matches && el !== context && !(found = el.matches(selector))) el = el.parentElement;
if (found) callback.call(el, e);
});
}
// in case the document is already rendered
if (document.readyState!='loading') run();
// modern browsers
else if (document.addEventListener) document.addEventListener('DOMContentLoaded', run);
// IE <= 8
else document.attachEvent('onreadystatechange', function(){
if (document.readyState=='complete') run();
});
+1 -1
View File
@@ -124,7 +124,7 @@ label[for=kiwixsearchbox] {
} }
body { body {
padding-top: 40px !important; padding-top: 3em !important;
} }
/* Try to fix buggy stuff in jquery-ui autocomplete */ /* Try to fix buggy stuff in jquery-ui autocomplete */
+14
View File
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8" />
<title>External link blocked</title>
</head>
<body class="kiwix">
<h1>External link blocked</h1>
<p>This instance of Kiwix protects you from accidentaly going to external (out-of ZIM) links.</p>
<p>If you intend to go to such locations, please click the link below.</p>
<p><a href="{{ source }}">Go to {{ source }}</a></p>
<div id="kiwixfooter">Powered by <a href="https://kiwix.org">Kiwix</a></div>
</body>
</html>
@@ -0,0 +1 @@
<script type="text/javascript" src="{{root}}/skin/block_external.js"></script>
+30 -28
View File
@@ -95,7 +95,7 @@
</head> </head>
<body bgcolor="white"> <body bgcolor="white">
<div class="header"> <div class="header">
{{#hasResult}} {{#hasResults}}
Results Results
<b> <b>
{{resultStart}}-{{resultEnd}} {{resultStart}}-{{resultEnd}}
@@ -104,10 +104,10 @@
</b> for <b> </b> for <b>
{{searchPattern}} {{searchPattern}}
</b> </b>
{{/hasResult}} {{/hasResults}}
{{^hasResult}} {{^hasResults}}
No results were found for <b>{{searchPattern}}</b> No results were found for <b>{{searchPattern}}</b>
{{/hasResult}} {{/hasResults}}
</div> </div>
<div class="results"> <div class="results">
@@ -129,30 +129,32 @@
</div> </div>
<div class="footer"> <div class="footer">
<ul> {{#hasPages}}
{{#resultLastPageStart}} <ul>
<li> {{#resultLastPageStart}}
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start=0&end={{resultRange}}"> <li>
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start=0&end={{resultRange}}">
</a>
</li> </a>
{{/resultLastPageStart}} </li>
{{#pages}} {{/resultLastPageStart}}
<li> {{#pages}}
<a {{#selected}}class="selected"{{/selected}} <li>
href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{start}}&end={{end}}"> <a {{#selected}}class="selected"{{/selected}}
{{label}} href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{start}}&end={{end}}">
</a> {{label}}
</li> </a>
{{/pages}} </li>
{{#resultLastPageStart}} {{/pages}}
<li> {{#resultLastPageStart}}
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{resultLastPageStart}}&end={{lastResult}}"> <li>
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{resultLastPageStart}}&end={{lastResult}}">
</a>
</li> </a>
{{/resultLastPageStart}} </li>
</ul> {{/resultLastPageStart}}
</ul>
{{/hasPages}}
</div> </div>
</body> </body>
</html> </html>
+33
View File
@@ -0,0 +1,33 @@
#include "gtest/gtest.h"
#include "../include/book.h"
TEST(BookTest, updateTest)
{
kiwix::Book book;
book.setReadOnly(false);
book.setPath("/home/user/Downloads/skin-of-color-society_en_all_2019-11.zim");
book.setPathValid(true);
book.setUrl("book-url");
book.setTags("youtube;_videos:yes;_ftindex:yes;_ftindex:yes;_pictures:yes;_details:yes");
book.setName("skin-of-color-society_en_all");
book.setFavicon("book-favicon");
book.setFaviconMimeType("book-favicon-mimetype");
kiwix::Book newBook;
newBook.setReadOnly(true);
EXPECT_FALSE(newBook.update(book));
newBook.setReadOnly(false);
EXPECT_TRUE(newBook.update(book));
EXPECT_EQ(newBook.readOnly(), book.readOnly());
EXPECT_EQ(newBook.getPath(), book.getPath());
EXPECT_EQ(newBook.isPathValid(), book.isPathValid());
EXPECT_EQ(newBook.getUrl(), book.getUrl());
EXPECT_EQ(newBook.getTags(), book.getTags());
EXPECT_EQ(newBook.getName(), book.getName());
EXPECT_EQ(newBook.getFavicon(), book.getFavicon());
EXPECT_EQ(newBook.getFaviconMimeType(), book.getFaviconMimeType());
}
+12
View File
@@ -0,0 +1,12 @@
#include "gtest/gtest.h"
#include "../include/kiwixserve.h"
TEST(KiwixServeTest, PortTest)
{
kiwix::KiwixServe kiwixServe("libraryPath", 8181);
EXPECT_EQ(kiwixServe.getPort(), 8181);
kiwixServe.setPort(8484);
EXPECT_EQ(kiwixServe.getPort(), 8484);
EXPECT_EQ(kiwixServe.setPort(0), -1);
EXPECT_EQ(kiwixServe.setPort(3456789), -1);
}
+33 -5
View File
@@ -180,6 +180,7 @@ const char * sampleOpdsStream = R"(
#include "../include/library.h" #include "../include/library.h"
#include "../include/manager.h" #include "../include/manager.h"
#include "../include/bookmark.h"
namespace namespace
{ {
@@ -191,9 +192,34 @@ class LibraryTest : public ::testing::Test {
manager.readOpds(sampleOpdsStream, "foo.urlHost"); manager.readOpds(sampleOpdsStream, "foo.urlHost");
} }
kiwix::Bookmark createBookmark(const std::string &id) {
kiwix::Bookmark bookmark;
bookmark.setBookId(id);
return bookmark;
};
kiwix::Library lib; kiwix::Library lib;
}; };
TEST_F(LibraryTest, getBookMarksTest)
{
auto bookId1 = lib.getBooksIds()[0];
auto bookId2 = lib.getBooksIds()[1];
lib.addBookmark(createBookmark(bookId1));
lib.addBookmark(createBookmark("invalid-bookmark-id"));
lib.addBookmark(createBookmark(bookId2));
auto onlyValidBookmarks = lib.getBookmarks();
auto allBookmarks = lib.getBookmarks(false);
EXPECT_EQ(onlyValidBookmarks[0].getBookId(), bookId1);
EXPECT_EQ(onlyValidBookmarks[1].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[0].getBookId(), bookId1);
EXPECT_EQ(allBookmarks[1].getBookId(), "invalid-bookmark-id");
EXPECT_EQ(allBookmarks[2].getBookId(), bookId2);
}
TEST_F(LibraryTest, sanityCheck) TEST_F(LibraryTest, sanityCheck)
{ {
EXPECT_EQ(lib.getBookCount(true, true), 10U); EXPECT_EQ(lib.getBookCount(true, true), 10U);
@@ -226,16 +252,18 @@ TEST_F(LibraryTest, filterCheck)
EXPECT_EQ(bookIds.size(), 1U); EXPECT_EQ(bookIds.size(), 1U);
bookIds = lib.filter(kiwix::Filter().query("Wiki")); bookIds = lib.filter(kiwix::Filter().query("Wiki"));
EXPECT_EQ(bookIds.size(), 3U); EXPECT_EQ(bookIds.size(), 4U);
bookIds = lib.filter(kiwix::Filter().query("Wiki").creator("Wiki")); bookIds = lib.filter(kiwix::Filter().query("Wiki").creator("Wiki"));
EXPECT_EQ(bookIds.size(), 1U); EXPECT_EQ(bookIds.size(), 1U);
} }
};
int main(int argc, char** argv) TEST_F(LibraryTest, getBookByPath)
{ {
::testing::InitGoogleTest(&argc, argv); auto& book = lib.getBookById(lib.getBooksIds()[0]);
return RUN_ALL_TESTS(); book.setPath("/some/abs/path.zim");
EXPECT_EQ(lib.getBookByPath("/some/abs/path.zim").getId(), book.getId());
EXPECT_THROW(lib.getBookByPath("non/existant/path.zim"), std::out_of_range);
} }
};
+8 -3
View File
@@ -3,16 +3,21 @@
tests = [ tests = [
'parseUrl', 'parseUrl',
'library', 'library',
'regex' 'regex',
'tagParsing',
'stringTools',
'pathTools',
'kiwixserve',
'book'
] ]
gtest_dep = dependency('gtest', gtest_dep = dependency('gtest',
main:true, main:true,
fallback: ['gtest', 'gtest_dep'], fallback: ['gtest', 'gtest_main_dep'],
required:false) required:false)
if gtest_dep.found() if gtest_dep.found() and not meson.is_cross_build()
foreach test_name : tests foreach test_name : tests
test_exe = executable(test_name, [test_name+'.cpp'], test_exe = executable(test_name, [test_name+'.cpp'],
link_with : kiwixlib, link_with : kiwixlib,
-6
View File
@@ -65,9 +65,3 @@ TEST(ParseUrlTest, valid)
ASSERT_EQ(title, "/title"); ASSERT_EQ(title, "/title");
} }
}; };
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+229
View File
@@ -0,0 +1,229 @@
/*
* Copyright (C) 2019 Matthieu Gautier
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
* NON-INFRINGEMENT. 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "gtest/gtest.h"
#include <string>
#include <vector>
#include "../include/tools/pathTools.h"
#ifdef _WIN32
# define S "\\"
# define AS "c:"
#else
# define S "/"
# define AS ""
#endif
#define P2(a, b) a S b
#define P3(a, b, c) P2(P2(a, b), c)
#define P4(a, b, c, d) P2(P3(a, b, c), d)
#define P5(a, b, c, d, e) P2(P4(a, b, c, d), e)
#define P6(a, b, c, d, e, f) P2(P5(a, b, c ,d, e), f)
#define A1(a) P2(AS,a)
#define A2(a, b) A1(P2(a, b))
#define A3(a, b, c) A1(P3(a, b, c))
#define A4(a, b, c, d) A1(P4(a, b, c, d))
#define A5(a, b, c, d, e) A1(P5(a, b, c, d, e))
std::vector<std::string> normalizeParts(std::vector<std::string> parts, bool absolute);
#ifdef _WIN32
std::wstring Utf8ToWide(const std::string& str);
std::string WideToUtf8(const std::wstring& wstr);
#endif
namespace
{
#define V std::vector<std::string>
TEST(pathTools, normalizePartsAbsolute)
{
#define N(...) normalizeParts(__VA_ARGS__, true)
ASSERT_EQ(N({}), V({}));
#ifdef _WIN32
ASSERT_EQ(N({"c:"}), V({"c:"}));
#else
ASSERT_EQ(N({""}), V({"", ""}));
#endif
ASSERT_EQ(N({AS, "a"}), V({AS, "a"}));
ASSERT_EQ(N({AS, "a", "b"}), V({AS, "a", "b"}));
ASSERT_EQ(N({AS, "a", "b", ".."}), V({AS, "a"}));
#ifdef _WIN32
ASSERT_EQ(N({AS, "a", "b", "..", ".."}), V({AS}));
#else
ASSERT_EQ(N({AS, "a", "b", "..", ".."}), V({AS, ""}));
#endif
ASSERT_EQ(N({AS, "a", "b", "..", "..", "..", "foo"}), V({AS, "foo"}));
ASSERT_EQ(N({AS, "..", "..", "c", "d", ".", "..", "foo"}), V({AS, "c", "foo"}));
ASSERT_EQ(N({AS, "a", "b", ".", "c", "d", "..", "foo"}), V({AS, "a", "b", "c", "foo"}));
#ifdef _WIN32
ASSERT_EQ(N({"c:", "a", "b", ".", "c", "d:", "..", "foo"}), V({"d:", "foo"}));
#endif
#undef N
}
TEST(pathTools, normalizePartsRelative)
{
#define N(...) normalizeParts(__VA_ARGS__, false)
ASSERT_EQ(N({}), V({}));
ASSERT_EQ(N({""}), V({}));
ASSERT_EQ(N({"a"}), V({"a"}));
ASSERT_EQ(N({"a", "b"}), V({"a", "b"}));
ASSERT_EQ(N({"a", "b", ".."}), V({"a"}));
ASSERT_EQ(N({"a", "b", "..", ".."}), V({}));
ASSERT_EQ(N({"a", "b", "..", "..", "..", "foo"}), V({"..", "foo"}));
ASSERT_EQ(N({"..", "..", "c", "d", ".", "..", "foo"}), V({"..", "..", "c", "foo"}));
ASSERT_EQ(N({"a", "b", ".", "c", "d", "..", "foo"}), V({"a", "b", "c", "foo"}));
#undef N
}
TEST(pathTools, isRelativePath)
{
ASSERT_TRUE(isRelativePath("foo"));
ASSERT_TRUE(isRelativePath(P2("foo","bar")));
ASSERT_TRUE(isRelativePath(P3(".","foo","bar")));
ASSERT_TRUE(isRelativePath(P2("..","foo")));
ASSERT_TRUE(isRelativePath(P4("foo","","bar","")));
ASSERT_FALSE(isRelativePath(A1("foo")));
ASSERT_FALSE(isRelativePath(A2("foo", "bar")));
}
TEST(pathTools, computeAbsolutePath)
{
ASSERT_EQ(computeAbsolutePath(A2("a","b"), "foo"),
A3("a","b","foo"));
ASSERT_EQ(computeAbsolutePath(A3("a","b",""), "foo"),
A3("a","b","foo"));
ASSERT_EQ(computeAbsolutePath(A2("a","b"), P2(".","foo")),
A3("a","b","foo"));
ASSERT_EQ(computeAbsolutePath(A2("a","b"), P2("..","foo")),
A2("a","foo"));
ASSERT_EQ(computeAbsolutePath(A3("a","b",""), P2("..","foo")),
A2("a","foo"));
ASSERT_EQ(computeAbsolutePath(A5("a","b","c","d","e"), P2("..","foo")),
A5("a","b","c","d","foo"));
ASSERT_EQ(computeAbsolutePath(A5("a","b","c","d","e"), P5("..","..","..","g","foo")),
A4("a","b","g","foo"));
}
TEST(pathTools, computeRelativePath)
{
ASSERT_EQ(computeRelativePath(A2("a","b"), A3("a","b","foo")),
"foo");
ASSERT_EQ(computeRelativePath(A3("a","b",""), A3("a","b","foo")),
"foo");
ASSERT_EQ(computeRelativePath(A2("a","b"), A2("a","foo")),
P2("..","foo"));
ASSERT_EQ(computeRelativePath(A3("a","b",""), A2("a","foo")),
P2("..","foo"));
ASSERT_EQ(computeRelativePath(A5("a","b","c","d","e"), A5("a","b","c","d","foo")),
P2("..","foo"));
ASSERT_EQ(computeRelativePath(A5("a","b","c","d","e"), A4("a","b","g","foo")),
P5("..","..","..","g","foo"));
}
TEST(pathTools, removeLastPathElement)
{
ASSERT_EQ(removeLastPathElement(P3("a","b","c")),
P2("a","b"));
ASSERT_EQ(removeLastPathElement(A3("a","b","c")),
A2("a","b"));
ASSERT_EQ(removeLastPathElement(P4("a","b","c","")),
P2("a","b"));
ASSERT_EQ(removeLastPathElement(A4("a","b","c","")),
A2("a","b"));
}
TEST(pathTools, appendToDirectory)
{
ASSERT_EQ(appendToDirectory(P3("a","b","c"), "foo.xml"),
P4("a","b","c","foo.xml"));
ASSERT_EQ(appendToDirectory(P4("a","b","c",""), "foo.xml"),
P4("a","b","c","foo.xml"));
ASSERT_EQ(appendToDirectory(P3("a","b","c"), P2("d","foo.xml")),
P5("a","b","c","d","foo.xml"));
ASSERT_EQ(appendToDirectory(P4("a","b","c",""), P2("d","foo.xml")),
P5("a","b","c","d","foo.xml"));
ASSERT_EQ(appendToDirectory(P3("a","b","c"), P2(".","foo.xml")),
P5("a","b","c",".","foo.xml"));
ASSERT_EQ(appendToDirectory(P4("a","b","c",""), P2(".","foo.xml")),
P5("a","b","c",".","foo.xml"));
}
TEST(pathTools, goUp)
{
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), ".."),
A2("a", "b"));
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P2("..","..")),
A1("a"));
#ifdef _WIN32
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P3("..","..","..")),
"c:");
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P4("..","..","..","..")),
"c:");
#else
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P3("..","..","..")),
"/");
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P4("..","..","..","..")),
"/");
#endif
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P2("..", "foo")),
A3("a", "b","foo"));
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P3("..","..","foo")),
A2("a","foo"));
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P4("..","..","..","foo")),
A1("foo"));
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P5("..","..","..","..","foo")),
A1("foo"));
}
#ifdef _WIN32
TEST(pathTools, dirChange)
{
std::string p1("c:\\a\\b\\c");
std::string p2("d:\\d\\e\\foo.xml");
std::string relative_path = computeRelativePath(p1, p2);
ASSERT_EQ(relative_path, "d:\\d\\e\\foo.xml");
std::string abs_path = computeAbsolutePath(p1, relative_path);
ASSERT_EQ(abs_path, p2);
ASSERT_EQ(computeAbsolutePath(p1, "..\\..\\..\\..\\..\\d:\\d\\e\\foo.xml"), p2);
}
TEST(pathTools, Utf8ToWide)
{
ASSERT_EQ(Utf8ToWide(u8""), L"");
ASSERT_EQ(Utf8ToWide(u8"test"), L"test");
ASSERT_EQ(Utf8ToWide(u8"testé`œà"), L"testé`œà");
}
TEST(pathTools, WideToUtf8)
{
ASSERT_EQ(WideToUtf8(L""), u8"");
ASSERT_EQ(WideToUtf8(L"test"), u8"test");
ASSERT_EQ(WideToUtf8(L"testé`œà"), u8"testé`œà");
}
#endif
};
-6
View File
@@ -101,9 +101,3 @@ TEST(append, middle)
} }
}; };
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+56
View File
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2019 Matthieu Gautier
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
* NON-INFRINGEMENT. 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "gtest/gtest.h"
#include <string>
#include <vector>
namespace kiwix {
std::string join(const std::vector<std::string>& list, const std::string& sep);
std::vector<std::string> split(const std::string& base, const std::string& sep, bool trimEmpty);
};
using namespace kiwix;
#define parse_tag getTagValueFromTagList
namespace
{
TEST(stringTools, join)
{
std::vector<std::string> list = { "a", "b", "c" };
ASSERT_EQ(join(list, ";"), "a;b;c");
}
TEST(stringTools, split)
{
std::vector<std::string> list1 = { "a", "b", "c" };
ASSERT_EQ(split("a;b;c", ";", false), list1);
ASSERT_EQ(split("a;b;c", ";", true), list1);
std::vector<std::string> list2 = { "", "a", "b", "c" };
ASSERT_EQ(split(";a;b;c", ";", false), list2);
ASSERT_EQ(split(";a;b;c", ";", true), list1);
std::vector<std::string> list3 = { "", "a", "b", "c", ""};
ASSERT_EQ(split(";a;b;c;", ";", false), list3);
ASSERT_EQ(split(";a;b;c;", ";", true), list1);
std::vector<std::string> list4 = { "", "a", "b", "", "c", ""};
ASSERT_EQ(split(";a;b;;c;", ";", false), list4);
ASSERT_EQ(split(";a;b;;c;", ";", true), list1);
}
};
+97
View File
@@ -0,0 +1,97 @@
/*
* Copyright (C) 2019 Matthieu Gautier
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
* NON-INFRINGEMENT. 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "gtest/gtest.h"
#include <string>
#include <vector>
namespace kiwix {
std::vector<std::string> convertTags(const std::string& tags);
std::string getTagValueFromTagList(const std::vector<std::string>& tagList, const std::string& tagName);
};
using namespace kiwix;
#define parse_tag getTagValueFromTagList
namespace
{
TEST(ParseTagTest, convert)
{
{
std::string tagStr = "";
std::vector<std::string> tagList = {"_ftindex:no", "_pictures:yes", "_videos:yes", "_details:yes"};
ASSERT_EQ(convertTags(tagStr), tagList);
}
{
std::string tagStr = "_category:foo;bar";
std::vector<std::string> tagList = {"_category:foo", "bar", "_ftindex:no", "_pictures:yes", "_videos:yes", "_details:yes"};
ASSERT_EQ(convertTags(tagStr), tagList);
}
{
std::string tagStr = "_ftindex:no;_pictures:yes;_videos:yes;_details:yes;_category:foo;bar";
std::vector<std::string> tagList = {"_ftindex:no", "_pictures:yes", "_videos:yes", "_details:yes", "_category:foo", "bar"};
ASSERT_EQ(convertTags(tagStr), tagList);
}
{
std::string tagStr = "_ftindex:yes;_pictures:no;_videos:no;_details:no;_category:foo;bar";
std::vector<std::string> tagList = {"_ftindex:yes", "_pictures:no", "_videos:no", "_details:no", "_category:foo", "bar"};
ASSERT_EQ(convertTags(tagStr), tagList);
}
{
std::string tagStr = "_ftindex;nopic;novid;nodet;foo;bar";
std::vector<std::string> tagList = {"_ftindex:yes", "_pictures:no", "_videos:no", "_details:no", "foo", "bar"};
ASSERT_EQ(convertTags(tagStr), tagList);
}
}
TEST(ParseTagTest, valid)
{
std::string tagStr = "_ftindex:yes;_pictures:no;_videos:no;_details:yes;_category:foo;bar";
auto tagList = convertTags(tagStr);
ASSERT_EQ(parse_tag(tagList, "ftindex"), "yes");
ASSERT_EQ(parse_tag(tagList, "pictures"), "no");
ASSERT_EQ(parse_tag(tagList, "category"), "foo");
ASSERT_EQ(parse_tag(tagList, "details"), "yes");
ASSERT_THROW(parse_tag(tagList, "detail"), std::out_of_range);
}
TEST(ParseTagTest, compat)
{
std::string tagStr = "_ftindex;nopic;foo;bar";
auto tagList = convertTags(tagStr);
ASSERT_EQ(parse_tag(tagList, "ftindex"), "yes");
ASSERT_EQ(parse_tag(tagList, "pictures"), "no");
ASSERT_EQ(parse_tag(tagList, "videos"), "yes");
ASSERT_EQ(parse_tag(tagList, "details"), "yes");
}
TEST(ParseTagTest, invalid)
{
std::string tagStr = "_ftindex:y;_pictures;_videos:;_details:yes;_details:no;_category:foo;bar";
auto tagList = convertTags(tagStr);
ASSERT_EQ(parse_tag(tagList, "ftindex"), "y");
ASSERT_EQ(parse_tag(tagList, "pictures"), "yes");
ASSERT_EQ(parse_tag(tagList, "videos"), "");
ASSERT_EQ(parse_tag(tagList, "details"), "yes");
}
};
-49
View File
@@ -1,49 +0,0 @@
#!/usr/bin/env bash
set -e
BUILD_DIR=${HOME}/BUILD_${PLATFORM}
INSTALL_DIR=${BUILD_DIR}/INSTALL
TEST=0
case ${PLATFORM} in
"native_static")
MESON_OPTION="--default-library=static"
TEST=1
;;
"native_dyn")
MESON_OPTION="--default-library=shared"
TEST=1
;;
"win32_static")
MESON_OPTION="--default-library=static --cross-file ${BUILD_DIR}/meson_cross_file.txt"
;;
"win32_dyn")
MESON_OPTION="--default-library=shared --cross-file ${BUILD_DIR}/meson_cross_file.txt"
;;
"android_arm")
MESON_OPTION="-Dandroid=true --default-library=shared --cross-file ${BUILD_DIR}/meson_cross_file.txt"
;;
"android_arm64")
MESON_OPTION="-Dandroid=true --default-library=shared --cross-file ${BUILD_DIR}/meson_cross_file.txt"
;;
esac
cd ${TRAVIS_BUILD_DIR}
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]
then
export PKG_CONFIG_PATH=${INSTALL_DIR}/lib/pkgconfig
else
export PKG_CONFIG_PATH=${INSTALL_DIR}/lib/x86_64-linux-gnu/pkgconfig
fi
export CPPFLAGS="-I${INSTALL_DIR}/include"
meson . build ${MESON_OPTION}
cd build
ninja
if [[ "$TEST" == "1" ]]
then
echo "Running test"
export LD_LIBRARY_PATH=${INSTALL_DIR}/lib:${INSTALL_DIR}/lib64:${INSTALL_DIR}/lib/x86_64-linux-gnu
ninja test
fi
Loaded 100 of 101 files, more files were not shown because too many files have changed in this diff. Show more