42 Commits
0.3.0 ... 0.4.0

Author SHA1 Message Date
Matthieu Gautier
c6ace1eafc Merge pull request #171 from kiwix/new_version
New version 0.4.0
2018-03-27 12:03:21 +02:00
Matthieu Gautier
8552985cd4 New version 0.4.0 2018-03-27 11:20:21 +02:00
Matthieu Gautier
9997a6ec18 Merge pull request #169 from kiwix/infinit_redirect
Correctly pre-increment the loopCounter.
2018-03-26 10:04:28 +02:00
Matthieu Gautier
8b6abc0e35 Correctly pre-increment the loopCounter.
In case of infinit redirection, if we post-increment the loop counter,
we will exit the loop when loopCounter will be 42 and increment it.
So loop counter will be 43.

Let's pre-increment the counter to still compare with 42 (more consistancy)

Fix #168
2018-03-25 17:07:38 +02:00
Kelson
426796dc37 Merge pull request #135 from kiwix/search_suggestions
Update usage() with --suggestion
2017-12-20 19:54:22 +01:00
kelson42
712cdf5e7b Update usage() with --suggestion 2017-12-20 19:07:38 +01:00
Matthieu Gautier
74cc1d0708 Merge pull request #133 from kiwix/better_search_tool
Better search tool
2017-12-19 15:24:29 +01:00
Matthieu Gautier
3ce668af66 Add a suggestion option to search in suggestion.
There are two kinds of search system :
- Suggestion (search in title only)
- "Classical" search (search in whole content)

The option `--suggestion` allow `kiwix-search` to search for suggestion.
Without the option, a "classical" search is made.

Fix #132.
2017-12-19 14:17:15 +01:00
Matthieu Gautier
2b69cc8ffc Pass verboseFlag to underlying kiwix::Searcher. 2017-12-19 14:13:36 +01:00
Matthieu Gautier
71ffaaa05a Merge pull request #127 from kiwix/compilation_fixes
Compilation fixes
2017-12-13 16:26:57 +00:00
Matthieu Gautier
f3fb5d61cc Use correct option to compile a static binary in travis.
We must use custom option `static-linkage` instead of meson's
`default-library`.
2017-12-13 17:17:28 +01:00
Matthieu Gautier
05c63536de Force usage of meson 0.43.0.
Static compilation is broken with meson 0.44.0.
2017-12-13 16:43:42 +01:00
Matthieu Gautier
16e12ad463 Stop workaround xapian-core.pc
`xapian-core.pc` is now correct stop workaround it adding extra
link args.
The workaround is broken with meson 0.44.0 and static compilation.

However, libmicrohttpd is using `librt` if present but doesn't declare
it in its `libmicrohttpd.pc` file so we must add it manually if we found
it.
2017-12-13 16:43:08 +01:00
Matthieu Gautier
c91285fc29 Merge pull request #123 from kiwix/fix_crash
Fix crash
2017-12-04 16:04:08 +00:00
Matthieu Gautier
5c11ff1a30 Correctly cast to int in debug message.
A `class enum` is not implicitly cast to an `int`.
It is better to explicitly cast it to avoid future error with different
versions of compiler.
2017-12-04 15:12:27 +00:00
Matthieu Gautier
9eaf512bf9 Correctly handle NULL string for request arguments.
When iterating over arguments of a request, the value can be a null pointer
if the argument is provided without a `=`.
https://www.gnu.org/software/libmicrohttpd/manual/html_node/microhttpd_002drequests.html

We have to handle this correctly.

Should fix #116.
2017-12-04 15:10:45 +00:00
Matthieu Gautier
9a29874f9f Better verbose message.
The parsing of the request in the `RequestContext` constructor may be
buggy and make kiwix-serve crash.

If we print debug information only after the request is parsed, we will
never print the debug information if the parsing crash.

It is better to, at least, write that we've got a new request to avoid
us to try to debug previous request where everything were ok.
2017-12-04 15:08:18 +00:00
Matthieu Gautier
aa9647e88c Merge pull request #122 from kiwix/gcc5
Compile using gcc-5 on native ubuntu.
2017-12-04 12:29:59 +00:00
Matthieu Gautier
da0c697963 Compile using gcc-5 on native ubuntu.
As dependencies prepared by kiwix-build are build using gcc-5
(kiwix/kiwix-build@7fc557d), we need to also compile libzim using gcc-5.
2017-12-04 11:15:50 +00:00
Matthieu Gautier
77d25686cb Merge pull request #115 from kiwix/better_search_handling
Better search handling
2017-11-29 16:21:45 +00:00
Matthieu Gautier
3df2fa708b Set the global humanReadableId.
This is needed by kiwix-lib to generate correct "next page" links.
2017-11-27 12:45:41 +00:00
Matthieu Gautier
9021f87715 Handle search with no content and empty query string.
We need to handle :

 - No content because it is how we do multisearch.
 - No query string because it is how we can do geosearch.
2017-11-27 12:44:25 +00:00
Matthieu Gautier
04b05b7902 Merge pull request #111 from kiwix/better_request_parsing
Better request parsing
2017-11-27 11:39:10 +00:00
Matthieu Gautier
a8e73aab98 Move the request context out of kiwix-serve.cpp.
RequestContext is now a "complex" class that handle a lot of thing for
the handlers :
- The rootLocation is correctly handle and remove from the url.
  So a handler doesn't have to care about it (when parsing the url)
- Request arguments and header are stored in a map and there are access
  methods to get it.
- Request arguments can be automatically convert to other type than string.
- Better parsing of the `byte` header. Related to #91.
2017-11-27 11:20:02 +00:00
Matthieu Gautier
234a9d9719 Let the different handlers look for the reader/searcher.
It is to the handlers to get the right reader/searcher they want and
create correct error/default page if the name doesn't correspond to a
zim file.

