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>
This commit is contained in:
parent
fb1524b4e2
commit
9137590e7a
59
src/io.c
59
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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user