From 9137590e7a6e29f9d3d512bc8e64e38a850fbef6 Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Thu, 7 Sep 2017 20:17:20 +0200 Subject: Do not blindly overwrite files when saving When reading the data files, compute a cryptographic hash of the file contents and store it. When saving the files later, ensure that the hash still matches the current file contents. If it does not, show a warning to the user and ask whether she wants to execute the merge tool. Signed-off-by: Lukas Fleischer --- src/io.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'src/io.c') diff --git a/src/io.c b/src/io.c index 1233fbe..51205f7 100644 --- a/src/io.c +++ b/src/io.c @@ -85,6 +85,8 @@ HTABLE_PROTOTYPE(ht_keybindings, ht_keybindings_s) load_keys_ht_compare) static int modified = 0; +static char apts_sha1[SHA1_DIGESTLEN * 2 + 1]; +static char todo_sha1[SHA1_DIGESTLEN * 2 + 1]; /* Draw a progress bar while saving, loading or exporting data. */ static void progress_bar(progress_bar_t type, int progress) @@ -467,6 +469,37 @@ static void io_merge_data(void) run_hook("post-save"); } +static int resolve_save_conflict(void) +{ + char *msg_um_asktype = NULL; + const char *msg_um_prefix = + _("Data files were changed since reading:"); + const char *msg_um_overwrite = _("(o)verwrite"); + const char *msg_um_merge = _("(m)erge"); + const char *msg_um_keep = _("(k)eep and cancel"); + const char *msg_um_choice = _("[omk]"); + int ret = 1; + + asprintf(&msg_um_asktype, "%s %s, %s, %s", msg_um_prefix, + msg_um_overwrite, msg_um_merge, msg_um_keep); + + switch (status_ask_choice(msg_um_asktype, msg_um_choice, 3)) { + case 1: + ret = 0; + break; + case 2: + io_merge_data(); + break; + case 3: + /* FALLTHROUGH */ + default: + wins_update(FLAG_STA); + } + + mem_free(msg_um_asktype); + return ret; +} + /* Save the calendar data */ void io_save_cal(enum save_display display) { @@ -475,10 +508,30 @@ void io_save_cal(enum save_display display) _("The data files were successfully saved"); const char *enter = _("Press [ENTER] to continue"); int show_bar; + FILE *fp; + char sha1_new[SHA1_DIGESTLEN * 2 + 1]; + int conflict = 0; if (read_only) return; + if ((fp = fopen(path_apts, "r"))) { + sha1_stream(fp, sha1_new); + fclose(fp); + if (strncmp(sha1_new, apts_sha1, SHA1_DIGESTLEN * 2) != 0) + conflict = 1; + } + + if (!conflict && (fp = fopen(path_todo, "r"))) { + sha1_stream(fp, sha1_new); + fclose(fp); + if (strncmp(sha1_new, todo_sha1, SHA1_DIGESTLEN * 2) != 0) + conflict = 1; + } + + if (conflict && resolve_save_conflict()) + return; + run_hook("pre-save"); pthread_mutex_lock(&io_save_mutex); @@ -551,6 +604,9 @@ void io_load_app(struct item_filter *filter) data_file = fopen(path_apts, "r"); EXIT_IF(data_file == NULL, _("failed to open appointment file")); + sha1_stream(data_file, apts_sha1); + rewind(data_file); + for (;;) { LLIST_INIT(&exc); is_appointment = is_event = is_recursive = 0; @@ -723,6 +779,9 @@ void io_load_todo(struct item_filter *filter) data_file = fopen(path_todo, "r"); EXIT_IF(data_file == NULL, _("failed to open todo file")); + sha1_stream(data_file, todo_sha1); + rewind(data_file); + for (;;) { line++; c = getc(data_file); -- cgit v1.2.3-54-g00ecf