fbde6b3b03aa7d806447ad31d83834ffb1f4eaa2
[modest] / src / modest-account-mgr.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 <string.h>
31 #include <modest-marshal.h>
32 #include <modest-account-mgr.h>
33 #include <modest-account-mgr-priv.h>
34 #include <modest-account-mgr-helpers.h>
35
36 /* 'private'/'protected' functions */
37 static void modest_account_mgr_class_init (ModestAccountMgrClass * klass);
38 static void modest_account_mgr_init       (ModestAccountMgr * obj);
39 static void modest_account_mgr_finalize   (GObject * obj);
40
41 /* list my signals */
42 enum {
43         ACCOUNT_CHANGED_SIGNAL,
44         ACCOUNT_REMOVED_SIGNAL,
45         LAST_SIGNAL
46 };
47
48
49 /* globals */
50 static GObjectClass *parent_class = NULL;
51 static guint signals[LAST_SIGNAL] = {0};
52
53 /* We signal key changes in batches, every X seconds: */
54 static gboolean
55 on_timeout_notify_changes (gpointer data)
56 {
57         ModestAccountMgr *self = MODEST_ACCOUNT_MGR (data);
58         ModestAccountMgrPrivate *priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
59         
60         /* TODO: Also store the account names, and notify one list for each account,
61          * if anything uses the account names. */
62         
63         if (priv->changed_conf_keys) {
64                 gchar *default_account =
65                                 modest_account_mgr_get_default_account (self);
66                 
67                 g_signal_emit (G_OBJECT(self), signals[ACCOUNT_CHANGED_SIGNAL], 0,
68                                  default_account, priv->changed_conf_keys, FALSE);
69                         
70                 g_free (default_account);
71                 
72                 g_slist_foreach (priv->changed_conf_keys, (GFunc) g_free, NULL);
73                 g_slist_free (priv->changed_conf_keys);
74                 priv->changed_conf_keys = NULL;
75         }
76         
77         return TRUE; /* Call this again later. */
78 }
79
80 static void
81 on_key_change (ModestConf *conf, const gchar *key, ModestConfEvent event, gpointer user_data)
82 {
83         /* printf("DEBUG: %s: key=%s\n", __FUNCTION__, key); */
84         
85         ModestAccountMgr *self = MODEST_ACCOUNT_MGR (user_data);
86         ModestAccountMgrPrivate *priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
87
88         /* there is only one not-really-account key which will still emit
89          * a signal: a change in MODEST_CONF_DEFAULT_ACCOUNT */
90         if (key && strcmp (key, MODEST_CONF_DEFAULT_ACCOUNT) == 0) {
91                 /* Get the default account instead. */
92                 
93                 /* Store the key for later notification in our timeout callback.
94                  * Notifying for every key change would cause unnecessary work: */
95                 priv->changed_conf_keys = g_slist_append (NULL, 
96                         (gpointer) g_strdup (key));
97         }
98         
99         gboolean is_account_key = FALSE;
100         gboolean is_server_account = FALSE;
101         gchar* account = _modest_account_mgr_account_from_key (key, &is_account_key, &is_server_account);
102         
103         /* if this is not an account-related key change, ignore */
104         if (!account)
105                 return;
106
107         /* account was removed -- emit this, even if the account was
108            disabled. This should not happen unless the user directly
109            does it in gconf */
110         if (is_account_key && event == MODEST_CONF_EVENT_KEY_UNSET) {
111                 g_signal_emit (G_OBJECT(self), signals[ACCOUNT_REMOVED_SIGNAL], 0, 
112                                account, is_server_account);
113                 g_free (account);
114                 return;
115         }
116
117         /* is this account enabled? */
118         gboolean enabled = FALSE;
119         if (is_server_account)
120                 enabled = TRUE;
121         else 
122                 enabled = modest_account_mgr_get_enabled (self, account);
123
124         /* Notify is server account was changed, default account was changed
125          * or when enabled/disabled changes:
126          */
127         if (enabled ||
128             g_str_has_suffix (key, MODEST_ACCOUNT_ENABLED) ||
129             strcmp (key, MODEST_CONF_DEFAULT_ACCOUNT) == 0) {
130                 /* Store the key for later notification in our timeout callback.
131                  * Notifying for every key change would cause unnecessary work: */
132                 priv->changed_conf_keys = g_slist_append (NULL, 
133                         (gpointer) g_strdup (key));
134         }
135
136         g_free (account);
137 }
138
139
140 GType
141 modest_account_mgr_get_type (void)
142 {
143         static GType my_type = 0;
144
145         if (!my_type) {
146                 static const GTypeInfo my_info = {
147                         sizeof (ModestAccountMgrClass),
148                         NULL,   /* base init */
149                         NULL,   /* base finalize */
150                         (GClassInitFunc) modest_account_mgr_class_init,
151                         NULL,   /* class finalize */
152                         NULL,   /* class data */
153                         sizeof (ModestAccountMgr),
154                         1,      /* n_preallocs */
155                         (GInstanceInitFunc) modest_account_mgr_init,
156                         NULL
157                 };
158
159                 my_type = g_type_register_static (G_TYPE_OBJECT,
160                                                   "ModestAccountMgr",
161                                                   &my_info, 0);
162         }
163         return my_type;
164 }
165
166 static void
167 modest_account_mgr_class_init (ModestAccountMgrClass * klass)
168 {
169         GObjectClass *gobject_class;
170         gobject_class = (GObjectClass *) klass;
171
172         parent_class = g_type_class_peek_parent (klass);
173         gobject_class->finalize = modest_account_mgr_finalize;
174
175         g_type_class_add_private (gobject_class,
176                                   sizeof (ModestAccountMgrPrivate));
177
178         /* signal definitions */
179         signals[ACCOUNT_REMOVED_SIGNAL] =
180                 g_signal_new ("account_removed",
181                               G_TYPE_FROM_CLASS (klass),
182                               G_SIGNAL_RUN_FIRST,
183                               G_STRUCT_OFFSET(ModestAccountMgrClass,account_removed),
184                               NULL, NULL,
185                               modest_marshal_VOID__STRING_BOOLEAN,
186                               G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN);
187         signals[ACCOUNT_CHANGED_SIGNAL] =
188                 g_signal_new ("account_changed",
189                                G_TYPE_FROM_CLASS (klass),
190                               G_SIGNAL_RUN_FIRST,
191                               G_STRUCT_OFFSET(ModestAccountMgrClass,account_changed),
192                               NULL, NULL,
193                               modest_marshal_VOID__STRING_POINTER_BOOLEAN,
194                               G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
195 }
196
197
198 static void
199 modest_account_mgr_init (ModestAccountMgr * obj)
200 {
201         ModestAccountMgrPrivate *priv =
202                 MODEST_ACCOUNT_MGR_GET_PRIVATE (obj);
203
204         priv->modest_conf = NULL;
205         
206         priv->timeout = g_timeout_add (1000 /* milliseconds */, on_timeout_notify_changes, obj);
207 }
208
209 static void
210 modest_account_mgr_finalize (GObject * obj)
211 {
212         ModestAccountMgrPrivate *priv = 
213                 MODEST_ACCOUNT_MGR_GET_PRIVATE (obj);
214
215         if (priv->modest_conf) {
216                 g_object_unref (G_OBJECT(priv->modest_conf));
217                 priv->modest_conf = NULL;
218         }
219         
220         if (priv->timeout)
221                 g_source_remove (priv->timeout);
222                 
223         if (priv->changed_conf_keys) {
224                 g_slist_foreach (priv->changed_conf_keys, (GFunc) g_free, NULL);
225                 g_slist_free (priv->changed_conf_keys);
226         }
227
228         G_OBJECT_CLASS(parent_class)->finalize (obj);
229 }
230
231
232 ModestAccountMgr *
233 modest_account_mgr_new (ModestConf *conf)
234 {
235         GObject *obj;
236         ModestAccountMgrPrivate *priv;
237
238         g_return_val_if_fail (conf, NULL);
239
240         obj = G_OBJECT (g_object_new (MODEST_TYPE_ACCOUNT_MGR, NULL));
241         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (obj);
242
243         g_object_ref (G_OBJECT(conf));
244         priv->modest_conf = conf;
245
246         g_signal_connect (G_OBJECT (conf), "key_changed",
247                           G_CALLBACK (on_key_change),
248                           obj);
249         
250         return MODEST_ACCOUNT_MGR (obj);
251 }
252
253
254 static const gchar *
255 null_means_empty (const gchar * str)
256 {
257         return str ? str : "";
258 }
259
260
261 gboolean
262 modest_account_mgr_add_account (ModestAccountMgr *self,
263                                 const gchar *name,
264                                 const gchar *store_account,
265                                 const gchar *transport_account,
266                                 gboolean enabled)
267 {
268         ModestAccountMgrPrivate *priv;
269         gchar *key;
270         gboolean ok;
271         gchar *default_account;
272         GError *err = NULL;
273         
274         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
275         g_return_val_if_fail (name, FALSE);
276         g_return_val_if_fail (strchr(name, '/') == NULL, FALSE);
277         
278         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
279         
280         /*
281          * we create the account by adding an account 'dir', with the name <name>,
282          * and in that the 'display_name' string key
283          */
284         key = _modest_account_mgr_get_account_keyname (name, MODEST_ACCOUNT_DISPLAY_NAME, FALSE);
285         if (modest_account_mgr_account_exists (self, key, FALSE)) {
286                 g_printerr ("modest: account already exists\n");
287                 g_free (key);
288                 return FALSE;
289         }
290         
291         ok = modest_conf_set_string (priv->modest_conf, key, name, &err);
292         g_free (key);
293         if (!ok) {
294                 g_printerr ("modest: cannot set display name\n");
295                 if (err) {
296                         g_printerr ("modest: Error adding account conf: %s\n", err->message);
297                         g_error_free (err);
298                 }
299                 return FALSE;
300         }
301         
302         if (store_account) {
303                 key = _modest_account_mgr_get_account_keyname (name, MODEST_ACCOUNT_STORE_ACCOUNT, FALSE);
304                 ok = modest_conf_set_string (priv->modest_conf, key, store_account, &err);
305                 g_free (key);
306                 if (!ok) {
307                         g_printerr ("modest: failed to set store account '%s'\n",
308                                 store_account);
309                         if (err) {
310                                 g_printerr ("modest: Error adding store account conf: %s\n", err->message);
311                                 g_error_free (err);
312                         }
313                         
314                         return FALSE;
315                 }
316         }
317         
318         if (transport_account) {
319                 key = _modest_account_mgr_get_account_keyname (name, MODEST_ACCOUNT_TRANSPORT_ACCOUNT,
320                                                                FALSE);
321                 ok = modest_conf_set_string (priv->modest_conf, key, transport_account, &err);
322                 g_free (key);
323                 if (!ok) {
324                         g_printerr ("modest: failed to set transport account '%s'\n",
325                                     transport_account);
326                         if (err) {
327                                 g_printerr ("modest: Error adding transport account conf: %s\n", err->message);
328                                 g_error_free (err);
329                         }       
330                         return FALSE;
331                 }
332         }
333
334         /* Make sure that leave-messages-on-server is enabled by default, 
335          * as per the UI spec, though it is only meaningful for accounts using POP.
336          * (possibly this gconf key should be under the server account): */
337         modest_account_mgr_set_bool (self, name,
338                 MODEST_ACCOUNT_LEAVE_ON_SERVER, TRUE, FALSE /* not server account */);
339
340
341         modest_account_mgr_set_enabled (self, name, enabled);
342
343         /* if no default account has been defined yet, do so now */
344         default_account = modest_account_mgr_get_default_account (self);
345         if (!default_account)
346                 modest_account_mgr_set_default_account (self, name);
347         g_free (default_account);
348         
349         return TRUE;
350 }
351
352
353 gboolean
354 modest_account_mgr_add_server_account (ModestAccountMgr * self,
355                                        const gchar * name, const gchar *hostname,
356                                        guint portnumber,
357                                        const gchar * username, const gchar * password,
358                                        ModestTransportStoreProtocol proto,
359                                        ModestConnectionProtocol security,
360                                        ModestAuthProtocol auth)
361 {
362         ModestAccountMgrPrivate *priv;
363         gchar *key;
364         gboolean ok = TRUE;
365         GError *err = NULL;
366         
367         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
368         g_return_val_if_fail (name, FALSE);
369         g_return_val_if_fail (strchr(name, '/') == NULL, FALSE);
370                               
371         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
372         
373         /* hostname */
374         key = _modest_account_mgr_get_account_keyname (name, MODEST_ACCOUNT_HOSTNAME, TRUE);
375         if (modest_conf_key_exists (priv->modest_conf, key, &err)) {
376                 g_printerr ("modest: server account '%s' already exists\n", name);
377                 g_free (key);
378                 ok =  FALSE;
379         }
380         if (!ok)
381                 goto cleanup;
382         
383         modest_conf_set_string (priv->modest_conf, key, null_means_empty(hostname), &err);
384         if (err) {
385                 g_printerr ("modest: failed to set %s: %s\n", key, err->message);
386                 g_error_free (err);
387                 ok = FALSE;
388         }
389         g_free (key);
390         if (!ok)
391                 goto cleanup;
392         
393         /* username */
394         key = _modest_account_mgr_get_account_keyname (name, MODEST_ACCOUNT_USERNAME, TRUE);
395         ok = modest_conf_set_string (priv->modest_conf, key, null_means_empty (username), &err);
396         if (err) {
397                 g_printerr ("modest: failed to set %s: %s\n", key, err->message);
398                 g_error_free (err);
399                 ok = FALSE;
400         }
401         g_free (key);
402         if (!ok)
403                 goto cleanup;
404         
405         
406         /* password */
407         key = _modest_account_mgr_get_account_keyname (name, MODEST_ACCOUNT_PASSWORD, TRUE);
408         ok = modest_conf_set_string (priv->modest_conf, key, null_means_empty (password), &err);
409         if (err) {
410                 g_printerr ("modest: failed to set %s: %s\n", key, err->message);
411                 g_error_free (err);
412                 ok = FALSE;
413         }
414         g_free (key);
415         if (!ok)
416                 goto cleanup;
417
418         /* proto */
419         key = _modest_account_mgr_get_account_keyname (name, MODEST_ACCOUNT_PROTO, TRUE);
420         ok = modest_conf_set_string (priv->modest_conf, key,
421                                      modest_protocol_info_get_transport_store_protocol_name(proto),
422                                      &err);
423         if (err) {
424                 g_printerr ("modest: failed to set %s: %s\n", key, err->message);
425                 g_error_free (err);
426                 ok = FALSE;
427         }
428         g_free (key);
429         if (!ok)
430                 goto cleanup;
431
432
433         /* portnumber */
434         key = _modest_account_mgr_get_account_keyname (name, MODEST_ACCOUNT_PORT, TRUE);
435         ok = modest_conf_set_int (priv->modest_conf, key, portnumber, &err);
436         if (err) {
437                 g_printerr ("modest: failed to set %s: %s\n", key, err->message);
438                 g_error_free (err);
439                 ok = FALSE;
440         }
441         g_free (key);
442         if (!ok)
443                 goto cleanup;
444
445         
446         /* auth mechanism */
447         key = _modest_account_mgr_get_account_keyname (name, MODEST_ACCOUNT_AUTH_MECH, TRUE);
448         ok = modest_conf_set_string (priv->modest_conf, key,
449                                      modest_protocol_info_get_auth_protocol_name (auth),
450                                      &err);
451         if (err) {
452                 g_printerr ("modest: failed to set %s: %s\n", key, err->message);
453                 g_error_free (err);
454                 ok = FALSE;
455         }
456         g_free (key);
457         if (!ok)
458                 goto cleanup;
459         
460         /* Add the security settings: */
461         modest_server_account_set_security (self, name, security);
462         
463 cleanup:
464         if (!ok) {
465                 g_printerr ("modest: failed to add server account\n");
466                 return FALSE;
467         }
468
469         return TRUE;
470 }
471
472 /** modest_account_mgr_add_server_account_uri:
473  * Only used for mbox and maildir accounts.
474  */
475 gboolean
476 modest_account_mgr_add_server_account_uri (ModestAccountMgr * self,
477                                            const gchar *name, ModestTransportStoreProtocol proto,
478                                            const gchar *uri)
479 {
480         ModestAccountMgrPrivate *priv;
481         gchar *key;
482         gboolean ok;
483         
484         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
485         g_return_val_if_fail (name, FALSE);
486         g_return_val_if_fail (strchr(name, '/') == NULL, FALSE);
487         g_return_val_if_fail (uri, FALSE);
488         
489         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
490         
491         
492         /* proto */
493         key = _modest_account_mgr_get_account_keyname (name, MODEST_ACCOUNT_PROTO, TRUE);
494         ok = modest_conf_set_string (priv->modest_conf, key,
495                                      modest_protocol_info_get_transport_store_protocol_name(proto),
496                                      NULL);
497         g_free (key);
498
499         if (!ok) {
500                 g_printerr ("modest: failed to set proto\n");
501                 return FALSE;
502         }
503         
504         /* uri */
505         key = _modest_account_mgr_get_account_keyname (name, MODEST_ACCOUNT_URI, TRUE);
506         ok = modest_conf_set_string (priv->modest_conf, key, uri, NULL);
507         g_free (key);
508
509         if (!ok) {
510                 g_printerr ("modest: failed to set uri\n");
511                 return FALSE;
512         }
513         return TRUE;
514 }
515
516
517
518 gboolean
519 modest_account_mgr_remove_account (ModestAccountMgr * self,
520                                    const gchar* name,  gboolean server_account)
521 {
522         ModestAccountMgrPrivate *priv;
523         gchar *key;
524         gboolean retval;
525         GError *err = NULL;
526
527         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
528         g_return_val_if_fail (name, FALSE);
529
530         if (!modest_account_mgr_account_exists (self, name, server_account)) {
531                 g_printerr ("modest: %s: account '%s' does not exist\n", __FUNCTION__, name);
532                 return FALSE;
533         }
534
535         /* Notify the observers. We need to do that here because they
536            could need to use the configuration keys before deleting
537            them, i.e., the account store will delete the cache. We
538            only notify about removals of modest accounts */
539         if (!server_account)
540                 g_signal_emit (G_OBJECT(self), signals[ACCOUNT_REMOVED_SIGNAL], 0, 
541                                name, server_account);
542
543         /* in case we're deleting an account, also delete the dependent store and transport account */
544         if (!server_account) {
545                 gchar *server_account_name;
546                 
547                 server_account_name = modest_account_mgr_get_string (self, name, MODEST_ACCOUNT_STORE_ACCOUNT,
548                                                                     FALSE);
549                 if (server_account_name) {
550                         if (!modest_account_mgr_remove_account (self, server_account_name, TRUE))
551                                 g_printerr ("modest: failed to remove store account '%s' (%s)\n",
552                                             server_account_name, name);
553                         g_free (server_account_name);
554                 } else
555                         g_printerr ("modest: could not find the store account for %s\n", name);
556                 
557                 server_account_name = modest_account_mgr_get_string (self, name, MODEST_ACCOUNT_TRANSPORT_ACCOUNT,
558                                                                     FALSE);
559                 if (server_account_name) {
560                         if (!modest_account_mgr_remove_account (self, server_account_name, TRUE))
561                                 g_printerr ("modest: failed to remove transport account '%s' (%s)\n",
562                                             server_account_name, name);
563                         g_free (server_account_name);
564                 } else
565                         g_printerr ("modest: could not find the transport account for %s\n", name);
566         }                       
567                         
568         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
569         key = _modest_account_mgr_get_account_keyname (name, NULL, server_account);
570         
571         retval = modest_conf_remove_key (priv->modest_conf, key, &err);
572         g_free (key);
573
574         if (err) {
575                 g_printerr ("modest: error removing key: %s\n", err->message);
576                 g_error_free (err);
577         }
578
579         /* If this was the default, then remove that setting: */
580         if (!server_account) {
581                 gchar *default_account_name = modest_account_mgr_get_default_account (self);
582                 if (default_account_name && (strcmp (default_account_name, name) == 0))
583                         modest_account_mgr_unset_default_account (self);
584                 g_free (default_account_name);
585                 
586                 /* pick another one as the new default account */
587                 modest_account_mgr_set_first_account_as_default (self);
588         }
589         return retval;
590 }
591
592
593
594 /* strip the first /n/ character from each element
595  * caller must make sure all elements are strings with
596  * length >= n, and also that data can be freed.
597  * change is in-place
598  */
599 static void
600 strip_prefix_from_elements (GSList * lst, guint n)
601 {
602         while (lst) {
603                 memmove (lst->data, lst->data + n,
604                          strlen(lst->data) - n + 1);
605                 lst = lst->next;
606         }
607 }
608
609 #if 0
610 /* Not used. */
611 GSList*
612 modest_account_mgr_search_server_accounts (ModestAccountMgr * self,
613                                            const gchar * account_name,
614                                            ModestTransportStoreProtocol proto)
615 {
616         GSList *accounts;
617         GSList *cursor;
618         ModestAccountMgrPrivate *priv;
619         gchar *key;
620         GError *err = NULL;
621         
622         g_return_val_if_fail (self, NULL);
623         
624         key      = _modest_account_mgr_get_account_keyname (account_name, NULL, TRUE);
625         priv     = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
626         
627         /* get the list of all server accounts */
628         accounts = modest_conf_list_subkeys (priv->modest_conf, key, &err);
629         if (err) {
630                 g_printerr ("modest: failed to get subkeys for '%s' (%s)\n", key,
631                         err->message);
632                 g_error_free(err);
633                 return NULL;
634         }
635         
636         /* filter out the ones with the wrong protocol */
637         /* we could optimize for unknown proto / unknown type, but it will only
638          * make the code more complex */
639         cursor = accounts;
640         while (cursor) { 
641                 gchar *account   = _modest_account_mgr_account_from_key ((gchar*)cursor->data, NULL, NULL);
642                 gchar *acc_proto = modest_account_mgr_get_string (self, account, MODEST_ACCOUNT_PROTO,TRUE);
643                 ModestTransportStoreProtocol this_proto = 
644                         modest_protocol_info_get_transport_store_protocol (acc_proto);
645                 if (this_proto != MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN && this_proto != proto) {
646                         GSList *nxt = cursor->next;
647                         accounts = g_slist_delete_link (accounts, cursor);
648                         cursor = nxt;
649                 } else
650                         cursor = cursor->next;
651                 
652                 g_free (account);
653                 g_free (acc_proto);
654         }
655         
656         /* +1 because we must remove the ending '/' as well */
657         strip_prefix_from_elements (accounts, strlen(key)+1);
658         return accounts;        
659 }
660 #endif
661
662 GSList*
663 modest_account_mgr_account_names (ModestAccountMgr * self, gboolean only_enabled)
664 {
665         GSList *accounts;
666         ModestAccountMgrPrivate *priv;
667         GError *err = NULL;
668         
669         const size_t prefix_len = strlen (MODEST_ACCOUNT_NAMESPACE "/");
670
671         g_return_val_if_fail (self, NULL);
672
673         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
674         
675         accounts = modest_conf_list_subkeys (priv->modest_conf,
676                                              MODEST_ACCOUNT_NAMESPACE, &err);
677         if (err) {
678                 g_printerr ("modest: failed to get subkeys (%s): %s\n",
679                             MODEST_ACCOUNT_NAMESPACE, err->message);
680                 g_error_free (err);
681                 return NULL; /* assume accounts did not get value when err is set...*/
682         }
683         
684         strip_prefix_from_elements (accounts, prefix_len);
685                 
686         GSList *result = NULL;
687         
688         /* Unescape the keys to get the account names: */
689         GSList *iter = accounts;
690         while (iter) {
691                 if (!(iter->data))
692                         continue;
693                         
694                 const gchar* account_name_key = (const gchar*)iter->data;
695                 /* printf ("DEBUG: %s: account_name_key=%s\n", __FUNCTION__, account_name_key); */
696                 gchar* unescaped_name = account_name_key ? 
697                         modest_conf_key_unescape (account_name_key) 
698                         : NULL;
699                 /* printf ("  DEBUG: %s: unescaped name=%s\n", __FUNCTION__, unescaped_name); */
700                 
701                 gboolean add = TRUE;
702                 if (only_enabled) {
703                         if (unescaped_name && 
704                                 !modest_account_mgr_get_enabled (self, unescaped_name)) {
705                                 add = FALSE;
706                         }
707                 }
708                 
709                 if (add) {      
710                         result = g_slist_append (result, unescaped_name);
711                 }
712                 else {
713                         g_free (unescaped_name);
714                 }
715                         
716                 iter = g_slist_next (iter);     
717         }
718         
719         /* TODO: Free the strings too? */
720         g_slist_free (accounts);
721         accounts = NULL;
722
723         return result;
724 }
725
726
727 gchar *
728 modest_account_mgr_get_string (ModestAccountMgr *self, const gchar *name,
729                                const gchar *key, gboolean server_account) {
730
731         ModestAccountMgrPrivate *priv;
732
733         gchar *keyname;
734         gchar *retval;
735         GError *err = NULL;
736
737         g_return_val_if_fail (self, NULL);
738         g_return_val_if_fail (name, NULL);
739         g_return_val_if_fail (key, NULL);
740
741         keyname = _modest_account_mgr_get_account_keyname (name, key, server_account);
742         
743         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
744         retval = modest_conf_get_string (priv->modest_conf, keyname, &err);     
745         if (err) {
746                 g_printerr ("modest: error getting string '%s': %s\n", keyname, err->message);
747                 g_error_free (err);
748                 retval = NULL;
749         }
750         g_free (keyname);
751
752         return retval;
753 }
754
755
756 gchar *
757 modest_account_mgr_get_password (ModestAccountMgr *self, const gchar *name,
758                                const gchar *key, gboolean server_account)
759 {
760         return modest_account_mgr_get_string (self, name, key, server_account);
761
762 }
763
764
765
766 gint
767 modest_account_mgr_get_int (ModestAccountMgr *self, const gchar *name, const gchar *key,
768                             gboolean server_account)
769 {
770         ModestAccountMgrPrivate *priv;
771
772         gchar *keyname;
773         gint retval;
774         GError *err = NULL;
775         
776         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), -1);
777         g_return_val_if_fail (name, -1);
778         g_return_val_if_fail (key, -1);
779
780         keyname = _modest_account_mgr_get_account_keyname (name, key, server_account);
781
782         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
783         retval = modest_conf_get_int (priv->modest_conf, keyname, &err);
784         if (err) {
785                 g_printerr ("modest: error getting int '%s': %s\n", keyname, err->message);
786                 g_error_free (err);
787                 retval = -1;
788         }
789         g_free (keyname);
790
791         return retval;
792 }
793
794
795
796 gboolean
797 modest_account_mgr_get_bool (ModestAccountMgr * self, const gchar *account,
798                              const gchar * key, gboolean server_account)
799 {
800         ModestAccountMgrPrivate *priv;
801
802         gchar *keyname;
803         gboolean retval;
804         GError *err = NULL;
805
806         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
807         g_return_val_if_fail (account, FALSE);
808         g_return_val_if_fail (key, FALSE);
809
810         keyname = _modest_account_mgr_get_account_keyname (account, key, server_account);
811         
812         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
813         retval = modest_conf_get_bool (priv->modest_conf, keyname, &err);               
814         if (err) {
815                 g_printerr ("modest: error getting bool '%s': %s\n", keyname, err->message);
816                 g_error_free (err);
817                 retval = FALSE;
818         }
819         g_free (keyname);
820
821         return retval;
822 }
823
824
825
826 GSList * 
827 modest_account_mgr_get_list (ModestAccountMgr *self, const gchar *name,
828                              const gchar *key, ModestConfValueType list_type,
829                              gboolean server_account)
830 {
831         ModestAccountMgrPrivate *priv;
832
833         gchar *keyname;
834         GSList *retval;
835         GError *err = NULL;
836         
837         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), NULL);
838         g_return_val_if_fail (name, NULL);
839         g_return_val_if_fail (key, NULL);
840
841         keyname = _modest_account_mgr_get_account_keyname (name, key, server_account);
842         
843         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
844         retval = modest_conf_get_list (priv->modest_conf, keyname, list_type, &err);
845         if (err) {
846                 g_printerr ("modest: error getting list '%s': %s\n", keyname,
847                             err->message);
848                 g_error_free (err);
849                 retval = FALSE;
850         }
851         g_free (keyname);
852
853         return retval;
854 }
855
856
857 gboolean
858 modest_account_mgr_set_string (ModestAccountMgr * self, const gchar * name,
859                                const gchar * key, const gchar * val, gboolean server_account)
860 {
861         ModestAccountMgrPrivate *priv;
862
863         gchar *keyname;
864         gboolean retval;
865         GError *err = NULL;
866
867         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
868         g_return_val_if_fail (name, FALSE);
869         g_return_val_if_fail (key, FALSE);
870
871         keyname = _modest_account_mgr_get_account_keyname (name, key, server_account);
872         
873         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
874
875         retval = modest_conf_set_string (priv->modest_conf, keyname, val, &err);
876         if (err) {
877                 g_printerr ("modest: error setting string '%s': %s\n", keyname, err->message);
878                 g_error_free (err);
879                 retval = FALSE;
880         }
881         g_free (keyname);       
882         return retval;
883 }
884
885
886 gboolean
887 modest_account_mgr_set_password (ModestAccountMgr * self, const gchar * name,
888                                  const gchar * key, const gchar * val, gboolean server_account)
889 {
890         return modest_account_mgr_set_password (self, name, key, val, server_account);
891 }
892
893
894
895 gboolean
896 modest_account_mgr_set_int (ModestAccountMgr * self, const gchar * name,
897                             const gchar * key, int val, gboolean server_account)
898 {
899         ModestAccountMgrPrivate *priv;
900
901         gchar *keyname;
902         gboolean retval;
903         GError *err = NULL;
904         
905         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
906         g_return_val_if_fail (name, FALSE);
907         g_return_val_if_fail (key, FALSE);
908
909         keyname = _modest_account_mgr_get_account_keyname (name, key, server_account);
910         
911         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
912
913         retval = modest_conf_set_int (priv->modest_conf, keyname, val, &err);
914         if (err) {
915                 g_printerr ("modest: error setting int '%s': %s\n", keyname, err->message);
916                 g_error_free (err);
917                 retval = FALSE;
918         }
919         g_free (keyname);
920         return retval;
921 }
922
923
924
925 gboolean
926 modest_account_mgr_set_bool (ModestAccountMgr * self, const gchar * name,
927                              const gchar * key, gboolean val, gboolean server_account)
928 {
929         ModestAccountMgrPrivate *priv;
930
931         gchar *keyname;
932         gboolean retval;
933         GError *err = NULL;
934
935         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
936         g_return_val_if_fail (name, FALSE);
937         g_return_val_if_fail (key, FALSE);
938
939         keyname = _modest_account_mgr_get_account_keyname (name, key, server_account);
940
941         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
942
943         retval = modest_conf_set_bool (priv->modest_conf, keyname, val, &err);
944         if (err) {
945                 g_printerr ("modest: error setting bool '%s': %s\n", keyname, err->message);
946                 g_error_free (err);
947                 retval = FALSE;
948         }
949         g_free (keyname);
950         return retval;
951 }
952
953
954 gboolean
955 modest_account_mgr_set_list (ModestAccountMgr *self,
956                              const gchar *name,
957                              const gchar *key,
958                              GSList *val,
959                              ModestConfValueType list_type,
960                              gboolean server_account)
961 {
962         ModestAccountMgrPrivate *priv;
963         gchar *keyname;
964         GError *err = NULL;
965         gboolean retval;
966         
967         g_return_val_if_fail (self, FALSE);
968         g_return_val_if_fail (name, FALSE);
969         g_return_val_if_fail (key,  FALSE);
970         g_return_val_if_fail (val,  FALSE);
971
972         keyname = _modest_account_mgr_get_account_keyname (name, key, server_account);
973         
974         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
975         retval = modest_conf_set_list (priv->modest_conf, keyname, val, list_type, &err);
976         if (err) {
977                 g_printerr ("modest: error setting list '%s': %s\n", keyname, err->message);
978                 g_error_free (err);
979                 retval = FALSE;
980         }
981         g_free (keyname);
982
983         return retval;
984 }
985
986 gboolean
987 modest_account_mgr_account_exists (ModestAccountMgr * self, const gchar * name,
988                                    gboolean server_account)
989 {
990         ModestAccountMgrPrivate *priv;
991
992         gchar *keyname;
993         gboolean retval;
994         GError *err = NULL;
995
996         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
997         g_return_val_if_fail (name, FALSE);
998
999         keyname = _modest_account_mgr_get_account_keyname (name, NULL, server_account);
1000         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
1001         retval = modest_conf_key_exists (priv->modest_conf, keyname, &err);
1002         if (err) {
1003                 g_printerr ("modest: error determining existance of '%s': %s\n", keyname,
1004                             err->message);
1005                 g_error_free (err);
1006                 retval = FALSE;
1007         }
1008         g_free (keyname);
1009         return retval;
1010 }
1011
1012 gboolean
1013 modest_account_mgr_account_with_display_name_exists  (ModestAccountMgr *self, const gchar *display_name)
1014 {
1015         GSList *account_names = NULL;
1016         GSList *cursor = NULL;
1017         
1018         cursor = account_names = modest_account_mgr_account_names (self, 
1019                 TRUE /* enabled accounts, because disabled accounts are not user visible. */);
1020
1021         gboolean found = FALSE;
1022         
1023         /* Look at each non-server account to check their display names; */
1024         while (cursor) {
1025                 const gchar * account_name = (gchar*)cursor->data;
1026                 
1027                 ModestAccountData *account_data = modest_account_mgr_get_account_data (self, account_name);
1028                 if (!account_data) {
1029                         g_printerr ("modest: failed to get account data for %s\n", account_name);
1030                         continue;
1031                 }
1032
1033                 if(account_data->display_name && (strcmp (account_data->display_name, display_name) == 0)) {
1034                         found = TRUE;
1035                         break;
1036                 }
1037
1038                 modest_account_mgr_free_account_data (self, account_data);
1039                 cursor = cursor->next;
1040         }
1041         g_slist_free (account_names);
1042         
1043         return found;
1044 }
1045
1046
1047
1048
1049 gboolean 
1050 modest_account_mgr_unset (ModestAccountMgr *self, const gchar *name,
1051                           const gchar *key, gboolean server_account)
1052 {
1053         ModestAccountMgrPrivate *priv;
1054         
1055         gchar *keyname;
1056         gboolean retval;
1057         GError *err = NULL;
1058         
1059         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
1060         g_return_val_if_fail (name, FALSE);
1061         g_return_val_if_fail (key, FALSE);
1062
1063         keyname = _modest_account_mgr_get_account_keyname (name, key, server_account);
1064
1065         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
1066         retval = modest_conf_remove_key (priv->modest_conf, keyname, &err);
1067         if (err) {
1068                 g_printerr ("modest: error unsetting'%s': %s\n", keyname,
1069                             err->message);
1070                 g_error_free (err);
1071                 retval = FALSE;
1072         }
1073         g_free (keyname);
1074         return retval;
1075 }
1076
1077 gchar*
1078 _modest_account_mgr_account_from_key (const gchar *key, gboolean *is_account_key, gboolean *is_server_account)
1079 {
1080         /* Initialize input parameters: */
1081         if (is_account_key)
1082                 *is_account_key = FALSE;
1083
1084         if (is_server_account)
1085                 *is_server_account = FALSE;
1086
1087         const gchar* account_ns        = MODEST_ACCOUNT_NAMESPACE "/";
1088         const gchar* server_account_ns = MODEST_SERVER_ACCOUNT_NAMESPACE "/";
1089         gchar *cursor;
1090         gchar *account = NULL;
1091
1092         /* determine whether it's an account or a server account,
1093          * based on the prefix */
1094         if (g_str_has_prefix (key, account_ns)) {
1095
1096                 if (is_server_account)
1097                         *is_server_account = FALSE;
1098                 
1099                 account = g_strdup (key + strlen (account_ns));
1100
1101         } else if (g_str_has_prefix (key, server_account_ns)) {
1102
1103                 if (is_server_account)
1104                         *is_server_account = TRUE;
1105                 
1106                 account = g_strdup (key + strlen (server_account_ns));  
1107         } else
1108                 return NULL;
1109
1110         /* if there are any slashes left in the key, it's not
1111          * the toplevel entry for an account
1112          */
1113         cursor = strstr(account, "/");
1114         
1115         if (is_account_key && cursor)
1116                 *is_account_key = TRUE;
1117
1118         /* put a NULL where the first slash was */
1119         if (cursor)
1120                 *cursor = '\0';
1121
1122         if (account) {
1123                 /* The key is an escaped string, so unescape it to get the actual account name: */
1124                 gchar *unescaped_name = modest_conf_key_unescape (account);
1125                 g_free (account);
1126                 return unescaped_name;
1127         } else
1128                 return NULL;
1129 }
1130
1131
1132
1133 /* must be freed by caller */
1134 gchar *
1135 _modest_account_mgr_get_account_keyname (const gchar *account_name, const gchar * name, gboolean server_account)
1136 {
1137         gchar *retval = NULL;
1138         
1139         gchar *namespace = server_account ? MODEST_SERVER_ACCOUNT_NAMESPACE : MODEST_ACCOUNT_NAMESPACE;
1140         
1141         if (!account_name)
1142                 return g_strdup (namespace);
1143         
1144         /* Always escape the conf keys, so that it is acceptable to gconf: */
1145         gchar *escaped_account_name = account_name ? modest_conf_key_escape (account_name) : NULL;
1146         gchar *escaped_name =  name ? modest_conf_key_escape (name) : NULL;
1147
1148         if (escaped_account_name && escaped_name)
1149                 retval = g_strconcat (namespace, "/", escaped_account_name, "/", escaped_name, NULL);
1150         else if (escaped_account_name)
1151                 retval = g_strconcat (namespace, "/", escaped_account_name, NULL);
1152
1153         /* Sanity check: */
1154         if (!modest_conf_key_is_valid (retval)) {
1155                 g_warning ("%s: Generated conf key was invalid: %s", __FUNCTION__, retval);
1156                 g_free (retval);
1157                 retval = NULL;
1158         }
1159
1160         g_free (escaped_name);
1161         g_free (escaped_account_name);
1162
1163         return retval;
1164 }
1165
1166 gboolean
1167 modest_account_mgr_has_accounts (ModestAccountMgr* self, gboolean enabled)
1168 {
1169         /* Check that at least one account exists: */
1170         GSList *account_names = modest_account_mgr_account_names (self,
1171                                 enabled);
1172         gboolean accounts_exist = account_names != NULL;
1173         g_slist_free (account_names);
1174         
1175         return accounts_exist;
1176 }