Removed unused macro from prefs.c.
[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_file_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 gint prefs_file_close(PrefFile *pfile)
116 {
117         FILE *fp, *orig_fp;
118         gchar *path;
119         gchar *tmppath;
120         gchar *bakpath = NULL;
121         gchar buf[BUFFSIZE];
122
123         cm_return_val_if_fail(pfile != NULL, -1);
124
125         fp = pfile->fp;
126         orig_fp = pfile->orig_fp;
127         path = pfile->path;
128
129         if (!pfile->writing) {
130                 fclose(fp);
131                 g_free(pfile);
132                 g_free(path);
133                 return 0;
134         }
135
136         if (orig_fp) {
137                 while (fgets(buf, sizeof(buf), orig_fp) != NULL) {
138                         /* next block */
139                         if (buf[0] == '[') {
140                                 if (fputs(buf, fp)  == EOF) {
141                                         g_warning("failed to write configuration to file");
142                                         prefs_file_close_revert(pfile);
143                                 
144                                         return -1;
145                                 }
146                                 break;
147                         }
148                 }
149                 
150                 while (fgets(buf, sizeof(buf), orig_fp) != NULL)
151                         if (fputs(buf, fp) == EOF) {
152                                 g_warning("failed to write configuration to file");
153                                 prefs_file_close_revert(pfile);                 
154                                 
155                                 return -1;
156                         }
157                 fclose(orig_fp);
158         }
159
160         tmppath = g_strconcat(path, ".tmp", NULL);
161
162         
163         if (prefs_common_get_flush_metadata() && fsync(fileno(fp)) < 0) {
164                 FILE_OP_ERROR(tmppath, "fsync");
165                 fclose(fp);
166                 claws_unlink(tmppath);
167                 g_free(path);
168                 g_free(tmppath);
169                 return -1;
170         }
171
172         if (fclose(fp) == EOF) {
173                 FILE_OP_ERROR(tmppath, "fclose");
174                 claws_unlink(tmppath);
175                 g_free(path);
176                 g_free(tmppath);
177                 return -1;
178         }
179
180         if (is_file_exist(path)) {
181                 bakpath = g_strconcat(path, ".bak", NULL);
182 #ifdef G_OS_WIN32
183                 claws_unlink(bakpath);
184 #endif
185                 if (g_rename(path, bakpath) < 0) {
186                         FILE_OP_ERROR(path, "rename");
187                         claws_unlink(tmppath);
188                         g_free(path);
189                         g_free(tmppath);
190                         g_free(bakpath);
191                         return -1;
192                 }
193         }
194
195 #ifdef G_OS_WIN32
196         claws_unlink(path);
197 #endif
198         if (g_rename(tmppath, path) < 0) {
199                 FILE_OP_ERROR(tmppath, "rename");
200                 claws_unlink(tmppath);
201                 g_free(path);
202                 g_free(tmppath);
203                 g_free(bakpath);
204                 return -1;
205         }
206
207         g_free(pfile);
208         g_free(path);
209         g_free(tmppath);
210         g_free(bakpath);
211         return 0;
212 }
213
214 /*!
215  *\brief        Close and free preferences file, delete temp file
216  *
217  *\param        pfile Preferences file struct
218  */
219 gint prefs_file_close_revert(PrefFile *pfile)
220 {
221         gchar *tmppath = NULL;
222
223         cm_return_val_if_fail(pfile != NULL, -1);
224
225         if (pfile->orig_fp)
226                 fclose(pfile->orig_fp);
227         if (pfile->writing)
228                 tmppath = g_strconcat(pfile->path, ".tmp", NULL);
229         fclose(pfile->fp);
230         if (pfile->writing) {
231                 if (claws_unlink(tmppath) < 0) FILE_OP_ERROR(tmppath, "unlink");
232                 g_free(tmppath);
233         }
234         g_free(pfile->path);
235         g_free(pfile);
236
237         return 0;
238 }
239
240 /*!
241  *\brief        Check if "path" is a file and readonly
242  */
243 static gboolean prefs_is_readonly(const gchar * path)
244 {
245         if (path == NULL)
246                 return TRUE;
247
248         return (access(path, W_OK) != 0 && access(path, F_OK) == 0);
249 }
250
251 /*!
252  *\brief        Check if "rcfile" is in rcdir, a file and readonly
253  */
254 gboolean prefs_rc_is_readonly(const gchar * rcfile)
255 {
256         gboolean result;
257         gchar * rcpath;
258
259         if (rcfile == NULL)
260                 return TRUE;
261
262         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, rcfile, NULL);
263         result = prefs_is_readonly(rcpath);
264         g_free(rcpath);
265
266         return result;
267 }
268
269 /*!
270  *\brief        Selects current section in preferences file
271  *              Creates section if file is written
272  *
273  *\param        pfile Preferences file struct
274  *
275  *\return       0 on success, -1 on failure
276  */
277 gint prefs_set_block_label(PrefFile *pfile, const gchar *label)
278 {
279         gchar *block_label;
280         gchar buf[BUFFSIZE];
281         
282         block_label = g_strdup_printf("[%s]", label);
283         if (!pfile->writing) {
284                 while (fgets(buf, sizeof(buf), pfile->fp) != NULL) {
285                         gint val;
286                         
287                         val = strncmp(buf, block_label, strlen(block_label));
288                         if (val == 0) {
289                                 debug_print("Found %s\n", block_label);
290                                 break;
291                         }
292                 }
293         } else {
294                 if ((pfile->orig_fp = g_fopen(pfile->path, "rb")) != NULL) {
295                         gboolean block_matched = FALSE;
296
297                         while (fgets(buf, sizeof(buf), pfile->orig_fp) != NULL) {
298                                 gint val;
299                                 
300                                 val = strncmp(buf, block_label, strlen(block_label));
301                                 if (val == 0) {
302                                         debug_print("Found %s\n", block_label);
303                                         block_matched = TRUE;
304                                         break;
305                                 } else {
306                                         if (fputs(buf, pfile->fp) == EOF) {
307                                                 g_warning("failed to write configuration to file");
308                                                 prefs_file_close_revert(pfile);
309                                                 g_free(block_label);
310                                                 
311                                                 return -1;
312                                         }
313                                 }
314                         }
315                         
316                         if (!block_matched) {
317                                 fclose(pfile->orig_fp);
318                                 pfile->orig_fp = NULL;
319                         }
320                         
321                         if (fputs(block_label, pfile->fp) == EOF ||
322                             fputc('\n', pfile->fp) == EOF) {
323                                 g_warning("failed to write configuration to file");
324                                 prefs_file_close_revert(pfile);
325                                 g_free(block_label);
326                                                 
327                                 return -1;
328                         }
329                 }
330         }
331
332         g_free(block_label);
333
334         return 0;
335 }