Implement secure rc file saving.
authorLaurent Monin <geeqie@norz.org>
Tue, 8 Apr 2008 21:55:58 +0000 (21:55 +0000)
committerLaurent Monin <geeqie@norz.org>
Tue, 8 Apr 2008 21:55:58 +0000 (21:55 +0000)
First data is written to a temporary file, then if nothing
was wrong, this file is renamed to the final name.
This way the risk of corrupted rc file is greatly reduced.
The code is borrowed from ELinks (http://elinks.cz).

configure.in
src/filelist.c
src/filelist.h
src/rcfile.c
src/rcfile.h
src/typedefs.h

index f860488..87c61b1 100644 (file)
@@ -24,7 +24,7 @@ AC_DEFINE_UNQUOTED(GQVIEW_HELPDIR, "$prefix/share/doc/geeqie-$VERSION", [Locatio
 AC_DEFINE_UNQUOTED(GQVIEW_HTMLDIR, "$prefix/share/doc/geeqie-$VERSION/html", [Location of html documentation])
 
 dnl checks for functions
-AC_CHECK_FUNCS(strverscmp)
+AC_CHECK_FUNCS(strverscmp access fsync fflush)
 
 dnl check for little cms (lcms, this test pulled from gimp)
 AC_ARG_WITH(lcms, [  --without-lcms          build without lcms support])
index cb34c4f..e99b81c 100644 (file)
@@ -364,7 +364,7 @@ gint filter_file_class(const gchar *name, FileFormatClass file_class)
        return FALSE;
 }
 
-void filter_write_list(FILE *f)
+void filter_write_list(SecureSaveInfo *ssi)
 {
        GList *work;
 
@@ -377,8 +377,9 @@ void filter_write_list(FILE *f)
                gchar *extensions = escquote_value(fe->extensions);
                gchar *description = escquote_value(fe->description);
 
-               fprintf(f, "filter_ext: \"%s%s\" %s %s\n", (fe->enabled) ? "" : "#",
-                       fe->key, extensions, description);
+               secure_fprintf(ssi, "filter_ext: \"%s%s\" %s %s\n",
+                              (fe->enabled) ? "" : "#",
+                              fe->key, extensions, description);
                g_free(extensions);
                g_free(description);
                }
@@ -490,9 +491,9 @@ void sidecar_ext_parse(const gchar *text, gint quoted)
        g_free(value);
 }
 
-void sidecar_ext_write(FILE *f)
+void sidecar_ext_write(SecureSaveInfo *ssi)
 {
-       fprintf(f, "\nsidecar_ext: \"%s\"\n", sidecar_ext_to_string());
+       secure_fprintf(ssi, "\nsidecar_ext: \"%s\"\n", sidecar_ext_to_string());
 }
 
 char *sidecar_ext_to_string()
index c0cec34..18fa1ec 100644 (file)
@@ -37,11 +37,11 @@ GList *filter_to_list(const gchar *extensions);
 gint filter_name_exists(const gchar *name);
 gint filter_file_class(const gchar *name, FileFormatClass file_class);
 
-void filter_write_list(FILE *f);
+void filter_write_list(SecureSaveInfo *ssi);
 void filter_parse(const gchar *text);
 
 void sidecar_ext_parse(const gchar *text, gint quoted);
-void sidecar_ext_write(FILE *f);
+void sidecar_ext_write(SecureSaveInfo *ssi);
 char *sidecar_ext_to_string(); 
 void sidecar_ext_add_defaults();
 
index c9e6b11..e8c8140 100644 (file)
@@ -9,6 +9,8 @@
  * This software comes with no warranty of any kind, use at your own risk!
  */
 
+#include <glib/gstdio.h>
+#include <errno.h>
 
 #include "gqview.h"
 #include "rcfile.h"
 #include "ui_fileops.h"
 #include "bar_exif.h"
 
+/* ABOUT SECURE SAVE */
+/* This code was borrowed from the ELinks project (http://elinks.cz)
+ * It was originally written by me (Laurent Monin aka Zas) and heavily
+ * modified and improved by all ELinks contributors.
+ * This code was released under the GPLv2 licence.
+ * It was modified to be included in geeqie on 2008/04/05 */
+
+/* If ssi->secure_save is TRUE:
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * A call to secure_open("/home/me/.geeqie/filename", mask) will open a file
+ * named "filename.tmp_XXXXXX" in /home/me/.geeqie/ and return a pointer to a
+ * structure SecureSaveInfo on success or NULL on error.
+ *
+ * filename.tmp_XXXXXX can't conflict with any file since it's created using
+ * mkstemp(). XXXXXX is a random string.
+ *
+ * Subsequent write operations are done using returned SecureSaveInfo FILE *
+ * field named fp.
+ *
+ * If an error is encountered, SecureSaveInfo int field named err is set
+ * (automatically if using secure_fp*() functions or by programmer)
+ *
+ * When secure_close() is called, "filename.tmp_XXXXXX" is flushed and closed,
+ * and if SecureSaveInfo err field has a value of zero, "filename.tmp_XXXXXX"
+ * is renamed to "filename". If this succeeded, then secure_close() returns 0.
+ *
+ * WARNING: since rename() is used, any symlink called "filename" may be
+ * replaced by a regular file. If destination file isn't a regular file,
+ * then secsave is disabled for that file.
+ *
+ * If ssi->secure_save is FALSE:
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * No temporary file is created, "filename" is truncated, all operations are
+ * done on it, no rename nor flush occur, symlinks are preserved.
+ *
+ * In both cases:
+ * ~~~~~~~~~~~~~
+ *
+ * Access rights are affected by secure_open() mask parameter.
+ */
+
+/* FIXME: locking system on files about to be rewritten ? */
+/* FIXME: Low risk race conditions about ssi->file_name. */
+
+SecureSaveErrno secsave_errno = SS_ERR_NONE;
+
+
+/** Open a file for writing in a secure way. @returns a pointer to a
+ * structure secure_save_info on success, or NULL on failure. */
+static SecureSaveInfo *
+secure_open_umask(gchar *file_name)
+{
+       struct stat st;
+       SecureSaveInfo *ssi;
+
+       secsave_errno = SS_ERR_NONE;
+
+       ssi = g_new0(SecureSaveInfo, 1);
+       if (!ssi) {
+               secsave_errno = SS_ERR_OUT_OF_MEM;
+               goto end;
+       }
+
+       ssi->secure_save = TRUE;
+
+       ssi->file_name = g_strdup(file_name);
+       if (!ssi->file_name) {
+               secsave_errno = SS_ERR_OUT_OF_MEM;
+               goto free_f;
+       }
+
+       /* Check properties of final file. */
+#ifndef NO_UNIX_SOFTLINKS
+       if (g_lstat(ssi->file_name, &st)) {
+#else
+       if (g_stat(ssi->file_name, &st)) {
+#endif
+               /* We ignore error caused by file inexistence. */
+               if (errno != ENOENT) {
+                       /* lstat() error. */
+                       ssi->err = errno;
+                       secsave_errno = SS_ERR_STAT;
+                       goto free_file_name;
+               }
+       } else {
+               if (!S_ISREG(st.st_mode)) {
+                       /* Not a regular file, secure_save is disabled. */
+                       ssi->secure_save = 0;
+               } else {
+#ifdef HAVE_ACCESS
+                       /* XXX: access() do not work with setuid programs. */
+                       if (g_access(ssi->file_name, R_OK | W_OK) < 0) {
+                               ssi->err = errno;
+                               secsave_errno = SS_ERR_ACCESS;
+                               goto free_file_name;
+                       }
+#else
+                       FILE *f1;
+
+                       /* We still have a race condition here between
+                        * [l]stat() and fopen() */
+
+                       f1 = g_fopen(ssi->file_name, "rb+");
+                       if (f1) {
+                               fclose(f1);
+                       } else {
+                               ssi->err = errno;
+                               secsave_errno = SS_ERR_OPEN_READ;
+                               goto free_file_name;
+                       }
+#endif
+               }
+       }
+
+       if (ssi->secure_save) {
+               /* We use a random name for temporary file, mkstemp() opens
+                * the file and return a file descriptor named fd, which is
+                * then converted to FILE * using fdopen().
+                */
+               gint fd;
+               gchar *randname = g_strconcat(ssi->file_name, ".tmp_XXXXXX", NULL);
+
+               if (!randname) {
+                       secsave_errno = SS_ERR_OUT_OF_MEM;
+                       goto free_file_name;
+               }
+
+               /* No need to use safe_mkstemp() here. --Zas */
+               fd = g_mkstemp(randname);
+               if (fd == -1) {
+                       secsave_errno = SS_ERR_MKSTEMP;
+                       g_free(randname);
+                       goto free_file_name;
+               }
+
+               ssi->fp = fdopen(fd, "wb");
+               if (!ssi->fp) {
+                       secsave_errno = SS_ERR_OPEN_WRITE;
+                       ssi->err = errno;
+                       g_free(randname);
+                       goto free_file_name;
+               }
+
+               ssi->tmp_file_name = randname;
+       } else {
+               /* No need to create a temporary file here. */
+               ssi->fp = g_fopen(ssi->file_name, "wb");
+               if (!ssi->fp) {
+                       secsave_errno = SS_ERR_OPEN_WRITE;
+                       ssi->err = errno;
+                       goto free_file_name;
+               }
+       }
+
+       return ssi;
+
+free_file_name:
+       g_free(ssi->file_name);
+       ssi->file_name = NULL;
+
+free_f:
+       g_free(ssi);
+       ssi = NULL;
+
+end:
+       return NULL;
+}
+
+SecureSaveInfo *
+secure_open(gchar *file_name)
+{
+       SecureSaveInfo *ssi;
+       mode_t saved_mask;
+#ifdef CONFIG_OS_WIN32
+       /* There is neither S_IRWXG nor S_IRWXO under crossmingw32-gcc */
+       const mode_t mask = 0177;
+#else
+       const mode_t mask = S_IXUSR | S_IRWXG | S_IRWXO;
+#endif
+
+       saved_mask = umask(mask);
+       ssi = secure_open_umask(file_name);
+       umask(saved_mask);
+
+       return ssi;
+}
+
+/** Close a file opened with secure_open(). Rreturns 0 on success,
+ * errno or -1 on failure.
+ */
+gint
+secure_close(SecureSaveInfo *ssi)
+{
+       gint ret = -1;
+
+       if (!ssi) return ret;
+       if (!ssi->fp) goto free;
+
+       if (ssi->err) { /* Keep previous errno. */
+               ret = ssi->err;
+               fclose(ssi->fp); /* Close file */
+               goto free;
+       }
+
+       /* Ensure data is effectively written to disk, we first flush libc buffers
+        * using fflush(), then fsync() to flush kernel buffers, and finally call
+        * fclose() (which call fflush() again, but the first one is needed since
+        * it doesn't make much sense to flush kernel buffers and then libc buffers,
+        * while closing file releases file descriptor we need to call fsync(). */
+#if defined(HAVE_FFLUSH) || defined(HAVE_FSYNC)
+       if (ssi->secure_save) {
+               int fail = 0;
+
+#ifdef HAVE_FFLUSH
+               fail = (fflush(ssi->fp) == EOF);
+#endif
+
+#ifdef HAVE_FSYNC
+               if (!fail) fail = fsync(fileno(ssi->fp));
+#endif
+
+               if (fail) {
+                       ret = errno;
+                       secsave_errno = SS_ERR_OTHER;
+
+                       fclose(ssi->fp); /* Close file, ignore errors. */
+                       goto free;
+               }
+       }
+#endif
+
+       /* Close file. */
+       if (fclose(ssi->fp) == EOF) {
+               ret = errno;
+               secsave_errno = SS_ERR_OTHER;
+               goto free;
+       }
+
+       if (ssi->secure_save && ssi->file_name && ssi->tmp_file_name) {
+               /* FIXME: Race condition on ssi->file_name. The file
+                * named ssi->file_name may have changed since
+                * secure_open() call (where we stat() file and
+                * more..).  */
+               if (debug > 2) g_printf("rename %s -> %s", ssi->tmp_file_name, ssi->file_name);
+               if (g_rename(ssi->tmp_file_name, ssi->file_name) == -1) {
+                       ret = errno;
+                       secsave_errno = SS_ERR_RENAME;
+                       goto free;
+               }
+       }
+
+       ret = 0;        /* Success. */
+
+free:
+       if (ssi->tmp_file_name) g_free(ssi->tmp_file_name);
+       if (ssi->file_name) g_free(ssi->file_name);
+       if (ssi) g_free(ssi);
+
+       return ret;
+}
+
+
+/** fputs() wrapper, set ssi->err to errno on error. If ssi->err is set when
+ * called, it immediatly returns EOF.
+ */
+gint
+secure_fputs(SecureSaveInfo *ssi, const gchar *s)
+{
+       gint ret;
+
+       if (!ssi || !ssi->fp || ssi->err) return EOF;
+
+       ret = fputs(s, ssi->fp);
+       if (ret == EOF) {
+               secsave_errno = SS_ERR_OTHER;
+               ssi->err = errno;
+       }
+
+       return ret;
+}
+
+
+/** fputc() wrapper, set ssi->err to errno on error. If ssi->err is set when
+ * called, it immediatly returns EOF.
+ */
+gint
+secure_fputc(SecureSaveInfo *ssi, gint c)
+{
+       gint ret;
+
+       if (!ssi || !ssi->fp || ssi->err) return EOF;
+
+       ret = fputc(c, ssi->fp);
+       if (ret == EOF) {
+               ssi->err = errno;
+               secsave_errno = SS_ERR_OTHER;
+       }
+
+       return ret;
+}
+
+/** fprintf() wrapper, set ssi->err to errno on error and return a negative
+ * value. If ssi->err is set when called, it immediatly returns -1.
+ */
+gint
+secure_fprintf(SecureSaveInfo *ssi, const gchar *format, ...)
+{
+       va_list ap;
+       gint ret;
+
+       if (!ssi || !ssi->fp || ssi->err) return -1;
+
+       va_start(ap, format);
+       ret = vfprintf(ssi->fp, format, ap);
+       if (ret < 0) ssi->err = errno;
+       va_end(ap);
+
+       return ret;
+}
+
+gchar *
+secsave_strerror(SecureSaveErrno secsave_error)
+{
+       switch (secsave_error) {
+       case SS_ERR_OPEN_READ:
+               return _("Cannot read the file");
+       case SS_ERR_STAT:
+               return _("Cannot get file status");
+       case SS_ERR_ACCESS:
+               return _("Cannot access the file");
+       case SS_ERR_MKSTEMP:
+               return _("Cannot create temp file");
+       case SS_ERR_RENAME:
+               return _("Cannot rename the file");
+       case SS_ERR_DISABLED:
+               return _("File saving disabled by option");
+       case SS_ERR_OUT_OF_MEM:
+               return _("Out of memory");
+       case SS_ERR_OPEN_WRITE:
+               return _("Cannot write the file");
+       case SS_ERR_NONE: /* Impossible. */
+       case SS_ERR_OTHER:
+       default:
+               return _("Secure file saving error");
+       }
+}
 
 /*
  *-----------------------------------------------------------------------------
@@ -103,11 +453,11 @@ gchar *escquote_value(const gchar *text)
        return g_strdup("\"\"");
 }
 
-static void write_char_option(FILE *f, gchar *label, gchar *text)
+static void write_char_option(SecureSaveInfo *ssi, gchar *label, gchar *text)
 {
        gchar *escval = escquote_value(text);
 
-       fprintf(f,"%s: %s\n", label, escval);
+       secure_fprintf(ssi, "%s: %s\n", label, escval);
        g_free(escval);
 }
 
@@ -128,17 +478,17 @@ static gchar *color_to_string(GdkColor *color)
        return g_strdup_printf("#%04X%04X%04X", color->red, color->green, color->blue);
 }
 
-static void write_color_option(FILE *f, gchar *label, GdkColor *color)
+static void write_color_option(SecureSaveInfo *ssi, gchar *label, GdkColor *color)
 {
        if (color)
                {
                gchar *colorstring = color_to_string(color);
 
-               write_char_option(f, label, colorstring);
+               write_char_option(ssi, label, colorstring);
                g_free(colorstring);
                }
        else
-               fprintf(f,"%s: \n", label);
+               secure_fprintf(ssi, "%s: \n", label);
 }
 
 static GdkColor *read_color_option(FILE *f, gchar *option, gchar *label, gchar *value, GdkColor *color)
@@ -153,9 +503,9 @@ static GdkColor *read_color_option(FILE *f, gchar *option, gchar *label, gchar *
 }
 
 
-static void write_int_option(FILE *f, gchar *label, gint n)
+static void write_int_option(SecureSaveInfo *ssi, gchar *label, gint n)
 {
-       fprintf(f,"%s: %d\n\n", label, n);
+       secure_fprintf(ssi, "%s: %d\n", label, n);
 }
 
 static gint read_int_option(FILE *f, gchar *option, gchar *label, gchar *value, gint n)
@@ -167,7 +517,7 @@ static gint read_int_option(FILE *f, gchar *option, gchar *label, gchar *value,
        return n;
 }
 
-static void write_int_unit_option(FILE *f, gchar *label, gint n, gint subunits)
+static void write_int_unit_option(SecureSaveInfo *ssi, gchar *label, gint n, gint subunits)
 {
        gint l, r;
 
@@ -182,7 +532,7 @@ static void write_int_unit_option(FILE *f, gchar *label, gint n, gint subunits)
                r = 0;
                }
 
-       fprintf(f,"%s: %d.%d\n\n", label, l, r);
+       secure_fprintf(ssi, "%s: %d.%d\n", label, l, r);
 }
 
 static gint read_int_unit_option(FILE *f, gchar *option, gchar *label, gchar *value, gint n, gint subunits)
@@ -213,11 +563,10 @@ static gint read_int_unit_option(FILE *f, gchar *option, gchar *label, gchar *va
        return n;
 }
 
-static void write_bool_option(FILE *f, gchar *label, gint n)
+static void write_bool_option(SecureSaveInfo *ssi, gchar *label, gint n)
 {
-       fprintf(f,"%s: ", label);
-       if (n) fprintf(f,"true\n"); else fprintf(f,"false\n");
-       fprintf(f,"\n");
+       secure_fprintf(ssi, "%s: ", label);
+       if (n) secure_fprintf(ssi, "true\n"); else secure_fprintf(ssi, "false\n");
 }
 
 static gint read_bool_option(FILE *f, gchar *option, gchar *label, gchar *value, gint n)
@@ -240,7 +589,7 @@ static gint read_bool_option(FILE *f, gchar *option, gchar *label, gchar *value,
 
 void save_options(void)
 {
-       FILE *f;
+       SecureSaveInfo *ssi;
        gchar *rc_path;
        gchar *rc_pathl;
        gint i;
@@ -248,9 +597,9 @@ void save_options(void)
        rc_path = g_strconcat(homedir(), "/", GQVIEW_RC_DIR, "/", RC_FILE_NAME, NULL);
 
        rc_pathl = path_from_utf8(rc_path);
-       f = fopen(rc_pathl, "w");
+       ssi = secure_open(rc_pathl);
        g_free(rc_pathl);
-       if (!f)
+       if (!ssi)
                {
                gchar *buf;
 
@@ -261,201 +610,214 @@ void save_options(void)
                g_free(rc_path);
                return;
                }
-
-       fprintf(f,"######################################################################\n");
-       fprintf(f,"#                         Geeqie config file         version %7s #\n", VERSION);
-       fprintf(f,"######################################################################\n");
-       fprintf(f,"\n");
-       fprintf(f,"# Note: This file is autogenerated. Options can be changed here,\n");
-       fprintf(f,"#       but user comments and formatting will be lost.\n");
-       fprintf(f,"\n");
-       fprintf(f,"##### General Options #####\n\n");
-
-       write_int_option(f, "layout_style", layout_style);
-       write_char_option(f, "layout_order", layout_order);
-       fprintf(f,"\n");
-       write_bool_option(f, "layout_view_as_icons", layout_view_icons);
-       write_bool_option(f, "layout_view_as_tree", layout_view_tree);
-       write_bool_option(f, "show_icon_names", show_icon_names);
-       fprintf(f,"\n");
-
-       write_bool_option(f, "tree_descend_folders", tree_descend_subdirs);
-       write_bool_option(f, "lazy_image_sync", lazy_image_sync);
-       write_bool_option(f, "update_on_time_change", update_on_time_change);
-       write_bool_option(f, "exif_auto_rotate", exif_rotate_enable);
-       fprintf(f,"\n");
-
-       write_bool_option(f, "enable_startup_path", startup_path_enable);
-       write_char_option(f, "startup_path", startup_path);
-       fprintf(f,"\n");
-
-       fprintf(f,"zoom_mode: ");
-       if (zoom_mode == ZOOM_RESET_ORIGINAL) fprintf(f,"original\n");
-       if (zoom_mode == ZOOM_RESET_FIT_WINDOW) fprintf(f,"fit\n");
-       if (zoom_mode == ZOOM_RESET_NONE) fprintf(f,"dont_change\n");
-
-       write_bool_option(f, "two_pass_scaling", two_pass_zoom);
-
-       write_bool_option(f, "zoom_to_fit_allow_expand", zoom_to_fit_expands);
-       fprintf(f,"\n");
-
-       write_bool_option(f, "fit_window_to_image", fit_window);
-       write_bool_option(f, "limit_window_size", limit_window_size);
-       write_int_option(f, "max_window_size", max_window_size);
-       write_bool_option(f, "limit_autofit_size", limit_autofit_size);
-       write_int_option(f, "max_autofit_size", max_autofit_size);
-       fprintf(f,"\n");
-
-       write_bool_option(f, "progressive_keyboard_scrolling", progressive_key_scrolling);
-       write_int_option(f, "scroll_reset_method", scroll_reset_method);
-       fprintf(f,"\n");
-
-       write_bool_option(f, "enable_thumbnails", thumbnails_enabled);
-       write_int_option(f, "thumbnail_width", thumb_max_width);
-       write_int_option(f, "thumbnail_height", thumb_max_height);
-       write_bool_option(f, "cache_thumbnails", enable_thumb_caching);
-       write_bool_option(f, "cache_thumbnails_into_dirs", enable_thumb_dirs);
-       write_bool_option(f, "thumbnail_fast", thumbnail_fast);
-       write_bool_option(f, "use_xvpics_thumbnails", use_xvpics_thumbnails);
-       write_bool_option(f, "thumbnail_spec_standard", thumbnail_spec_standard);
-       fprintf(f,"\n");
-
-       write_bool_option(f, "local_metadata", enable_metadata_dirs);
-       fprintf(f,"\n");
-
-       write_int_option(f, "sort_method", (gint)file_sort_method);
-       write_bool_option(f, "sort_ascending", file_sort_ascending);
-       write_bool_option(f, "sort_case_sensitive", file_sort_case_sensitive);
-       fprintf(f,"\n");
-
-       write_bool_option(f, "confirm_delete", confirm_delete);
-       write_bool_option(f, "enable_delete_key", enable_delete_key);
-       write_bool_option(f, "safe_delete", safe_delete_enable);
-       write_char_option(f, "safe_delete_path", safe_delete_path);
-       write_int_option(f, "safe_delete_size", safe_delete_size);
-       fprintf(f,"\n");
-       write_bool_option(f, "tools_float", tools_float);
-       write_bool_option(f, "tools_hidden", tools_hidden);
-       write_bool_option(f, "restore_tool_state", restore_tool);
-
-       write_bool_option(f, "toolbar_hidden", toolbar_hidden);
-
-       write_bool_option(f, "mouse_wheel_scrolls", mousewheel_scrolls);
-       write_bool_option(f, "in_place_rename", enable_in_place_rename);
-
-       write_int_option(f, "open_recent_max", recent_list_max);
-
-       write_int_option(f, "image_cache_size_max", tile_cache_max);
-
-       write_int_option(f, "thumbnail_quality", thumbnail_quality);
-       write_int_option(f, "zoom_quality", zoom_quality);
-       write_int_option(f, "dither_quality", dither_quality);
-
-       write_int_option(f, "zoom_increment", zoom_increment);
-
-       write_bool_option(f, "enable_read_ahead", enable_read_ahead);
-
-       write_bool_option(f, "display_dialogs_under_mouse", place_dialogs_under_mouse);
-
-       write_bool_option(f, "user_specified_window_background", user_specified_window_background);
-       write_color_option(f, "window_background_color", &window_background_color);
-
-       write_int_option(f, "fullscreen_screen", fullscreen_screen);
-       write_bool_option(f, "fullscreen_clean_flip", fullscreen_clean_flip);
-       write_bool_option(f, "fullscreen_disable_saver", fullscreen_disable_saver);
-       write_bool_option(f, "fullscreen_above", fullscreen_above);
-       write_bool_option(f, "show_fullscreen_info", show_fullscreen_info);
-       write_char_option(f, "fullscreen_info", fullscreen_info);
-
-       write_int_option(f, "custom_similarity_threshold", dupe_custom_threshold);
-
-       fprintf(f,"\n##### Slideshow Options #####\n\n");
-
-       write_int_unit_option(f, "slideshow_delay", slideshow_delay, SLIDESHOW_SUBSECOND_PRECISION);
-
-       write_bool_option(f, "slideshow_random", slideshow_random);
-       write_bool_option(f, "slideshow_repeat", slideshow_repeat);
-
-       fprintf(f,"\n##### Filtering Options #####\n\n");
-
-       write_bool_option(f, "show_dotfiles", show_dot_files);
-       write_bool_option(f, "disable_filtering", file_filter_disable);
-       filter_write_list(f);
        
-       sidecar_ext_write(f);
+       secure_fprintf(ssi, "######################################################################\n");
+       secure_fprintf(ssi, "#                         Geeqie config file         version %7s #\n", VERSION);
+       secure_fprintf(ssi, "######################################################################\n");
+       secure_fputc(ssi, '\n');
+
+       secure_fprintf(ssi, "# Note: This file is autogenerated. Options can be changed here,\n");
+       secure_fprintf(ssi, "#       but user comments and formatting will be lost.\n");
+       secure_fputc(ssi, '\n');
+
+       secure_fprintf(ssi, "##### General Options #####\n\n");
+
+       write_int_option(ssi, "layout_style", layout_style);
+       write_char_option(ssi, "layout_order", layout_order);
+       secure_fputc(ssi, '\n');
+
+       write_bool_option(ssi, "layout_view_as_icons", layout_view_icons);
+       write_bool_option(ssi, "layout_view_as_tree", layout_view_tree);
+       write_bool_option(ssi, "show_icon_names", show_icon_names);
+       secure_fputc(ssi, '\n');
+
+       write_bool_option(ssi, "tree_descend_folders", tree_descend_subdirs);
+       write_bool_option(ssi, "lazy_image_sync", lazy_image_sync);
+       write_bool_option(ssi, "update_on_time_change", update_on_time_change);
+       write_bool_option(ssi, "exif_auto_rotate", exif_rotate_enable);
+       secure_fputc(ssi, '\n');
+
+       write_bool_option(ssi, "enable_startup_path", startup_path_enable);
+       write_char_option(ssi, "startup_path", startup_path);
+       secure_fputc(ssi, '\n');
+
+       secure_fprintf(ssi, "zoom_mode: ");
+       if (zoom_mode == ZOOM_RESET_ORIGINAL) secure_fprintf(ssi, "original\n");
+       if (zoom_mode == ZOOM_RESET_FIT_WINDOW) secure_fprintf(ssi, "fit\n");
+       if (zoom_mode == ZOOM_RESET_NONE) secure_fprintf(ssi, "dont_change\n");
+       write_bool_option(ssi, "two_pass_scaling", two_pass_zoom);
+       write_bool_option(ssi, "zoom_to_fit_allow_expand", zoom_to_fit_expands);
+       secure_fputc(ssi, '\n');
+
+       write_bool_option(ssi, "fit_window_to_image", fit_window);
+       write_bool_option(ssi, "limit_window_size", limit_window_size);
+       write_int_option(ssi, "max_window_size", max_window_size);
+       write_bool_option(ssi, "limit_autofit_size", limit_autofit_size);
+       write_int_option(ssi, "max_autofit_size", max_autofit_size);
+       secure_fputc(ssi, '\n');
+
+       write_bool_option(ssi, "progressive_keyboard_scrolling", progressive_key_scrolling);
+       write_int_option(ssi, "scroll_reset_method", scroll_reset_method);
+       secure_fputc(ssi, '\n');
+
+       write_bool_option(ssi, "enable_thumbnails", thumbnails_enabled);
+       write_int_option(ssi, "thumbnail_width", thumb_max_width);
+       write_int_option(ssi, "thumbnail_height", thumb_max_height);
+       write_bool_option(ssi, "cache_thumbnails", enable_thumb_caching);
+       write_bool_option(ssi, "cache_thumbnails_into_dirs", enable_thumb_dirs);
+       write_bool_option(ssi, "thumbnail_fast", thumbnail_fast);
+       write_bool_option(ssi, "use_xvpics_thumbnails", use_xvpics_thumbnails);
+       write_bool_option(ssi, "thumbnail_spec_standard", thumbnail_spec_standard);
+       secure_fputc(ssi, '\n');
+
+       write_bool_option(ssi, "local_metadata", enable_metadata_dirs);
+       secure_fputc(ssi, '\n');
+
+       write_int_option(ssi, "sort_method", (gint)file_sort_method);
+       write_bool_option(ssi, "sort_ascending", file_sort_ascending);
+       write_bool_option(ssi, "sort_case_sensitive", file_sort_case_sensitive);
+       secure_fputc(ssi, '\n');
+
+       write_bool_option(ssi, "confirm_delete", confirm_delete);
+       write_bool_option(ssi, "enable_delete_key", enable_delete_key);
+       write_bool_option(ssi, "safe_delete", safe_delete_enable);
+       write_char_option(ssi, "safe_delete_path", safe_delete_path);
+       write_int_option(ssi, "safe_delete_size", safe_delete_size);
+       secure_fputc(ssi, '\n');
+
+       write_bool_option(ssi, "tools_float", tools_float);
+       write_bool_option(ssi, "tools_hidden", tools_hidden);
+       write_bool_option(ssi, "restore_tool_state", restore_tool);
+       write_bool_option(ssi, "toolbar_hidden", toolbar_hidden);
+       secure_fputc(ssi, '\n');
+
+       write_bool_option(ssi, "mouse_wheel_scrolls", mousewheel_scrolls);
+       write_bool_option(ssi, "in_place_rename", enable_in_place_rename);
+       write_int_option(ssi, "open_recent_max", recent_list_max);
+       write_int_option(ssi, "image_cache_size_max", tile_cache_max);
+       write_int_option(ssi, "thumbnail_quality", thumbnail_quality);
+       write_int_option(ssi, "zoom_quality", zoom_quality);
+       write_int_option(ssi, "dither_quality", dither_quality);
+       write_int_option(ssi, "zoom_increment", zoom_increment);
+       write_bool_option(ssi, "enable_read_ahead", enable_read_ahead);
+       write_bool_option(ssi, "display_dialogs_under_mouse", place_dialogs_under_mouse);
+       secure_fputc(ssi, '\n');
+
+       write_bool_option(ssi, "user_specified_window_background", user_specified_window_background);
+       write_color_option(ssi, "window_background_color", &window_background_color);
+       secure_fputc(ssi, '\n');
+
+       write_int_option(ssi, "fullscreen_screen", fullscreen_screen);
+       write_bool_option(ssi, "fullscreen_clean_flip", fullscreen_clean_flip);
+       write_bool_option(ssi, "fullscreen_disable_saver", fullscreen_disable_saver);
+       write_bool_option(ssi, "fullscreen_above", fullscreen_above);
+       write_bool_option(ssi, "show_fullscreen_info", show_fullscreen_info);
+       write_char_option(ssi, "fullscreen_info", fullscreen_info);
+       secure_fputc(ssi, '\n');
+
+       write_int_option(ssi, "custom_similarity_threshold", dupe_custom_threshold);
+
+       secure_fprintf(ssi, "\n##### Slideshow Options #####\n\n");
+
+       write_int_unit_option(ssi, "slideshow_delay", slideshow_delay, SLIDESHOW_SUBSECOND_PRECISION);
+
+       write_bool_option(ssi, "slideshow_random", slideshow_random);
+       write_bool_option(ssi, "slideshow_repeat", slideshow_repeat);
+
+       secure_fprintf(ssi, "\n##### Filtering Options #####\n\n");
+
+       write_bool_option(ssi, "show_dotfiles", show_dot_files);
+       write_bool_option(ssi, "disable_filtering", file_filter_disable);
+       
+       filter_write_list(ssi);
+       
+       sidecar_ext_write(ssi);
 
-       fprintf(f,"\n##### Color Profiles #####\n\n");
+       secure_fprintf(ssi, "\n##### Color Profiles #####\n\n");
 
 #ifndef HAVE_LCMS
-       fprintf(f,"# NOTICE: Geeqie was not built with support for color profiles,\n"
-                 "#         color profile options will have no effect.\n\n");
+       secure_fprintf(ssi, "# NOTICE: Geeqie was not built with support for color profiles,\n"
+                          "#         color profile options will have no effect.\n\n");
 #endif
 
-       write_bool_option(f, "color_profile_enabled", color_profile_enabled);
-       write_bool_option(f, "color_profile_use_image", color_profile_use_image);
-       write_int_option(f, "color_profile_input_type", color_profile_input_type);
+       write_bool_option(ssi, "color_profile_enabled", color_profile_enabled);
+       write_bool_option(ssi, "color_profile_use_image", color_profile_use_image);
+       write_int_option(ssi, "color_profile_input_type", color_profile_input_type);
        for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
                {
                gchar *buf;
 
                buf = g_strdup_printf("color_profile_input_file_%d", i + 1);
-               write_char_option(f, buf, color_profile_input_file[i]);
+               write_char_option(ssi, buf, color_profile_input_file[i]);
                g_free(buf);
 
                buf = g_strdup_printf("color_profile_input_name_%d", i + 1);
-               write_char_option(f, buf, color_profile_input_name[i]);
+               write_char_option(ssi, buf, color_profile_input_name[i]);
                g_free(buf);
                }
-       fprintf(f,"\n");
-       write_int_option(f, "color_profile_screen_type", color_profile_screen_type);
-       write_char_option(f, "color_profile_screen_file_1", color_profile_screen_file);
+       secure_fputc(ssi, '\n');
+       write_int_option(ssi, "color_profile_screen_type", color_profile_screen_type);
+       write_char_option(ssi, "color_profile_screen_file_1", color_profile_screen_file);
 
-       fprintf(f,"\n##### External Programs #####\n");
-       fprintf(f,"# Maximum of 10 programs (external_1 through external_10)\n");
-       fprintf(f,"# format: external_n: \"menu name\" \"command line\"\n\n");
+       secure_fprintf(ssi, "\n##### External Programs #####\n");
+       secure_fprintf(ssi, "# Maximum of 10 programs (external_1 through external_10)\n");
+       secure_fprintf(ssi, "# format: external_n: \"menu name\" \"command line\"\n\n");
 
        for (i = 0; i < GQVIEW_EDITOR_SLOTS; i++)
                {
-               char *qname = escquote_value(editor_name[i]);
-               char *qcommand = escquote_value(editor_command[i]);
-               fprintf(f,"external_%d: %s %s\n", i+1, qname, qcommand);
+               gchar *qname = escquote_value(editor_name[i]);
+               gchar *qcommand = escquote_value(editor_command[i]);
+               secure_fprintf(ssi, "external_%d: %s %s\n", i+1, qname, qcommand);
                g_free(qname);
                g_free(qcommand);
                }
 
-       fprintf(f,"\n##### Collection Options #####\n\n");
+       secure_fprintf(ssi, "\n##### Collection Options #####\n\n");
 
-       write_bool_option(f, "rectangular_selections", collection_rectangular_selection);
+       write_bool_option(ssi, "rectangular_selections", collection_rectangular_selection);
 
-       fprintf(f,"\n##### Window Positions #####\n\n");
+       secure_fprintf(ssi, "\n##### Window Positions #####\n\n");
 
-       write_bool_option(f, "restore_window_positions", save_window_positions);
-       fprintf(f,"\n");
-       write_int_option(f, "main_window_x", main_window_x);
-       write_int_option(f, "main_window_y", main_window_y);
-       write_int_option(f, "main_window_width", main_window_w);
-       write_int_option(f, "main_window_height", main_window_h);
-       write_bool_option(f, "main_window_maximized", main_window_maximized);
-       write_int_option(f, "float_window_x", float_window_x);
-       write_int_option(f, "float_window_y", float_window_y);
-       write_int_option(f, "float_window_width", float_window_w);
-       write_int_option(f, "float_window_height", float_window_h);
-       write_int_option(f, "float_window_divider", float_window_divider);
-       write_int_option(f, "divider_position_h", window_hdivider_pos);
-       write_int_option(f, "divider_position_v", window_vdivider_pos);
+       write_bool_option(ssi, "restore_window_positions", save_window_positions);
+       secure_fputc(ssi, '\n');
+       write_int_option(ssi, "main_window_x", main_window_x);
+       write_int_option(ssi, "main_window_y", main_window_y);
+       write_int_option(ssi, "main_window_width", main_window_w);
+       write_int_option(ssi, "main_window_height", main_window_h);
+       write_bool_option(ssi, "main_window_maximized", main_window_maximized);
+       write_int_option(ssi, "float_window_x", float_window_x);
+       write_int_option(ssi, "float_window_y", float_window_y);
+       write_int_option(ssi, "float_window_width", float_window_w);
+       write_int_option(ssi, "float_window_height", float_window_h);
+       write_int_option(ssi, "float_window_divider", float_window_divider);
+       write_int_option(ssi, "divider_position_h", window_hdivider_pos);
+       write_int_option(ssi, "divider_position_v", window_vdivider_pos);
 
-       fprintf(f,"\n##### Exif #####\n# 0: never\n# 1: if set\n# 2: always\n");
+       secure_fprintf(ssi, "\n##### Exif #####\n# 0: never\n# 1: if set\n# 2: always\n\n");
        for (i = 0; ExifUIList[i].key; i++)
                {
-               fprintf(f,"exif_");
-               write_int_option(f, (gchar *)ExifUIList[i].key, ExifUIList[i].current);
+               secure_fprintf(ssi, "exif_");
+               write_int_option(ssi, (gchar *)ExifUIList[i].key, ExifUIList[i].current);
                }
 
-       fprintf(f,"######################################################################\n");
-       fprintf(f,"#                      end of Geeqie config file                     #\n");
-       fprintf(f,"######################################################################\n");
+       secure_fputc(ssi, '\n');
+       secure_fprintf(ssi, "######################################################################\n");
+       secure_fprintf(ssi, "#                      end of Geeqie config file                     #\n");
+       secure_fprintf(ssi, "######################################################################\n");
 
-       fclose(f);
+       
+       if (secure_close(ssi))
+               {
+               gchar *buf;
+
+               buf = g_strdup_printf(_("error saving config file: %s\nerror: %s\n"), rc_path,
+                                     secsave_strerror(secsave_errno));
+               print_term(buf);
+               g_free(buf);
+
+               g_free(rc_path);
+               return;
+               }
 
        g_free(rc_path);
 }
index 0095213..b72bf02 100644 (file)
 #define RCFILE_H
 
 
+extern SecureSaveErrno secsave_errno; /**< internal secsave error number */
+
+SecureSaveInfo *secure_open(gchar *);
+
+gint secure_close(SecureSaveInfo *);
+
+gint secure_fputs(SecureSaveInfo *, const gchar *);
+gint secure_fputc(SecureSaveInfo *, gint);
+
+gint secure_fprintf(SecureSaveInfo *, const gchar *, ...);
+
+gchar *secsave_strerror(SecureSaveErrno);
+
+
+
 gchar *quoted_value(const gchar *text, const gchar **tail);
 gchar *escquote_value(const gchar *text);
 
index a7f3151..a487987 100644 (file)
@@ -96,6 +96,21 @@ typedef enum {
        FILE_FORMAT_CLASSES
 } FileFormatClass;
 
+typedef enum {
+       SS_ERR_NONE = 0,
+       SS_ERR_DISABLED, /**< secsave is disabled. */
+       SS_ERR_OUT_OF_MEM, /**< memory allocation failure */
+
+       /* see err field in SecureSaveInfo */
+       SS_ERR_OPEN_READ,
+       SS_ERR_OPEN_WRITE,
+       SS_ERR_STAT,
+       SS_ERR_ACCESS,
+       SS_ERR_MKSTEMP,
+       SS_ERR_RENAME,
+       SS_ERR_OTHER,
+} SecureSaveErrno;
+
 
 #define MAX_SPLIT_IMAGES 4
 
@@ -124,6 +139,7 @@ typedef struct _FullScreenData FullScreenData;
 typedef struct _PixmapFolders PixmapFolders;
 typedef struct _Histogram Histogram;
 
+typedef struct _SecureSaveInfo SecureSaveInfo;
 
 
 struct _ImageLoader
@@ -706,6 +722,14 @@ struct _PixmapFolders
        GdkPixbuf *parent;
 };
 
+struct _SecureSaveInfo {
+       FILE *fp; /**< file stream pointer */
+       gchar *file_name; /**< final file name */
+       gchar *tmp_file_name; /**< temporary file name */
+       gint err; /**< set to non-zero value in case of error */
+       gint secure_save; /**< use secure save for this file */
+};
+
 
 #endif