Compare commits

...

90 Commits
6.0.2 ... 8.2.2

Author SHA1 Message Date
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
Matthieu Gautier
828bd032c4 Merge pull request #269 from kiwix/fix-multiple-column-suggestions
Force one columned suggestions in kiwix-serve
2019-09-04 17:40:45 +02:00
Kelson
26d32a36ad Force one columned suggestions in kiwix-serve 2019-09-04 17:34:15 +02:00
Matthieu Gautier
c031547461 Fix fulltext search link in kiwix-serve suggestions (#268)
Fix fulltext search link in kiwix-serve suggestions
2019-09-04 17:17:34 +02:00
Kelson
d0833bdcd4 Fix fulltext search link in kiwix-serve suggestions 2019-09-04 17:07:05 +02:00
Matthieu Gautier
1bb5e278ed Allow the gradle to add an extra build version in the pom file.
This is needed to be able to publish new build of the same version on
bintray.
2019-09-04 11:01:28 +02:00
Matthieu Gautier
0a331f8ba9 Fix release of 6.0.3. 2019-09-04 11:00:15 +02:00
Kelson
b1a4bbd345 Update changelog for 6.0.3 2019-09-03 19:42:10 +02:00
Kelson
14dbe843b9 Merge pull request #266 from kiwix/kiwix-serve-box-size
Kiwix serve welcome page box size fix
2019-08-29 21:06:07 +02:00
Kelson
e111316636 Kiwix serve welcome page box size fix 2019-08-29 21:01:21 +02:00
Kelson
bbb346b685 Slight improvements of the README.md 2019-08-27 16:31:04 +02:00
Kelson
56a08f49b2 Merge pull request #263 from kiwix/improve-again-top-padding
Better kiwix-serve content (because of taskbar) top-padding
2019-08-27 16:25:54 +02:00
Kelson
fc2ad81185 Better kiwix-serve content (because of taskbar) top-padding 2019-08-27 16:24:52 +02:00
Kelson
af78aa5fd0 Remove spaces which had visual impact 2019-08-27 16:24:14 +02:00
50 changed files with 1214 additions and 332 deletions

12
.codecov.yml Normal file
View File

@@ -0,0 +1,12 @@
codecov:
notify:
require_ci_to_pass: yes
coverage:
status:
project:
default:
threshold: 1%
ignore:
- "test"

12
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
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
.github/move.yml vendored Normal file
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
.github/stale.yml vendored Normal file
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.

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
.idea/
*.swp
subprojects/googletest-release*

View File

@@ -31,6 +31,11 @@ addons:
- gcc-mingw-w64-i686
- gcc-mingw-w64-base
- mingw-w64-tools
- gcovr
homebrew:
update: true
packages:
- gcovr
matrix:
include:
- env: PLATFORM="native_dyn"

View File

@@ -77,7 +77,7 @@ modification follow.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"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
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties

View File

@@ -1,9 +1,66 @@
kiwix-lib 8.2.2
===============
* Improve a few compilation scripts
kiwix-lib 8.2.1
===============
* Reintroduce kiwix-serve taskbar
kiwix-lib 8.2.0
===============
* More debug information if aria2c command fails
* Allow to set kiwix-serve port
* Better (dead) bookmarks mgmt
kiwix-lib 8.1.0
===============
* Fix pathTools manipulation.
* Add missing implementation of getArticleCount and getMediaCount on android.
* Correctly convert windows path to utf8.
* Add code coverage in the CI
kiwix-lib 8.0.1
===============
* Fix join function
kiwix-lib 8.0.0
===============
* Add new methods to get all (and new) metadata from the zim file.
* Add methods to get the value of a specific tag.
* [API Change] Convert tags value to the new convention.
* [API Change] Rename `getMatatag` method to `getMetadata`
* [ABI Change] Correctly detect executable path in appimage.
kiwix-lib 7.0.0
===============
* [API break] Add a argument to kiwix-serve to specify the library to use.
kiwix-lib 6.0.4
===============
* Fix HTML rendering of the search result if there is no result.
* Do not crash at html rendering if request ask for 0 results (start == end)
* Correctly find the executable path if we are using AppImage
kiwix-lib 6.0.3
===============
* force one column suggestion in kiwix-serve suggestions
* fix fulltext search link in suggestions
* UI fixes in kiwix-serve rendering
kiwix-lib 6.0.2
===============
* Correctly set the groupId in the pom file.
kiwix-lib 6.0.1
===============

View File

@@ -1,12 +1,15 @@
Kiwix library
=============
The Kiwix library provides the Kiwix software core. It contains the
code shared by all Kiwix ports (Windows, Linux, OSX, Android, ...).
The Kiwix library provides the [Kiwix](https://kiwix.org) software
suite core. It contains the code shared by all Kiwix ports (Windows,
GNU/Linux, macOS, Android, iOS, ...).
[![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://travis-ci.com/kiwix/kiwix-lib.svg?branch=master)](https://travis-ci.com/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)
Disclaimer
@@ -57,9 +60,9 @@ The Kiwix library builds using [Meson](https://mesonbuild.com/) version
compilation tools.
Install first the few common compilation tools:
* Meson
* Ninja
* Pkg-config
* [Meson](https://mesonbuild.com/)
* [Ninja](https://ninja-build.org/)
* [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/)
These tools should be packaged if you use a cutting edge operating
system. If not, have a look to the [Troubleshooting](#Troubleshooting)
@@ -70,7 +73,6 @@ Compilation
Once all dependencies are installed, you can compile the Kiwix library
with:
```bash
meson . build
ninja -C build
@@ -88,32 +90,29 @@ Installation
If you want to install the Kiwix library and the headers you just have
compiled on your system, here we go:
```bash
ninja -C build install
```
You might need to run the command as root (or using `sudo`), depending
where you want to install the libraries. After the installation
succeeded, you may need to run `ldconfig` (as root).
succeeded, you may need to run `ldconfig` (as `root`).
Uninstallation
------------
If you want to uninstall the Kiwix library:
```bash
ninja -C build uninstall
```
Like for the installation, you might need to run the command as root
Like for the installation, you might need to run the command as `root`
(or using `sudo`).
Troubleshooting
---------------
If you need to install Meson "manually":
```bash
virtualenv -p python3 ./ # Create virtualenv
source bin/activate # Activate the virtualenv
@@ -122,7 +121,6 @@ hash -r # Refresh bash paths
```
If you need to install Ninja "manually":
```bash
git clone git://github.com/ninja-build/ninja.git
cd ninja
@@ -142,4 +140,5 @@ repository.
License
-------
GPLv3 or later, see COPYING for more details.
[GPLv3](https://www.gnu.org/licenses/gpl-3.0) or later, see
[COPYING](COPYING) for more details.

0
android-kiwix-lib-publisher/gradlew vendored Normal file → Executable file
View File

0
android-kiwix-lib-publisher/gradlew.bat vendored Normal file → Executable file
View File

View File

@@ -26,7 +26,7 @@ task writePom {
project {
groupId 'org.kiwix.kiwixlib'
artifactId 'kiwixlib'
version '6.0.2'
version '8.2.2' + (System.env.KIWIXLIB_BUILDVERSION == null ? '' : '-'+System.env.KIWIXLIB_BUILDVERSION)
packaging 'aar'
name 'kiwixlib'
url 'https://github.com/kiwix/kiwix-lib'

View File

@@ -97,6 +97,7 @@ class Downloader
size_t getNbDownload() { return m_knownDownloads.size(); }
std::vector<std::string> getDownloadIds();
const std::string &getAria2LaunchCmd();
private:
std::map<std::string, std::unique_ptr<Download>> m_knownDownloads;

View File

@@ -64,14 +64,14 @@ class Entry
* @return the path of the entry.
*/
std::string getPath() const;
/**
* Get the title of the entry.
*
* @return the title of the entry.
*/
std::string getTitle() const;
/**
* Get the content of the entry.
*
@@ -81,7 +81,7 @@ class Entry
* @return the content of the entry.
*/
std::string getContent() const;
/**
* Get the blob of the entry.
*
@@ -91,7 +91,7 @@ class Entry
* @return the blob of the entry.
*/
zim::Blob getBlob(offset_type offset = 0) const;
/**
* Get the blob of the entry.
*
@@ -102,7 +102,7 @@ class Entry
* @return the blob of the entry.
*/
zim::Blob getBlob(offset_type offset, size_type size) const;
/**
* Get the info for direct access to the content of the entry.
*
@@ -118,7 +118,7 @@ class Entry
* Return <"",0> if is not possible to read directly.
*/
std::pair<std::string, offset_type> getDirectAccessInfo() const;
/**
* Get the size of the entry.
*
@@ -132,8 +132,8 @@ class Entry
* @return the mime_type of the entry.
*/
std::string getMimetype() const;
/**
* Get if the entry is a redirect entry.
*

View File

@@ -2,6 +2,7 @@
#define KIWIXLIB_KIWIX_SERVE_H_
#include <memory>
#include <string>
class Subprocess;
namespace kiwix {
@@ -9,17 +10,19 @@ namespace kiwix {
class KiwixServe
{
public:
KiwixServe(int port = 8181);
KiwixServe(const std::string& libraryPath, int port = 8181);
~KiwixServe();
void run();
void shutDown();
bool isRunning();
int getPort() { return m_port; }
int setPort(int port);
private:
std::unique_ptr<Subprocess> mp_kiwixServe;
int m_port;
std::string m_libraryPath;
};
}; //end namespace kiwix

View File

@@ -208,7 +208,7 @@ class Library
*
* @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.

View File

@@ -158,31 +158,7 @@ class Reader
* @param[out] value The value will be set to the content of the metadata.
* @return True if it was possible to get the content of the metadata.
*/
bool getMetatag(const string& name, string& value) const;
/**
* Get the title of the zim file.
*
* @return The title of zim file as specified in the zim metadata.
* If no title has been set, return a title computed from the
* file path.
*/
string getTitle() const;
/**
* Get the description of the zim file.
*
* @return The description of the zim file as specified in the zim metadata.
* If no description has been set, return the subtitle.
*/
string getDescription() const;
/**
* Get the language of the zim file.
*
* @return The language of the zim file as specified in the zim metadata.
*/
string getLanguage() const;
bool getMetadata(const string& name, string& value) const;
/**
* Get the name of the zim file.
@@ -192,18 +168,13 @@ class Reader
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;
/**
* Get the date of the zim file.
*
* @return The date of the zim file as specified in the zim metadata.
*/
string getDate() const;
string getTitle() const;
/**
* Get the creator of the zim file.
@@ -219,6 +190,100 @@ class Reader
*/
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.
*

View File

@@ -25,9 +25,7 @@
bool isRelativePath(const std::string& path);
std::string computeAbsolutePath(const std::string& path, const std::string& relativePath);
std::string computeRelativePath(const std::string& path, const std::string& absolutePath);
std::string removeLastPathElement(const std::string& path,
const bool removePreSeparator = false,
const bool removePostSeparator = false);
std::string removeLastPathElement(const std::string& path);
std::string appendToDirectory(const std::string& directoryPath, const std::string& filename);
unsigned int getFileSize(const std::string& path);
@@ -38,7 +36,7 @@ bool makeDirectory(const std::string& path);
std::string makeTmpDirectory();
bool copyFile(const std::string& sourcePath, const std::string& destPath);
std::string getLastPathElement(const std::string& path);
std::string getExecutablePath();
std::string getExecutablePath(bool realPathOnly = false);
std::string getCurrentDirectory();
std::string getDataDirectory();
bool writeTextFile(const std::string& path, const std::string& content);

View File

@@ -43,10 +43,8 @@ void loadICUExternalTables();
std::string urlEncode(const std::string& value, bool encodeReserved = 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 char*, const char*);
std::vector<std::string> split(const std::string&, const char*);
std::vector<std::string> split(const char*, const std::string&);
std::vector<std::string> split(const std::string&, const std::string&, bool trimEmpty = true);
std::string join(const std::vector<std::string>& list, const std::string& sep);
std::string ucAll(const std::string& word);
std::string lcAll(const std::string& word);

View File

@@ -1,5 +1,5 @@
project('kiwix-lib', 'cpp',
version : '6.0.2',
version : '8.2.2', # Also change this in android-kiwix-lib-publisher/kiwixLibAndroid/build.gradle
license : 'GPL',
default_options : ['c_std=c11', 'cpp_std=c++11', 'werror=true'])

View File

@@ -81,12 +81,12 @@ class Resource:
data_identifier="_".join([""]+self.identifier),
resource_content=",\n ".join(", ".join("{:#04x}".format(i) for i in r) for r in sliced),
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)),
identifier=self.identifier[-1],
env_identifier="RES_"+"_".join(self.identifier)+"_PATH"
)
def dump_getter(self):
return resource_getter_template.format(
common_name=self.filename,
@@ -95,11 +95,11 @@ class Resource:
def dump_decl(self):
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)),
identifier=self.identifier[-1]
)
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);
if (NULL == resPath)
return std::string(reinterpret_cast<const char*>(content), len);
std::ifstream ifs(resPath);
if (!ifs.good())
return std::string(reinterpret_cast<const char*>(content), len);
@@ -137,7 +137,7 @@ def gen_c_file(resources, basename):
include_file=basename,
basename=to_identifier(basename)
)
master_h_template = """//This file is automaically generated. Do not modify it.

View File

@@ -353,14 +353,17 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_searchSuggestions(JNIEnv* env,
JNIEXPORT jboolean JNICALL
Java_org_kiwix_kiwixlib_JNIKiwixReader_getNextSuggestion(JNIEnv* env,
jobject obj,
jobject titleObj)
jobject titleObj,
jobject urlObj)
{
jboolean retVal = JNI_FALSE;
std::string cTitle;
std::string cUrl;
try {
if (READER->getNextSuggestion(cTitle)) {
if (READER->getNextSuggestion(cTitle, cUrl)) {
setStringObjValue(cTitle, titleObj, env);
setStringObjValue(cUrl, urlObj, env);
retVal = JNI_TRUE;
}
} catch (std::exception& e) {
@@ -424,6 +427,35 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getDescription(JNIEnv* env, jobject obj)
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);
} catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get article count.");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", 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);
} catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get media count.");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
}
return mediaCount;
}
JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getRandomPage(
JNIEnv* env, jobject obj, jobject urlObj)
{

View File

@@ -102,7 +102,7 @@ public class JNIKiwixReader
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);

View File

@@ -49,7 +49,7 @@ Aria2::Aria2():
m_secret = "token:"+m_secret;
std::string aria2cmd = appendToDirectory(
removeLastPathElement(getExecutablePath(), true, true),
removeLastPathElement(getExecutablePath(true)),
ARIA2_CMD);
if (fileExists(aria2cmd)) {
// A local aria2c exe exists (packaged with kiwix-desktop), use it.
@@ -78,18 +78,32 @@ Aria2::Aria2():
callCmd.push_back("--max-concurrent-downloads=42");
callCmd.push_back("--rpc-max-request-size=6M");
callCmd.push_back("--file-allocation=none");
for (auto &cmd : callCmd) {
m_launchCmd.append(cmd).append(" ");
}
mp_aria = Subprocess::run(callCmd);
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_PORT, m_port);
curl_easy_setopt(mp_curl, CURLOPT_POST, 1L);
curl_easy_setopt(mp_curl, CURLOPT_ERRORBUFFER, errbuf);
int watchdog = 50;
while(--watchdog) {
sleep(10);
errbuf[0] = 0;
auto res = curl_easy_perform(mp_curl);
if (res == CURLE_OK) {
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) {

View File

@@ -26,6 +26,7 @@ class Aria2
std::string m_downloadDir;
CURL* mp_curl;
pthread_mutex_t m_lock;
std::string m_launchCmd;
std::string doRequest(const MethodCall& methodCall);
@@ -43,6 +44,7 @@ class Aria2
void pause(const std::string& gid);
void unpause(const std::string& gid);
void remove(const std::string& gid);
const std::string &getLaunchCmd() { return m_launchCmd; };
};
}; //end namespace kiwix

View File

@@ -127,17 +127,24 @@ void Download::cancelDownload()
Downloader::Downloader() :
mp_aria(new Aria2())
{
for (auto gid : mp_aria->tellActive()) {
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
m_knownDownloads[gid]->updateStatus();
try {
for (auto gid : mp_aria->tellActive()) {
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()) {
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
m_knownDownloads[gid]->updateStatus();
try {
for (auto gid : mp_aria->tellWaiting()) {
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 */
Downloader::~Downloader()
{
@@ -156,6 +163,11 @@ std::vector<std::string> Downloader::getDownloadIds() {
return ret;
}
const std::string &Downloader::getAria2LaunchCmd()
{
return mp_aria->getLaunchCmd();
}
Download* Downloader::startDownload(const std::string& uri)
{
for (auto& p: m_knownDownloads) {

View File

@@ -14,7 +14,9 @@
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::string kiwixServeCmd = appendToDirectory(
removeLastPathElement(getExecutablePath(), true, true),
removeLastPathElement(getExecutablePath(true)),
KIWIXSERVE_CMD);
if (fileExists(kiwixServeCmd)) {
// 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.
callCmd.push_back(KIWIXSERVE_CMD);
}
std::string libraryPath = getDataDirectory() + "/library.xml";
std::string attachProcessOpt = "-a" + to_string(pid);
std::string portOpt = "-p" + to_string(m_port);
callCmd.push_back(attachProcessOpt.c_str());
callCmd.push_back(portOpt.c_str());
callCmd.push_back("-l");
callCmd.push_back(libraryPath.c_str());
callCmd.push_back(m_libraryPath.c_str());
mp_kiwixServe = Subprocess::run(callCmd);
}
@@ -67,4 +68,14 @@ bool KiwixServe::isRunning()
return false;
}
int KiwixServe::setPort(int port)
{
if (port >= 1 && port <= 65535) {
m_port = port;
} else {
return -1;
}
return m_port;
}
}

View File

@@ -115,7 +115,7 @@ unsigned int Library::getBookCount(const bool localBooks,
bool Library::writeToFile(const std::string& path)
{
auto baseDir = removeLastPathElement(path, true, false);
auto baseDir = removeLastPathElement(path);
LibXMLDumper dumper(this);
dumper.setBaseDir(baseDir);
return writeTextFile(path, dumper.dumpLibXMLContent(getBooksIds()));
@@ -184,6 +184,21 @@ std::vector<std::string> Library::getBooksPublishers()
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> bookIds;

View File

@@ -59,7 +59,7 @@ bool Manager::parseXmlDom(const pugi::xml_document& doc,
book.setReadOnly(readOnly);
book.updateFromXml(bookNode,
removeLastPathElement(libraryPath, true, false));
removeLastPathElement(libraryPath));
/* Update the book properties with the new importer */
if (libraryVersion.empty()
@@ -177,7 +177,7 @@ std::string Manager::addBookFromPathAndGetId(const std::string& pathToOpen,
if (pathToSave != pathToOpen) {
book.setPath(isRelativePath(pathToSave)
? computeAbsolutePath(
removeLastPathElement(writableLibraryPath, true, false),
removeLastPathElement(writableLibraryPath),
pathToSave)
: pathToSave);
}

View File

@@ -278,7 +278,7 @@ string Reader::getZimFilePath() const
return this->zimFilePath;
}
/* Return a metatag value */
bool Reader::getMetatag(const string& name, string& value) const
bool Reader::getMetadata(const string& name, string& value) const
{
try {
auto entry = getEntryFromPath("M/"+name);
@@ -289,10 +289,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 value;
this->getMetatag("Title", value);
this->getMetadata("Title", value);
if (value.empty()) {
value = getLastPathElement(zimFileHandler->getFilename());
std::replace(value.begin(), value.end(), '_', ' ');
@@ -302,65 +309,164 @@ string Reader::getTitle() const
return value;
}
string Reader::getName() const
string Reader::getCreator() const
{
string value;
this->getMetatag("Name", value);
return value;
METADATA("Creator")
}
string Reader::getTags() const
string Reader::getPublisher() const
{
string value;
this->getMetatag("Tags", value);
return value;
METADATA("Publisher")
}
string Reader::getDate() const
{
METADATA("Date")
}
string Reader::getDescription() const
{
string value;
this->getMetatag("Description", value);
this->getMetadata("Description", value);
/* Mediawiki Collection tends to use the "Subtitle" name */
if (value.empty()) {
this->getMetatag("Subtitle", value);
this->getMetadata("Subtitle", value);
}
return value;
}
string Reader::getLongDescription() const
{
METADATA("LongDescription")
}
string Reader::getLanguage() const
{
string value;
this->getMetatag("Language", value);
return value;
METADATA("Language")
}
string Reader::getDate() const
string Reader::getLicense() const
{
string value;
this->getMetatag("Date", value);
return value;
METADATA("License")
}
string Reader::getCreator() const
std::vector<std::string> convertTags(const std::string& tags_str)
{
string value;
this->getMetatag("Creator", value);
return value;
auto tags = 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 |= 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;
}
string Reader::getPublisher() const
string Reader::getTags(bool original) const
{
string value;
this->getMetatag("Publisher", value);
return value;
string tags_str;
getMetadata("Tags", tags_str);
if (original) {
return tags_str;
}
auto tags = convertTags(tags_str);
return join(tags, ";");
}
string getTagValueFromTagList(const std::vector<std::string>& tagList, const std::string& tagName)
{
for (auto tag: tagList) {
if (tag[0] == '_') {
auto delimPos = tag.find(':');
if (delimPos == 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());
}
string Reader::getTagStr(const std::string& tagName) const
{
string tags_str;
getMetadata("Tags", tags_str);
return getTagValueFromTagList(convertTags(tags_str), tagName);
}
bool Reader::getTagBool(const std::string& tagName) const
{
auto tagValue = getTagStr(tagName);
if (tagValue == "yes") {
return true;
} else if (tagValue == "no") {
return false;
} else {
std::stringstream ss;
ss << "Tag value '" << tagValue << "' for " << tagName << " cannot be converted to bool.";
throw std::domain_error(ss.str());
}
}
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 value;
this->getMetatag("startfileuid", value);
this->getMetadata("startfileuid", value);
if (value.empty()) {
return "";
}

View File

@@ -96,27 +96,29 @@ std::string SearchRenderer::getHtml()
auto resultEnd = mp_searcher->getResultEnd();
auto resultCountPerPage = resultEnd - resultStart;
auto estimatedResultCount = mp_searcher->getEstimatedResultCount();
unsigned int pageStart
= resultStart / resultCountPerPage >= 5
? resultStart / resultCountPerPage - 4
: 0;
unsigned int pageCount
= estimatedResultCount / resultCountPerPage + 1 - pageStart;
if (pageCount > 10) {
pageCount = 10;
} else if (pageCount == 1) {
pageCount = 0;
auto currentPage = 0U;
auto pageStart = 0U;
auto pageEnd = 0U;
auto lastPageStart = 0U;
if (resultCountPerPage) {
currentPage = resultStart/resultCountPerPage;
pageStart = currentPage > 4 ? currentPage-4 : 0;
pageEnd = currentPage + 5;
if (pageEnd > estimatedResultCount / resultCountPerPage) {
pageEnd = estimatedResultCount / resultCountPerPage;
}
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;
page.set("label", to_string(i + 1));
page.set("start", to_string(i * resultCountPerPage));
page.set("end", to_string((i + 1) * resultCountPerPage));
if (i * resultCountPerPage == resultStart) {
if (i == currentPage) {
page.set("selected", true);
}
pages.push_back(page);
@@ -128,16 +130,15 @@ std::string SearchRenderer::getHtml()
kainjow::mustache::data allData;
allData.set("results", results);
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("searchPattern", kiwix::encodeDiples(this->searchPattern));
allData.set("searchPatternEncoded", urlEncode(this->searchPattern));
allData.set("resultStart", to_string(resultStart + 1));
allData.set("resultEnd", to_string(min(resultEnd, estimatedResultCount)));
allData.set("resultRange", to_string(resultCountPerPage));
allData.set("resultLastPageStart", to_string(estimatedResultCount > resultCountPerPage
? round(estimatedResultCount / resultCountPerPage) * resultCountPerPage
: 0));
allData.set("resultLastPageStart", to_string(lastPageStart));
allData.set("lastResult", to_string(estimatedResultCount));
allData.set("protocolPrefix", this->protocolPrefix);
allData.set("searchProtocolPrefix", this->searchProtocolPrefix);

View File

@@ -108,12 +108,12 @@ void Searcher::search(std::string& search,
cout << "Performing query `" << search << "'" << endl;
}
this->searchPattern = search;
this->resultStart = resultStart;
this->resultEnd = resultEnd;
/* Try to find results */
if (resultStart != resultEnd) {
/* Perform the search */
this->searchPattern = search;
this->resultStart = resultStart;
this->resultEnd = resultEnd;
string unaccentedSearch = removeAccents(search);
std::vector<const zim::File*> zims;
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;
}
/* Try to find results */
if (resultStart == resultEnd) {
return;
}
/* Perform the search */
std::ostringstream oss;
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->resultEnd = resultEnd;
/* Try to find results */
if (resultStart == resultEnd) {
return;
}
std::vector<const zim::File*> zims;
for (auto current = this->readers.begin(); current != this->readers.end();
current++) {

View File

@@ -519,7 +519,7 @@ Response InternalServer::handle_suggest(const RequestContext& request)
if (reader->hasFulltextIndex()) {
kainjow::mustache::data result;
result.set("label", "containing '" + term + "'...");
result.set("value", term);
result.set("value", term + " ");
result.set("first", first);
results.push_back(result);
}
@@ -870,23 +870,11 @@ Response InternalServer::handle_content(const RequestContext& request)
zim::Blob raw_content = entry.getBlob();
content = string(raw_content.data(), raw_content.size());
auto response = get_default_response();
response.set_mimeType(mimeType);
/* Special rewrite URL in case of ZIM file use intern *asbolute* url like
* /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|\\-])/");
if (mimeType.find("text/html") != string::npos)
response.set_taskbar(bookName, reader->getTitle());
} else if (mimeType.find("text/css") != string::npos) {
content = replaceRegex(content,
"$1$2" + m_root + "/" + bookName + "/$3/",
"(url|URL)(\\([\"|\']{0,1})/([A-Z|\\-])/");
}
response.set_mimeType(mimeType);
response.set_content(content);
response.set_compress(true);
response.set_cache(true);

View File

@@ -140,7 +140,7 @@ int Response::send(const RequestContext& request, MHD_Connection* connection)
bool shouldCompress = m_compress && request.can_compress();
shouldCompress &= m_mimeType.find("text/") != 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);

View File

@@ -1,4 +1,4 @@
/*
/*
base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger
@@ -27,7 +27,7 @@
#include <tools/base64.h>
#include <iostream>
static const std::string base64_chars =
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

View File

@@ -39,11 +39,12 @@
#include <fstream>
#include <iomanip>
#include <iostream>
#include <algorithm>
#ifdef _WIN32
const std::string SEPARATOR("\\");
#define SEPARATOR "\\"
#else
const std::string SEPARATOR("/");
#define SEPARATOR "/"
#include <unistd.h>
#endif
@@ -53,6 +54,25 @@ const std::string SEPARATOR("/");
#define PATH_MAX 1024
#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)
{
#ifdef _WIN32
@@ -62,11 +82,65 @@ bool isRelativePath(const std::string& path)
#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::vector<std::string> pathParts = kiwix::split(path, SEPARATOR);
std::vector<std::string> absolutePathParts
= kiwix::split(absolutePath, SEPARATOR);
auto pathParts = normalizeParts(kiwix::split(path, SEPARATOR, false), false);
auto absolutePathParts = kiwix::split(absolutePath, SEPARATOR, false);
unsigned int commonCount = 0;
while (commonCount < pathParts.size()
@@ -75,108 +149,60 @@ std::string computeRelativePath(const std::string& path, const std::string& abso
commonCount++;
}
std::string relativePath;
#ifdef _WIN32
/* On Windows you have a token more because the root is represented
by a letter */
if (commonCount == 0) {
relativePath = ".." + SEPARATOR;
}
#endif
std::vector<std::string> relativeParts;
for (unsigned int i = commonCount; i < pathParts.size(); i++) {
relativePath += ".." + SEPARATOR;
relativeParts.push_back("..");
}
for (unsigned int i = commonCount; i < absolutePathParts.size(); i++) {
relativePath += absolutePathParts[i];
relativePath += i + 1 < absolutePathParts.size() ? SEPARATOR : "";
relativeParts.push_back(absolutePathParts[i]);
}
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 absolutePath;
std::string absolutePath = path;
if (path.empty()) {
char* path = NULL;
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;
absolutePath = getCurrentDirectory();
}
#if _WIN32
char* cRelativePath = _strdup(relativePath.c_str());
#else
char* cRelativePath = strdup(relativePath.c_str());
#endif
char* saveptr = nullptr;
char* token = STRTOK(cRelativePath, "/", &saveptr);
auto absoluteParts = normalizeParts(kiwix::split(absolutePath, SEPARATOR, false), true);
auto relativeParts = kiwix::split(relativePath, SEPARATOR, false);
while (token != NULL) {
if (std::string(token) == "..") {
absolutePath = removeLastPathElement(absolutePath, true, false);
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;
absoluteParts.insert(absoluteParts.end(), relativeParts.begin(), relativeParts.end());
auto ret = kiwix::join(normalizeParts(absoluteParts, true), SEPARATOR);
return ret;
}
std::string removeLastPathElement(const std::string& path,
const bool removePreSeparator,
const bool removePostSeparator)
std::string removeLastPathElement(const std::string& path)
{
std::string newPath = path;
size_t offset = newPath.find_last_of(SEPARATOR);
if (removePreSeparator &&
#ifndef _WIN32
offset != newPath.find_first_of(SEPARATOR) &&
#endif
offset == newPath.length() - 1) {
newPath = newPath.substr(0, offset);
offset = newPath.find_last_of(SEPARATOR);
auto parts = normalizeParts(kiwix::split(path, SEPARATOR, false), false);
if (!parts.empty()) {
parts.pop_back();
}
newPath = removePostSeparator ? newPath.substr(0, offset)
: newPath.substr(0, offset + 1);
return newPath;
auto ret = kiwix::join(parts, SEPARATOR);
return ret;
}
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;
}
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)
@@ -216,7 +242,7 @@ std::string getFileContent(const std::string& path)
bool fileExists(const std::string& path)
{
#ifdef _WIN32
return PathFileExists(path.c_str());
return PathFileExistsW(Utf8ToWide(path).c_str());
#else
bool flag = false;
std::fstream fin;
@@ -232,7 +258,7 @@ bool fileExists(const std::string& path)
bool makeDirectory(const std::string& path)
{
#ifdef _WIN32
int status = _mkdir(path.c_str());
int status = _wmkdir(Utf8ToWide(path).c_str());
#else
int status = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#endif
@@ -242,15 +268,15 @@ bool makeDirectory(const std::string& path)
std::string makeTmpDirectory()
{
#ifdef _WIN32
char cbase[MAX_PATH];
char ctmp[MAX_PATH];
GetTempPath(MAX_PATH-14, cbase);
wchar_t cbase[MAX_PATH];
wchar_t ctmp[MAX_PATH];
GetTempPathW(MAX_PATH-14, cbase);
// This create a file for us, ensure it is unique.
// So we need to delete it and create the directory using the same name.
GetTempFileName(cbase, "kiwix", 0, ctmp);
DeleteFile(ctmp);
_mkdir(ctmp);
return std::string(ctmp);
GetTempFileNameW(cbase, L"kiwix", 0, ctmp);
DeleteFileW(ctmp);
_wmkdir(ctmp);
return WideToUtf8(ctmp);
#else
char _template_array[] = {"/tmp/kiwix-lib_XXXXXX"};
std::string dir = mkdtemp(_template_array);
@@ -279,25 +305,38 @@ bool copyFile(const std::string& sourcePath, const std::string& destPath)
return true;
}
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
GetModuleFileName(NULL, binRootPath, PATH_MAX);
return std::string(binRootPath);
std::wstring binRootPath(PATH_MAX, 0);
GetModuleFileNameW(NULL, &binRootPath[0], PATH_MAX);
std::string ret = WideToUtf8(binRootPath);
return ret;
#elif __APPLE__
char binRootPath[PATH_MAX];
uint32_t max = (uint32_t)PATH_MAX;
_NSGetExecutablePath(binRootPath, &max);
return std::string(binRootPath);
#else
char binRootPath[PATH_MAX];
ssize_t size = readlink("/proc/self/exe", binRootPath, PATH_MAX);
if (size != -1) {
return std::string(binRootPath, size);
}
#endif
return "";
#endif
}
bool writeTextFile(const std::string& path, const std::string& content)
@@ -311,10 +350,16 @@ bool writeTextFile(const std::string& path, const std::string& content)
std::string getCurrentDirectory()
{
char* a_cwd = getcwd(NULL, 0);
std::string s_cwd(a_cwd);
#ifdef _WIN32
wchar_t* a_cwd = _wgetcwd(NULL, 0);
std::string ret = WideToUtf8(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()
@@ -325,8 +370,9 @@ std::string getDataDirectory()
char* cDataDir = ::getenv("KIWIX_DATA_DIR");
#endif
std::string dataDir = cDataDir==nullptr ? "" : cDataDir;
if (!dataDir.empty())
if (!dataDir.empty()) {
return dataDir;
}
#ifdef _WIN32
cDataDir = ::getenv("USERPROFILE");
dataDir = cDataDir==nullptr ? getCurrentDirectory() : cDataDir;
@@ -340,7 +386,8 @@ std::string getDataDirectory()
dataDir = appendToDirectory(dataDir, "share");
}
#endif
return appendToDirectory(dataDir, "kiwix");
auto ret = appendToDirectory(dataDir, "kiwix");
return ret;
}
static std::map<std::string, std::string> extMimeTypes = {

View File

@@ -267,36 +267,42 @@ std::string kiwix::urlDecode(const std::string& value, bool component)
/* Split string in a token array */
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 pos = str.find_first_of(delims, lastPos);
std::string::size_type lastPos = 0;
std::string::size_type pos = 0;
std::vector<std::string> tokens;
while (std::string::npos != pos || std::string::npos != lastPos) {
tokens.push_back(str.substr(lastPos, pos - lastPos));
lastPos = str.find_first_not_of(delims, pos);
pos = str.find_first_of(delims, lastPos);
while( (pos = str.find_first_of(delims, lastPos)) < str.length() )
{
auto token = str.substr(lastPos, pos - lastPos);
if (!trimEmpty || !token.empty()) {
tokens.push_back(token);
}
lastPos = pos + 1;
}
auto token = str.substr(lastPos);
if (!trimEmpty || !token.empty()) {
tokens.push_back(token);
}
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);
return split(m1, m2);
std::stringstream ss;
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)
{

View File

@@ -2,7 +2,7 @@
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>Zim catalog search</ShortName>
<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"
indexOffset="0"
template="/{{root}}/catalog/search?q={searchTerms}&lang={language}&count={count}&start={startIndex}"/>

View File

@@ -124,7 +124,7 @@ label[for=kiwixsearchbox] {
}
body {
padding-top: 40px !important;
padding-top: 3em !important;
}
/* Try to fix buggy stuff in jquery-ui autocomplete */
@@ -132,6 +132,7 @@ body {
.ui-autocomplete {
background: white !important;
border: solid 1px grey !important;
column-count: 1 !important;
}
li.ui-state-focus {

View File

@@ -31,14 +31,14 @@
.book:hover { background-color: #F9F9F9; box-shadow: none;}
.book__background { background-repeat: no-repeat; background-size: auto; background-position: top right; }
.book__title {
padding: 0 55px 0 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
font-size: 18px; color: #0645ad;
padding: 0 55px 0 0;overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
font-size: 18px; color: #0645ad; line-height: 1em;
}
.book__description {
padding: 5px 55px 5px 0px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
font-size: 15px;
font-size: 15px; line-height: 1em;
}
.book__info { line-height: 18px; color: #777; font-weight: bold; font-size: 13px; }
.book__info { color: #777; font-weight: bold; font-size: 13px; line-height: 1em; }
</style>
<script type="text/javascript" src="{{root}}/skin/taskbar.js" async></script>
</head>
@@ -47,15 +47,13 @@
<div class="kiwix">
<div class='book__list'>
{{#books}}
<a href="{{root}}/{{name}}">
<div class='book'>
<a href="{{root}}/{{name}}"><div class='book'>
<div class='book__background' style="background-image: url('{{root}}/meta?content={{#urlencoded}}{{{name}}}{{/urlencoded}}&name=favicon');">
<div class='book__title' title='{{title}}'>{{title}}</div>
<div class='book__description' title='{{description}}'>{{description}}</div>
<div class='book__info'>{{articleCount}} articles, {{mediaCount}} medias</div>
</div>
</div>
</a>
</div></a>
{{/books}}
</div>
</div>

View File

@@ -95,7 +95,7 @@
</head>
<body bgcolor="white">
<div class="header">
{{#hasResult}}
{{#hasResults}}
Results
<b>
{{resultStart}}-{{resultEnd}}
@@ -104,10 +104,10 @@
</b> for <b>
{{searchPattern}}
</b>
{{/hasResult}}
{{^hasResult}}
{{/hasResults}}
{{^hasResults}}
No results were found for <b>{{searchPattern}}</b>
{{/hasResult}}
{{/hasResults}}
</div>
<div class="results">
@@ -129,30 +129,32 @@
</div>
<div class="footer">
<ul>
{{#resultLastPageStart}}
<li>
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start=0&end={{resultRange}}">
</a>
</li>
{{/resultLastPageStart}}
{{#pages}}
<li>
<a {{#selected}}class="selected"{{/selected}}
href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{start}}&end={{end}}">
{{label}}
</a>
</li>
{{/pages}}
{{#resultLastPageStart}}
<li>
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{resultLastPageStart}}&end={{lastResult}}">
</a>
</li>
{{/resultLastPageStart}}
</ul>
{{#hasPages}}
<ul>
{{#resultLastPageStart}}
<li>
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start=0&end={{resultRange}}">
</a>
</li>
{{/resultLastPageStart}}
{{#pages}}
<li>
<a {{#selected}}class="selected"{{/selected}}
href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{start}}&end={{end}}">
{{label}}
</a>
</li>
{{/pages}}
{{#resultLastPageStart}}
<li>
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{resultLastPageStart}}&end={{lastResult}}">
</a>
</li>
{{/resultLastPageStart}}
</ul>
{{/hasPages}}
</div>
</body>
</html>

View File

@@ -23,4 +23,3 @@
</div>
</span>
</span>
<div style="display: block; height: 5em;"></div>

18
test/kiwixserve.cpp Normal file
View File

@@ -0,0 +1,18 @@
#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);
}
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -180,6 +180,7 @@ const char * sampleOpdsStream = R"(
#include "../include/library.h"
#include "../include/manager.h"
#include "../include/bookmark.h"
namespace
{
@@ -191,9 +192,34 @@ class LibraryTest : public ::testing::Test {
manager.readOpds(sampleOpdsStream, "foo.urlHost");
}
kiwix::Bookmark createBookmark(const std::string &id) {
kiwix::Bookmark bookmark;
bookmark.setBookId(id);
return bookmark;
};
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)
{
EXPECT_EQ(lib.getBookCount(true, true), 10U);

View File

@@ -3,7 +3,11 @@
tests = [
'parseUrl',
'library',
'regex'
'regex',
'tagParsing',
'stringTools',
'pathTools',
'kiwixserve'
]

234
test/pathTools.cpp Normal file
View File

@@ -0,0 +1,234 @@
/*
* 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
};
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

61
test/stringTools.cpp Normal file
View File

@@ -0,0 +1,61 @@
/*
* 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);
}
};
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

102
test/tagParsing.cpp Normal file
View File

@@ -0,0 +1,102 @@
/*
* 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");
}
};
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -38,6 +38,10 @@ else
export PKG_CONFIG_PATH=${INSTALL_DIR}/lib/x86_64-linux-gnu/pkgconfig
fi
export CPPFLAGS="-I${INSTALL_DIR}/include"
if [[ "$TEST" == "1" ]]
then
MESON_OPTION="${MESON_OPTION} -Db_coverage=true"
fi
meson . build ${MESON_OPTION}
cd build
ninja
@@ -45,5 +49,9 @@ 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
meson test --verbose
ninja coverage
cd ..
echo "*** Publish code coverage"
bash <(curl -s https://codecov.io/bash)
fi

View File

@@ -8,6 +8,8 @@ REPO_NAME=${TRAVIS_REPO_SLUG#*/}
cd $HOME
if [[ "$TRAVIS_OS_NAME" == "osx" ]]
then
# upgrade wget as openssl is updated by travis
brew upgrade wget
pip3 install meson==0.49.2
wget https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-mac.zip