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; }
void sendreceive_changes () { if (sendreceive_changes_watchdog) { int time = filter_date_seconds_since_epoch (); if (time < (sendreceive_changes_watchdog + 900)) { Database_Logs::log (sendreceive_changes_text () + translate("Still busy"), Filter_Roles::translator ()); return; } Database_Logs::log (sendreceive_changes_text () + translate("Watchdog timeout"), Filter_Roles::translator ()); } sendreceive_changes_kick_watchdog (); config_globals_syncing_changes = true; Database_Logs::log (sendreceive_changes_sendreceive_text (), Filter_Roles::translator ()); Webserver_Request request; Sync_Logic sync_logic = Sync_Logic (&request); Database_Modifications database_modifications; if (!database_modifications.healthy ()) { Database_Logs::log (sendreceive_changes_text () + translate("Recreate damaged modifications database"), Filter_Roles::translator ()); database_modifications.erase (); database_modifications.create (); } string response = client_logic_connection_setup (); int iresponse = convert_to_int (response); if (iresponse < Filter_Roles::guest () || iresponse > Filter_Roles::admin ()) { Database_Logs::log (sendreceive_changes_text () + translate("Failure to initiate connection"), Filter_Roles::translator ()); send_receive_changes_done (); return; } // Set the correct user in the session: The sole user on the Client. vector <string> users = request.database_users ()->getUsers (); if (users.empty ()) { Database_Logs::log (translate("No user found"), Filter_Roles::translator ()); send_receive_changes_done (); return; } string user = users [0]; request.session_logic ()->setUsername (user); string password = request.database_users ()->getmd5 (user); // The basic request to be POSTed to the server. // It contains the user's credentials. map <string, string> post; post ["u"] = bin2hex (user); post ["p"] = password; post ["l"] = convert_to_string (request.database_users ()->getUserLevel (user)); // Error variables. string error; bool communication_errors = false; // Server URL to call. string address = Database_Config_General::getServerAddress (); int port = Database_Config_General::getServerPort (); string url = client_logic_url (address, port, sync_changes_url ()); // Send the removed change notifications to the server. vector <int> ids = request.database_config_user ()->getRemovedChanges (); if (!ids.empty ()) Database_Logs::log (sendreceive_changes_text () + "Sending removed notifications: " + convert_to_string (ids.size()), Filter_Roles::translator ()); for (auto & id : ids) { post ["a"] = convert_to_string (Sync_Logic::changes_delete_modification); post ["i"] = convert_to_string (id); response = sync_logic.post (post, url, error); if (!error.empty ()) { communication_errors = true; Database_Logs::log (sendreceive_changes_text () + "Failure sending removed notification: " + error, Filter_Roles::translator ()); } else { request.database_config_user ()->removeRemovedChange (id); } } if (communication_errors) { Database_Logs::log (sendreceive_changes_text () + translate("Not downloading change notifications due to communication error"), Filter_Roles::translator ()); send_receive_changes_done (); return; } // Compare the total checksum for the change notifications for the active user on client and server. // Checksum is cached for future re-use. // Take actions based on that. string client_checksum = request.database_config_user ()->getChangeNotificationsChecksum (); if (client_checksum.empty ()) { client_checksum = Sync_Logic::changes_checksum (user); request.database_config_user ()->setChangeNotificationsChecksum (client_checksum); } string server_checksum; post ["a"] = convert_to_string (Sync_Logic::changes_get_checksum); response = sync_logic.post (post, url, error); if (!error.empty ()) { Database_Logs::log (sendreceive_changes_text () + "Failure receiving checksum: " + error, Filter_Roles::translator ()); send_receive_changes_done (); return; } server_checksum = response; if (client_checksum == server_checksum) { Database_Logs::log (sendreceive_changes_up_to_date_text (), Filter_Roles::translator ()); send_receive_changes_done (); return; } // Get all identifiers for the notifications on the server for the user. // Get the identifiers on the client. vector <int> client_identifiers = database_modifications.getNotificationIdentifiers (user, false); vector <int> server_identifiers; post ["a"] = convert_to_string (Sync_Logic::changes_get_identifiers); response = sync_logic.post (post, url, error); if (!error.empty ()) { Database_Logs::log (sendreceive_changes_text () + "Failure receiving identifiers: " + error, Filter_Roles::translator ()); send_receive_changes_done (); return; } { vector <string> ids = filter_string_explode (response, '\n'); for (auto & id : ids) server_identifiers.push_back (convert_to_int (id)); } // Any identifiers on the client, but not on the server, remove them from the client. vector <int> remove_identifiers = filter_string_array_diff (client_identifiers, server_identifiers); for (auto & id : remove_identifiers) { database_modifications.deleteNotification (id); request.database_config_user ()->setChangeNotificationsChecksum (""); Database_Logs::log (sendreceive_changes_text () + "Removing notification: " + convert_to_string (id), Filter_Roles::translator ()); } // Any identifiers on the server, but not on the client, download them from the server. vector <int> download_identifiers = filter_string_array_diff (server_identifiers, client_identifiers); for (auto & id : download_identifiers) { sendreceive_changes_kick_watchdog (); Database_Logs::log (sendreceive_changes_text () + "Downloading notification: " + convert_to_string (id), Filter_Roles::translator ()); post ["a"] = convert_to_string (Sync_Logic::changes_get_modification); post ["i"] = convert_to_string (id); response = sync_logic.post (post, url, error); if (!error.empty ()) { Database_Logs::log (sendreceive_changes_text () + "Failure downloading notification: " + error, Filter_Roles::translator ()); } else { // The server has put all bits together, one bit per line. vector <string> lines = filter_string_explode (response, '\n'); string category; if (!lines.empty ()) { category = lines [0]; lines.erase (lines.begin ()); } string bible; if (!lines.empty ()) { bible = lines [0]; lines.erase (lines.begin ()); } int book = 0; if (!lines.empty ()) { book = convert_to_int (lines [0]); lines.erase (lines.begin ()); } int chapter = 0; if (!lines.empty ()) { chapter = convert_to_int (lines [0]); lines.erase (lines.begin ()); } int verse = 0; if (!lines.empty ()) { verse = convert_to_int (lines [0]); lines.erase (lines.begin ()); } string oldtext; if (!lines.empty ()) { oldtext = lines [0]; lines.erase (lines.begin ()); } string modification; if (!lines.empty ()) { modification = lines [0]; lines.erase (lines.begin ()); } string newtext; if (!lines.empty ()) { newtext = lines [0]; lines.erase (lines.begin ()); } database_modifications.storeClientNotification (id, user, category, bible, book, chapter, verse, oldtext, modification, newtext); request.database_config_user ()->setChangeNotificationsChecksum (""); } } // Done. Database_Logs::log (sendreceive_changes_text () + "Ready", Filter_Roles::translator ()); send_receive_changes_done (); }