// handleEmailComment - handles an email received from from with subject subject and body body. // Returns true if the mail was processed, else false. // The email is considered to have been processed if it comments on an existing Consultation Note. bool Notes_Logic::handleEmailComment (string from, string subject, string body) { // Check whether the Consultation Notes Identifier in the subject exists in the notes database. // The CNID looks like: (CNID123456789) size_t pos = subject.find ("(CNID"); if (pos == string::npos) return false; subject = subject.substr (pos + 5); pos = subject.find (")"); if (pos == string::npos) return false; subject = subject.substr (0, pos); // Webserver request. Webserver_Request * request = (Webserver_Request *) webserver_request; // At this stage, the subject contains an identifier. // Check that the identifier is an existing Consultation Note. int identifier = convert_to_int (subject); Database_Notes database_notes (webserver_request); if (!database_notes.identifierExists (identifier)) return false; // Check that the from address of the email belongs to an existing user. // Or else use the obfuscated email address as the user name. string username; from = filter_string_extract_email (from); if (request->database_users()->emailExists (from)) { username = request->database_users()->getEmailToUser (from); } else { username = from; username = filter_string_str_replace ("@", " ", username); username = filter_string_str_replace (".", " ", username); } // Clean the email's body. string year = convert_to_string (filter_date_numerical_year (filter_date_seconds_since_epoch ())); string sender = Database_Config_General::getSiteMailName(); body = filter_string_extract_body (body, year, sender); // Remove any new lines from the body. This cleans up the email considerably, // because some emails that get posted would otherwise look untidy, // when the many new lines take up a lot of space. body = filter_string_str_replace ("\n", " ", body); // Make comment on the consultation note. string sessionuser = request->session_logic ()->currentUser (); request->session_logic ()->setUsername (username); addComment (identifier, body); request->session_logic ()->setUsername (sessionuser); // Mail confirmation to the username. if (request->database_config_user()->getUserNotifyMeOfMyPosts (username)) { Database_Mail database_mail = Database_Mail (webserver_request); string subject = translate("Your comment was posted"); subject.append (" [CNID"); subject.append (convert_to_string (identifier)); subject.append ("]"); database_mail.send (username, subject, body); } // Log operation. Database_Logs::log ("Comment posted: " + body); // Job done. return true; }
void Editor_Export::load (string html) { // The web editor may insert non-breaking spaces. Convert them to normal ones. html = filter_string_str_replace (" ", " ", html); // The web editor produces <hr> following the HTML specs, but Bibledit needs // <hr/> for its XML parser. html = filter_string_str_replace ("<hr>", "<hr/>", html); // The user may add several spaces in sequence. Convert them to single spaces. html = filter_string_str_replace (" ", " ", html); html = filter_string_str_replace (" ", " ", html); // DOMDocument deals well with imperfect markup, but it may throw warnings to the default error handler. // Therefore keep the errors separate. xmlGenericErrorFunc handler = (xmlGenericErrorFunc) error_handler; initGenericErrorDefaultFunc (&handler); // To help loadHTML() process utf8 correctly, set the correct meta tag before any other text. /* string prefix = "<!DOCTYPE html>\n" "<html>\n" "<head>\n" "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n" "</head>\n" "<body>\n"; string suffix = "\n" "</body>\n" "</html>\n"; string xml = prefix + html + suffix; htmlParserCtxtPtr context = htmlNewParserCtxt(); document = htmlCtxtReadMemory (context, xml.c_str(), xml.length(), "", "UTF-8", HTML_PARSE_RECOVER); htmlFreeParserCtxt (context); */ // On Android, the HTML parser fails. It returns a NULL document. // Therefore use the XML parser instead of the HTML one. string prefix = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<html>\n" "<head>\n" "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\"></meta>\n" "</head>\n" "<body>\n"; string suffix = "\n" "</body>\n" "</html>\n"; string xml = prefix + html + suffix; xmlParserCtxtPtr context = xmlNewParserCtxt(); document = xmlCtxtReadMemory (context, xml.c_str(), xml.length(), "", "UTF-8", XML_PARSE_RECOVER); xmlFreeParserCtxt (context); }
void Text_Text::paragraph (string text) { if (thisline != "") { // The filter that converts from USFM to clear texts inserts some stuff // that's being removed here again, as not desirable in clear text. thisline = filter_string_str_replace (en_space(), " ", thisline); thisline = filter_string_str_replace (" ", " ", thisline); output.push_back (thisline); thisline = ""; } addtext (text); }
void sword_logic_log (string message) { // Remove less comely stuff, warnings, confusing information. message = filter_string_str_replace ("-=+*", "", message); message = filter_string_str_replace ("WARNING", "", message); message = filter_string_str_replace ("*+=-", "", message); message = filter_string_str_replace ("enable?", "", message); message = filter_string_str_replace ("[no]", "", message); // Clean message up. message = filter_string_trim (message); // Record in the journal. Database_Logs::log (message); }
/* * The function returns the filename for a html file for a Bible. * $path - The path where to store the files. * $book - The book identifier. * $chapter - The chapter number. */ string filter_url_html_file_name_bible (string path, int book, int chapter) { string filename; // If a path is given, prefix it. if (path != "") { filename = path + "/"; } // No book ID given: Return the name for the index file for the Bible. if (book == 0) { filename += "index.html"; return filename; } // Add the name for the book. No spaces. filename += filter_string_fill (convert_to_string (book), 2, '0'); string sbook = Database_Books::getEnglishFromId (book); sbook = filter_string_str_replace (" ", "", sbook); filename += '-' + sbook; // Chapter given: Provide name for the chaper. if (chapter >= 0) { filename += '-' + filter_string_fill (convert_to_string (chapter), 3, '0'); } filename += ".html"; return filename; }
// C++ equivalent for PHP's escapeshellarg function. string filter_url_escape_shell_argument (string argument) { argument = filter_string_str_replace ("'", "\\'", argument); argument.insert (0, "'"); argument.append ("'"); return argument; }
string filter_passage_clean_passage (string text) { // Trim text. text = filter_string_trim (text); // As references could be, e.g.: Genesis 10.2, or Genesis 10:2, // it needs to convert a the full stop and the colon to a space. text = filter_string_str_replace (".", " ", text); text = filter_string_str_replace (":", " ", text); // Change double spaces into single ones. text = filter_string_str_replace (" ", " ", text); text = filter_string_str_replace (" ", " ", text); // Trim again. text = filter_string_trim (text); // Result. return text; }
void Database_Logs::log (string description, int level) { // No new lines. description = filter_string_str_replace ("\n", " ", description); // Discard empty line. if (filter_string_trim (description).empty()) return; // Truncate long entry. int length = description.length (); if (length > 1000) { description.erase (1000); description.append ("... This entry was too large and has been truncated: " + convert_to_string (length) + " bytes"); } // Save this logbook entry to a filename with seconds and microseconds. string seconds = convert_to_string (filter_date_seconds_since_epoch ()); string time = seconds + filter_string_fill (convert_to_string (filter_date_numerical_microseconds ()), 8, '0'); string file = filter_url_create_path (folder (), time); // The microseconds granularity depends on the platform. // On Windows it is lower than on Linux. // There may be the rare case of more than one entry per file. // Append the data so it won't overwrite an earlier entry. if (file_exists (file)) { description.insert (0, " | "); } else { description.insert (0, convert_to_string (level) + " " + seconds + " "); } filter_url_file_put_contents_append (file, description); // Delay to cover for lower usec granularity on Windows. if (config_logic_windows ()) { this_thread::sleep_for (chrono::milliseconds (1)); } }
// Return the raw data of default versification system $name. string versification_logic_data (string name) { name = filter_string_str_replace (" ", "_", name); name.append (".txt"); string file = filter_url_create_root_path ("versification", name); return filter_url_file_get_contents (file); }
string edit_load (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; string bible = request->query ["bible"]; int book = convert_to_int (request->query ["book"]); int chapter = convert_to_int (request->query ["chapter"]); // Store a copy of the USFM loaded in the editor for later reference. storeLoadedUsfm (webserver_request, bible, book, chapter, "edit"); string stylesheet = request->database_config_user()->getStylesheet (); string usfm = request->database_bibles()->getChapter (bible, book, chapter); Editor_Usfm2Html editor_usfm2html; editor_usfm2html.load (usfm); editor_usfm2html.stylesheet (stylesheet); editor_usfm2html.run (); string html = editor_usfm2html.get (); // To make editing empty verses easier, convert spaces to non-breaking spaces, so they appear in the editor. if (usfm_contains_empty_verses (usfm)) { string search = "<span> </span>"; string replace = "<span>" + unicode_non_breaking_space_entity () + "</span>"; html = filter_string_str_replace (search, replace, html); } string user = request->session_logic ()->currentUser (); bool write = access_bible_book_write (webserver_request, user, bible, book); return Checksum_Logic::send (html, write); }
// This function removes the notes from the USFM. // $usfm: Where to extract from. // $markers: Array of markers surrounding the notes. // It returns the USFM without the notes. string usfm_remove_notes (string usfm, const vector <string> & markers) { vector <UsfmNote> notes = usfm_extract_notes (usfm, markers); for (auto note : notes) { string search = note.data; usfm = filter_string_str_replace (search, "", usfm); } return usfm; }
void Styles_Css::customize (const string& bible) { string cls = Filter_Css::getClass (bible); string font = Fonts_Logic::getTextFont (bible); bool uploaded_font = Fonts_Logic::fontExists (font); font = Fonts_Logic::getFontPath (font); int direction = Database_Config_Bible::getTextDirection (bible); string css = Filter_Css::getCss (cls, font, direction); if (uploaded_font) css = filter_string_str_replace ("../fonts/", "", css); code.push_back (css); }
string Editor_Html2Usfm::cleanUSFM (string usfm) { // Replace a double space after a note opener. for (string noteOpener : noteOpeners) { string opener = usfm_get_opening_usfm (noteOpener); usfm = filter_string_str_replace (opener + " ", opener, usfm); } // Done. return usfm; }
// Returns the string $code as an array alternating between marker and text. // Example, input is: \id GEN // \c 10 // ... // Output would be: array ("\id ", "GEN", "\c ", "10", ...) // If $code does not start with a marker, this becomes visible in the output too. vector <string> usfm_get_markers_and_text (string code) { vector <string> markers_and_text; code = filter_string_str_replace ("\n\\", "\\", code); // New line followed by backslash: leave new line out. code = filter_string_str_replace ("\n", " ", code); // New line only: change to space, according to the USFM specification. // No removal of double spaces, because it would remove an opening marker (which already has its own space), followed by a space. code = filter_string_trim (code); while (!code.empty ()) { size_t pos = code.find ("\\"); if (pos == 0) { // Marker found. // The marker ends // - after the first space, or // - after the first asterisk (*), or // - at the first backslash (\), or // - at the end of the string, // whichever comes first. vector <size_t> positions; pos = code.find (" "); if (pos != string::npos) positions.push_back (pos + 1); pos = code.find ("*"); if (pos != string::npos) positions.push_back (pos + 1); pos = code.find ("\\", 1); if (pos != string::npos) positions.push_back (pos); positions.push_back (code.length()); sort (positions.begin (), positions.end()); pos = positions[0]; string marker = code.substr (0, pos); markers_and_text.push_back (marker); code = code.substr (pos); } else { // Text found. It ends at the next backslash or at the end of the string. pos = code.find ("\\"); if (pos == string::npos) pos = code.length(); string text = code.substr (0, pos); markers_and_text.push_back (text); code = code.substr (pos); } } return markers_and_text; }
void Editor_Html2Usfm::load (string html) { // The web editor may insert non-breaking spaces. Convert them to normal ones. html = filter_string_str_replace (non_breaking_space_entity (), " ", html); // The web editor produces <hr> and other elements following the HTML specs, // but Bibledit's XML parser needs <hr/> and similar elements. html = html2xml (html); // The user may add several spaces in sequence. Convert them to single spaces. html = filter_string_str_replace (" ", " ", html); html = filter_string_str_replace (" ", " ", html); string xml = "<body>\n" + html + "\n</body>"; // Parse document with important option, so it parses the white space in the following: // <node> </node> // This is significant for, for example, the space after verse numbers, among others. xml_parse_result result = document.load_string (xml.c_str(), parse_ws_pcdata_single); // Log parsing errors. pugixml_utils_error_logger (&result, xml); }
void Checks_Sentences::enterNames (string names_in) { names.clear (); names_in = filter_string_str_replace ("\n", " ", names_in); vector <string> names2 = filter_string_explode (names_in, ' '); for (auto name : names2) { if (name != "") { // Limit the length to the left of the suffix in the test. name = unicode_string_substr (name, 0, 11); names.push_back (name); } } }
string index_listing (void * webserver_request, string url) { string page; page = Assets_Page::header ("Bibledit", webserver_request); // No breadcrumbs because the user can arrive here from more than one place. Assets_View view; url = filter_url_urldecode (url); url = filter_url_create_path ("", url); url = filter_string_str_replace ("\\", "/", url); view.set_variable ("url", url); string parent = filter_url_dirname_web (url); if (parent.length () > 1) { view.enable_zone ("parent"); view.set_variable ("parent", parent); } string directory = filter_url_create_root_path (url); if (!file_or_dir_exists (directory) || filter_url_is_dir (directory)) { vector <string> files = filter_url_scandir (directory); for (auto & file : files) { string path = filter_url_create_path (directory, file); string line; line.append ("<tr>"); line.append ("<td>"); line.append ("<a href=\"" + filter_url_create_path (url, file) + "\">"); line.append (file); line.append ("</a>"); line.append ("</td>"); line.append ("<td>"); if (!filter_url_is_dir (path)) { line.append (convert_to_string (filter_url_filesize (path))); } line.append ("</td>"); line.append ("</tr>"); file = line; } string listing = filter_string_implode (files, "\n"); if (listing.empty ()) listing = translate ("No files in this folder"); else { listing.insert (0, "<table>"); listing.append ("</table>"); } view.set_variable ("listing", listing); } else { string filename = filter_url_create_root_path (url); return filter_url_file_get_contents (filename); } page += view.render ("index", "listing"); page += Assets_Page::footer (); return page; }
// Prepares a sample Bible. // The output of this is supposed to be manually put into the source tree, folder "samples". // This will be used to quickly create a sample Bible, that is fast, even on mobile devices. void demo_prepare_sample_bible () { Database_Bibles database_bibles; // Remove the Bible to remove all stuff that might have been in it. database_bibles.deleteBible (demo_sample_bible_name ()); search_logic_delete_bible (demo_sample_bible_name ()); // Create a new one. database_bibles.createBible (demo_sample_bible_name ()); // Location of the USFM files for the sample Bible. string directory = filter_url_create_root_path ("demo"); vector <string> files = filter_url_scandir (directory); for (auto file : files) { // Only process the USFM files. if (filter_url_get_extension (file) == "usfm") { cout << file << endl; // Read the USFM. file = filter_url_create_path (directory, file); string usfm = filter_url_file_get_contents (file); usfm = filter_string_str_replace (" ", " ", usfm); // Import the USFM into the Bible. vector <BookChapterData> book_chapter_data = usfm_import (usfm, styles_logic_standard_sheet ()); for (auto data : book_chapter_data) { Bible_Logic::storeChapter (demo_sample_bible_name (), data.book, data.chapter, data.data); } } } // Clean the destination location for the Bible. string destination = sample_bible_bible_path (); filter_url_rmdir (destination); // Copy the Bible data to the destination. string source = database_bibles.bibleFolder (demo_sample_bible_name ()); filter_url_dir_cp (source, destination); // Clean the destination location for the Bible search index. destination = sample_bible_index_path (); filter_url_rmdir (destination); // Create destination location. filter_url_mkdir (destination); // Copy the index files over to the destination. source = search_logic_index_folder (); files = filter_url_scandir (source); for (auto file : files) { if (file.find (demo_sample_bible_name ()) != string::npos) { string source_file = filter_url_create_path (source, file); string destination_file = filter_url_create_path (destination, file); filter_url_file_cp (source_file, destination_file); } } }
// Get the names of the available default versification systems that come with Bibledit. vector <string> versification_logic_names () { vector <string> names; string directory = filter_url_create_root_path ("versification"); vector <string> files = filter_url_scandir (directory); for (auto file : files) { if (filter_url_get_extension (file) == "txt") { // Remove the dot and extension. file = file.substr (0, file.length () - 4); // Change underscores to spaces for the names. file = filter_string_str_replace ("_", " ", file); names.push_back (file); } } return names; }
// Searches the text of the Bibles. // Returns an array with matching passages. // $search: Contains the text to search for. // $bibles: Array of Bible names to search in. vector <Passage> search_logic_search_text (string search, vector <string> bibles) { vector <Passage> passages; if (search == "") return passages; search = unicode_string_casefold (search); search = filter_string_str_replace (",", "", search); Database_Bibles database_bibles; for (auto bible : bibles) { vector <int> books = database_bibles.getBooks (bible); for (auto book : books) { vector <int> chapters = database_bibles.getChapters (bible, book); for (auto chapter : chapters) { string path = search_logic_chapter_file (bible, book, chapter); string index = filter_url_file_get_contents (path); if (index.find (search) != string::npos) { vector <string> lines = filter_string_explode (index, '\n'); int index_verse = 0; bool read_index_verse = false; int index_item = 0; for (auto & line : lines) { if (read_index_verse) { index_verse = convert_to_int (line); read_index_verse = false; } else if (line == search_logic_verse_separator ()) { read_index_verse = true; index_item = 0; } else if (line == search_logic_index_separator ()) { index_item++; } else if (index_item == PLAIN_LOWER) { if (line.find (search) != string::npos) { passages.push_back (Passage (bible, book, chapter, convert_to_string (index_verse))); } } } } } } } return passages; }
string administration_timezone (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; string page; Assets_Header header = Assets_Header (translate("Timezone"), webserver_request); header.addBreadCrumb (menu_logic_settings_menu (), menu_logic_settings_text ()); page = header.run (); Assets_View view; string success; string error; if (request->post.count ("submit")) { string input = request->post ["timezone"]; input = filter_string_str_replace ("UTC", "", input); int timezone = convert_to_int (input); bool clipped = false; if (timezone < MINIMUM_TIMEZONE) { timezone = MINIMUM_TIMEZONE; clipped = true; } if (timezone > MAXIMUM_TIMEZONE) { timezone = MAXIMUM_TIMEZONE; clipped = true; } if (clipped) error = translate ("The timezone was clipped"); Database_Config_General::setTimezone (timezone); success = translate ("The timezone was saved"); } int timezone = Database_Config_General::getTimezone(); view.set_variable ("timezone", convert_to_string (timezone)); view.set_variable ("success", success); view.set_variable ("error", error); page += view.render ("administration", "timezone"); page += Assets_Page::footer (); return page; }
string render_journal_entry (string entry) { // Remove the user's level. entry.erase (0, 2); // Extract and remove the seconds since the Unix epoch. time_t seconds = convert_to_int (entry); entry.erase (0, 11); // Localize the seconds. seconds = filter_date_local_seconds (seconds); // Sanitize HTML. entry = filter_string_sanitize_html (entry); // Convert \n to <br> entry = filter_string_str_replace ("\n", "<br>", entry); // Convert the seconds into a human readable time. string timestamp = filter_string_fill (convert_to_string (filter_date_numerical_hour (seconds)), 2, '0'); timestamp.append (":"); timestamp.append (filter_string_fill (convert_to_string (filter_date_numerical_minute (seconds)), 2, '0')); timestamp.append (":"); timestamp.append (filter_string_fill (convert_to_string (filter_date_numerical_second (seconds)), 2, '0')); // Done. return timestamp + " | " + entry; }
string editverse_load (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; string bible = request->query ["bible"]; int book = convert_to_int (request->query ["book"]); int chapter = convert_to_int (request->query ["chapter"]); int verse = convert_to_int (request->query ["verse"]); string usfm = request->database_bibles()->getChapter (bible, book, chapter); usfm = usfm_get_verse_text (usfm, verse); usfm = filter_string_str_replace ("\n", "<br>", usfm); string chapter_verse_text; string needle = "\\c"; if (verse) needle = "\\v"; size_t pos = usfm.find (needle); if (pos != string::npos) { if (pos < 2) { usfm.erase (0, pos + 2); usfm = filter_string_trim (usfm); chapter_verse_text = usfm_peek_verse_number (usfm); usfm.erase (0, chapter_verse_text.length ()); usfm = filter_string_trim (usfm); } } usfm.insert (0, "<span contenteditable=\"true\">"); usfm.append ("</span>"); if (!chapter_verse_text.empty ()) { string spacer; if (verse) spacer = " "; usfm.insert (0, "<span " + filter_css_grey_background () + ">" + needle + " " + chapter_verse_text + spacer + "</span>"); } string user = request->session_logic ()->currentUser (); bool write = access_bible_book_write (webserver_request, user, bible, book); return Checksum_Logic::send (usfm, write); }
// Create a consultation note. // $bible: The notes's Bible. // $book, $chapter, $verse: The note's passage. // $summary: The note's summary. // $contents: The note's contents. // $raw: Import $contents as it is. // It returns the $identifier of this new note. int Notes_Logic::createNote (string bible, int book, int chapter, int verse, string summary, string contents, bool raw) { summary = filter_string_str_replace ("\n", "", summary); Database_Notes database_notes (webserver_request); int note_id = database_notes.storeNewNote (bible, book, chapter, verse, summary, contents, raw); if (client_logic_client_enabled ()) { // Client: record the action in the database. Database_NoteActions database_noteactions; Webserver_Request * request = (Webserver_Request *) webserver_request; database_noteactions.record (request->session_logic()->currentUser (), note_id, Sync_Logic::notes_put_create_initiate, ""); database_noteactions.record (request->session_logic()->currentUser (), note_id, Sync_Logic::notes_put_summary, ""); // The contents to submit to the server, take it from the database, as it was updated in the logic above. database_noteactions.record (request->session_logic()->currentUser (), note_id, Sync_Logic::notes_put_contents, database_notes.getContents (note_id)); database_noteactions.record (request->session_logic()->currentUser (), note_id, Sync_Logic::notes_put_bible, ""); database_noteactions.record (request->session_logic()->currentUser (), note_id, Sync_Logic::notes_put_passages, ""); database_noteactions.record (request->session_logic()->currentUser (), note_id, Sync_Logic::notes_put_create_complete, ""); } else { // Server: do the notifications. handlerNewNote (note_id); } return note_id; }
string Editor_Export::cleanUSFM (string usfm) { // The user may accidentally omit or erase the note caller. // The note caller is one character that immediately follows the note opener. // E.g.: \f + ...\f*. // Check for missing note caller, and if it's not there, add the default "+". // Also replace a double space after a note opener. for (string noteOpener : noteOpeners) { string opener = usfm_get_opening_usfm (noteOpener); usfm = filter_string_str_replace (opener + " ", opener, usfm); size_t pos = unicode_string_strpos (usfm, opener); while (pos != string::npos) { bool isClean = true; // Check that the character that follows the note opener is a non-space. size_t pos2 = pos + unicode_string_length (opener); string character = unicode_string_substr (usfm, pos2, 1); if (character == " ") isClean = false; // Check that the following character is not a space. pos2++; character = unicode_string_substr (usfm, pos2, 1); if (character != " ") isClean = false; // Fix the note caller if necessary. if (!isClean) { Database_Logs::log ("Fixing note caller in " + usfm); pos2--; usfm = unicode_string_substr (usfm, 0, pos2) + "+" + unicode_string_substr (usfm, pos2); } // Next iteration. pos = unicode_string_strpos (usfm, opener, pos + 5); } } return usfm; }
// This function takes the html from an editor that edits one verse, // and converts it to USFM. // It properly deals with cases when a verse does not start a new paragraph. string editor_export_verse (string stylesheet, string html) { // When the $html starts with a paragraph without a style, // put a recognizable style there. string style = "oneversestyle"; size_t pos = html.find ("<p>"); if (pos == 0) { html.insert (2, " class=\"" + style + "\""); } // Convert html to USFM. Editor_Html2Usfm editor_export; editor_export.load (html); editor_export.stylesheet (stylesheet); editor_export.run (); string usfm = editor_export.get (); // Remove that recognizable style converted to USFM. usfm = filter_string_str_replace ("\\" + style, "", usfm); usfm = filter_string_trim (usfm); return usfm; }
// handleEmailNew - handles an email received from $from with subject $subject and body $body. // Returns true if the mail was processed, else false. // The email is considered to have been processed if it created a new Consultation Note. bool Notes_Logic::handleEmailNew (string from, string subject, string body) { // Webserver request. Webserver_Request * request = (Webserver_Request *) webserver_request; // Store the original subject. string originalSubject = subject; // Check that the subject indicates that a new consultation note is to be created. size_t pos = unicode_string_casefold (subject).find ("new note"); if (pos == string::npos) return false; // There is a new note. Remove that bit from the subject. if (pos > 0) subject.erase (0, pos + 8); // Clean the subject line. subject = filter_string_trim (subject); subject = filter_string_str_replace (".", " ", subject); subject = filter_string_str_replace (":", " ", subject); subject = filter_string_str_replace (" ", " ", subject); subject = filter_string_str_replace (" ", " ", subject); // Check that the from address of the email belongs to an existing user. from = filter_string_extract_email (from); if (!request->database_users()->emailExists (from)) return false; string username = request->database_users()->getEmailToUser (from); // Extract book, chapter, verse, and note summary from the subject int book = -1; int chapter = -1; int verse = -1; string summary; vector <string> subjectlines = filter_string_explode (subject, ' '); if (!subjectlines.empty()) { book = filter_passage_interpret_book (subjectlines[0]); subjectlines.erase (subjectlines.begin()); } if (!subjectlines.empty()) { chapter = convert_to_int (subjectlines[0]); subjectlines.erase (subjectlines.begin()); } if (!subjectlines.empty()) { verse = convert_to_int (subjectlines[0]); subjectlines.erase (subjectlines.begin()); } summary = filter_string_implode (subjectlines, " "); // Check book, chapter, verse, and summary. Give feedback if there's anything wrong. string noteCheck; if (book <= 0) noteCheck.append (translate("Unknown book")); if (chapter < 0) { noteCheck.append (" "); noteCheck.append (translate("Unknown chapter")); } if (verse < 0) { noteCheck.append (" "); noteCheck.append (translate("Unknown verse")); } if (summary.empty ()) { noteCheck.append (" "); noteCheck.append (translate("Unknown summary")); } // Mail user if the note could not be posted. Database_Mail database_mail = Database_Mail (webserver_request); if (noteCheck != "") { subject = translate("Your new note could not be posted"); database_mail.send (username, subject + ": " + originalSubject, noteCheck); return false; } // Clean the email's body. body = filter_string_extract_body (body); // Post the note. string sessionuser = request->session_logic()->currentUser (); request->session_logic()->setUsername (username); Database_Notes database_notes = Database_Notes(webserver_request); string bible = request->database_config_user()->getBible (); int identifier = database_notes.storeNewNote (bible, book, chapter, verse, summary, body, false); handlerNewNote (identifier); request->session_logic()->setUsername (sessionuser); // Mail confirmation to the username. if (request->database_config_user()->getUserNotifyMeOfMyPosts (username)) { subject = translate("Your new note was posted"); database_mail.send (username, subject + ": " + originalSubject, body); } // Log operation. Database_Logs::log ("New note posted : " + body); // Job done. return true; }
string sync_changes (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Sync_Logic sync_logic = Sync_Logic (webserver_request); Database_Modifications database_modifications; // Check on the credentials. if (!sync_logic.credentials_okay ()) return ""; // Bail out if the change notifications are not now available to clients. if (!config_globals_change_notifications_available) { request->response_code = 503; return ""; } // Client makes a prioritized server call: Record the client's IP address. sync_logic.prioritized_ip_address_record (); // Get the relevant parameters the client may have POSTed to us, the server. string user = hex2bin (request->post ["u"]); int action = convert_to_int (request->post ["a"]); int id = convert_to_int (request->post ["i"]); switch (action) { case Sync_Logic::changes_delete_modification: { // The server deletes the change notification. database_modifications.deleteNotification (id); Database_Logs::log ("Client deletes change notification from server: " + convert_to_string (id), Filter_Roles::translator ()); request->database_config_user ()->setChangeNotificationsChecksum (""); return ""; } case Sync_Logic::changes_get_checksum: { // The server responds with the possibly cached total checksum for the user's change notifications. string checksum = request->database_config_user ()->getChangeNotificationsChecksum (); if (checksum.empty ()) { checksum = Sync_Logic::changes_checksum (user); request->database_config_user ()->setChangeNotificationsChecksum (checksum); } return checksum; } case Sync_Logic::changes_get_identifiers: { // The server responds with the identifiers of all the user's change notifications. vector <int> ids = database_modifications.getNotificationIdentifiers (user, false); string response; for (auto & id : ids) { if (!response.empty ()) response.append ("\n"); response.append (convert_to_string (id)); } return response; } case Sync_Logic::changes_get_modification: { // The server responds with the relevant data of the requested modification. vector <string> lines; // category lines.push_back (database_modifications.getNotificationCategory (id)); // bible lines.push_back (database_modifications.getNotificationBible (id)); // book // chapter // verse Passage passage = database_modifications.getNotificationPassage (id); lines.push_back (convert_to_string (passage.book)); lines.push_back (convert_to_string (passage.chapter)); lines.push_back (passage.verse); // oldtext (ensure it's one line for correct transfer to client) string oldtext = database_modifications.getNotificationOldText (id); oldtext = filter_string_str_replace ("\n", " ", oldtext); lines.push_back (oldtext); // modification (ensure it's one line for correct transfer to client) string modification = database_modifications.getNotificationModification (id); modification = filter_string_str_replace ("\n", " ", modification); lines.push_back (modification); // newtext (ensure it's one line for correct transfer to client) string newtext = database_modifications.getNotificationNewText (id); newtext = filter_string_str_replace ("\n", " ", newtext); lines.push_back (newtext); // Result. return filter_string_implode (lines, "\n"); } } // Bad request. // Delay a while to obstruct a flood of bad requests. this_thread::sleep_for (chrono::seconds (1)); request->response_code = 400; return ""; }
string search_replacepre2 (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; string siteUrl = Database_Config_General::getSiteURL (); // Get search variables from the query. string searchfor = request->query ["q"]; string replacewith = request->query ["r"]; bool casesensitive = (request->query ["c"] == "true"); string id = request->query ["id"]; bool searchplain = (request->query ["p"] == "true"); // Get the Bible and passage for this identifier. Passage details = Passage::from_text (id); string bible = details.bible; int book = details.book; int chapter = details.chapter; string verse = details.verse; // Get the plain text or the USFM. string text; if (searchplain) { text = search_logic_get_bible_verse_text (bible, book, chapter, convert_to_int (verse)); } else { text = search_logic_get_bible_verse_usfm (bible, book, chapter, convert_to_int (verse)); } // Clickable passage. string link = filter_passage_link_for_opening_editor_at (book, chapter, verse); string oldtext = filter_string_markup_words ({searchfor}, text); string newtext (text); if (casesensitive) { newtext = filter_string_str_replace (searchfor, replacewith, newtext); } else { vector <string> needles = filter_string_search_needles (searchfor, text); for (auto & needle : needles) { newtext = filter_string_str_replace (needle, replacewith, newtext); } } if (replacewith != "") newtext = filter_string_markup_words ({replacewith}, newtext); // The id sent to the browser contains bible identifier, book, chapter, and verse. int bibleID = request->database_bibles()->getID (bible); vector <string> bits = {convert_to_string (bibleID), convert_to_string (book), convert_to_string (chapter), verse}; string s_id = filter_string_implode (bits, "_"); // Check whether the user has write access to the book. string user = request->session_logic ()->currentUser (); bool write = access_bible_book_write (webserver_request, user, bible, book); // Create output. string output; output.append ("<div id=\"" + s_id + "\">\n"); output.append ("<p>"); if (write) output.append ("<a href=\"replace\"> ✔ </a> <a href=\"delete\"> ✗ </a> "); output.append (link); output.append ("</p>\n"); output.append ("<p>" + oldtext + "</p>\n"); output.append ("<p>"); if (write) output.append (newtext); else output.append (locale_logic_text_no_privileges_modify_book ()); output.append ("</p>\n"); output.append ("</div>\n"); // Output to browser. return output; }
// This is the most basic version that fetches the text of a $resource. // It works on server and on client. // It uses the cache. string resource_logic_get_verse (void * webserver_request, string resource, int book, int chapter, int verse) { Webserver_Request * request = (Webserver_Request *) webserver_request; string data; Database_UsfmResources database_usfmresources; Database_ImageResources database_imageresources; // Lists of the various types of resources. // As from February 2016 a client no longer automatically downloads USFM resources from the Cloud. // But a client takes in account existing USFM resources it has downloaded before. vector <string> bibles = request->database_bibles()->getBibles (); vector <string> local_usfms = database_usfmresources.getResources (); vector <string> remote_usfms; if (config_logic_client_prepared ()) { remote_usfms = client_logic_usfm_resources_get (); } vector <string> externals = resource_external_names (); vector <string> images = database_imageresources.names (); vector <string> lexicons = lexicon_logic_resource_names (); // Possible SWORD details. string sword_module = sword_logic_get_remote_module (resource); string sword_source = sword_logic_get_source (resource); // Determine the type of the current resource. bool isBible = in_array (resource, bibles); bool isLocalUsfm = in_array (resource, local_usfms); bool isRemoteUsfm = in_array (resource, remote_usfms); bool isExternal = in_array (resource, externals); bool isImage = in_array (resource, images); bool isLexicon = in_array (resource, lexicons); bool isSword = (!sword_source.empty () && !sword_module.empty ()); if (isBible || isLocalUsfm) { string chapter_usfm; if (isBible) chapter_usfm = request->database_bibles()->getChapter (resource, book, chapter); if (isLocalUsfm) chapter_usfm = database_usfmresources.getUsfm (resource, book, chapter); string verse_usfm = usfm_get_verse_text (chapter_usfm, verse); string stylesheet = styles_logic_standard_sheet (); Filter_Text filter_text = Filter_Text (resource); filter_text.html_text_standard = new Html_Text (""); filter_text.addUsfmCode (verse_usfm); filter_text.run (stylesheet); data = filter_text.html_text_standard->getInnerHtml (); } else if (isRemoteUsfm) { data = resource_logic_client_fetch_cache_from_cloud (resource, book, chapter, verse); } else if (isExternal) { if (config_logic_client_prepared ()) { // A client fetches it from the cache or from the Cloud, // or, for older versions, from the offline resources database. // As of 12 December 2015, the offline resources database is not needed anymore. // It can be removed after a year or so. Database_OfflineResources database_offlineresources; if (database_offlineresources.exists (resource, book, chapter, verse)) { data = database_offlineresources.get (resource, book, chapter, verse); } else { data = resource_logic_client_fetch_cache_from_cloud (resource, book, chapter, verse); } } else { // The server fetches it from the web, via the http cache. data.append (resource_external_cloud_fetch_cache_extract (resource, book, chapter, verse)); } } else if (isImage) { vector <string> images = database_imageresources.get (resource, book, chapter, verse); for (auto & image : images) { data.append ("<div><img src=\"/resource/imagefetch?name=" + resource + "&image=" + image + "\" alt=\"Image resource\" style=\"width:100%\"></div>"); } } else if (isLexicon) { data = lexicon_logic_get_html (request, resource, book, chapter, verse); } else if (isSword) { data = sword_logic_get_text (sword_source, sword_module, book, chapter, verse); } else { // Nothing found. } // Any font size given in a paragraph style may interfere with the font size setting for the resources // as given in Bibledit. For that reason remove the class name from a paragraph style. for (unsigned int i = 0; i < 5; i++) { string fragment = "p class=\""; size_t pos = data.find (fragment); if (pos != string::npos) { size_t pos2 = data.find ("\"", pos + fragment.length () + 1); if (pos2 != string::npos) { data.erase (pos + 1, pos2 - pos + 1); } } } // NET Bible updates. data = filter_string_str_replace ("<span class=\"s ", "<span class=\"", data); return data; }