71e0950456b1d37b5734a95dbd1372f34570f347
[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 "defs.h"
26
27 #include <glib.h>
28
29 #include "prefs.h"
30 #include "utils.h"
31
32 static gboolean prefs_is_readonly       (const gchar    *path);
33
34 /*!
35  *\brief        Open preferences file for reading
36  *
37  *\param        path Filename with path of preferences file to read
38  *
39  *\return       PrefFile * preferences file struct
40  */
41 PrefFile *prefs_read_open(const gchar *path)
42 {
43         PrefFile *pfile;
44         FILE *fp;
45
46         cm_return_val_if_fail(path != NULL, NULL);
47
48         if ((fp = g_fopen(path, "rb")) == NULL) {
49                 FILE_OP_ERROR(path, "fopen");
50                 return NULL;
51         }
52
53         pfile = g_new(PrefFile, 1);
54         pfile->fp = fp;
55         pfile->orig_fp = NULL;
56         pfile->path = g_strdup(path);
57         pfile->writing = FALSE;
58
59         return pfile;
60 }
61
62 /*!
63  *\brief        Open preferences file for writing
64  *              Prefs are written to a temp file: Call prefs_write_close()
65  *              to rename this to the final filename
66  *
67  *\param        path Filename with path of preferences file to write
68  *
69  *\return       PrefFile * preferences file struct
70  */
71 PrefFile *prefs_write_open(const gchar *path)
72 {
73         PrefFile *pfile;
74         gchar *tmppath;
75         FILE *fp;
76
77         cm_return_val_if_fail(path != NULL, NULL);
78
79         if (prefs_is_readonly(path)) {
80                 g_warning("no write permission on '%s'", path);
81                 return NULL;
82         }
83
84         tmppath = g_strconcat(path, ".tmp", NULL);
85         if ((fp = g_fopen(tmppath, "wb")) == NULL) {
86                 FILE_OP_ERROR(tmppath, "fopen");
87                 g_free(tmppath);
88                 return NULL;
89         }
90
91         if (change_file_mode_rw(fp, tmppath) < 0)
92                 FILE_OP_ERROR(tmppath, "chmod");
93
94         g_free(tmppath);
95
96         pfile = g_new(PrefFile, 1);
97         pfile->fp = fp;
98         pfile->orig_fp = NULL;
99         pfile->path = g_strdup(path);
100         pfile->writing = TRUE;
101
102         return pfile;
103 }
104
105 gboolean prefs_common_get_flush_metadata (void);
106
107 /*!
108  *\brief        Close and free preferences file
109  *              Creates final file from temp, creates backup
110  *
111  *\param        pfile Preferences file struct
112  *
113  *\return       0 on success, -1 on failure
114  */
115 #define PREFS_FILE_FREE() \
116 { \
117   g_free(path); \
118   g_free(tmppath); \
119   g_free(bakpath); \
120   g_free(pfile); \
121 }
122 gint prefs_file_close(PrefFile *pfile)
123 {
124         FILE *fp, *orig_fp;
125         gchar *path;
126         gchar *tmppath;
127         gchar *bakpath = NULL;
128         gchar buf[BUFFSIZE];
129
130         cm_return_val_if_fail(pfile != NULL, -1);
131
132         fp = pfile->fp;
133         orig_fp = pfile->orig_fp;
134         path = pfile->path;
135
136         if (!pfile->writing) {
137                 fclose(fp);
138                 g_free(pfile);
139                 g_free(path);
140                 return 0;
141         }
142
143         if (orig_fp) {
144                 while (fgets(buf, sizeof(buf), orig_fp) != NULL) {
145                         /* next block */
146                         if (buf[0] == '[') {
147                                 if (fputs(buf, fp)  == EOF) {
148                                         g_warning("failed to write configuration to file");
149                                         prefs_file_close_revert(pfile);
150                                 
151                                         return -1;
152                                 }
153                                 break;
154                         }
155                 }
156                 
157                 while (fgets(buf, sizeof(buf), orig_fp) != NULL)
158                         if (fputs(buf, fp) == EOF) {
159                                 g_warning("failed to write configuration to file");
160                                 prefs_file_close_revert(pfile);                 
161                                 
162                                 return -1;
163                         }
164                 fclose(orig_fp);
165         }
166
167         tmppath = g_strconcat(path, ".tmp", NULL);
168
169         
170         if (prefs_common_get_flush_metadata() && fsync(fileno(fp)) < 0) {
171                 FILE_OP_ERROR(tmppath, "fsync");
172                 fclose(fp);
173                 claws_unlink(tmppath);
174                 g_free(path);
175                 g_free(tmppath);
176                 return -1;
177         }
178
179         if (fclose(fp) == EOF) {
180                 FILE_OP_ERROR(tmppath, "fclose");
181                 claws_unlink(tmppath);
182                 g_free(path);
183                 g_free(tmppath);
184                 return -1;
185         }
186
187         if (is_file_exist(path)) {
188                 bakpath = g_strconcat(path, ".bak", NULL);
189 #ifdef G_OS_WIN32
190                 claws_unlink(bakpath);
191 #endif
192                 if (g_rename(path, bakpath) < 0) {
193                         FILE_OP_ERROR(path, "rename");
194                         claws_unlink(tmppath);
195                         g_free(path);
196                         g_free(tmppath);
197                         g_free(bakpath);
198                         return -1;
199                 }
200         }
201
202 #ifdef G_OS_WIN32
203         claws_unlink(path);
204 #endif
205         if (g_rename(tmppath, path) < 0) {
206                 FILE_OP_ERROR(tmppath, "rename");
207                 claws_unlink(tmppath);
208                 g_free(path);
209                 g_free(tmppath);
210                 g_free(bakpath);
211                 return -1;
212         }
213
214         g_free(pfile);
215         g_free(path);
216         g_free(tmppath);
217         g_free(bakpath);
218         return 0;
219 }
220 #undef PREFS_FILE_FREE
221
222 /*!
223  *\brief        Close and free preferences file, delete temp file
224  *
225  *\param        pfile Preferences file struct
226  */
227 gint prefs_file_close_revert(PrefFile *pfile)
228 {
229         gchar *tmppath = NULL;
230
231         cm_return_val_if_fail(pfile != NULL, -1);
232
233         if (pfile->orig_fp)
234                 fclose(pfile->orig_fp);
235         if (pfile->writing)
236                 tmppath = g_strconcat(pfile->path, ".tmp", NULL);
237         fclose(pfile->fp);
238         if (pfile->writing) {
239                 if (claws_unlink(tmppath) < 0) FILE_OP_ERROR(tmppath, "unlink");
240                 g_free(tmppath);
241         }
242         g_free(pfile->path);
243         g_free(pfile);
244
245         return 0;
246 }
247
248 /*!
249  *\brief        Check if "path" is a file and readonly
250  */
251 static gboolean prefs_is_readonly(const gchar * path)
252 {
253         if (path == NULL)
254                 return TRUE;
255
256         return (access(path, W_OK) != 0 && access(path, F_OK) == 0);
257 }
258
259 /*!
260  *\brief        Check if "rcfile" is in rcdir, a file and readonly
261  */
262 gboolean prefs_rc_is_readonly(const gchar * rcfile)
263 {
264         gboolean result;
265         gchar * rcpath;
266
267         if (rcfile == NULL)
268                 return TRUE;
269
270         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, rcfile, NULL);
271         result = prefs_is_readonly(rcpath);
272         g_free(rcpath);
273
274         return result;
275 }
276
277 /*!
278  *\brief        Selects current section in preferences file
279  *              Creates section if file is written
280  *
281  *\param        pfile Preferences file struct
282  *
283  *\return       0 on success, -1 on failure
284  */
285 gint prefs_set_block_label(PrefFile *pfile, const gchar *label)
286 {
287         gchar *block_label;
288         gchar buf[BUFFSIZE];
289         
290         block_label = g_strdup_printf("[%s]", label);
291         if (!pfile->writing) {
292                 while (fgets(buf, sizeof(buf), pfile->fp) != NULL) {
293                         gint val;
294                         
295                         val = strncmp(buf, block_label, strlen(block_label));
296                         if (val == 0) {
297                                 debug_print("Found %s\n", block_label);
298                                 break;
299                         }
300                 }
301         } else {
302                 if ((pfile->orig_fp = g_fopen(pfile->path, "rb")) != NULL) {
303                         gboolean block_matched = FALSE;
304
305                         while (fgets(buf, sizeof(buf), pfile->orig_fp) != NULL) {
306                                 gint val;
307                                 
308                                 val = strncmp(buf, block_label, strlen(block_label));
309                                 if (val == 0) {
310                                         debug_print("Found %s\n", block_label);
311                                         block_matched = TRUE;
312                                         break;
313                                 } else {
314                                         if (fputs(buf, pfile->fp) == EOF) {
315                                                 g_warning("failed to write configuration to file");
316                                                 prefs_file_close_revert(pfile);
317                                                 g_free(block_label);
318                                                 
319                                                 return -1;
320                                         }
321                                 }
322                         }
323                         
324                         if (!block_matched) {
325                                 fclose(pfile->orig_fp);
326                                 pfile->orig_fp = NULL;
327                         }
328                         
329                         if (fputs(block_label, pfile->fp) == EOF ||
330                             fputc('\n', pfile->fp) == EOF) {
331                                 g_warning("failed to write configuration to file");
332                                 prefs_file_close_revert(pfile);
333                                 g_free(block_label);
334                                                 
335                                 return -1;
336                         }
337                 }
338         }
339
340         g_free(block_label);
341
342         return 0;
343 }