2006-04-06 [mones] 2.1.0cvs7
[claws.git] / src / common / prefs.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws 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 2 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, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
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 /*!
32  *\brief        Open preferences file for reading
33  *
34  *\param        path Filename with path of preferences file to read
35  *
36  *\return       PrefFile * preferences file struct
37  */
38 PrefFile *prefs_read_open(const gchar *path)
39 {
40         PrefFile *pfile;
41         FILE *fp;
42
43         g_return_val_if_fail(path != NULL, NULL);
44
45         if ((fp = g_fopen(path, "rb")) == NULL) {
46                 FILE_OP_ERROR(path, "fopen");
47                 return NULL;
48         }
49
50         pfile = g_new(PrefFile, 1);
51         pfile->fp = fp;
52         pfile->orig_fp = NULL;
53         pfile->path = g_strdup(path);
54         pfile->writing = FALSE;
55
56         return pfile;
57 }
58
59 /*!
60  *\brief        Open preferences file for writing
61  *              Prefs are written to a temp file: Call prefs_write_close()
62  *              to rename this to the final filename
63  *
64  *\param        path Filename with path of preferences file to write
65  *
66  *\return       PrefFile * preferences file struct
67  */
68 PrefFile *prefs_write_open(const gchar *path)
69 {
70         PrefFile *pfile;
71         gchar *tmppath;
72         FILE *fp;
73
74         g_return_val_if_fail(path != NULL, NULL);
75
76         if (prefs_is_readonly(path)) {
77                 g_warning("no permission - %s\n", path);
78                 return NULL;
79         }
80
81         tmppath = g_strconcat(path, ".tmp", NULL);
82         if ((fp = g_fopen(tmppath, "wb")) == NULL) {
83                 FILE_OP_ERROR(tmppath, "fopen");
84                 g_free(tmppath);
85                 return NULL;
86         }
87
88         if (change_file_mode_rw(fp, tmppath) < 0)
89                 FILE_OP_ERROR(tmppath, "chmod");
90
91         g_free(tmppath);
92
93         pfile = g_new(PrefFile, 1);
94         pfile->fp = fp;
95         pfile->orig_fp = NULL;
96         pfile->path = g_strdup(path);
97         pfile->writing = TRUE;
98
99         return pfile;
100 }
101
102 /*!
103  *\brief        Close and free preferences file
104  *              Creates final file from temp, creates backup
105  *
106  *\param        pfile Preferences file struct
107  *
108  *\return       0 on success, -1 on failure
109  */
110 #define PREFS_FILE_FREE() \
111 { \
112   g_free(path); \
113   g_free(tmppath); \
114   g_free(bakpath); \
115   g_free(pfile); \
116 }
117 gint prefs_file_close(PrefFile *pfile)
118 {
119         FILE *fp, *orig_fp;
120         gchar *path;
121         gchar *tmppath;
122         gchar *bakpath = NULL;
123         gchar buf[BUFFSIZE];
124
125         g_return_val_if_fail(pfile != NULL, -1);
126
127         fp = pfile->fp;
128         orig_fp = pfile->orig_fp;
129         path = pfile->path;
130
131         if (!pfile->writing) {
132                 fclose(fp);
133                 g_free(pfile);
134                 g_free(path);
135                 return 0;
136         }
137
138         if (orig_fp) {
139                 while (fgets(buf, sizeof(buf), orig_fp) != NULL) {
140                         /* next block */
141                         if (buf[0] == '[') {
142                                 if (fputs(buf, fp)  == EOF) {
143                                         g_warning("failed to write configuration to file\n");
144                                         fclose(orig_fp);
145                                         prefs_file_close_revert(pfile);
146                                 
147                                         return -1;
148                                 }
149                                 break;
150                         }
151                 }
152                 
153                 while (fgets(buf, sizeof(buf), orig_fp) != NULL)
154                         if (fputs(buf, fp) == EOF) {
155                                 g_warning("failed to write configuration to file\n");
156                                 fclose(orig_fp);
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                 g_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                 g_unlink(bakpath);
177 #endif
178                 if (rename(path, bakpath) < 0) {
179                         FILE_OP_ERROR(path, "rename");
180                         g_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         g_unlink(path);
190 #endif
191         if (rename(tmppath, path) < 0) {
192                 FILE_OP_ERROR(tmppath, "rename");
193                 g_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 (g_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 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                                                 fclose(pfile->orig_fp);
303                                                 prefs_file_close_revert(pfile);
304                                                 g_free(block_label);
305                                                 
306                                                 return -1;
307                                         }
308                                 }
309                         }
310                         
311                         if (!block_matched) {
312                                 fclose(pfile->orig_fp);
313                                 pfile->orig_fp = NULL;
314                         }
315                         
316                         if (fputs(block_label, pfile->fp) == EOF ||
317                             fputc('\n', pfile->fp) == EOF) {
318                                 g_warning("failed to write configuration to file\n");
319                                 fclose(pfile->orig_fp);
320                                 prefs_file_close_revert(pfile);
321                                 g_free(block_label);
322                                                 
323                                 return -1;
324                         }
325                 }
326         }
327
328         g_free(block_label);
329
330         return 0;
331 }