diff options
Diffstat (limited to 'org/spine_search_cgi.org')
| -rw-r--r-- | org/spine_search_cgi.org | 101 |
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 |
