aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukas Fleischer <lfleischer@calcurse.org>2017-09-07 20:17:20 +0200
committerLukas Fleischer <lfleischer@calcurse.org>2017-09-08 21:08:53 +0200
commit9137590e7a6e29f9d3d512bc8e64e38a850fbef6 (patch)
treef0d56042b52152c3ba59b4758d5f9e695f8853d5
parentfb1524b4e26c55e15c12332fc85b3805645ae228 (diff)
downloadcalcurse-9137590e7a6e29f9d3d512bc8e64e38a850fbef6.tar.gz
calcurse-9137590e7a6e29f9d3d512bc8e64e38a850fbef6.zip
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 <lfleischer@calcurse.org>
-rw-r--r--src/io.c59
1 files changed, 59 insertions, 0 deletions
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);