The `handle_default` function has also be renamed to `build_homepage`
because it is what is done. (And it doesn't handle a request).
2017-11-22 16:16:54 +00:00
Matthieu Gautier
c93617473d Move some variable out of the request's context.
`humanReadableBookId`, `reader` and `searcher` are not so global that
it seems.

Let's move it out of the request object as handler may or not use them.
2017-11-22 15:53:34 +00:00
Matthieu Gautier
09e2039aff Rename variable request_context to request. 2017-11-22 15:47:23 +00:00
Matthieu Gautier
5c6a1870b9 Merge pull request #107 from kiwix/geo_loc
Add a small (private) support for geo query.
2017-11-22 12:46:47 +01:00
Matthieu Gautier
4fffaf41b6 Add a small (private) support for geo query.
This use the small API of kiwix-lib and so, cannot search a text query
and filter around a geo position in the same time.

There is no way to do a search but than write directly the search url
by hand.

If the request is wrongly formatted, the search is simply not done without
error message.
2017-11-14 17:42:01 +01:00
Matthieu Gautier
05c734fc31 Merge pull request #104 from kiwix/library_relative_path
Relative paths are relative to current directory not executable.
2017-11-13 15:14:42 +01:00
Matthieu Gautier
fa9b027a39 Relative paths are relative to current directory not executable.
Almost nothing should be relative to the executable directory.
Content coming from the user should be relative to where the user
is (its working directory).

Fixes #70.
2017-11-13 14:42:52 +01:00
Matthieu Gautier
1fcc1ad9d4 Merge pull request #98 from kiwix/http_byte_range_fix
Fix HTTP request byte range handling #91
2017-11-13 14:42:16 +01:00
kelson42
5fd8dd3c36 Fix HTTP request byte range handling #91 2017-11-13 12:01:45 +01:00
Matthieu Gautier
f03ed85972 Merge pull request #87 from swiftugandan/master
do not cache results for searches from the welcome page
2017-11-09 15:48:15 +01:00
Philip Munaawa
52308b764d do not cache results for searches from the welcome page 2017-11-09 14:24:03 +00:00
Kelson
03bac00019 Merge pull request #99 from kiwix/dattaz-patch_issue_19_good
Dattaz patch issue 19 good
2017-11-08 20:19:57 +01:00
kelson42
905b83b3b1 Small beautification of the code 2017-11-08 20:11:55 +01:00
dattaz
894ed12f37 escape the & character of humanReadableBookId to not be interpreted in urls as a new argument 2017-11-08 19:50:23 +01:00
Kelson
573b826861 Merge pull request #62 from kiwix/julianharty-patch-1
Update kiwix-serve.cpp
2017-11-07 20:01:28 +01:00
Julian Harty
8051773124 Fixed a typo in an error message 2017-11-07 19:10:23 +01:00
Matthieu Gautier
69dd6cd869 Merge pull request #94 from kiwix/man_cleaning
Remove kiwix-index and kiiwx-compact man pages
2017-11-06 15:45:11 +01:00
kelson42
8d32bfd16e Remove kiwix-index and kiiwx-compact man pages 2017-11-06 15:31:06 +01:00
25 changed files with 641 additions and 408 deletions

View File

@@ -2,10 +2,22 @@ language: cpp
dist: trusty
sudo: required
cache: ccache
before_install:
- eval "${MATRIX_EVAL}"
- ${CXX} --version
install: travis/install_deps.sh
script: travis/compile.sh
env:
- PLATFORM="native_static"
- PLATFORM="native_dyn"
- PLATFORM="win32_static"
- PLATFORM="win32_dyn"
global:
- MATRIX_EVAL="CC=gcc-5 && CXX=g++-5"
matrix:
- PLATFORM="native_static"
- PLATFORM="native_dyn"
- PLATFORM="win32_static"
- PLATFORM="win32_dyn"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-5

View File

@@ -1,3 +1,27 @@
kiwix-tools 0.4.0
=================
* Use gcc-5 on travis.
kiwix-serve
-----------
* Accept zim file with `&` in the name
* Do not cache (on client side) global search (as it can depends on the zim
files handled)
* Fix HTTP byte range handling. (#91)
* Fix opening of relative path (#70)
* Add a small (and hidden) API to do geo search.
* Better request parsing. (#91)
* Better handling of invalid request (#116)
* Various bug fixes (#146, #150, #153, #165, #168, #165)
kiwix-search
------------
* Add an option `--suggestion` to do suggestion search with
kiwix-search.(#132)
kiwix-tools 0.3.0
=================

View File

@@ -1,5 +1,5 @@
project('kiwix-tools', 'cpp',
version : '0.3.0',
version : '0.4.0',
license : 'GPL',
default_options: ['c_std=c11', 'cpp_std=c++11'])
@@ -15,22 +15,14 @@ kiwixlib_dep = dependency('kiwix', version:'>=1.0.0', static:static_linkage)
microhttpd_dep = dependency('libmicrohttpd', static:static_linkage)
z_dep = dependency('zlib', static:static_linkage)
# This is a temporary workaround until xapian fix it's xapian-core.pc
# For a correct dependency checking we should test if uuid is really installed
# but for a workaround, we assume that in xapian is installed, uuid also.
if meson.is_cross_build() and host_machine.system() == 'windows'
# xapian doesn't use uuid on windows.
uuid_dep = declare_dependency()
else
xapian_dep = dependency('xapian-core', required : false)
if xapian_dep.found()
uuid_dep = declare_dependency(link_args:['-luuid', '-lrt'])
else
uuid_dep = declare_dependency()
endif
endif
all_deps = [thread_dep, kiwixlib_dep, microhttpd_dep, z_dep]
all_deps = [thread_dep, kiwixlib_dep, microhttpd_dep, z_dep, uuid_dep]
if static_linkage
librt = compiler.find_library('rt', required:false)
if librt.found()
all_deps += librt
endif
endif
#subdir('include')
subdir('static')

View File

@@ -1,23 +0,0 @@
.TH KIWIX 1 "21 May 2012"
.SH NAME
kiwix-compact \- Compacteur d'index pour fichier ZIM
.SH SYNOPSIS
.IX Header SYNOPSIS
kiwix-compact INDEX_PATH
.SH DESCRIPTION
.PP
Compacte les fichiers d'index des ZIM à l'aide de xapian-compact.
.TP
\fBINDEX_PATH\fR
Chemin de l'index plein texte à compacter.
.SH SEE ALSO
kiwix(1) kiwix-index(1) kiwix-manage(1) xapian-compact(1)
.br
kiwix-install(1) kiwix-serve(1)
.SH AUTHOR
Emmanuel Engelhart <kelson@kiwix.org>
.br
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)

View File

@@ -1,31 +0,0 @@
.TH KIWIX 1 "21 May 2012"
.SH NAME
kiwix\-index \- Indexeur de fichiers ZIM
.SH SYNOPSIS
.IX Header SYNOPSIS
kiwix\-index [\-\-backend|\-b=xapian|clucene] ZIM_PATH INDEX_PATH
.SH DESCRIPTION
.PP
Génère un index plein texte xapian ou cLucene pour un fichier ZIM.
.TP
\fB\-\-backend=xapian|clucene\fR
Séléctionne un moteur d'indexation.
.TP
\fBZIM_PATH\fR
Chemin du fichier ZIM à indexer.
.TP
\fBINDEX_PATH\fR
Chemin où enregistrer l'index plein texte.
.SH SEE ALSO
kiwix\-compact(1) kiwix\-manage(1) kiwix\-serve(1)
.br
kiwix(1) kiwix\-install(1)
.SH AUTHOR
Emmanuel Engelhart <kelson@kiwix.org>
.br
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)

View File

@@ -33,8 +33,8 @@ Chemin d'accès dossier kiwix/.
Le chemin doit contenit le binaire statique de Kiwix.
.SH SEE ALSO
kiwix(1) kiwix\-index(1) kiwix\-serve(1) kiwix\-compact(1) kiwix\-manage(1)
kiwix(1) kiwix\-serve(1) kiwix\-manage(1)
.SH AUTHOR
Emmanuel Engelhart <kelson@kiwix.org>
.br
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)

View File

@@ -57,8 +57,8 @@ Chemin vers l'index plein texte correspondant au fichier ZIM.
Définit l'adresse URL corresponsant au fichier ZIM pour pouvoir être téléchargé depuis Kiwix.
.SH SEE ALSO
kiwix(1) kiwix\-index(1) kiwix\-install(1) kiwix\-serve(1) kiwix\-compact(1)
kiwix(1) kiwix\-install(1) kiwix\-serve(1)
.SH AUTHOR
Emmanuel Engelhart <kelson@kiwix.org>
.br
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)

View File

@@ -52,10 +52,10 @@ Chemin vers le fichier bibliothèque de Kiwix.
Le fichier bibliothèque est un fichier XML créé avec \fBkiwix-manage\fB.
.SH SEE ALSO
kiwix(1) kiwix\-index(1) kiwix\-manage(1)
kiwix(1) kiwix\-manage(1)
.br
kiwix\-install(1) kiwix\-compact(1)
kiwix\-install(1)
.SH AUTHOR
Emmanuel Engelhart <kelson@kiwix.org>
.br
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)

View File

@@ -34,9 +34,9 @@ Fonctionnalités:
* Bibliothèque de contenus intégrée
.SH SEE ALSO
kiwix-index(1) kiwix-install(1) kiwix-serve(1)
kiwix\-install(1) kiwix\-serve(1)
.br
kiwix-compact(1) kiwix-manage(1)
kiwix\-manage(1)
.SH TROUBLESHOOTING

View File

@@ -1,5 +1,4 @@
install_man('kiwix-index.1',
'kiwix-install.1',
install_man('kiwix-install.1',
'kiwix-manage.1',
'kiwix-serve.1',
install_dir:get_option('mandir')+'/fr/man1')

View File

@@ -1,23 +0,0 @@
.TH KIWIX 1 "21 May 2012"
.SH NAME
kiwix-compact \- ZIM index compacter
.SH SYNOPSIS
.IX Header SYNOPSIS
kiwix-compact INDEX_PATH
.SH DESCRIPTION
.PP
Compacts ZIM index files used by Kiwix using xapian-compact
.TP
\fBINDEX_PATH\fR
Full-text index path to compact.
.SH SEE ALSO
kiwix(1) kiwix-index(1) kiwix-manage(1) xapian-compact(1)
.br
kiwix-install(1) kiwix-serve(1)
.SH AUTHOR
Emmanuel Engelhart <kelson@kiwix.org>
.br
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)

View File

@@ -1,31 +0,0 @@
.TH KIWIX 1 "21 May 2012"
.SH NAME
kiwix\-index \- Kiwix ZIM Indexer
.SH SYNOPSIS
.IX Header SYNOPSIS
kiwix\-index [\-\-backend|\-b=xapian|clucene] ZIM_PATH INDEX_PATH
.SH DESCRIPTION
.PP
Generates a xapian or clucene full-text index for a ZIM file.
.TP
\fB\-\-backend=xapian|clucene\fR
Select an indexing backend.
.TP
\fBZIM_PATH\fR
ZIM file path to index.
.TP
\fBINDEX_PATH\fR
Path to save the full\-text index to.
.SH SEE ALSO
kiwix\-compact(1) kiwix\-manage(1) kiwix\-serve(1)
.br
kiwix(1) kiwix\-install(1)
.SH AUTHOR
Emmanuel Engelhart <kelson@kiwix.org>
.br
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)

View File

@@ -33,8 +33,8 @@ Path to the kiwix/ folder.
The path must contain the kiwix standalone binary.
.SH SEE ALSO
kiwix(1) kiwix\-index(1) kiwix\-serve(1) kiwix\-compact(1) kiwix\-manage(1)
kiwix(1) kiwix\-serve(1) kiwix\-manage(1)
.SH AUTHOR
Emmanuel Engelhart <kelson@kiwix.org>
.br
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)

