Example #1
0
// 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;
}
Example #2
0
// 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;
}
Example #3
0
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);
}
Example #4
0
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;
}
Example #5
0
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 "";
}