* check for a valid foldername
[modest] / src / modest-conf.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <gconf/gconf-client.h>
31 #include <config.h>
32 #include <string.h>
33 #include <glib/gi18n.h>
34 #include "modest-conf.h"
35 #include "modest-marshal.h"
36 #include <stdio.h>
37
38 static void   modest_conf_class_init     (ModestConfClass *klass);
39
40 static void   modest_conf_init           (ModestConf *obj);
41
42 static void   modest_conf_finalize       (GObject *obj);
43
44 static void   modest_conf_on_change      (GConfClient *client, guint conn_id,
45                                           GConfEntry *entry, gpointer data);
46
47 static GConfValueType modest_conf_type_to_gconf_type (ModestConfValueType value_type, 
48                                                       GError **err);
49
50 /* list my signals */
51 enum {
52         KEY_CHANGED_SIGNAL,
53         LAST_SIGNAL
54 };
55 static guint signals[LAST_SIGNAL] = {0}; 
56
57
58 typedef struct _ModestConfPrivate ModestConfPrivate;
59 struct _ModestConfPrivate {
60         GConfClient *gconf_client;
61 };
62 #define MODEST_CONF_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
63                                                                      MODEST_TYPE_CONF, \
64                                                                      ModestConfPrivate))
65 /* globals */
66 static GObjectClass *parent_class = NULL;
67
68 GType
69 modest_conf_get_type (void)
70 {
71         static GType my_type = 0;
72         if (!my_type) {
73                 static const GTypeInfo my_info = {
74                         sizeof(ModestConfClass),
75                         NULL,           /* base init */
76                         NULL,           /* base finalize */
77                         (GClassInitFunc) modest_conf_class_init,
78                         NULL,           /* class finalize */
79                         NULL,           /* class data */
80                         sizeof(ModestConf),
81                         1,              /* n_preallocs */
82                         (GInstanceInitFunc) modest_conf_init,
83                         NULL
84                 };
85                 my_type = g_type_register_static (G_TYPE_OBJECT,
86                                                   "ModestConf",
87                                                   &my_info, 0);
88         }
89         return my_type;
90 }
91
92 static void
93 modest_conf_class_init (ModestConfClass *klass)
94 {
95         GObjectClass *gobject_class;
96         gobject_class = (GObjectClass*) klass;
97
98         parent_class            = g_type_class_peek_parent (klass);
99         gobject_class->finalize = modest_conf_finalize;
100
101         g_type_class_add_private (gobject_class, sizeof(ModestConfPrivate));
102         
103         signals[KEY_CHANGED_SIGNAL] =
104                 g_signal_new ("key_changed",
105                               G_TYPE_FROM_CLASS (gobject_class),
106                               G_SIGNAL_RUN_FIRST,
107                               G_STRUCT_OFFSET (ModestConfClass,key_changed),
108                               NULL, NULL,
109                               modest_marshal_VOID__STRING_INT_INT,
110                               G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT); 
111 }
112
113 static void
114 modest_conf_init (ModestConf *obj)
115 {
116         GConfClient *conf = NULL;
117         ModestConfPrivate *priv = MODEST_CONF_GET_PRIVATE(obj);
118 /*      GError *err      = NULL; */
119
120         
121         priv->gconf_client = NULL;
122         
123         conf = gconf_client_get_default ();
124         if (!conf) {
125                 g_printerr ("modest: could not get gconf client\n");
126                 return;
127         }
128 /*      gconf_client_add_dir (conf,MODEST_CONF_NAMESPACE, */
129 /*                            GCONF_CLIENT_PRELOAD_NONE, */
130 /*                            &err); */
131 /*      if (err) { */
132 /*              g_printerr ("modest: error %d with gconf_client_add_dir: '%s'\n", */
133 /*                          err->code, err->message); */
134 /*              g_object_unref (conf); */
135 /*              g_error_free (err); */
136 /*              return; */
137 /*      } */
138         
139 /*      gconf_client_notify_add (conf, MODEST_CONF_NAMESPACE, */
140 /*                               modest_conf_on_change, */
141 /*                               obj, NULL, &err); */
142 /*      if (err) { */
143 /*              g_printerr ("modest: gconf_client_notify_add error %d: '%s'\n", */
144 /*                          err->code, err->message); */
145 /*              g_object_unref (conf); */
146 /*              g_error_free (err); */
147 /*              return; */
148 /*      } */
149         priv->gconf_client = conf;      /* all went well! */
150 }
151
152 static void
153 modest_conf_finalize (GObject *obj)
154 {
155         ModestConfPrivate *priv = MODEST_CONF_GET_PRIVATE(obj);
156         if (priv->gconf_client) {
157
158                 gconf_client_suggest_sync (priv->gconf_client, NULL);
159
160                 g_object_unref (priv->gconf_client);
161                 priv->gconf_client = NULL;
162         }       
163
164         G_OBJECT_CLASS(parent_class)->finalize (obj);
165 }
166
167 ModestConf*
168 modest_conf_new (void)
169 {
170         ModestConf *conf;
171         ModestConfPrivate *priv;
172         
173         conf = MODEST_CONF(g_object_new(MODEST_TYPE_CONF, NULL));
174         if (!conf) {
175                 g_printerr ("modest: failed to init ModestConf (GConf)\n");
176                 return NULL;
177         }
178
179         priv = MODEST_CONF_GET_PRIVATE(conf);
180         if (!priv->gconf_client) {
181                 g_printerr ("modest: failed to init gconf\n");
182                 g_object_unref (conf);
183                 return NULL;
184         }
185         
186         return conf;
187 }
188
189
190 gchar*
191 modest_conf_get_string (ModestConf* self, const gchar* key, GError **err)
192 {
193         ModestConfPrivate *priv;
194         
195         g_return_val_if_fail (self, NULL);
196         g_return_val_if_fail (key,  NULL);
197
198         priv = MODEST_CONF_GET_PRIVATE(self);
199         return gconf_client_get_string (priv->gconf_client, key, err);
200 }
201
202
203 gint
204 modest_conf_get_int (ModestConf* self, const gchar* key, GError **err)
205 {
206         ModestConfPrivate *priv;
207
208         g_return_val_if_fail (self, -1);
209         g_return_val_if_fail (key, -1);
210
211         priv = MODEST_CONF_GET_PRIVATE(self);
212         
213         return gconf_client_get_int (priv->gconf_client, key, err);
214 }
215
216
217 gboolean
218 modest_conf_get_bool (ModestConf* self, const gchar* key, GError **err)
219 {
220         ModestConfPrivate *priv;
221
222         g_return_val_if_fail (self, FALSE);
223         g_return_val_if_fail (key, FALSE);
224
225         priv = MODEST_CONF_GET_PRIVATE(self);
226         
227         return gconf_client_get_bool (priv->gconf_client, key, err);
228 }
229
230
231 GSList * 
232 modest_conf_get_list (ModestConf* self, const gchar* key, ModestConfValueType list_type,
233                       GError **err)
234 {
235         ModestConfPrivate *priv;
236         GConfValueType gconf_type;
237        
238         g_return_val_if_fail (self, NULL);
239         g_return_val_if_fail (key,  NULL);
240
241         priv = MODEST_CONF_GET_PRIVATE(self);
242
243         gconf_type = modest_conf_type_to_gconf_type (list_type, err);
244
245         return gconf_client_get_list (priv->gconf_client, key, gconf_type, err);
246 }
247
248
249
250
251 gboolean
252 modest_conf_set_string (ModestConf* self, const gchar* key, const gchar* val,
253                         GError **err)
254 {
255         ModestConfPrivate *priv;
256                 
257         g_return_val_if_fail (self,FALSE);
258         g_return_val_if_fail (key, FALSE);
259         g_return_val_if_fail (val, FALSE);
260         
261         priv = MODEST_CONF_GET_PRIVATE(self);
262
263         if (!gconf_client_key_is_writable(priv->gconf_client,key,err)) {
264                 g_printerr ("modest: '%s' is not writable\n", key);
265                 return FALSE;
266         }
267                         
268         return gconf_client_set_string (priv->gconf_client, key, val, err);
269 }
270
271 gboolean
272 modest_conf_set_int  (ModestConf* self, const gchar* key, gint val,
273                       GError **err)
274 {
275         ModestConfPrivate *priv;
276                 
277         g_return_val_if_fail (self,FALSE);
278         g_return_val_if_fail (key, FALSE);
279         
280         priv = MODEST_CONF_GET_PRIVATE(self);
281
282         if (!gconf_client_key_is_writable(priv->gconf_client,key,err)) {
283                 g_printerr ("modest: '%s' is not writable\n", key);
284                 return FALSE;
285         }
286                         
287         return gconf_client_set_int (priv->gconf_client, key, val, err);
288 }
289
290
291 gboolean
292 modest_conf_set_bool (ModestConf* self, const gchar* key, gboolean val,
293                       GError **err)
294 {
295         ModestConfPrivate *priv;
296                 
297         g_return_val_if_fail (self,FALSE);
298         g_return_val_if_fail (key, FALSE);
299         
300         priv = MODEST_CONF_GET_PRIVATE(self);
301
302         if (!gconf_client_key_is_writable(priv->gconf_client,key, err)) {
303                 g_warning ("modest: '%s' is not writable\n", key);
304                 return FALSE;
305         }
306
307         return gconf_client_set_bool (priv->gconf_client, key, val, err);
308 }
309
310
311 gboolean
312 modest_conf_set_list (ModestConf* self, const gchar* key, 
313                       GSList *val, ModestConfValueType list_type, 
314                       GError **err)
315 {
316         ModestConfPrivate *priv;
317         GConfValueType gconf_type;
318         gboolean result;
319        
320         g_return_val_if_fail (self, FALSE);
321         g_return_val_if_fail (key, FALSE);
322
323         priv = MODEST_CONF_GET_PRIVATE(self);
324
325         gconf_type = modest_conf_type_to_gconf_type (list_type, err);
326         if (*err)
327                 return FALSE;
328
329         result = gconf_client_set_list (priv->gconf_client, key, gconf_type, val, err);
330         if(*err) {
331                 g_warning("gconf_client_set_list() failed with key=%s. error=%s", key,
332                           (*err)->message);
333                 result = FALSE;
334         }
335        
336         /* TODO: Remove this, when we fix the problem: */
337         /* This shows that sometimes set_list fails, while saying that it succeeded: */
338         if (result) {
339                 const gint debug_list_length_start = g_slist_length(val);
340                 GSList* debug_list = gconf_client_get_list(priv->gconf_client, key, gconf_type, err);
341                 const gint debug_list_length_after = g_slist_length(debug_list);
342                
343                 if(debug_list_length_start != debug_list_length_after)
344                         g_warning("modest_conf_set_list(): The list length after setting is "
345                                   "not the same as the specified list. key=%s. "
346                                   "We think that we fixed this, so tell us if you see this.", key);
347                 g_slist_free(debug_list);
348         }
349         
350         return result;
351 }
352
353
354 GSList*
355 modest_conf_list_subkeys (ModestConf* self, const gchar* key, GError **err)
356 {
357         ModestConfPrivate *priv;
358                 
359         g_return_val_if_fail (self,FALSE);
360         g_return_val_if_fail (key, FALSE);
361         
362         priv = MODEST_CONF_GET_PRIVATE(self);
363                         
364         return gconf_client_all_dirs (priv->gconf_client,key,err);
365 }
366
367
368 gboolean
369 modest_conf_remove_key (ModestConf* self, const gchar* key, GError **err)
370 {
371         ModestConfPrivate *priv;
372         gboolean retval;
373         
374         g_return_val_if_fail (self,FALSE);
375         g_return_val_if_fail (key, FALSE);
376         
377         priv = MODEST_CONF_GET_PRIVATE(self);
378                         
379         retval = gconf_client_recursive_unset (priv->gconf_client,key,0,err);
380         gconf_client_suggest_sync (priv->gconf_client, NULL);
381
382         return retval;
383 }
384
385
386 gboolean
387 modest_conf_key_exists (ModestConf* self, const gchar* key, GError **err)
388 {
389         ModestConfPrivate *priv;
390         GConfValue *val;
391
392         g_return_val_if_fail (self,FALSE);
393         g_return_val_if_fail (key, FALSE);
394         
395         priv = MODEST_CONF_GET_PRIVATE(self);
396
397         /* the fast way... */
398         if (gconf_client_dir_exists (priv->gconf_client,key,err))
399                 return TRUE;
400         
401         val = gconf_client_get (priv->gconf_client, key, NULL);
402         if (!val)
403                 return FALSE;
404         else {
405                 gconf_value_free (val);
406                 return TRUE;
407         }       
408 }
409
410
411 gchar*
412 modest_conf_key_escape (const gchar* key)
413 {
414         g_return_val_if_fail (key, NULL);
415         g_return_val_if_fail (strlen (key) > 0, g_strdup (key));
416         
417         return gconf_escape_key (key, strlen(key));
418 }
419
420
421 gchar*
422 modest_conf_key_unescape (const gchar* key)
423 {
424         g_return_val_if_fail (key, NULL);
425
426         return gconf_unescape_key (key, strlen(key));
427 }
428
429 gboolean
430 modest_conf_key_is_valid (const gchar* key)
431 {
432         return gconf_valid_key (key, NULL);
433 }
434
435 static void
436 modest_conf_on_change (GConfClient *client,
437                        guint conn_id,
438                        GConfEntry *entry,
439                        gpointer data)
440 {
441         ModestConfEvent event;
442         const gchar* key;
443
444         event = (entry->value) ? MODEST_CONF_EVENT_KEY_CHANGED : MODEST_CONF_EVENT_KEY_UNSET;
445         key    = gconf_entry_get_key (entry);
446
447         g_signal_emit (G_OBJECT(data),
448                        signals[KEY_CHANGED_SIGNAL], 0,
449                        key, event, conn_id);
450 }
451
452
453 static GConfValueType
454 modest_conf_type_to_gconf_type (ModestConfValueType value_type, GError **err)
455 {
456         GConfValueType gconf_type;
457
458         switch (value_type) {
459         case MODEST_CONF_VALUE_INT:
460                 gconf_type = GCONF_VALUE_INT;
461                 break;
462         case MODEST_CONF_VALUE_BOOL:
463                 gconf_type = GCONF_VALUE_BOOL;
464                 break;
465         case MODEST_CONF_VALUE_FLOAT:
466                 gconf_type = GCONF_VALUE_FLOAT;
467                 break;
468         case MODEST_CONF_VALUE_STRING:
469                 gconf_type = GCONF_VALUE_STRING;
470                 break;
471         default:
472                 /* FIXME: use MODEST_ERROR, and error code */
473                 gconf_type = GCONF_VALUE_INVALID;
474                 g_printerr ("modest: invalid list value type %d\n", value_type);
475                 *err = g_error_new_literal (0, 0, "invalid list value type");
476         }       
477         return gconf_type;
478 }
479
480 ModestConfNotificationId
481 modest_conf_listen_to_namespace (ModestConf *self,
482                                  const gchar *namespace)
483 {
484         ModestConfPrivate *priv;
485         GError *error = NULL;
486         ModestConfNotificationId notification_id;
487
488         g_return_val_if_fail (MODEST_IS_CONF (self), 0);
489         g_return_val_if_fail (namespace, 0);
490         
491         priv = MODEST_CONF_GET_PRIVATE(self);
492
493         /* Add the namespace to the list of the namespaces that will
494            be observed */
495         gconf_client_add_dir (priv->gconf_client, namespace,
496                               GCONF_CLIENT_PRELOAD_NONE,
497                               &error);
498
499         if (error)
500                 return 0;
501
502         /* Notify every change under namespace */
503         notification_id = gconf_client_notify_add (priv->gconf_client,
504                                                    namespace,
505                                                    modest_conf_on_change,
506                                                    self,
507                                                    NULL,
508                                                    &error);
509         if (error)
510                 return 0;
511         else
512                 return notification_id;
513 }
514
515 void 
516 modest_conf_forget_namespace (ModestConf *self,
517                               const gchar *namespace,
518                               ModestConfNotificationId id)
519 {
520         ModestConfPrivate *priv;
521
522         g_return_if_fail (MODEST_IS_CONF (self));
523         g_return_if_fail (namespace);
524         
525         priv = MODEST_CONF_GET_PRIVATE(self);
526
527         /* Remove the namespace to the list of the namespaces that will
528            be observed */
529         gconf_client_remove_dir (priv->gconf_client, namespace, NULL);
530
531         /* Notify every change under namespace */
532         gconf_client_notify_remove (priv->gconf_client, id);
533 }