View File

@@ -55,8 +55,8 @@ Path to full-text index for that ZIM file.
Set the content location of the ZIM file over the network for in\-kiwix download.
.SH SEE ALSO
kiwix(1) kiwix\-index(1) kiwix\-install(1) kiwix\-serve(1) kiwix\-compact(1)
kiwix(1) kiwix\-install(1) kiwix\-serve(1)
.SH AUTHOR
Emmanuel Engelhart <kelson@kiwix.org>
.br
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)

View File

@@ -13,7 +13,7 @@ Stand\-alone HTTP server for serving ZIM content over the network.
.TP
\fB\-\-index=INDEX_PATH\fR
Path to index folder created using \fBkiwix-index\fB.
Path to index folder created using \fBkiwix-install\fB.
.TP
\fB\-\-port=PORT\fR
@@ -52,10 +52,10 @@ Kiwix library file path.
Library is XML file created using \fBkiwix-manage\fB.
.SH SEE ALSO
kiwix(1) kiwix\-index(1) kiwix\-manage(1)
kiwix(1) kiwix\-manage(1)
.br
kiwix\-install(1) kiwix\-compact(1)
kiwix\-install(1)
.SH AUTHOR
Emmanuel Engelhart <kelson@kiwix.org>
.br
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)

View File

@@ -1,5 +1,4 @@
install_man('kiwix-index.1',
'kiwix-install.1',
install_man('kiwix-install.1',
'kiwix-manage.1',
'kiwix-serve.1')
subdir('fr')

View File

@@ -22,9 +22,16 @@
#include <kiwix/searcher.h>
#include <unistd.h>
void usage()
{
cout << "Usage: kiwix-search [--verbose|-v] ZIM_PATH SEARCH" << endl;
cout << "Usage: kiwix-search [OPTIONS] ZIM PATTERN" << endl << endl
<< " kiwix-search allows to find articles based on the a fulltext search pattern." << endl << endl
<< " ZIM is the full path of the ZIM file. This can also be a disctinct fulltext" << endl
<< " index directory (usually distributed with the *.idx extension)." << endl
<< " PATTERN is/are word(s) - or part of - to search in the ZIM." << endl << endl
<< " -s, --suggestion\tSuggest article titles based on the few letters of the PATTERN instead of making a fulltext search. Work a bit like a completion solution." << endl
<< " -v, --verbose\t\tGive details about the search process" << endl;
exit(1);
}
@@ -37,6 +44,7 @@ int main(int argc, char** argv)
const char* zimPath = NULL;
const char* search = NULL;
bool verboseFlag = false;
bool suggestionFlag = false;
int option_index = 0;
int c = 0;
@@ -46,15 +54,20 @@ int main(int argc, char** argv)
/* Argument parsing */
while (42) {
static struct option long_options[]
= {{"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0}};
= {{"verbose", no_argument, 0, 'v'},
{"suggestion", no_argument, 0, 's'},
{0, 0, 0, 0}};
if (c != -1) {
c = getopt_long(argc, argv, "vb:", long_options, &option_index);
c = getopt_long(argc, argv, "vsb:", long_options, &option_index);
switch (c) {
case 'v':
verboseFlag = true;
break;
case 's':
suggestionFlag = true;
break;
}
} else {
if (optind < argc) {
@@ -100,7 +113,11 @@ int main(int argc, char** argv)
/* Start the indexing */
if (searcher != NULL) {
string searchString(search);
searcher->search(searchString, 0, 10);
if (suggestionFlag) {
searcher->suggestions(searchString, verboseFlag);
} else {
searcher->search(searchString, 0, 10, verboseFlag);
}
kiwix::Result* p_result;
while ((p_result = searcher->getNextResult())) {
cout << p_result->get_title() << endl;

View File

@@ -81,6 +81,8 @@ extern "C" {
#include <unistd.h>
#endif
#include "request_context.h"
#ifdef interface
#undef interface
#endif
@@ -124,35 +126,11 @@ static std::string getMimeTypeForFile(const std::string& filename)
}
struct RequestContext {
struct MHD_Connection* connection;
int httpResponseCode;
kiwix::Reader* reader;
kiwix::Searcher* searcher;
const std::string urlStr;
const std::string humanReadableBookId;
bool acceptEncodingDeflate;
bool acceptRange;
int range_start;
int range_end;
RequestContext(struct MHD_Connection* connection, int httpResponseCode,
kiwix::Reader* reader, kiwix::Searcher* searcher,
const std::string& urlStr, const std::string& humanReadableBookId,
bool acceptEncodingDeflate,
bool acceptRange, int range_start, int range_end) :
connection(connection),
httpResponseCode(httpResponseCode),
reader(reader),
searcher(searcher),
urlStr(urlStr),
humanReadableBookId(humanReadableBookId),
acceptEncodingDeflate(acceptEncodingDeflate),
acceptRange(acceptRange),
range_start(range_start),
range_end(range_end)
{}
};
static bool startswith(const std::string& base, const std::string& start)
{
return start.length() <= base.length()
&& std::equal(start.begin(), start.end(), base.begin());
}
void introduceTaskbar(string& content, const string& humanReadableBookId)
@@ -162,9 +140,7 @@ void introduceTaskbar(string& content, const string& humanReadableBookId)
content = appendToFirstOccurence(
content,
"<head>",
replaceRegex(
RESOURCE::include_html_part, humanReadableBookId, "__CONTENT__")
+ (noLibraryButtonFlag
RESOURCE::include_html_part + (noLibraryButtonFlag
? "<style>#kiwix_serve_taskbar_library_button { display: none }</style>"
: "")
);
@@ -183,6 +159,7 @@ void introduceTaskbar(string& content, const string& humanReadableBookId)
"__CONTENT__"));
}
content = replaceRegex(content, rootLocation, "__ROOT_LOCATION__");
content = replaceRegex(content, replaceRegex(humanReadableBookId, "%26", "&"), "__CONTENT_ESCAPED__");
}
pthread_mutex_unlock(&regexLock);
}
@@ -286,22 +263,34 @@ static struct MHD_Response* build_response(const void* data,
}
static struct MHD_Response* build_404(RequestContext* request_context) {
static struct MHD_Response* build_404(RequestContext* request,
const std::string& humanReadableBookId) {
std::string content
= "<!DOCTYPE html>\n<html><head><meta "
"content=\"text/html;charset=UTF-8\" http-equiv=\"content-type\" "
"/><title>Content not found</title></head><body><h1>Not "
"Found</h1><p>The requested URL \""
+ request_context->urlStr + "\" was not found on this server.</p></body></html>";
+ request->get_full_url() + "\" was not found on this server.</p></body></html>";
auto mimeType = "text/html";
request_context->httpResponseCode = MHD_HTTP_NOT_FOUND;
introduceTaskbar(content, request_context->humanReadableBookId);
request->httpResponseCode = MHD_HTTP_NOT_FOUND;
introduceTaskbar(content, humanReadableBookId);
bool deflated
= request_context->acceptEncodingDeflate && compress_content(content, mimeType);
= request->can_compress() && compress_content(content, mimeType);
return build_response(
content.data(), content.size(), "", mimeType, deflated, false);
}
static struct MHD_Response* build_homepage(RequestContext* request)
{
std::string content = welcomeHTML;
std::string mimeType = "text/html; charset=utf-8";
bool deflated = request->can_compress() && compress_content(content, mimeType);
return build_response(
content.data(), content.size(), "", mimeType, deflated, false);
}
struct RunningResponse {
zim::Article* article;
int range_start;
@@ -381,29 +370,54 @@ static struct MHD_Response* build_callback_response_from_article(
return response;
}
static struct MHD_Response* handle_suggest(RequestContext* request_context)
std::pair<kiwix::Reader*, kiwix::Searcher*>
get_from_humanReadableBookId(const std::string& humanReadableBookId) {
kiwix::Searcher* searcher
= searchers.find(humanReadableBookId) != searchers.end()
? searchers.find(humanReadableBookId)->second
: globalSearcher;
kiwix::Reader* reader = readers.find(humanReadableBookId) != readers.end()
? readers.find(humanReadableBookId)->second
: NULL;
return std::pair<kiwix::Reader*, kiwix::Searcher*>(reader, searcher);
}
static struct MHD_Response* handle_suggest(RequestContext* request)
{
if (isVerbose.load()) {
printf("** running handle_suggest\n");
}
std::string content;
std::string mimeType;
unsigned int maxSuggestionCount = 10;
unsigned int suggestionCount = 0;
std::string suggestion;
/* Get the suggestion pattern from the HTTP request */
const char* cTerm = MHD_lookup_connection_value(
request_context->connection, MHD_GET_ARGUMENT_KIND, "term");
std::string term = cTerm == NULL ? "" : cTerm;
std::string humanReadableBookId;
std::string term;
try {
humanReadableBookId = request->get_argument("content");
term = request->get_argument("term");
} catch (const std::out_of_range&) {
return build_homepage(request);
}
if (isVerbose.load()) {
printf("Searching suggestions for: \"%s\"\n", term.c_str());
}
auto reader_searcher = get_from_humanReadableBookId(humanReadableBookId);
auto reader = reader_searcher.first;
auto searcher = reader_searcher.second;
pthread_mutex_lock(&searchLock);
/* Get the suggestions */
content = "[";
if (request_context->reader != NULL) {
if (reader != nullptr) {
/* Get the suggestions */
request_context->reader->searchSuggestionsSmart(term, maxSuggestionCount);
while (request_context->reader->getNextSuggestion(suggestion)) {
reader->searchSuggestionsSmart(term, maxSuggestionCount);
while (reader->getNextSuggestion(suggestion)) {
kiwix::stringReplacement(suggestion, "\"", "\\\"");
content += (content == "[" ? "" : ",");
content += "{\"value\":\"" + suggestion + "\",\"label\":\"" + suggestion
@@ -414,129 +428,205 @@ static struct MHD_Response* handle_suggest(RequestContext* request_context)
pthread_mutex_unlock(&searchLock);
/* Propose the fulltext search if possible */
if (request_context->searcher != NULL) {
if (searcher != NULL) {
content += (suggestionCount == 0 ? "" : ",");
content += "{\"value\":\"" + std::string(term)
+ " \", \"label\":\"containing '" + std::string(term)
content += "{\"value\":\"" + term
+ " \", \"label\":\"containing '" + term
+ "'...\"}";
}
content += "]";
mimeType = "application/json; charset=utf-8";
bool deflated = request_context->acceptEncodingDeflate && compress_content(content, mimeType);
bool deflated = request->can_compress() && compress_content(content, mimeType);
return build_response(
content.data(), content.size(), "", mimeType, deflated, true);
}
static struct MHD_Response* handle_skin(RequestContext* request_context)
static struct MHD_Response* handle_skin(RequestContext* request)
{
std::string content;
try {
content = getResource(request_context->urlStr.substr(rootLocation.size() + 6));
} catch (const ResourceNotFound& e) {
return build_404(request_context);
if (isVerbose.load()) {
printf("** running handle_skin\n");
}
std::string mimeType = getMimeTypeForFile(request_context->urlStr);
bool deflated = request_context->acceptEncodingDeflate && compress_content(content, mimeType);
std::string content;
auto resourceName = request->get_url().substr(6);
try {
content = getResource(resourceName);
} catch (const ResourceNotFound& e) {
return build_404(request, "");
}
std::string mimeType = getMimeTypeForFile(resourceName);
bool deflated = request->can_compress() && compress_content(content, mimeType);
return build_response(
content.data(), content.size(), "", mimeType, deflated, true);
}
static struct MHD_Response* handle_search(RequestContext* request_context)
static struct MHD_Response* handle_search(RequestContext* request)
{
if (isVerbose.load()) {
printf("** running handle_search\n");
}
std::string content;
std::string mimeType;
std::string httpRedirection;
/* Retrieve the pattern to search */
const char* pattern = MHD_lookup_connection_value(
request_context->connection, MHD_GET_ARGUMENT_KIND, "pattern");
std::string patternString
= kiwix::urlDecode(pattern == NULL ? "" : string(pattern));
std::string patternCorrespondingUrl;
std::string humanReadableBookId;
std::string patternString;
try {
humanReadableBookId = request->get_argument("content");
} catch (const std::out_of_range&) {}
try {
patternString = request->get_argument("pattern");
} catch (const std::out_of_range&) {}
/* Retrive geo search */
bool has_geo_query = false;
float latitude;
float longitude;
float distance;
try {
latitude = request->get_argument<float>("latitude");
longitude = request->get_argument<float>("longitude");
distance = request->get_argument<float>("distance");
has_geo_query = true;
} catch(const std::out_of_range&) {}
catch(const std::invalid_argument&) {}
/* Search results for searches from the welcome page should not
be cached
*/
auto reader_searcher = get_from_humanReadableBookId(humanReadableBookId);
auto reader = reader_searcher.first;
auto searcher = reader_searcher.second;
bool cacheEnabled = !(searcher == globalSearcher);
/* Try first to load directly the article */
if (request_context->reader != NULL) {
std::vector<std::string> variants = request_context->reader->getTitleVariants(patternString);
std::vector<std::string>::iterator variantsItr = variants.begin();
if (reader != nullptr && !patternString.empty()) {
std::string patternCorrespondingUrl;
auto variants = reader->getTitleVariants(patternString);
auto variantsItr = variants.begin();
while (patternCorrespondingUrl.empty() && variantsItr != variants.end()) {
request_context->reader->getPageUrlFromTitle(*variantsItr, patternCorrespondingUrl);
reader->getPageUrlFromTitle(*variantsItr, patternCorrespondingUrl);
variantsItr++;
}
/* If article found then redirect directly to it */
if (!patternCorrespondingUrl.empty()) {
httpRedirection
= rootLocation + "/" + request_context->humanReadableBookId + "/" + patternCorrespondingUrl;
request_context->httpResponseCode = MHD_HTTP_FOUND;
= rootLocation + "/" + humanReadableBookId + "/" + patternCorrespondingUrl;
request->httpResponseCode = MHD_HTTP_FOUND;
return build_response("", 0, httpRedirection, "", false, true);
}
}
/* Make the search */
if (request_context->searcher != NULL) {
const char* start = MHD_lookup_connection_value(
request_context->connection, MHD_GET_ARGUMENT_KIND, "start");
const char* end = MHD_lookup_connection_value(
request_context->connection, MHD_GET_ARGUMENT_KIND, "end");
unsigned int startNumber = start != NULL ? atoi(start) : 0;
unsigned int endNumber = end != NULL ? atoi(end) : 25;
if (searcher != nullptr &&
(!patternString.empty() || has_geo_query)) {
auto start = 0;
try {
start = request->get_argument<unsigned int>("start");
} catch (const std::exception&) {}
auto end = 25;
try {
end = request->get_argument<unsigned int>("end");
} catch (const std::exception&) {}
/* Get the results */
pthread_mutex_lock(&searchLock);
try {
request_context->searcher->search(patternString, startNumber, endNumber, isVerbose.load());
content = request_context->searcher->getHtml();
if (patternString.empty()) {
searcher->geo_search(latitude, longitude, distance,
start, end, isVerbose.load());
} else {
searcher->search(patternString,
start, end, isVerbose.load());
}
content = searcher->getHtml();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
pthread_mutex_unlock(&searchLock);
} else {
content = "<!DOCTYPE html>\n<html><head><meta content=\"text/html;charset=UTF-8\" http-equiv=\"content-type\" /><title>Fulltext search unavailable</title></head><body><h1>Not Found</h1><p>There is no article with the title <b>\"" + kiwix::encodeDiples(patternString) + "\"</b> and the fulltext search engine is not available for this content.</p></body></html>";
request_context->httpResponseCode = MHD_HTTP_NOT_FOUND;
request->httpResponseCode = MHD_HTTP_NOT_FOUND;
}
mimeType = "text/html; charset=utf-8";
introduceTaskbar(content, request_context->humanReadableBookId);
introduceTaskbar(content, humanReadableBookId);
bool deflated = request_context->acceptEncodingDeflate && compress_content(content, mimeType);
bool deflated = request->can_compress() && compress_content(content, mimeType);
return build_response(content.data(),
content.size(),
httpRedirection,
mimeType,
deflated,
true);
cacheEnabled);
}
static struct MHD_Response* handle_random(RequestContext* request_context)
static struct MHD_Response* handle_random(RequestContext* request)
{
std::string httpRedirection;
request_context->httpResponseCode = MHD_HTTP_FOUND;
if (request_context->reader != NULL) {
std::string randomUrl = request_context->reader->getRandomPageUrl();
httpRedirection
= rootLocation + "/" + request_context->humanReadableBookId + "/" + kiwix::urlEncode(randomUrl);
if (isVerbose.load()) {
printf("** running handle_random\n");
}
std::string httpRedirection;
request->httpResponseCode = MHD_HTTP_FOUND;
std::string humanReadableBookId;
try {
humanReadableBookId = request->get_argument("content");
} catch (const std::out_of_range&) {
return build_homepage(request);
}
auto reader = get_from_humanReadableBookId(humanReadableBookId).first;
if (reader == nullptr) {
return build_homepage(request);
}
std::string randomUrl = reader->getRandomPageUrl();
httpRedirection
= rootLocation + "/" + humanReadableBookId + "/" + kiwix::urlEncode(randomUrl);
return build_response("", 0, httpRedirection, "", false, false);
}
static struct MHD_Response* handle_content(RequestContext* request_context)
static struct MHD_Response* handle_content(RequestContext* request)
{
if (isVerbose.load()) {
printf("** running handle_content\n");
}
std::string baseUrl;
std::string content;
std::string mimeType;
bool found = false;
zim::Article article;
std::string humanReadableBookId;
try {
found = request_context->reader->getArticleObjectByDecodedUrl(request_context->urlStr, article);
humanReadableBookId = request->get_url_part(0);
} catch (const std::out_of_range& e) {
return build_homepage(request);
}
auto reader = get_from_humanReadableBookId(humanReadableBookId).first;
if (reader == nullptr) {
return build_homepage(request);
}
auto urlStr = request->get_url().substr(humanReadableBookId.size()+1);
try {
found = reader->getArticleObjectByDecodedUrl(urlStr, article);
if (found) {
/* If redirect */
unsigned int loopCounter = 0;
while (article.isRedirect() && loopCounter++ < 42) {
while (article.isRedirect() && ++loopCounter < 42) {
article = article.getRedirectArticle();
}
@@ -551,9 +641,9 @@ static struct MHD_Response* handle_content(RequestContext* request_context)
if (!found) {
if (isVerbose.load())
printf("Failed to find %s\n", request_context->urlStr.c_str());
printf("Failed to find %s\n", urlStr.c_str());
return build_404(request_context);
return build_404(request, humanReadableBookId);
}
try {
@@ -563,7 +653,7 @@ static struct MHD_Response* handle_content(RequestContext* request_context)
}
if (isVerbose.load()) {
printf("Found %s\n", request_context->urlStr.c_str());
printf("Found %s\n", urlStr.c_str());
printf("mimeType: %s\n", mimeType.c_str());
}
@@ -580,59 +670,48 @@ static struct MHD_Response* handle_content(RequestContext* request_context)
+ article.getUrl();
pthread_mutex_lock(&regexLock);
content = replaceRegex(content,
"$1$2" + rootLocation + "/" + request_context->humanReadableBookId + "/$3/",
"$1$2" + rootLocation + "/" + humanReadableBookId + "/$3/",
"(href|src)(=[\"|\']{0,1})/([A-Z|\\-])/");
content = replaceRegex(content,
"$1$2" + rootLocation + "/" + request_context->humanReadableBookId + "/$3/",
"$1$2" + rootLocation + "/" + humanReadableBookId + "/$3/",
"(@import[ ]+)([\"|\']{0,1})/([A-Z|\\-])/");
content = replaceRegex(
content,
"<head><base href=\"" + rootLocation + "/" + request_context->humanReadableBookId + baseUrl + "\" />",
"<head><base href=\"" + rootLocation + "/" + humanReadableBookId + baseUrl + "\" />",
"<head>");
pthread_mutex_unlock(&regexLock);
introduceTaskbar(content, request_context->humanReadableBookId);
introduceTaskbar(content, humanReadableBookId);
} else if (mimeType.find("text/css") != string::npos) {
pthread_mutex_lock(&regexLock);
content = replaceRegex(content,
"$1$2" + rootLocation + "/" + request_context->humanReadableBookId + "/$3/",
"$1$2" + rootLocation + "/" + humanReadableBookId + "/$3/",
"(url|URL)(\\([\"|\']{0,1})/([A-Z|\\-])/");
pthread_mutex_unlock(&regexLock);
}
bool deflated
= request_context->acceptEncodingDeflate && compress_content(content, mimeType);
= request->can_compress() && compress_content(content, mimeType);
return build_response(
content.data(), content.size(), "", mimeType, deflated, true);
} else {
int range_len;
if (request_context->range_end == -1) {
range_len = article.getArticleSize() - request_context->range_start;
if (request->get_range().second == -1) {
range_len = article.getArticleSize() - request->get_range().first;
} else {
range_len = request_context->range_end - request_context->range_start;
range_len = request->get_range().second - request->get_range().first;
}
return build_callback_response_from_article(
article,
request_context->range_start,
request->get_range().first,
range_len,
mimeType);
}
}
static struct MHD_Response* handle_default(RequestContext* request_context)
{
std::string content = welcomeHTML;
std::string mimeType = "text/html; charset=utf-8";
bool deflated = request_context->acceptEncodingDeflate && compress_content(content, mimeType);
return build_response(
content.data(), content.size(), "", mimeType, deflated, false);
}
int print_out_key (void *cls, enum MHD_ValueKind kind,
int print_key_value (void *cls, enum MHD_ValueKind kind,
const char *key, const char *value)
{
printf ("%s: %s\n", key, value);
printf (" - %s: '%s'\n", key, value);
return MHD_YES;
}
@@ -645,135 +724,52 @@ static int accessHandlerCallback(void* cls,
size_t* upload_data_size,
void** ptr)
{
/* Unexpected method */
if (0 != strcmp(method, "GET") && 0 != strcmp(method, "POST"))
return MHD_NO;
if (isVerbose.load()) {
if (isVerbose.load() ) {
printf("======================\n");
printf("Requesting : \n");
printf("u : %s\n", url);
printf("m : %s\n", method);
printf("v : %s\n", version);
MHD_get_connection_values (connection, MHD_HEADER_KIND, &print_out_key, NULL);
printf("full_url : %s\n", url);
}
RequestContext request(connection, rootLocation, url, method, version);
/* Check if the response can be compressed */
const char* acceptEncodingHeaderValue = MHD_lookup_connection_value(
connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT_ENCODING);
const bool acceptEncodingDeflate
= acceptEncodingHeaderValue
&& string(acceptEncodingHeaderValue).find("deflate") != string::npos;
/* Check if range is requested. */
const char* acceptRangeHeaderValue = MHD_lookup_connection_value(
connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_RANGE);
bool acceptRange = false;
int range_start = 0;
int range_end = -1;
if (acceptRangeHeaderValue != NULL) {
auto range = std::string(acceptRangeHeaderValue);
if (range.substr(0, 6) == "bytes=")
{
range = range.substr(6);
std::istringstream iss(range);
iss >> range_start;
range = range.substr(iss.tellg());
if (range[0] == '-') {
range = range.substr(1);
if (! range.empty()) {
std::istringstream iss(range);
iss >> range_end;
range = range.substr(iss.tellg());
}
if (range.empty()) {
// Nothing left to read. We are OK.
acceptRange = true;
}
}
}
if (isVerbose.load() ) {
request.print_debug_info();
}
/* Unexpected method */
if (request.get_method() != RequestMethod::GET && request.get_method() != RequestMethod::POST) {
printf("Reject request because of unhandled request method.\n");
printf("----------------------\n");
return MHD_NO;
}
/* Prepare the variables */
struct MHD_Response* response;
int httpResponseCode = acceptRange ? MHD_HTTP_PARTIAL_CONTENT : MHD_HTTP_OK;
std::string urlStr = string(url);
request.httpResponseCode = request.has_range() ? MHD_HTTP_PARTIAL_CONTENT : MHD_HTTP_OK;
/* Get searcher and reader */
std::string humanReadableBookId = "";
if (!rootLocation.empty() && urlStr.substr(0, rootLocation.size() + 1) != rootLocation + "/"){
humanReadableBookId = "";
}
else if (!(urlStr.size() > rootLocation.size() + 5 && urlStr.substr(rootLocation.size() , 6) == "/skin/")) {
if ((urlStr == rootLocation + "/" + "search") || (urlStr == rootLocation + "/" + "suggest")
|| (urlStr == rootLocation + "/" + "random")) {
const char* tmpGetValue = MHD_lookup_connection_value(
connection, MHD_GET_ARGUMENT_KIND, "content");
humanReadableBookId = (tmpGetValue != NULL ? string(tmpGetValue) : "");
if (! request.is_valid_url()) {
response = build_homepage(&request);
} else {
if (startswith(request.get_url(), "/skin/")) {
response = handle_skin(&request);
} else if (request.get_url() == "/search") {
response = handle_search(&request);
} else if (request.get_url() == "/suggest") {
response = handle_suggest(&request);
} else if (request.get_url() == "/random") {
response = handle_random(&request);
} else {
humanReadableBookId = urlStr.substr(rootLocation.size() + 1,
urlStr.find("/", rootLocation.size() + 1) != string::npos
? urlStr.find("/", rootLocation.size() + 1) - (rootLocation.size() + 1)
: urlStr.size() - (rootLocation.size() + 2));
if (!humanReadableBookId.empty()) {
urlStr = urlStr.substr(urlStr.find("/", rootLocation.size() + 1) != string::npos
? urlStr.find("/", rootLocation.size() + 1)
: humanReadableBookId.size());
}
response = handle_content(&request);
}
}
kiwix::Searcher* searcher
= searchers.find(humanReadableBookId) != searchers.end()
? searchers.find(humanReadableBookId)->second
: globalSearcher;
kiwix::Reader* reader = readers.find(humanReadableBookId) != readers.end()
? readers.find(humanReadableBookId)->second
: NULL;
if (reader == NULL) {
humanReadableBookId = "";
}
RequestContext request_context(connection, httpResponseCode,
reader, searcher,
urlStr, humanReadableBookId,
acceptEncodingDeflate,
acceptRange, range_start, range_end);
/* Get suggestions */
if ((urlStr == (rootLocation + "/" + "suggest")) && reader != NULL) {
response = handle_suggest(&request_context);
}
/* Get static skin stuff */
else if (urlStr.size() > rootLocation.size() + 5 && urlStr.substr(rootLocation.size() , 6) == "/skin/") {
response = handle_skin(&request_context);
}
/* Display the search restults */
else if (urlStr == (rootLocation + "/" + "search")) {
response = handle_search(&request_context);
}
/* Display a random article */
else if (urlStr == (rootLocation + "/" + "random")) {
response = handle_random(&request_context);
}
/* Display the content of a ZIM content (article, image, ...) */
else if (reader != NULL) {
response = handle_content(&request_context);
}
/* Display the global Welcome page */
else {
response = handle_default(&request_context);
}
/* Queue the response */
int ret = MHD_queue_response(connection, request_context.httpResponseCode, response);
if (isVerbose.load()) {
printf("Response :\n");
printf("httpResponseCode : %d\n", request.httpResponseCode);
printf("headers :\n");
MHD_get_response_headers(response, print_key_value, nullptr);
printf("----------------------\n");
}
int ret = MHD_queue_response(connection, request.httpResponseCode, response);
MHD_destroy_response(response);
return ret;
@@ -912,9 +908,7 @@ int main(int argc, char** argv)
try {
string libraryPath
= isRelativePath(*itr)
? computeAbsolutePath(removeLastPathElement(
getExecutablePath(), true, false),
*itr)
? computeAbsolutePath(getCurrentDirectory(), *itr)
: *itr;
retVal = libraryManager.readFile(libraryPath, true);
} catch (...) {
@@ -977,7 +971,7 @@ int main(int argc, char** argv)
readers[humanReadableId] = reader;
if ( reader->hasFulltextIndex()) {
kiwix::Searcher* searcher = new kiwix::Searcher();
kiwix::Searcher* searcher = new kiwix::Searcher(humanReadableId);
searcher->setProtocolPrefix(rootLocation + "/");
searcher->setSearchProtocolPrefix(rootLocation + "/" + "search?");
searcher->add_reader(reader, humanReadableId);
@@ -1154,7 +1148,7 @@ int main(int argc, char** argv)
}
if (daemon == NULL) {
cerr << "Unable to instanciate the HTTP daemon. The port " << serverPort
cerr << "Unable to instantiate the HTTP daemon. The port " << serverPort
<< " is maybe already occupied or need more permissions to be open. "
"Please try as root or with a port number higher or equal to 1024."
<< endl;

View File

@@ -1,5 +1,5 @@
sources = ['kiwix-serve.cpp']
sources = ['kiwix-serve.cpp', 'request_context.cpp']
sources += server_resources
executable('kiwix-serve', sources,

View File

@@ -0,0 +1,211 @@
/*
* Copyright 2009-2016 Emmanuel Engelhart <kelson@kiwix.org>
* Copyright 2017 Matthieu Gautier<mgautier@kymeria.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "request_context.h"
#include <string.h>
#include <stdexcept>
#include <sstream>
RequestContext::RequestContext(struct MHD_Connection* connection,
std::string rootLocation,
const std::string& _url,
const std::string& method,
const std::string& version) :
connection(connection),
full_url(_url),
url(_url),
valid_url(true),
version(version),
acceptEncodingDeflate(false),
accept_range(false),
range_pair(0, -1)
{
if (method == "GET") {
this->method = RequestMethod::GET;
} else if (method == "HEAD") {
this->method = RequestMethod::HEAD;
} else if (method == "POST") {
this->method = RequestMethod::POST;
} else if (method == "PUT") {
this->method = RequestMethod::PUT;
} else if (method == "DELETE") {
this->method = RequestMethod::DELETE_;
} else if (method == "CONNECT") {
this->method = RequestMethod::CONNECT;
} else if (method == "OPTIONS") {
this->method = RequestMethod::OPTIONS;
} else if (method == "TRACE") {
this->method = RequestMethod::TRACE;
} else if (method == "PATCH") {
this->method = RequestMethod::PATCH;
} else {
this->method = RequestMethod::OTHER;
}
MHD_get_connection_values(connection, MHD_HEADER_KIND, &RequestContext::fill_header, this);
MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, &RequestContext::fill_argument, this);
valid_url = true;
if (rootLocation.empty()) {
// nothing special to handle.
url = full_url;
} else {
if (full_url.size() > rootLocation.size() &&
full_url.substr(0, rootLocation.size()+1) == rootLocation + "/") {
url = full_url.substr(rootLocation.size());
} else {
valid_url = false;
}
}
try {
acceptEncodingDeflate =
(get_header(MHD_HTTP_HEADER_ACCEPT_ENCODING).find("deflate") != std::string::npos);
} catch (const std::out_of_range&) {}
/*Check if range is requested. */
try {
auto range = get_header(MHD_HTTP_HEADER_RANGE);
int start = 0;
int end = -1;
std::istringstream iss(range);
char c;
iss >> start >> c;
if (iss.good() && c=='-') {
iss >> end;
if (iss.fail()) {
// Something went wrong will extracting.
end = -1;
}
if (iss.eof()) {
accept_range = true;
range_pair = std::pair<int, int>(start, end);
}
}
} catch (const std::out_of_range&) {}
}
RequestContext::~RequestContext()
{}
int RequestContext::fill_header(void *__this, enum MHD_ValueKind kind,
const char *key, const char *value)
{
RequestContext *_this = static_cast<RequestContext*>(__this);
_this->headers[key] = value;
return MHD_YES;
}
int RequestContext::fill_argument(void *__this, enum MHD_ValueKind kind,
const char *key, const char* value)
{
RequestContext *_this = static_cast<RequestContext*>(__this);
_this->arguments[key] = value == nullptr ? "" : value;
return MHD_YES;
}
void RequestContext::print_debug_info() {
printf("method : %s (%d)\n", method==RequestMethod::GET ? "GET" :
method==RequestMethod::POST ? "POST" :
"OTHER", (int)method);
printf("version : %s\n", version.c_str());
printf("headers :\n");
for (auto it=headers.begin(); it!=headers.end(); it++) {
printf(" - %s : '%s'\n", it->first.c_str(), it->second.c_str());
}
printf("arguments :\n");
for (auto it=arguments.begin(); it!=arguments.end(); it++) {
printf(" - %s : '%s'\n", it->first.c_str(), it->second.c_str());
}
printf("Parsed : \n");
printf("url : %s\n", url.c_str());
printf("acceptEncodingDeflate : %d\n", acceptEncodingDeflate);
printf("has_range : %d\n", accept_range);
printf(".............\n");
}
RequestMethod RequestContext::get_method() {
return method;
}
std::string RequestContext::get_url() {
return url;
}
std::string RequestContext::get_url_part(int number) {
size_t start = 1;
while(true) {
auto found = url.find('/', start);
if (number == 0) {
if (found == std::string::npos) {
return url.substr(start);
} else {
return url.substr(start, found-start);
}
} else {
if (found == std::string::npos) {
throw std::out_of_range("No parts");
}
start = found + 1;
number -= 1;
}
}
}
std::string RequestContext::get_full_url() {
return full_url;
}
bool RequestContext::is_valid_url() {
return valid_url;
}
bool RequestContext::has_range() {
return accept_range;
}
std::pair<int, int> RequestContext::get_range() {
return range_pair;
}
template<>
std::string RequestContext::get_argument(const std::string& name) {
return arguments.at(name);
}
template<>
unsigned int RequestContext::get_argument(const std::string& name) {
return std::stoi(arguments.at(name).c_str());
}
template<>
float RequestContext::get_argument(const std::string& name) {
return std::stof(arguments.at(name).c_str());
}
std::string RequestContext::get_header(const std::string& name) {
return headers.at(name);
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2009-2016 Emmanuel Engelhart <kelson@kiwix.org>
* Copyright 2017 Matthieu Gautier<mgautier@kymeria.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef REQUEST_CONTEXT_H
#define REQUEST_CONTEXT_H
#include <string>
#include <map>
#include <stdexcept>
extern "C" {
#include <microhttpd.h>
}
enum class RequestMethod {
GET,
HEAD,
POST,
PUT,
DELETE_,
CONNECT,
OPTIONS,
TRACE,
PATCH,
OTHER
};
class KeyError : public std::runtime_error {};
class IndexError: public std::runtime_error {};
class RequestContext {
public:
RequestContext(struct MHD_Connection* connection,
std::string rootLocation,
const std::string& url,
const std::string& method,
const std::string& version);
~RequestContext();
void print_debug_info();
bool is_valid_url();
std::string get_header(const std::string& name);
template<typename T=std::string>
T get_argument(const std::string& name);
RequestMethod get_method();
std::string get_url();
std::string get_url_part(int part);
std::string get_full_url();
bool has_range();
std::pair<int, int> get_range();
bool can_compress() { return acceptEncodingDeflate; }
// [TODO] Move this to the response builder
int httpResponseCode;
private:
struct MHD_Connection* connection;
std::string full_url;
std::string url;
bool valid_url;
RequestMethod method;
std::string version;
bool acceptEncodingDeflate;
bool accept_range;
std::pair<int, int> range_pair;
std::map<std::string, std::string> headers;
std::map<std::string, std::string> arguments;
static int fill_header(void *, enum MHD_ValueKind, const char*, const char*);
static int fill_argument(void *, enum MHD_ValueKind, const char*, const char*);
};
#endif //REQUEST_CONTEXT_H

View File

@@ -8,7 +8,7 @@
jk(function() {
jk( "#kiwixsearchbox" ).autocomplete({
source: "__ROOT_LOCATION__/suggest?content=__CONTENT__",
source: "__ROOT_LOCATION__/suggest?content=__CONTENT_ESCAPED__",
dataType: "json",
cache: false,

View File

@@ -4,7 +4,7 @@
<div class="kiwix_button_wrapper">
<a id="kiwix_serve_taskbar_library_button" href="__ROOT_LOCATION__/"><button>Library</button></a>
<a id="kiwix_serve_taskbar_home_button" href="__ROOT_LOCATION__/__CONTENT__/"><button>Home</button></a>
<a id="kiwix_serve_taskbar_random_button" href="__ROOT_LOCATION__/random?content=__CONTENT__"><button>Random</button></a>
<a id="kiwix_serve_taskbar_random_button" href="__ROOT_LOCATION__/random?content=__CONTENT_ESCAPED__"><button>Random</button></a>
</div>
<div class="kiwix_searchform">
<form class="kiwixsearch" method="GET" action="__ROOT_LOCATION__/search" id="kiwixsearchform">

View File

@@ -8,24 +8,17 @@ INSTALL_DIR=${BUILD_DIR}/INSTALL
case ${PLATFORM} in
"native_static")
MESON_OPTION="--default-library=static"
MESON_OPTION="-Dstatic-linkage=true"
;;
"native_dyn")
MESON_OPTION="--default-library=shared"
MESON_OPTION=""
;;
"win32_static")
MESON_OPTION="--default-library=static --cross-file ${BUILD_DIR}/meson_cross_file.txt"
MESON_OPTION="-Dstatic-linkage=true --cross-file ${BUILD_DIR}/meson_cross_file.txt"
;;
"win32_dyn")
MESON_OPTION="--default-library=shared --cross-file ${BUILD_DIR}/meson_cross_file.txt"
MESON_OPTION="--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}

View File

@@ -29,7 +29,7 @@ esac
sudo apt-get update -qq
sudo apt-get install -qq python3-pip ${PACKAGES}
sudo pip3 install meson
sudo pip3 install meson==0.43.0
# Ninja
cd $HOME