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