fixed acessing members of freed pfile struct
[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         gchar *tmppath;
42         FILE *fp;
43
44         g_return_val_if_fail(path != NULL, NULL);
45
46         if ((fp = fopen(path, "rb")) == NULL) {
47                 FILE_OP_ERROR(tmppath, "fopen");
48                 return NULL;
49         }
50
51         pfile = g_new(PrefFile, 1);
52         pfile->fp = fp;
53         pfile->orig_fp = NULL;
54         pfile->path = g_strdup(path);
55         pfile->writing = FALSE;
56
57         return pfile;
58 }
59
60 /*!
61  *\brief        Open preferences file for writing
62  *              Prefs are written to a temp file: Call prefs_write_close()
63  *              to rename this to the final filename
64  *
65  *\param        path Filename with path of preferences file to write
66  *
67  *\return       PrefFile * preferences file struct
68  */
69 PrefFile *prefs_write_open(const gchar *path)
70 {
71         PrefFile *pfile;
72         gchar *tmppath;
73         FILE *fp;
74
75         g_return_val_if_fail(path != NULL, NULL);
76
77         if (prefs_is_readonly(path)) {
78                 g_warning("no permission - %s\n", path);
79                 return NULL;
80         }
81
82         tmppath = g_strconcat(path, ".tmp", NULL);
83         if ((fp = fopen(tmppath, "wb")) == NULL) {
84                 FILE_OP_ERROR(tmppath, "fopen");
85                 g_free(tmppath);
86                 return NULL;
87         }
88
89         if (change_file_mode_rw(fp, tmppath) < 0)
90                 FILE_OP_ERROR(tmppath, "chmod");
91
92         g_free(tmppath);
93
94         pfile = g_new(PrefFile, 1);
95         pfile->fp = fp;
96         pfile->orig_fp = NULL;
97         pfile->path = g_strdup(path);
98         pfile->writing = TRUE;
99
100         return pfile;
101 }
102
103 /*!
104  *\brief        Close and free preferences file
105  *              Creates final file from temp, creates backup
106  *
107  *\param        pfile Preferences file struct
108  *
109  *\return       0 on success, -1 on failure
110  */
111 #define PREFS_FILE_FREE() \
112 { \
113   if (path)     g_free(path); \
114   if (tmppath)  g_free(tmppath); \
115   if (bakpath)  g_free(bakpath); \
116   if (pfile)    g_free(pfile); \
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         g_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                 fclose(fp);
134                 g_free(pfile);
135                 g_free(path);
136                 return 0;
137         }
138
139         if (orig_fp) {
140                 while (fgets(buf, sizeof(buf), orig_fp) != NULL) {
141                         /* next block */
142                         if (buf[0] == '[') {
143                                 if (fputs(buf, fp)  == EOF) {
144                                         g_warning("failed to write configuration to file\n");
145                                         fclose(orig_fp);
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                                 fclose(orig_fp);
158                                 prefs_file_close_revert(pfile);                 
159                                 
160                                 return -1;
161                         }
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 }