Skip to content

Commit

Permalink
working on performing the encryption and backup in a separate thread …
Browse files Browse the repository at this point in the history
…with verification of the backup before rotating (#2148)
  • Loading branch information
giuspen committed Nov 23, 2022
1 parent f87c8ff commit 9b26fa4
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 88 deletions.
2 changes: 1 addition & 1 deletion src/ct/ct_actions_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ void CtActions::file_save_as()
{
fileSelArgs.curr_folder = currDocFilepath.parent_path();
fs::path suggested_basename = currDocFilepath.filename();
fileSelArgs.curr_file_name = suggested_basename.stem().string() + CtMiscUtil::get_doc_extension(storageSelArgs.ctDocType, storageSelArgs.ctDocEncrypt);
fileSelArgs.curr_file_name = suggested_basename.stem() + CtMiscUtil::get_doc_extension(storageSelArgs.ctDocType, storageSelArgs.ctDocEncrypt);
}
fileSelArgs.filter_name = _("CherryTree Document");
std::string fileExtension = CtMiscUtil::get_doc_extension(storageSelArgs.ctDocType, storageSelArgs.ctDocEncrypt);
Expand Down
9 changes: 4 additions & 5 deletions src/ct/ct_filesystem.cc
Original file line number Diff line number Diff line change
Expand Up @@ -575,18 +575,17 @@ path relative(const path& p, const path& base)
return p;
}

path path::extension() const
std::string path::extension() const
{
std::string name = filename().string();
auto last_pos = name.find_last_of('.');
if (last_pos == std::string::npos || last_pos == name.size() - 1 || last_pos == 0) {
return path("");
} else {
return path(name.begin() + last_pos, name.end());
return "";
}
return name.substr(last_pos);
}

