summaryrefslogtreecommitdiffhomepage
path: root/org/spine_search_cgi.org
diff options
context:
space:
mode:
Diffstat (limited to 'org/spine_search_cgi.org')
-rw-r--r--org/spine_search_cgi.org101
1 files changed, 99 insertions, 2 deletions
diff --git a/org/spine_search_cgi.org b/org/spine_search_cgi.org
index e160c2b..b32277f 100644
--- a/org/spine_search_cgi.org
+++ b/org/spine_search_cgi.org
@@ -82,6 +82,7 @@ version (Posix) {
module spine_search;
@system:
<<spine_search_imports>>
+<<spine_search_schema_version>>
void spine_cgi_search_function(Cgi cgi) {
mixin(import("configuration.txt"));
<<spine_search_init_1>>
@@ -184,7 +185,56 @@ void spine_cgi_search_function(Cgi cgi) {
// <<spine_search_debug_info>>
}
}
- auto db = Database(conf.db_path ~ "/" ~ cv.db_selected);
+ string db_file_with_path = conf.db_path ~ "/" ~ cv.db_selected;
+ /+ ↓ report a missing db rather than let sqlite create an empty one +/
+ if (!(db_file_with_path.exists && db_file_with_path.isFile)) {
+ cgi.write("<hr><p class=\"publication\">search database not found: "
+ ~ cv.db_selected
+ ~ "</p>\n</body>\n");
+ return;
+ }
+ /+ ↓ the search form only ever reads, open read only +/
+ auto db = Database(db_file_with_path, SQLITE_OPEN_READONLY);
+ { /+ ↓ a db that carries neither of the tables searched is not a spine search db +/
+ long _tables_found = 0;
+ try {
+ _tables_found = db.execute(
+ "SELECT count(*) FROM sqlite_master WHERE type = 'table'"
+ ~ " AND name IN ('metadata_and_text', 'doc_objects');"
+ ).oneValue!long;
+ } catch (Exception) {
+ _tables_found = 0;
+ }
+ if (_tables_found < 2) {
+ cgi.write("<hr><p class=\"publication\">not a spine search database: "
+ ~ cv.db_selected
+ ~ "</p><p>expected tables metadata_and_text and doc_objects;"
+ ~ " build the database with: spine --sqlite-db-recreate ... then"
+ ~ " spine --sqlite-update ... </p>\n</body>\n");
+ db.close;
+ return;
+ }
+ }
+ { /+ ↓ db schema version, as stamped by spine in PRAGMA user_version +/
+ int _schema_version = 0;
+ try {
+ _schema_version = db.execute("PRAGMA user_version;").oneValue!int;
+ } catch (Exception) {
+ _schema_version = 0;
+ }
+ if (_schema_version != 0
+ && _schema_version != sqlite_db_schema_version_expected) {
+ cgi.write("<hr><p class=\"publication\">search database schema version "
+ ~ _schema_version.to!string
+ ~ ", this search form expects version "
+ ~ sqlite_db_schema_version_expected.to!string
+ ~ "</p><p>the database needs to be rebuilt by a matching spine:<br>"
+ ~ "spine --sqlite-db-recreate --sqlite-db-path=... --sqlite-db-filename=...<br>"
+ ~ "spine --sqlite-update ... </p>\n</body>\n");
+ db.close;
+ return;
+ }
+ }
{
uint sql_match_offset_counter(T)(T cv) {
sql_match_offset_count += cv.sql_match_limit.to!uint;
@@ -232,6 +282,41 @@ import d2sqlite3;
import std.process : environment;
#+END_SRC
+**** schema version
+
+#+NAME: spine_search_schema_version
+#+BEGIN_SRC d
+import std.file : exists, isFile;
+/+ ↓ sqlite db schema version this search form expects, as stamped by spine in
+ PRAGMA user_version; keep in step with sqlite_db_schema_version in
+ sisudoc-spine src/sisudoc/outputs/io_out/sqlite.d
++/
+enum sqlite_db_schema_version_expected = <<sqlite_collection_search_db_schema_version>>;
+/+ ↓ the db to search is named by a request parameter, so it must be a plain
+ filename within the configured db directory: no directory separators, no
+ parent references, no leading dot; anything else falls back to the default
++/
+string db_filename_sanitised(string _requested, string _default) {
+ import std.algorithm : canFind;
+ if (_requested.length == 0
+ || _requested.canFind("/")
+ || _requested.canFind("\\")
+ || _requested.canFind("..")
+ || _requested[0] == '.') {
+ return _default;
+ }
+ foreach (_c; _requested) {
+ if (!((_c >= 'a' && _c <= 'z')
+ || (_c >= 'A' && _c <= 'Z')
+ || (_c >= '0' && _c <= '9')
+ || _c == '.' || _c == '_' || _c == '-')) {
+ return _default;
+ }
+ }
+ return _requested;
+}
+#+END_SRC
+
**** inits
#+NAME: spine_search_init_1
@@ -295,7 +380,7 @@ conf.query_base_url = conf.http_request_type ~ "://" ~ conf.http_host ~
conf.query_string = environment.get("QUERY_STRING", "");
conf.http_url = environment.get("HTTP_REFERER", conf.query_base_url ~ conf.query_string);
conf.request_method = environment.get("REQUEST_METHOD", "POST");
-conf.db_path = _cfg.db_sqlite_path;
+conf.db_path = environment.get("SPINE_SEARCH_DB_PATH", _cfg.db_sqlite_path);
#+END_SRC
**** cgi val
@@ -1001,6 +1086,11 @@ return val;
#+NAME: spine_search_set_selections
#+BEGIN_SRC d
cv.db_selected = set_value("selected_db", _cfg.db_sqlite_filename); // selected_db_name == db (spine.search.db or whatever)
+/+ ↓ selected_db names a file in the configured db directory, never a path
+ a value carrying a directory separator, a parent reference or a leading
+ dot is discarded in favour of the configured default
++/
+cv.db_selected = db_filename_sanitised(cv.db_selected, _cfg.db_sqlite_filename);
cv.sql_match_limit = set_value("sml", "1000");
cv.sql_match_offset = set_value("smo", "0");
cv.search_text = set_value("sf", "");
@@ -2207,6 +2297,13 @@ _cfg.doc_root_db
/var/www/sqlite/
#+END_SRC
+*** sqlite collection search db schema version
+
+#+NAME: sqlite_collection_search_db_schema_version
+#+BEGIN_SRC text
+<<./sisudoc_spine_version_info_and_doc_header_including_copyright_and_license.org:sqlite_collection_search_db_schema_version()>>
+#+END_SRC
+
*** spine_search configuration.txt
see configuration_cfte.org