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