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