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)
|
load_keys_ht_compare)
|
||||||
|
|
||||||
static int modified = 0;
|
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. */
|
/* Draw a progress bar while saving, loading or exporting data. */
|
||||||
static void progress_bar(progress_bar_t type, int progress)
|
static void progress_bar(progress_bar_t type, int progress)
|
||||||
@ -467,6 +469,37 @@ static void io_merge_data(void)
|
|||||||
run_hook("post-save");
|
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 */
|
/* Save the calendar data */
|
||||||
void io_save_cal(enum save_display display)
|
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");
|
_("The data files were successfully saved");
|
||||||
const char *enter = _("Press [ENTER] to continue");
|
const char *enter = _("Press [ENTER] to continue");
|
||||||
int show_bar;
|
int show_bar;
|
||||||
|
FILE *fp;
|
||||||
|
char sha1_new[SHA1_DIGESTLEN * 2 + 1];
|
||||||
|
int conflict = 0;
|
||||||
|
|
||||||
if (read_only)
|
if (read_only)
|
||||||
return;
|
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");
|
run_hook("pre-save");
|
||||||
pthread_mutex_lock(&io_save_mutex);
|
pthread_mutex_lock(&io_save_mutex);
|
||||||
|
|
||||||
@ -551,6 +604,9 @@ void io_load_app(struct item_filter *filter)
|
|||||||
data_file = fopen(path_apts, "r");
|
data_file = fopen(path_apts, "r");
|
||||||
EXIT_IF(data_file == NULL, _("failed to open appointment file"));
|
EXIT_IF(data_file == NULL, _("failed to open appointment file"));
|
||||||
|
|
||||||
|
sha1_stream(data_file, apts_sha1);
|
||||||
|
rewind(data_file);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
LLIST_INIT(&exc);
|
LLIST_INIT(&exc);
|
||||||
is_appointment = is_event = is_recursive = 0;
|
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");
|
data_file = fopen(path_todo, "r");
|
||||||
EXIT_IF(data_file == NULL, _("failed to open todo file"));
|
EXIT_IF(data_file == NULL, _("failed to open todo file"));
|
||||||
|
|
||||||
|
sha1_stream(data_file, todo_sha1);
|
||||||
|
rewind(data_file);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
line++;
|
line++;
|
||||||
c = getc(data_file);
|
c = getc(data_file);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user