path path::stem() const
std::string path::stem() const
{
if (empty()) return "";
std::string name = filename().string();
Expand Down
4 changes: 2 additions & 2 deletions src/ct/ct_filesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ class path
bool empty() const noexcept { return _path.empty(); }
path filename() const { return Glib::path_get_basename(_path); }
path parent_path() const { return Glib::path_get_dirname(_path); }
path extension() const;
path stem() const;
std::string extension() const;
std::string stem() const;
std::string string_native() const;
std::string string_unix() const;

Expand Down
10 changes: 5 additions & 5 deletions src/ct/ct_imports.cc
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ std::unique_ptr<CtImportedNode> CtHtmlImport::import_file(const fs::path& file)
return nullptr;
}
std::string htmlStr = Glib::file_get_contents(file.string());
auto node = std::make_unique<CtImportedNode>(file, file.stem().string());
auto node = std::make_unique<CtImportedNode>(file, file.stem());
CtHtml2Xml html2xml{_config};
html2xml.set_local_dir(file.parent_path().string());
html2xml.set_outter_xml_doc(node->xml_content.get());
Expand Down Expand Up @@ -421,7 +421,7 @@ std::unique_ptr<CtImportedNode> CtZimImport::import_file(const fs::path& file)
{
if (file.extension() != ".txt") return nullptr;

std::unique_ptr<CtImportedNode> node = std::make_unique<CtImportedNode>(file, file.stem().string());
std::unique_ptr<CtImportedNode> node = std::make_unique<CtImportedNode>(file, file.stem());

const std::string file_contents = Glib::file_get_contents(file.string());

Expand All @@ -446,7 +446,7 @@ std::unique_ptr<CtImportedNode> CtPlainTextImport::import_file(const fs::path& f
try {
std::string converted = Glib::file_get_contents(file.string());
CtStrUtil::convert_if_not_utf8(converted, true/*sanitise*/);
auto node = std::make_unique<CtImportedNode>(file, file.stem().string());
auto node = std::make_unique<CtImportedNode>(file, file.stem());
node->xml_content->create_root_node("root")->add_child("slot")->add_child("rich_text")->add_child_text(converted);
node->node_syntax = CtConst::PLAIN_TEXT_ID;
return node;
Expand All @@ -470,7 +470,7 @@ std::unique_ptr<CtImportedNode> CtMDImport::import_file(const fs::path& file)
_parser->wipe_doc();
_parser->feed(file_contents);

std::unique_ptr<CtImportedNode> node = std::make_unique<CtImportedNode>(file, file.stem().string());
std::unique_ptr<CtImportedNode> node = std::make_unique<CtImportedNode>(file, file.stem());
node->xml_content = _parser->doc().document();

return node;
Expand All @@ -488,7 +488,7 @@ std::unique_ptr<CtImportedNode> CtKeepnoteImport::import_file(const fs::path& fi
CtHtml2Xml parser{_config};
parser.set_local_dir(file.parent_path().string());
parser.feed(keepnoteStr);
auto node = std::make_unique<CtImportedNode>(file, file.parent_path().stem().string());
auto node = std::make_unique<CtImportedNode>(file, file.parent_path().stem());
node->xml_content->create_root_node_by_import(parser.doc().get_root_node());
//spdlog::debug(keepnoteStr);
//spdlog::debug(node->xml_content->write_to_string());
Expand Down
44 changes: 30 additions & 14 deletions src/ct/ct_storage_control.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ std::unique_ptr<CtStorageEntity> get_entity_by_type(CtMainWin* pCtMainWin, CtDoc
std::unique_ptr<CtStorageEntity> storage;
fs::path extracted_file_path = file_path;
try {
if (!fs::is_regular_file(file_path)) throw std::runtime_error("no file");
if (not fs::is_regular_file(file_path)) throw std::runtime_error("no file");

// unpack file if need
if (fs::get_doc_encrypt(file_path) == CtDocEncrypt::True) {
Expand All @@ -67,7 +67,7 @@ std::unique_ptr<CtStorageEntity> get_entity_by_type(CtMainWin* pCtMainWin, CtDoc
storage = get_entity_by_type(pCtMainWin, fs::get_doc_type(file_path));

// load from file
if (!storage->populate_treestore(extracted_file_path, error)) throw std::runtime_error(error);
if (not storage->populate_treestore(extracted_file_path, error)) throw std::runtime_error(error);

// it's ready
CtStorageControl* doc = new CtStorageControl{pCtMainWin};
Expand All @@ -88,6 +88,13 @@ std::unique_ptr<CtStorageEntity> get_entity_by_type(CtMainWin* pCtMainWin, CtDoc
}
}

/*static*/bool CtStorageControl::document_integrity_check_pass(CtMainWin* pCtMainWin, const fs::path& file_path, Glib::ustring& error)
{
std::unique_ptr<CtStorageEntity> storage = get_entity_by_type(pCtMainWin, fs::get_doc_type(file_path));
storage->set_is_dry_run();
return storage->populate_treestore(file_path, error);
}

/*static*/CtStorageControl* CtStorageControl::save_as(CtMainWin* pCtMainWin,
const fs::path& file_path,
const Glib::ustring& password,
Expand Down Expand Up @@ -164,7 +171,7 @@ bool CtStorageControl::save(bool need_vacuum, Glib::ustring &error)

const std::string str_timestamp = std::to_string(g_get_monotonic_time());
fs::path main_backup = _file_path;
main_backup += str_timestamp;
main_backup += (str_timestamp + _file_path.extension());
const bool need_backup = _pCtConfig->backupCopy and _pCtConfig->backupNum > 0;
const bool need_encrypt = _file_path != _extracted_file_path;
try {
Expand Down Expand Up @@ -206,7 +213,7 @@ bool CtStorageControl::save(bool need_vacuum, Glib::ustring &error)
pBackupEncryptData->file_path = _file_path.string();
pBackupEncryptData->main_backup = main_backup.string();
if (need_encrypt) {
pBackupEncryptData->extracted_copy = _extracted_file_path.string() + str_timestamp;
pBackupEncryptData->extracted_copy = _extracted_file_path.string() + (str_timestamp + _extracted_file_path.extension());
_storage->close_connect(); // temporary, because of sqlite keepig the file
if (not fs::copy_file(_extracted_file_path, pBackupEncryptData->extracted_copy)) {
throw std::runtime_error(str::format(_("You Have No Write Access to %s"), _extracted_file_path.parent_path().string()));
Expand Down Expand Up @@ -304,16 +311,6 @@ Glib::RefPtr<Gsv::Buffer> CtStorageControl::get_delayed_text_buffer(const gint64
return true;
}

/*static*/bool CtStorageControl::document_integrity_check_pass(CtMainWin* pCtMainWin, const fs::path& file_path, Glib::ustring& error)
{
CtStorageControl* new_storage = CtStorageControl::load_from(pCtMainWin, file_path, error);
if (new_storage) {
delete new_storage;
return true;
}
return false;
}

CtStorageControl::CtStorageControl(CtMainWin* pCtMainWin)
: _pCtMainWin{pCtMainWin}
, _pCtConfig{pCtMainWin->get_ct_config()}
Expand All @@ -340,6 +337,14 @@ void CtStorageControl::_backupEncryptThread()

// encrypt the file
if (pBackupEncryptData->needEncrypt) {
Glib::ustring error;
if (not CtStorageControl::document_integrity_check_pass(_pCtMainWin, pBackupEncryptData->extracted_copy, error)) {
spdlog::error("{} {}", __FUNCTION__, error.raw());
_pCtMainWin->errorsDEQueue.push_back(_("Failed integrity check of the saved document. Try File-->Save As"));
_pCtMainWin->dispatcherErrorMsg.emit();
continue;
}
spdlog::debug("{} integrity check ok", pBackupEncryptData->extracted_copy);
const bool retValEncrypt = _package_file(pBackupEncryptData->extracted_copy, pBackupEncryptData->file_path, pBackupEncryptData->password);
if (not fs::remove(pBackupEncryptData->extracted_copy)) {
spdlog::debug("Failed to remove {}", pBackupEncryptData->extracted_copy);
Expand All @@ -359,6 +364,17 @@ void CtStorageControl::_backupEncryptThread()
continue;
}

if (not pBackupEncryptData->needEncrypt) {
Glib::ustring error;
if (not CtStorageControl::document_integrity_check_pass(_pCtMainWin, pBackupEncryptData->main_backup, error)) {
spdlog::error("{} {}", __FUNCTION__, error.raw());
_pCtMainWin->errorsDEQueue.push_back(_("Failed integrity check of the saved document. Try File-->Save As"));
_pCtMainWin->dispatcherErrorMsg.emit();
continue;
}
spdlog::debug("{} integrity check ok", pBackupEncryptData->main_backup);
}

// backups with tildas can either be in the same directory where the db is or in a custom backup dir
// main_backup is always in the same directory of the main db
auto get_custom_backup_file = [&]()->std::string {
Expand Down
67 changes: 34 additions & 33 deletions src/ct/ct_storage_sqlite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -224,21 +224,23 @@ void CtStorageSqlite::test_connection()
bool CtStorageSqlite::populate_treestore(const fs::path& file_path, Glib::ustring& error)
{
_close_db();
try
{
try {
// open db
_open_db(file_path);
_file_path = file_path;

if (!_check_database_integrity()) return false;


// load bookmarks
Sqlite3StmtAuto stmt{_pDb, "SELECT node_id FROM bookmark ORDER BY sequence ASC"};
if (stmt.is_bad())
throw std::runtime_error(ERR_SQLITE_PREPV2 + sqlite3_errmsg(_pDb));
while (sqlite3_step(stmt) == SQLITE_ROW)
_pCtMainWin->get_tree_store().bookmarks_add(sqlite3_column_int64(stmt, 0));
while (sqlite3_step(stmt) == SQLITE_ROW) {
const auto bkmrk = sqlite3_column_int64(stmt, 0);
if (not _isDryRun) {
_pCtMainWin->get_tree_store().bookmarks_add(bkmrk);
}
}

// load node tree
std::function<void(guint node_id, const gint64, Gtk::TreeIter)> nodes_from_db;
Expand Down Expand Up @@ -269,17 +271,16 @@ bool CtStorageSqlite::save_treestore(const fs::path& file_path,
const int start_offset/*= 0*/,
const int end_offset/*= -1*/)
{
try
{
try {
// it's the first time (or an export), a new file will be created
if (_pDb == nullptr)
{
if (_pDb == nullptr) {
_open_db(file_path);
_file_path = file_path;

_create_all_tables_in_db();
if ( CtExporting::NONE == exporting or
CtExporting::ALL_TREE == exporting ) {
CtExporting::ALL_TREE == exporting )
{
_write_bookmarks_to_db(_pCtMainWin->get_tree_store().bookmarks_get());
}
CtStorageNodeState node_state;
Expand All @@ -296,7 +297,8 @@ bool CtStorageSqlite::save_treestore(const fs::path& file_path,
save_node_fun = [&](CtTreeIter ct_tree_iter, const gint64 sequence, const gint64 father_id) {
_write_node_to_db(&ct_tree_iter, sequence, father_id, node_state, start_offset, end_offset, &storage_cache);
if ( CtExporting::CURRENT_NODE != exporting and
CtExporting::SELECTED_TEXT != exporting ) {
CtExporting::SELECTED_TEXT != exporting )
{
gint64 child_sequence{0};
CtTreeIter ct_tree_iter_child = ct_tree_iter.first_child();
while (ct_tree_iter_child) {
Expand All @@ -310,7 +312,8 @@ bool CtStorageSqlite::save_treestore(const fs::path& file_path,
// saving nodes
gint64 sequence{0};
if ( CtExporting::NONE == exporting or
CtExporting::ALL_TREE == exporting ) {
CtExporting::ALL_TREE == exporting )
{
CtTreeIter ct_tree_iter = _pCtMainWin->get_tree_store().get_ct_iter_first();
while (ct_tree_iter) {
++sequence;
Expand All @@ -324,8 +327,7 @@ bool CtStorageSqlite::save_treestore(const fs::path& file_path,
}
}
// or need just update some info
else
{
else {
CtStorageCache storage_cache;
storage_cache.generate_cache(_pCtMainWin, &syncPending, false);

Expand All @@ -338,16 +340,16 @@ bool CtStorageSqlite::save_treestore(const fs::path& file_path,
_write_bookmarks_to_db(_pCtMainWin->get_tree_store().bookmarks_get());
}
// update changed nodes
for (const auto& node_pair : syncPending.nodes_to_write_dict)
{
for (const auto& node_pair : syncPending.nodes_to_write_dict) {
CtTreeIter ct_tree_iter = _pCtMainWin->get_tree_store().get_node_from_node_id(node_pair.first);
CtTreeIter ct_tree_iter_parent = ct_tree_iter.parent();
_write_node_to_db(&ct_tree_iter, ct_tree_iter.get_node_sequence(),
ct_tree_iter_parent ? ct_tree_iter_parent.get_node_id() : 0, node_pair.second, 0, -1, &storage_cache);
}
// remove nodes and their sub nodes
for (const auto node_id : syncPending.nodes_to_rm_set)
for (const auto node_id : syncPending.nodes_to_rm_set) {
_remove_db_node_with_children(node_id);
}
}

return true;
Expand All @@ -368,8 +370,7 @@ void CtStorageSqlite::vacuum()
void CtStorageSqlite::_open_db(const fs::path& path)
{
if (_pDb) return;
if (sqlite3_open(path.c_str(), &_pDb) != SQLITE_OK)
{
if (sqlite3_open(path.c_str(), &_pDb) != SQLITE_OK) {
std::string error = sqlite3_errmsg(_pDb);
sqlite3_close(_pDb); // even after error, _pDb is initialized
_pDb = nullptr;
Expand Down Expand Up @@ -423,6 +424,10 @@ Gtk::TreeIter CtStorageSqlite::_node_from_db(gint64 node_id, gint64 sequence, Gt
nodeData.tsCreation = sqlite3_column_int64(*uStmt, 6);
nodeData.tsLastSave = sqlite3_column_int64(*uStmt, 7);

if (_isDryRun) {
return Gtk::TreeIter{};
}

// buffer for imported node should be loaded now because file will be closed
if (new_id != -1) {
nodeData.rTextBuffer = get_delayed_text_buffer(node_id, nodeData.syntax, nodeData.anchoredWidgets);
Expand All @@ -432,34 +437,29 @@ Gtk::TreeIter CtStorageSqlite::_node_from_db(gint64 node_id, gint64 sequence, Gt
}

Glib::RefPtr<Gsv::Buffer> CtStorageSqlite::get_delayed_text_buffer(const gint64& node_id,
const std::string& syntax,
std::list<CtAnchoredWidget*>& widgets) const
const std::string& syntax,
std::list<CtAnchoredWidget*>& widgets) const
{
Sqlite3StmtAuto stmt{_pDb, "SELECT txt, has_codebox, has_table, has_image FROM node WHERE node_id=?"};
if (stmt.is_bad())
{
if (stmt.is_bad()) {
spdlog::error("{}: {}", ERR_SQLITE_PREPV2, sqlite3_errmsg(_pDb));
return Glib::RefPtr<Gsv::Buffer>();
}

sqlite3_bind_int64(stmt, 1, node_id);
if (sqlite3_step(stmt) != SQLITE_ROW)
{
if (sqlite3_step(stmt) != SQLITE_ROW) {
spdlog::error("!! missing node properties for id {}", node_id);
return Glib::RefPtr<Gsv::Buffer>();
}

Glib::RefPtr<Gsv::Buffer> rRetTextBuffer{nullptr};
const char* textContent = safe_sqlite3_column_text(stmt, 0);
if (CtConst::RICH_TEXT_ID != syntax)
{
if (CtConst::RICH_TEXT_ID != syntax) {
rRetTextBuffer = _pCtMainWin->get_new_text_buffer(textContent);
}
else
{
rRetTextBuffer = CtStorageXmlHelper(_pCtMainWin).create_buffer_no_widgets(syntax, textContent);
if (!rRetTextBuffer)
{
else {
rRetTextBuffer = CtStorageXmlHelper{_pCtMainWin}.create_buffer_no_widgets(syntax, textContent);
if (!rRetTextBuffer) {
spdlog::error("!! xml read: {}", textContent);
return rRetTextBuffer;
}
Expand All @@ -469,8 +469,9 @@ Glib::RefPtr<Gsv::Buffer> CtStorageSqlite::get_delayed_text_buffer(const gint64&

widgets.sort([](CtAnchoredWidget* w1, CtAnchoredWidget* w2) { return w1->getOffset() < w2->getOffset(); });
rRetTextBuffer->begin_not_undoable_action();
for (auto widget: widgets)
for (auto widget : widgets) {
widget->insertInTextBuffer(rRetTextBuffer);
}
rRetTextBuffer->end_not_undoable_action();
rRetTextBuffer->set_modified(false);
}
Expand Down

0 comments on commit 9b26fa4

Please sign in to comment.