Wrap file I/O to claws_* to benefit from custom locking when
[claws.git] / src / common / xmlprops.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2002-2012 Match Grun 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 /*
21  * General functions for saving properties to an XML file.
22  *
23  * The file is structured as follows:
24  *
25  *   <property-list>
26  *     <property name="first-name" value="Axle" >/
27  *     <property name="last-name"  value="Rose" >/
28  *   </property-list>
29  *              
30  * ***********************************************************************
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #  include "config.h"
35 #include "claws-features.h"
36 #endif
37
38 #include <glib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdlib.h>
42
43 #include "prefs.h"
44 #include "xml.h"
45 #include "mgutils.h"
46 #include "xmlprops.h"
47 #include "utils.h"
48 #include "claws_io.h"
49
50 /* Element tag names */
51 #define XMLS_ELTAG_PROP_LIST     "property-list"
52 #define XMLS_ELTAG_PROPERTY      "property"
53
54 /* Attribute tag names */
55 #define XMLS_ATTAG_NAME          "name"
56 #define XMLS_ATTAG_VALUE         "value"
57
58 static void xmlprops_clear              ( XmlProperty *props );
59
60 typedef struct _HashLoopData {
61         FILE *fp;
62         int error;
63 } HashLoopData;
64
65 /*
66  * Create new props.
67  */
68 XmlProperty *xmlprops_create( void ) {
69         XmlProperty *props;
70
71         props = g_new0( XmlProperty, 1 );
72         props->path = NULL;
73         props->encoding = NULL;
74         props->propertyTable = g_hash_table_new( g_str_hash, g_str_equal );
75         props->retVal = MGU_SUCCESS;
76         return props;
77 }
78
79 /*
80  * Properties - file path.
81  */
82 void xmlprops_set_path( XmlProperty *props, const gchar *value ) {
83         cm_return_if_fail( props != NULL );
84         props->path = mgu_replace_string( props->path, value );
85 }
86
87 /*
88  * Free hash table visitor function.
89  */
90 static gint xmlprops_free_entry_vis( gpointer key, gpointer value, gpointer data ) {
91         g_free( key );
92         g_free( value );
93         key = NULL;
94         value = NULL;
95         return TRUE;
96 }
97
98 /*
99  * Clear all properties.
100  * Enter: props Property object.
101  */
102 static void xmlprops_clear( XmlProperty *props ) {
103         cm_return_if_fail( props != NULL );
104         g_hash_table_foreach_remove(
105                 props->propertyTable, xmlprops_free_entry_vis, NULL );
106 }
107
108 /*
109  * Free props.
110  * Enter: props Property object.
111  */
112 void xmlprops_free( XmlProperty *props ) {
113         cm_return_if_fail( props != NULL );
114
115         /* Clear property table */
116         xmlprops_clear( props );
117         g_hash_table_destroy( props->propertyTable );
118
119         /* Free up internal objects */
120         g_free( props->path );
121         g_free( props->encoding );
122
123         props->path = NULL;
124         props->encoding = NULL;
125         props->propertyTable = NULL;
126         props->retVal = 0;
127
128         g_free( props );
129 }
130
131 static int xmlprops_write_elem_s( FILE *fp, gint lvl, gchar *name ) {
132         gint i;
133         for( i = 0; i < lvl; i++ ) {
134                 if(claws_fputs( "  ", fp ) == EOF)
135                         return -1;
136         }
137         if(claws_fputs( "<", fp ) == EOF)
138                 return -1;
139         if(claws_fputs( name, fp ) == EOF)
140                 return -1;
141         
142         return 0;
143 }
144
145 static int xmlprops_write_elem_e( FILE *fp, gint lvl, gchar *name ) {
146         gint i;
147         for( i = 0; i < lvl; i++ ) {
148                 if(claws_fputs( "  ", fp ) == EOF)
149                         return -1;
150         }
151         if(claws_fputs( "</", fp ) == EOF)
152                 return -1;
153         if(claws_fputs( name, fp ) == EOF)
154                 return -1;
155         if(claws_fputs( ">\n", fp ) == EOF)
156                 return -1;
157         
158         return 0;
159 }
160
161 static int xmlprops_write_attr( FILE *fp, gchar *name, gchar *value ) {
162         if(claws_fputs( " ", fp ) == EOF)
163                 return -1;
164         if(claws_fputs( name, fp ) == EOF)
165                 return -1;
166         if(claws_fputs( "=\"", fp ) == EOF)
167                 return -1;
168         if(xml_file_put_escape_str( fp, value ) < 0)
169                 return -1;
170         if(claws_fputs( "\"", fp ) == EOF)
171                 return -1;
172         
173         return 0;
174 }
175
176 static void xmlprops_write_vis( gpointer key, gpointer value, gpointer d ) {
177         HashLoopData *data = (HashLoopData *)d;
178
179         if(xmlprops_write_elem_s( data->fp, 1, XMLS_ELTAG_PROPERTY ) < 0)
180                 data->error = 1;
181         if(xmlprops_write_attr( data->fp, XMLS_ATTAG_NAME, key ) < 0)
182                 data->error = 1;
183         if(xmlprops_write_attr( data->fp, XMLS_ATTAG_VALUE, value ) < 0)
184                 data->error = 1;
185         if(claws_fputs( " />\n", data->fp ) == EOF)
186                 data->error = 1;
187 }
188
189 static gint xmlprops_write_to( XmlProperty *props, const gchar *fileSpec ) {
190         PrefFile *pfile;
191         FILE *fp;
192         HashLoopData data;
193
194         props->retVal = MGU_OPEN_FILE;
195         pfile = prefs_write_open( fileSpec );
196         if( pfile ) {
197                 fp = pfile->fp;
198                 if(fprintf( fp, "<?xml version=\"1.0\"" ) < 0)
199                         goto revert;
200                 if( props->encoding && *props->encoding ) {
201                         if(fprintf( fp, " encoding=\"%s\"", props->encoding ) < 0)
202                                 goto revert;
203                 }
204                 if(fprintf( fp, " ?>\n" ) < 0)
205                         goto revert;
206                 if(xmlprops_write_elem_s( fp, 0, XMLS_ELTAG_PROP_LIST ) < 0)
207                         goto revert;
208                 if(claws_fputs( ">\n", fp ) == EOF)
209                         goto revert;
210
211                 /* Output all properties */
212                 data.fp = fp;
213                 data.error = 0;
214                 g_hash_table_foreach( props->propertyTable, xmlprops_write_vis, &data );
215
216                 if (data.error)
217                         goto revert;
218
219                 if(xmlprops_write_elem_e( fp, 0, XMLS_ELTAG_PROP_LIST ) < 0)
220                         goto revert;
221                 
222                 props->retVal = MGU_SUCCESS;
223                 if( prefs_file_close( pfile ) < 0 ) {
224                         props->retVal = MGU_ERROR_WRITE;
225                         goto out;
226                 }
227                 goto out;
228 revert:
229                 props->retVal = MGU_ERROR_WRITE;
230                 if( prefs_file_close_revert( pfile ) < 0 ) {
231                         props->retVal = MGU_ERROR_WRITE;
232                 }
233         
234         }
235 out:
236         return props->retVal;
237 }
238
239 /*
240  * Save properties to file.
241  * return: Status code.
242  */
243 gint xmlprops_save_file( XmlProperty *props ) {
244         cm_return_val_if_fail( props != NULL, -1 );
245
246         props->retVal = MGU_NO_FILE;
247         if( props->path == NULL || *props->path == '\0' ) return props->retVal;
248         xmlprops_write_to( props, props->path );
249
250         return props->retVal;
251 }
252
253 static void xmlprops_save_property(
254                 XmlProperty *props, const gchar *name, const gchar *value )
255 {
256         gchar *key;
257         gchar *val;
258
259         if( strlen( name ) == 0 ) return;
260         if( strlen( value ) == 0 ) return;
261         if( g_hash_table_lookup( props->propertyTable, name ) ) return;
262         key = g_strdup( name );
263         val = g_strdup( value );
264         g_hash_table_insert( props->propertyTable, key, val );
265 }
266
267 #define ATTR_BUFSIZE 256
268
269 static void xmlprops_read_props( XmlProperty *props, XMLFile *file ) {
270         GList *attr;
271         gchar *name, *value;
272         gchar *pName;
273         gchar *pValue;
274
275         while( TRUE ) {
276                 pName = g_strdup("");
277                 pValue = g_strdup("");
278                 if (! file->level ) break;
279                 xml_parse_next_tag( file );
280                 if( xml_compare_tag( file, XMLS_ELTAG_PROPERTY ) ) {
281                         attr = xml_get_current_tag_attr( file );
282                         while( attr ) {
283                                 name = ( ( XMLAttr * ) attr->data )->name;
284                                 value = ( ( XMLAttr * ) attr->data )->value;
285                                 if( strcmp( name, XMLS_ATTAG_NAME ) == 0 ) {
286                                         g_free(pName);
287                                         pName = g_strdup( value );
288                                 }
289                                 else if( strcmp( name, XMLS_ATTAG_VALUE ) == 0 ) {
290                                         g_free(pValue);
291                                         pValue = g_strdup( value );
292                                 }
293                                 attr = g_list_next( attr );
294                         }
295                         xmlprops_save_property( props, pName, pValue );
296                 }
297                 g_free(pName);
298                 g_free(pValue);
299         }
300 }
301
302 #undef ATTR_BUFSIZE
303
304 /*
305  * Load properties from file.
306  * return: Status code.
307  */
308 gint xmlprops_load_file( XmlProperty *props ) {
309         XMLFile *file = NULL;
310
311         cm_return_val_if_fail( props != NULL, -1 );
312         props->retVal = MGU_NO_FILE;
313         file = xml_open_file( props->path );
314         if( file == NULL ) {
315                 return props->retVal;
316         }
317
318         props->retVal = MGU_BAD_FORMAT;
319         if( xml_get_dtd( file ) == 0 ) {
320                 if( xml_parse_next_tag( file ) == 0 ) {
321                         if( xml_compare_tag( file, XMLS_ELTAG_PROP_LIST ) ) {
322                                 xmlprops_read_props( props, file );
323                                 props->retVal = MGU_SUCCESS;
324                         }
325                 }
326         }
327         xml_close_file( file );
328
329         return props->retVal;
330 }
331
332 /*
333  * Set property.
334  * Enter: props Property object.
335  *        name  Property name.
336  *        value New value to save.
337  */
338 void xmlprops_set_property(
339                 XmlProperty *props, const gchar *name, const gchar *value )
340 {
341         gchar *key = NULL;
342         gchar *val;
343
344         cm_return_if_fail( props != NULL );
345         if( name == NULL || strlen( name ) == 0 ) return;
346         if( value == NULL || strlen( value ) == 0 ) return;
347         val = g_hash_table_lookup( props->propertyTable, name );
348         if( val == NULL ) {
349                 key = g_strdup( name );
350         }
351         else {
352                 g_free( val );
353         }
354         val = g_strdup( value );
355         g_hash_table_insert( props->propertyTable, key, val );
356 }
357
358 /*
359  * Set property to integer value.
360  * Enter: props Property object.
361  *        name  Property name.
362  *        value New value to save.
363  */
364 void xmlprops_set_property_i(
365                 XmlProperty *props, const gchar *name, const gint value )
366 {
367         gchar buf[32];
368
369         cm_return_if_fail( props != NULL );
370         sprintf( buf, "%d", value );
371         xmlprops_set_property( props, name, buf );
372 }
373
374 /*
375  * Set property to boolean value.
376  * Enter: props Property object.
377  *        name  Property name.
378  *        value New value to save.
379  */
380 void xmlprops_set_property_b(
381                 XmlProperty *props, const gchar *name, const gboolean value )
382 {
383         cm_return_if_fail( props != NULL );
384         if( value ) {
385                 xmlprops_set_property( props, name, "y" );
386         }
387         else {
388                 xmlprops_set_property( props, name, "n" );
389         }
390 }
391
392 /*
393  * Get property into a buffer.
394  * Enter:  props Property object.
395  *         name  Property name.
396  * Return: value found, or NULL if none. Should be g_free() when done.
397  */
398 void xmlprops_get_property_s(
399                 XmlProperty *props, const gchar *name, gchar *buffer ) {
400         gchar *val;
401
402         cm_return_if_fail( props != NULL );
403         if( buffer == NULL ) return;
404         val = g_hash_table_lookup( props->propertyTable, name );
405         if( val ) {
406                 strcpy( buffer, val );
407         }
408 }
409
410 /*
411  * Get property as integer value.
412  * Enter:  props Property object.
413  *         name  Property name.
414  * Return: value found, or zero if not found.
415  */
416 gint xmlprops_get_property_i( XmlProperty *props, const gchar *name ) {
417         gchar *val;
418         gchar *endptr;
419         gint value;
420
421         value = 0;
422         cm_return_val_if_fail( props != NULL, value );
423         val = g_hash_table_lookup( props->propertyTable, name );
424         if( val ) {
425                 endptr = NULL;
426                 value = strtol( val, &endptr, 10 );
427         }
428         return value;
429 }
430
431 /*
432  * Get property as boolean value.
433  * Enter:  props Property object.
434  *         name  Property name.
435  * Return: value found, or FALSE if not found.
436  */
437 gboolean xmlprops_get_property_b( XmlProperty *props, const gchar *name ) {
438         gchar *val;
439         gboolean value;
440
441         value = FALSE;
442         cm_return_val_if_fail( props != NULL, value );
443         val = g_hash_table_lookup( props->propertyTable, name );
444         if( val ) {
445                 value = ( g_ascii_strcasecmp( val, "y" ) == 0 );
446         }
447         return value;
448 }
449
450 /*
451 * End of Source.
452 */
453
454