update copyright year
[claws.git] / src / common / prefs.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include <stdio.h>
26
27 #include "defs.h"
28
29 #include <glib.h>
30
31 #include "prefs.h"
32 #include "utils.h"
33 #include "file-utils.h"
34
35 static gboolean prefs_is_readonly       (const gchar    *path);
36
37 /*!
38  *\brief        Open preferences file for reading
39  *
40  *\param        path Filename with path of preferences file to read
41  *
42  *\return       PrefFile * preferences file struct
43  */
44 PrefFile *prefs_read_open(const gchar *path)
45 {
46         PrefFile *pfile;
47         FILE *fp;
48
49         cm_return_val_if_fail(path != NULL, NULL);
50
51         if ((fp = claws_fopen(path, "rb")) == NULL) {
52                 FILE_OP_ERROR(path, "claws_fopen");
53                 return NULL;
54         }
55
56         pfile = g_new(PrefFile, 1);
57         pfile->fp = fp;
58         pfile->orig_fp = NULL;
59         pfile->path = g_strdup(path);
60         pfile->writing = FALSE;
61
62         return pfile;
63 }
64
65 /*!
66  *\brief        Open preferences file for writing
67  *              Prefs are written to a temp file: Call prefs_file_close()
68  *              to rename this to the final filename
69  *
70  *\param        path Filename with path of preferences file to write
71  *
72  *\return       PrefFile * preferences file struct
73  */
74 PrefFile *prefs_write_open(const gchar *path)
75 {
76         PrefFile *pfile;
77         gchar *tmppath;
78         FILE *fp;
79
80         cm_return_val_if_fail(path != NULL, NULL);
81
82         if (prefs_is_readonly(path)) {
83                 g_warning("no write permission on '%s'", path);
84                 return NULL;
85         }
86
87         tmppath = g_strconcat(path, ".tmp", NULL);
88         if ((fp = claws_fopen(tmppath, "wb")) == NULL) {
89                 FILE_OP_ERROR(tmppath, "claws_fopen");
90                 g_free(tmppath);
91                 return NULL;
92         }
93
94         if (change_file_mode_rw(fp, tmppath) < 0)
95                 FILE_OP_ERROR(tmppath, "chmod");
96
97         g_free(tmppath);
98
99         pfile = g_new(PrefFile, 1);
100         pfile->fp = fp;
101         pfile->orig_fp = NULL;
102         pfile->path = g_strdup(path);
103         pfile->writing = TRUE;
104
105         return pfile;
106 }
107
108 gboolean prefs_common_get_flush_metadata (void);
109
110 /*!
111  *\brief        Close and free preferences file
112  *              Creates final file from temp, creates backup
113  *
114  *\param        pfile Preferences file struct
115  *
116  *\return       0 on success, -1 on failure
117  */
118 gint prefs_file_close(PrefFile *pfile)
119 {
120         FILE *fp, *orig_fp;
121         gchar *path;
122         gchar *tmppath;
123         gchar *bakpath = NULL;
124         gchar buf[BUFFSIZE];
125
126         cm_return_val_if_fail(pfile != NULL, -1);
127
128         fp = pfile->fp;
129         orig_fp = pfile->orig_fp;
130         path = pfile->path;
131
132         if (!pfile->writing) {
133                 claws_fclose(fp);
134                 g_free(pfile);
135                 g_free(path);
136                 return 0;
137         }
138
139         if (orig_fp) {
140                 while (claws_fgets(buf, sizeof(buf), orig_fp) != NULL) {
141                         /* next block */
142                         if (buf[0] == '[') {
143                                 if (claws_fputs(buf, fp)  == EOF) {
144                                         g_warning("failed to write configuration to file");
145                                         prefs_file_close_revert(pfile);
146                                 
147                                         return -1;
148                                 }
149                                 break;
150                         }
151                 }
152                 
153                 while (claws_fgets(buf, sizeof(buf), orig_fp) != NULL)
154                         if (claws_fputs(buf, fp) == EOF) {
155                                 g_warning("failed to write configuration to file");
156                                 prefs_file_close_revert(pfile);                 
157                                 
158                                 return -1;
159                         }
160                 claws_fclose(orig_fp);
161         }
162
163         tmppath = g_strconcat(path, ".tmp", NULL);
164
165         if (claws_safe_fclose(fp) == EOF) {
166                 FILE_OP_ERROR(tmppath, "claws_fclose");
167                 claws_unlink(tmppath);
168                 g_free(path);
169                 g_free(tmppath);
170                 return -1;
171         }
172
173         if (is_file_exist(path)) {
174                 bakpath = g_strconcat(path, ".bak", NULL);
175 #ifdef G_OS_WIN32
176                 claws_unlink(bakpath);
177 #endif
178                 if (g_rename(path, bakpath) < 0) {
179                         FILE_OP_ERROR(path, "rename");
180                         claws_unlink(tmppath);
181                         g_free(path);
182                         g_free(tmppath);
183                         g_free(bakpath);
184                         return -1;
185                 }
186         }
187
188 #ifdef G_OS_WIN32
189         claws_unlink(path);
190 #endif
191         if (g_rename(tmppath, path) < 0) {
192                 FILE_OP_ERROR(tmppath, "rename");
193                 claws_unlink(tmppath);
194                 g_free(path);
195                 g_free(tmppath);
196                 g_free(bakpath);
197                 return -1;
198         }
199
200         g_free(pfile);
201         g_free(path);
202         g_free(tmppath);
203         g_free(bakpath);
204         return 0;
205 }
206
207 /*!
208  *\brief        Close and free preferences file, delete temp file
209  *
210  *\param        pfile Preferences file struct
211  */
212 gint prefs_file_close_revert(PrefFile *pfile)
213 {
214         gchar *tmppath = NULL;
215
216         cm_return_val_if_fail(pfile != NULL, -1);
217
218         if (pfile->orig_fp)
219                 claws_fclose(pfile->orig_fp);
220         if (pfile->writing)
221                 tmppath = g_strconcat(pfile->path, ".tmp", NULL);
222         claws_fclose(pfile->fp);
223         if (pfile->writing) {
224                 if (claws_unlink(tmppath) < 0) FILE_OP_ERROR(tmppath, "unlink");
225                 g_free(tmppath);
226         }
227         g_free(pfile->path);
228         g_free(pfile);
229
230         return 0;
231 }
232
233 /*!
234  *\brief        Check if "path" is a file and read-only
235  */
236 static gboolean prefs_is_readonly(const gchar * path)
237 {
238         if (path == NULL)
239                 return TRUE;
240
241         return (access(path, W_OK) != 0 && access(path, F_OK) == 0);
242 }
243
244 /*!
245  *\brief        Check if "rcfile" is in rcdir, a file and read-only
246  */
247 gboolean prefs_rc_is_readonly(const gchar * rcfile)
248 {
249         gboolean result;
250         gchar * rcpath;
251
252         if (rcfile == NULL)
253                 return TRUE;
254
255         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, rcfile, NULL);
256         result = prefs_is_readonly(rcpath);
257         g_free(rcpath);
258
259         return result;
260 }
261
262 /*!
263  *\brief        Selects current section in preferences file
264  *              Creates section if file is written
265  *
266  *\param        pfile Preferences file struct
267  *
268  *\return       0 on success, -1 on failure
269  */
270 gint prefs_set_block_label(PrefFile *pfile, const gchar *label)
271 {
272         gchar *block_label;
273         gchar buf[BUFFSIZE];
274
275         block_label = g_strdup_printf("[%s]", label);
276         if (!pfile->writing) {
277                 while (claws_fgets(buf, sizeof(buf), pfile->fp) != NULL) {
278                         gint val;
279                         
280                         val = strncmp(buf, block_label, strlen(block_label));
281                         if (val == 0) {
282                                 debug_print("Found %s\n", block_label);
283                                 break;
284                         }
285                 }
286         } else {
287                 if ((pfile->orig_fp = claws_fopen(pfile->path, "rb")) != NULL) {
288                         gboolean block_matched = FALSE;
289
290                         while (claws_fgets(buf, sizeof(buf), pfile->orig_fp) != NULL) {
291                                 gint val;
292
293                                 val = strncmp(buf, block_label, strlen(block_label));
294                                 if (val == 0) {
295                                         debug_print("Found %s\n", block_label);
296                                         block_matched = TRUE;
297                                         break;
298                                 } else {
299                                         if (claws_fputs(buf, pfile->fp) == EOF) {
300                                                 g_warning("failed to write configuration to file");
301                                                 prefs_file_close_revert(pfile);
302                                                 g_free(block_label);
303                                                 
304                                                 return -1;
305                                         }
306                                 }
307                         }
308
309                         if (!block_matched) {
310                                 claws_fclose(pfile->orig_fp);
311                                 pfile->orig_fp = NULL;
312                         }
313                 }
314
315                 if (claws_fputs(block_label, pfile->fp) == EOF ||
316                         claws_fputc('\n', pfile->fp) == EOF) {
317                         g_warning("failed to write configuration to file");
318                         prefs_file_close_revert(pfile);
319                         g_free(block_label);
320
321                         return -1;
322                 }
323         }
324
325         g_free(block_label);
326
327         return 0;
328 }