2005-03-04 [colin] 1.0.1cvs22.1
[claws.git] / src / common / prefs.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 = 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 = 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   if (path)     g_free(path); \
113   if (tmppath)  g_free(tmppath); \
114   if (bakpath)  g_free(bakpath); \
115   if (pfile)    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                 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                 if (rename(path, bakpath) < 0) {
176                         FILE_OP_ERROR(path, "rename");
177                         unlink(tmppath);
178                         g_free(path);
179                         g_free(tmppath);
180                         g_free(bakpath);
181                         return -1;
182                 }
183         }
184
185         if (rename(tmppath, path) < 0) {
186                 FILE_OP_ERROR(tmppath, "rename");
187                 unlink(tmppath);
188                 g_free(path);
189                 g_free(tmppath);
190                 g_free(bakpath);
191                 return -1;
192         }
193
194         g_free(pfile);
195         g_free(path);
196         g_free(tmppath);
197         g_free(bakpath);
198         return 0;
199 }
200 #undef PREFS_FILE_FREE
201
202 /*!
203  *\brief        Close and free preferences file, delete temp file
204  *
205  *\param        pfile Preferences file struct
206  */
207 gint prefs_file_close_revert(PrefFile *pfile)
208 {
209         gchar *tmppath;
210
211         g_return_val_if_fail(pfile != NULL, -1);
212
213         if (pfile->orig_fp)
214                 fclose(pfile->orig_fp);
215         if (pfile->writing)
216                 tmppath = g_strconcat(pfile->path, ".tmp", NULL);
217         fclose(pfile->fp);
218         if (pfile->writing) {
219                 if (unlink(tmppath) < 0) FILE_OP_ERROR(tmppath, "unlink");
220                 g_free(tmppath);
221         }
222         g_free(pfile->path);
223         g_free(pfile);
224
225         return 0;
226 }
227
228 /*!
229  *\brief        Check if "path" is a file and readonly
230  */
231 gboolean prefs_is_readonly(const gchar * path)
232 {
233         if (path == NULL)
234                 return TRUE;
235
236         return (access(path, W_OK) != 0 && access(path, F_OK) == 0);
237 }
238
239 /*!
240  *\brief        Check if "rcfile" is in rcdir, a file and readonly
241  */
242 gboolean prefs_rc_is_readonly(const gchar * rcfile)
243 {
244         gboolean result;
245         gchar * rcpath;
246
247         if (rcfile == NULL)
248                 return TRUE;
249
250         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, rcfile, NULL);
251         result = prefs_is_readonly(rcpath);
252         g_free(rcpath);
253
254         return result;
255 }
256
257 /*!
258  *\brief        Selects current section in preferences file
259  *              Creates section if file is written
260  *
261  *\param        pfile Preferences file struct
262  *
263  *\return       0 on success, -1 on failure
264  */
265 gint prefs_set_block_label(PrefFile *pfile, const gchar *label)
266 {
267         gchar *block_label;
268         gchar buf[BUFFSIZE];
269         
270         block_label = g_strdup_printf("[%s]", label);
271         if (!pfile->writing) {
272                 while (fgets(buf, sizeof(buf), pfile->fp) != NULL) {
273                         gint val;
274                         
275                         val = strncmp(buf, block_label, strlen(block_label));
276                         if (val == 0) {
277                                 debug_print("Found %s\n", block_label);
278                                 break;
279                         }
280                 }
281         } else {
282                 if ((pfile->orig_fp = fopen(pfile->path, "rb")) != NULL) {
283                         gboolean block_matched = FALSE;
284
285                         while (fgets(buf, sizeof(buf), pfile->orig_fp) != NULL) {
286                                 gint val;
287                                 
288                                 val = strncmp(buf, block_label, strlen(block_label));
289                                 if (val == 0) {
290                                         debug_print("Found %s\n", block_label);
291                                         block_matched = TRUE;
292                                         break;
293                                 } else {
294                                         if (fputs(buf, pfile->fp) == EOF) {
295                                                 g_warning("failed to write configuration to file\n");
296                                                 fclose(pfile->orig_fp);
297                                                 prefs_file_close_revert(pfile);
298                                                 g_free(block_label);
299                                                 
300                                                 return -1;
301                                         }
302                                 }
303                         }
304                         
305                         if (!block_matched) {
306                                 fclose(pfile->orig_fp);
307                                 pfile->orig_fp = NULL;
308                         }
309                         
310                         if (fputs(block_label, pfile->fp) == EOF ||
311                             fputc('\n', pfile->fp) == EOF) {
312                                 g_warning("failed to write configuration to file\n");
313                                 fclose(pfile->orig_fp);
314                                 prefs_file_close_revert(pfile);
315                                 g_free(block_label);
316                                                 
317                                 return -1;
318                         }
319                 }
320         }
321
322         g_free(block_label);
323
324         return 0;
325 }