// Returns a warning in case the client is connected to the open demo server. string demo_client_warning () { string warning; if (client_logic_client_enabled ()) { string address = Database_Config_General::getServerAddress (); if (address == demo_address ()) { int port = Database_Config_General::getServerPort (); if (port == demo_port ()) { warning.append (translate("You are connected to a public demo of Bibledit Cloud.")); warning.append (" "); warning.append (translate("Everybody can modify the data on that server.")); warning.append (" "); warning.append (translate("After send and receive your data will reflect the data on the server.")); } } } return warning; }
// The client runs this function to fetch a general resource $name from the Cloud, // or from its local cache, // and to update the local cache with the fetched content, if needed, // and to return the requested content. string resource_logic_client_fetch_cache_from_cloud (string resource, int book, int chapter, int verse) { // Ensure that the cache for this resource exists on the client. if (!Database_Cache::exists (resource, book)) { Database_Cache::create (resource, book); } // If the content exists in the cache, return that content. if (Database_Cache::exists (resource, book, chapter, verse)) { return Database_Cache::retrieve (resource, book, chapter, verse); } // Fetch this resource from Bibledit Cloud or from the cache. string address = Database_Config_General::getServerAddress (); int port = Database_Config_General::getServerPort (); if (!client_logic_client_enabled ()) { // If the client has not been connected to a cloud instance, // fetch the resource from the Bibledit Cloud demo. address = demo_address (); port = demo_port (); } string url = client_logic_url (address, port, sync_resources_url ()); url = filter_url_build_http_query (url, "r", filter_url_urlencode (resource)); url = filter_url_build_http_query (url, "b", convert_to_string (book)); url = filter_url_build_http_query (url, "c", convert_to_string (chapter)); url = filter_url_build_http_query (url, "v", convert_to_string (verse)); string error; string content = filter_url_http_get (url, error); if (error.empty ()) { // No error: Cache content. Database_Cache::cache (resource, book, chapter, verse, content); } else { // Error: Log it, and return it. Database_Logs::log (resource + ": " + error); content.append (error); } // Done. return content; }
void sendreceive_resources () { if (sendreceive_resources_watchdog) { int time = filter_date_seconds_since_epoch (); if (time < (sendreceive_resources_watchdog + 900)) { return; } Database_Logs::log ("Resources: " + translate("Watchdog timeout"), Filter_Roles::translator ()); sendreceive_resources_done (); } // If any of the prioritized synchronization tasks run, postpone the current task and do not start it. if (sendreceive_logic_prioritized_task_is_active ()) { sendreceive_resources_done (); this_thread::sleep_for (chrono::seconds (5)); tasks_logic_queue (SYNCRESOURCES); return; } sendreceive_resources_interrupt = false; // If there's nothing to cache, bail out. vector <string> resources = Database_Config_General::getResourcesToCache (); if (resources.empty ()) return; sendreceive_resources_kick_watchdog (); // Error counter. int error_count = 0; // Resource to cache. string resource = resources [0]; // Erase the two older storage locations that were used to cache resources in earlier versions of Bibledit. { Database_OfflineResources database_offlineresources; Database_UsfmResources database_usfmresources; database_offlineresources.erase (resource); database_usfmresources.deleteResource (resource); } Database_Logs::log ("Starting to install resource:" " " + resource, Filter_Roles::consultant ()); Database_Versifications database_versifications; vector <int> books = database_versifications.getMaximumBooks (); for (auto & book : books) { sendreceive_resources_delay_during_prioritized_tasks (); if (sendreceive_resources_interrupt) continue; // Database layout is per book: Create a database for this book. Database_Cache::create (resource, book); // Last downloaded passage in a previous download operation. int last_downloaded_passage = 0; { pair <int, int> progress = Database_Cache::progress (resource, book); int chapter = progress.first; int verse = progress.second; Passage passage ("", book, chapter, convert_to_string (verse)); last_downloaded_passage = filter_passage_to_integer (passage); } // List of passages recorded in the database that had errors on a previous download operation. vector <int> previous_errors; { vector <pair <int, int> > errors = Database_Cache::errors (resource, book); for (auto & element : errors) { int chapter = element.first; int verse = element.second; Passage passage ("", book, chapter, convert_to_string (verse)); int numeric_error = filter_passage_to_integer (passage); previous_errors.push_back (numeric_error); } } string bookName = Database_Books::getEnglishFromId (book); vector <int> chapters = database_versifications.getMaximumChapters (book); for (auto & chapter : chapters) { sendreceive_resources_delay_during_prioritized_tasks (); if (sendreceive_resources_interrupt) continue; bool downloaded = false; string message = resource + ": " + bookName + " chapter " + convert_to_string (chapter); vector <int> verses = database_versifications.getMaximumVerses (book, chapter); for (auto & verse : verses) { sendreceive_resources_delay_during_prioritized_tasks (); if (sendreceive_resources_interrupt) continue; // Numeric representation of passage to deal with. Passage passage ("", book, chapter, convert_to_string (verse)); int numeric_passage = filter_passage_to_integer (passage); // Conditions to download this verse: // 1. The passage is past the last downloaded passage. bool download_verse_past = numeric_passage > last_downloaded_passage; // 2. The passage was recorded as an error in a previous download operation. bool download_verse_error = in_array (numeric_passage, previous_errors); // Whether to download the verse. if (download_verse_past || download_verse_error) { // Fetch the text for the passage. bool server_is_installing_module = false; int wait_iterations = 0; string html, error; do { // Fetch this resource from the server. string address = Database_Config_General::getServerAddress (); int port = Database_Config_General::getServerPort (); // If the client has not been connected to a cloud instance, // fetch the resource from the Bibledit Cloud demo. if (!client_logic_client_enabled ()) { address = demo_address (); port = demo_port (); } string url = client_logic_url (address, port, sync_resources_url ()); url = filter_url_build_http_query (url, "r", filter_url_urlencode (resource)); url = filter_url_build_http_query (url, "b", convert_to_string (book)); url = filter_url_build_http_query (url, "c", convert_to_string (chapter)); url = filter_url_build_http_query (url, "v", convert_to_string (verse)); error.clear (); html = filter_url_http_get (url, error); server_is_installing_module = (html == sword_logic_installing_module_text ()); if (server_is_installing_module) { Database_Logs::log ("Waiting while Bibledit Cloud installs the requested SWORD module"); this_thread::sleep_for (chrono::seconds (60)); wait_iterations++; } } while (server_is_installing_module && (wait_iterations < 5)); // Record the passage as having been done in case it was a regular download, // rather than one to retry a previous download error. if (download_verse_past) Database_Cache::progress (resource, book, chapter, verse); // Clear the registered error in case the verse download corrects it. if (download_verse_error) Database_Cache::error (resource, book, chapter, verse, false); if (error.empty ()) { // Cache the verse data. if (!Database_Cache::exists (resource, book)) Database_Cache::create (resource, book); Database_Cache::cache (resource, book, chapter, verse, html); } else { // Record an error. Database_Cache::error (resource, book, chapter, verse, true); if (message.find (error) == string::npos) { message.append ("; " + error); } error_count++; this_thread::sleep_for (chrono::seconds (1)); } downloaded = true; } sendreceive_resources_kick_watchdog (); } message += "; done"; if (downloaded) Database_Logs::log (message, Filter_Roles::manager ()); } } // Done. if (error_count) { string msg = "Error count while downloading resource: " + convert_to_string (error_count); Database_Logs::log (msg, Filter_Roles::consultant ()); } Database_Logs::log ("Completed installing resource:" " " + resource, Filter_Roles::consultant ()); // In case of too many errors, schedule the resource download again. bool re_schedule_download = false; if (error_count) { if (!sendreceive_resources_interrupt) { re_schedule_download = true; Database_Logs::log ("Errors: Re-scheduling resource installation", Filter_Roles::consultant ()); } } // Store new download schedule. resources = Database_Config_General::getResourcesToCache (); resources = filter_string_array_diff (resources, {resource}); if (re_schedule_download) { resources.push_back (resource); } Database_Config_General::setResourcesToCache (resources); sendreceive_resources_done (); sendreceive_resources_interrupt = false; // If there's another resource waiting to be cached, schedule it for caching. if (!resources.empty ()) tasks_logic_queue (SYNCRESOURCES); }
string client_index (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Assets_View view; if (request->query.count ("disable")) { client_logic_enable_client (false); client_index_remove_all_users (request); Database_Config_General::setRepeatSendReceive (0); Database_Config_General::setUnsentBibleDataTime (0); Database_Config_General::setUnreceivedBibleDataTime (0); } bool connect = request->post.count ("connect"); bool demo = request->query.count ("demo"); if (connect || demo) { string address; if (connect) address = request->post ["address"]; if (demo) address = demo_address (); if (address.find ("http") == string::npos) address = filter_url_set_scheme (address, false); Database_Config_General::setServerAddress (address); int port = convert_to_int (config_logic_http_network_port ()); if (connect) port = convert_to_int (request->post ["port"]); if (demo) port = demo_port (); Database_Config_General::setServerPort (port); string user; if (connect) user = request->post ["user"]; if (demo) user = session_admin_credentials (); string pass; if (connect) pass = request->post ["pass"]; if (demo) pass = session_admin_credentials (); string response = client_logic_connection_setup (user, md5 (pass)); int iresponse = convert_to_int (response); if ((iresponse >= Filter_Roles::guest ()) && (iresponse <= Filter_Roles::admin ())) { // Enable client mode upon a successful connection. client_index_enable_client (request, user, pass, iresponse); // Feedback. view.set_variable ("success", translate("Connection is okay.")); } else { view.set_variable ("error", translate ("Could not create a connection with Bibledit Cloud") + ": " + response); } } if (client_logic_client_enabled ()) view.enable_zone ("clienton"); else view.enable_zone ("clientoff"); string address = Database_Config_General::getServerAddress (); view.set_variable ("address", address); int port = Database_Config_General::getServerPort (); view.set_variable ("port", convert_to_string (port)); view.set_variable ("url", client_logic_link_to_cloud ("", "")); vector <string> users = request->database_users ()->getUsers (); for (auto & user : users) { int level = request->database_users()->get_level (user); view.set_variable ("role", Filter_Roles::text (level)); } view.set_variable ("demo", demo_client_warning ()); string page; // Since the role of the user may change after a successful connection to the server, // the menu generation in the header should be postponed till when the actual role is known. page = Assets_Page::header (translate ("Server"), webserver_request); page += view.render ("client", "index"); page += Assets_Page::footer (); return page; }
string sword_logic_get_text (string source, string module, int book, int chapter, int verse) { #ifdef HAVE_CLIENT // Client checks for and optionally creates the cache for this SWORD module. if (!Database_Cache::exists (module, book)) { Database_Cache::create (module, book); } // If this module/passage exists in the cache, return it (it updates the access days in the cache). if (Database_Cache::exists (module, book, chapter, verse)) { return Database_Cache::retrieve (module, book, chapter, verse); } // Fetch this SWORD resource from the server. string address = Database_Config_General::getServerAddress (); int port = Database_Config_General::getServerPort (); if (!client_logic_client_enabled ()) { // If the client has not been connected to a cloud instance, // fetch the SWORD content from the Bibledit Cloud demo. address = demo_address (); port = demo_port (); } string url = client_logic_url (address, port, sync_resources_url ()); string resource = "[" + source + "][" + module + "]"; url = filter_url_build_http_query (url, "r", resource); url = filter_url_build_http_query (url, "b", convert_to_string (book)); url = filter_url_build_http_query (url, "c", convert_to_string (chapter)); url = filter_url_build_http_query (url, "v", convert_to_string (verse)); string error; string html = filter_url_http_get (url, error, true); // In case of an error, don't cache that error, but let the user see it. if (!error.empty ()) return error; // Client caches this info for later. // Except in case of predefined responses from the Cloud. if (html != sword_logic_installing_module_text ()) { if (html != sword_logic_fetch_failure_text ()) { Database_Cache::cache (module, book, chapter, verse, html); } } return html; #else string module_text; bool module_available = false; string osis = Database_Books::getOsisFromId (book); string chapter_verse = convert_to_string (chapter) + ":" + convert_to_string (verse); // See notes on function sword_logic_diatheke // for why it is not currently fetching content via a SWORD library call. // module_text = sword_logic_diatheke (module, osis, chapter, verse, module_available); // Running diatheke only works when it runs in the SWORD installation directory. string sword_path = sword_logic_get_path (); // Running several instances of diatheke simultaneously fails. sword_logic_diatheke_run_mutex.lock (); // The server fetches the module text as follows: // diatheke -b KJV -k Jn 3:16 int result = filter_shell_vfork (module_text, sword_path, "diatheke", "-b", module.c_str(), "-k", osis.c_str(), chapter_verse.c_str()); sword_logic_diatheke_run_mutex.unlock (); if (result != 0) return sword_logic_fetch_failure_text (); // Touch the cache so the server knows that the module has been accessed just now. string url = sword_logic_virtual_url (module, 0, 0, 0); database_filebased_cache_get (url); // If the module has not been installed, the output of "diatheke" will be empty. // If the module was installed, but the requested passage is out of range, // the output of "diatheke" contains the module name, so it won't be empty. module_available = !module_text.empty (); if (!module_available) { // Check whether the SWORD module exists. vector <string> modules = sword_logic_get_available (); string smodules = filter_string_implode (modules, ""); if (smodules.find ("[" + module + "]") != string::npos) { // Schedule SWORD module installation. // (It used to be the case that this function, to get the text, // would wait till the SWORD module was installed, and then after installation, // return the text from that module. // But due to long waiting on Bibledit demo, while it would install multiple modules, // the Bibledit demo would become unresponsive. // So, it's better to return immediately with an informative text.) sword_logic_install_module_schedule (source, module); // Return standard 'installing' information. Client knows not to cache this. return sword_logic_installing_module_text (); } else { return "Cannot find SWORD module " + module; } } // Remove any OSIS elements. filter_string_replace_between (module_text, "<", ">", ""); // Remove the passage name that diatheke adds. // A reliable signature for this is the chapter and verse plus subsequent colon. size_t pos = module_text.find (" " + chapter_verse + ":"); if (pos != string::npos) { pos += 2; pos += chapter_verse.size (); module_text.erase (0, pos); } // Remove the module name that diatheke adds. module_text = filter_string_str_replace ("(" + module + ")", "", module_text); // Clean whitespace away. module_text = filter_string_trim (module_text); return module_text; #endif return ""; }