reverting one issue
[accounts-sso:accounts-glib.git] / libaccounts-glib / ag-account.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of libaccounts-glib
5  *
6  * Copyright (C) 2009-2010 Nokia Corporation.
7  *
8  * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * version 2.1 as published by the Free Software Foundation.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22  * 02110-1301 USA
23  */
24
25 /**
26  * SECTION:ag-account
27  * @title: AgAccount
28  * @short_description: A representation of an account.
29  *
30  * An #AgAccount is an object which represents an account. It provides a
31  * method for enabling/disabling the account and methods for editing the
32  * account settings.
33  */
34
35 #include "ag-manager.h"
36 #include "ag-account.h"
37 #include "ag-errors.h"
38
39 #include "ag-internals.h"
40 #include "ag-marshal.h"
41 #include "ag-service.h"
42 #include "ag-util.h"
43
44 #include "config.h"
45
46 #ifdef HAVE_AEGISCRYPTO
47   #include <aegis_crypto.h>
48 #endif
49
50 #include <string.h>
51
52 #define SERVICE_GLOBAL "global"
53
54 enum
55 {
56     PROP_0,
57
58     PROP_ID,
59     PROP_MANAGER,
60     PROP_PROVIDER,
61     PROP_FOREIGN,
62 };
63
64 enum
65 {
66     ENABLED,
67     DISPLAY_NAME_CHANGED,
68     DELETED,
69     LAST_SIGNAL
70 };
71
72 static guint signals[LAST_SIGNAL] = { 0 };
73
74 typedef struct _AgServiceChanges {
75     AgService *service; /* this is set only if the change came from this
76                            instance */
77     gchar *service_type;
78
79     GHashTable *settings;
80     GHashTable *signatures;
81 } AgServiceChanges;
82
83 typedef struct _AgServiceSettings {
84     AgService *service;
85     GHashTable *settings;
86 } AgServiceSettings;
87
88 struct _AgAccountPrivate {
89     AgManager *manager;
90
91     /* selected service */
92     AgService *service;
93
94     gchar *provider_name;
95     gchar *display_name;
96
97     /* cached settings: keys are service names, values are AgServiceSettings
98      * structures.
99      * It may be that not all services are loaded in this hash table. But if a
100      * service is present, then all of its settings are.
101      */
102     GHashTable *services;
103
104     AgAccountChanges *changes;
105
106     /* Watches: it's a GHashTable whose keys are pointers to AgService
107      * elements, and values are GHashTables whose keys and values are
108      * AgAccountWatch-es. */
109     GHashTable *watches;
110
111     /* The "foreign" flag means that the account has been created by another
112      * instance and we got informed about it from D-Bus. In this case, all the
113      * informations that we get via D-Bus will be cached in the
114      * AgServiceSetting structures. */
115     guint foreign : 1;
116     guint enabled : 1;
117     guint deleted : 1;
118 };
119
120 struct _AgAccountWatch {
121     AgService *service;
122     gchar *key;
123     gchar *prefix;
124     AgAccountNotifyCb callback;
125     gpointer user_data;
126 };
127
128 /* Same size and member types as AgAccountSettingIter */
129 typedef struct {
130     AgAccount *account;
131     GHashTableIter iter;
132     const gchar *key_prefix;
133     gpointer ptr2;
134     gint stage;
135     gint idx2;
136 } RealIter;
137
138 typedef struct _AgSignature {
139     gchar *signature;
140     gchar *token;
141 } AgSignature;
142
143 #define AG_ITER_STAGE_UNSET     0
144 #define AG_ITER_STAGE_ACCOUNT   1
145 #define AG_ITER_STAGE_SERVICE   2
146
147 G_DEFINE_TYPE (AgAccount, ag_account, G_TYPE_OBJECT);
148
149 #define AG_ACCOUNT_PRIV(obj) (AG_ACCOUNT(obj)->priv)
150
151 DBusMessage *
152 _ag_account_build_signal (AgAccount *account, AgAccountChanges *changes,
153                           const struct timespec *ts)
154 {
155     DBusMessage *msg;
156     DBusMessageIter iter, i_serv, dict, i_struct, i_list;
157     gboolean ret;
158     const gchar *provider_name;
159
160     msg = dbus_message_new_signal (AG_DBUS_PATH, AG_DBUS_IFACE,
161                                    AG_DBUS_SIG_CHANGED);
162     g_return_val_if_fail (msg != NULL, NULL);
163
164     provider_name = account->priv->provider_name;
165     if (!provider_name) provider_name = "";
166
167     ret = dbus_message_append_args (msg,
168                                     DBUS_TYPE_UINT32, &ts->tv_sec,
169                                     DBUS_TYPE_UINT32, &ts->tv_nsec,
170                                     DBUS_TYPE_UINT32, &account->id,
171                                     DBUS_TYPE_BOOLEAN, &changes->created,
172                                     DBUS_TYPE_BOOLEAN, &changes->deleted,
173                                     DBUS_TYPE_STRING, &provider_name,
174                                     DBUS_TYPE_INVALID);
175     if (G_UNLIKELY (!ret)) goto error;
176
177     /* Append the settings */
178     dbus_message_iter_init_append (msg, &iter);
179     dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY,
180                                       "(ssua{sv}as)", &i_serv);
181     if (changes->services)
182     {
183         GHashTableIter iter;
184         AgServiceChanges *sc;
185         gchar *service_name;
186
187         g_hash_table_iter_init (&iter, changes->services);
188         while (g_hash_table_iter_next (&iter,
189                                        (gpointer)&service_name, (gpointer)&sc))
190         {
191             GSList *removed_keys = NULL;
192             GHashTableIter si;
193             gchar *key;
194             GValue *value;
195             guint service_id;
196
197             dbus_message_iter_open_container (&i_serv, DBUS_TYPE_STRUCT,
198                                               NULL, &i_struct);
199             /* Append the service name */
200             dbus_message_iter_append_basic (&i_struct, DBUS_TYPE_STRING,
201                                             &service_name);
202             /* Append the service type */
203             dbus_message_iter_append_basic (&i_struct, DBUS_TYPE_STRING,
204                                             &sc->service_type);
205             /* Append the service id */
206             if (sc->service == NULL)
207                 service_id = 0;
208             else
209                 service_id = sc->service->id;
210
211             dbus_message_iter_append_basic (&i_struct, DBUS_TYPE_UINT32, &service_id);
212             /* Append the dictionary of service settings */
213             dbus_message_iter_open_container (&i_struct, DBUS_TYPE_ARRAY,
214                                               "{sv}", &dict);
215
216             g_hash_table_iter_init (&si, sc->settings);
217             while (g_hash_table_iter_next (&si,
218                                            (gpointer)&key, (gpointer)&value))
219             {
220                 if (value)
221                     _ag_iter_append_dict_entry (&dict, key, value);
222                 else
223                     removed_keys = g_slist_prepend (removed_keys, key);
224             }
225             dbus_message_iter_close_container (&i_struct, &dict);
226
227             /* append the list of removed keys */
228             dbus_message_iter_open_container (&i_struct, DBUS_TYPE_ARRAY,
229                                               "s", &i_list);
230             while (removed_keys)
231             {
232                 dbus_message_iter_append_basic (&i_list, DBUS_TYPE_STRING,
233                                                 &removed_keys->data);
234                 removed_keys = g_slist_delete_link (removed_keys, removed_keys);
235             }
236             dbus_message_iter_close_container (&i_struct, &i_list);
237             dbus_message_iter_close_container (&i_serv, &i_struct);
238         }
239     }
240     dbus_message_iter_close_container (&iter, &i_serv);
241     return msg;
242
243 error:
244     dbus_message_unref (msg);
245     return NULL;
246 }
247
248 static void
249 ag_account_watch_free (AgAccountWatch watch)
250 {
251     g_return_if_fail (watch != NULL);
252     g_free (watch->key);
253     g_free (watch->prefix);
254     g_slice_free (struct _AgAccountWatch, watch);
255 }
256
257 static AgService *
258 ag_service_ref_null (AgService *service)
259 {
260     if (service)
261         ag_service_ref (service);
262     return service;
263 }
264
265 static void
266 ag_service_unref_null (AgService *service)
267 {
268     if (service)
269         ag_service_unref (service);
270 }
271
272 static AgAccountWatch
273 ag_account_watch_int (AgAccount *account, gchar *key, gchar *prefix,
274                       AgAccountNotifyCb callback, gpointer user_data)
275 {
276     AgAccountPrivate *priv = account->priv;
277     AgAccountWatch watch;
278     GHashTable *service_watches;
279
280     if (!priv->watches)
281     {
282         priv->watches =
283             g_hash_table_new_full (g_direct_hash, g_direct_equal,
284                                    (GDestroyNotify)ag_service_unref_null,
285                                    (GDestroyNotify)g_hash_table_destroy);
286     }
287
288     service_watches = g_hash_table_lookup (priv->watches, priv->service);
289     if (!service_watches)
290     {
291         service_watches =
292             g_hash_table_new_full (g_direct_hash, g_direct_equal,
293                                    NULL,
294                                    (GDestroyNotify)ag_account_watch_free);
295         g_hash_table_insert (priv->watches,
296                              ag_service_ref_null (priv->service),
297                              service_watches);
298     }
299
300     watch = g_slice_new (struct _AgAccountWatch);
301     watch->service = priv->service;
302     watch->key = key;
303     watch->prefix = prefix;
304     watch->callback = callback;
305     watch->user_data = user_data;
306
307     g_hash_table_insert (service_watches, watch, watch);
308
309     return watch;
310 }
311
312 #ifdef HAVE_AEGISCRYPTO
313 static gboolean
314 got_account_signature (sqlite3_stmt *stmt, AgSignature *sgn)
315 {
316     sgn->signature = g_strdup ((gchar *)sqlite3_column_text (stmt, 0));
317     sgn->token = g_strdup ((gchar *)sqlite3_column_text (stmt, 1));
318
319     return TRUE;
320 }
321 #endif
322
323 static gboolean
324 got_account_setting (sqlite3_stmt *stmt, GHashTable *settings)
325 {
326     gchar *key;
327     GValue *value;
328
329     key = g_strdup ((gchar *)sqlite3_column_text (stmt, 0));
330     g_return_val_if_fail (key != NULL, FALSE);
331
332     value = _ag_value_from_db (stmt, 1, 2);
333
334     g_hash_table_insert (settings, key, value);
335     return TRUE;
336 }
337
338 static void
339 ag_service_settings_free (AgServiceSettings *ss)
340 {
341     if (ss->service)
342         ag_service_unref (ss->service);
343     g_hash_table_unref (ss->settings);
344     g_slice_free (AgServiceSettings, ss);
345 }
346
347 static AgServiceSettings *
348 get_service_settings (AgAccountPrivate *priv, AgService *service,
349                       gboolean create)
350 {
351     AgServiceSettings *ss;
352     const gchar *service_name;
353
354     if (G_UNLIKELY (!priv->services))
355     {
356         priv->services = g_hash_table_new_full
357             (g_str_hash, g_str_equal,
358              NULL, (GDestroyNotify) ag_service_settings_free);
359     }
360
361     service_name = service ? service->name : SERVICE_GLOBAL;
362     ss = g_hash_table_lookup (priv->services, service_name);
363     if (!ss && create)
364     {
365         ss = g_slice_new (AgServiceSettings);
366         ss->service = service ? ag_service_ref (service) : NULL;
367         ss->settings = g_hash_table_new_full
368             (g_str_hash, g_str_equal,
369              g_free, (GDestroyNotify)_ag_value_slice_free);
370         g_hash_table_insert (priv->services, (gchar *)service_name, ss);
371     }
372
373     return ss;
374 }
375
376 static gboolean
377 ag_account_changes_get_enabled (AgAccountChanges *changes, gboolean *enabled)
378 {
379     AgServiceChanges *sc;
380     const GValue *value;
381
382     sc = g_hash_table_lookup (changes->services, SERVICE_GLOBAL);
383     if (sc)
384     {
385         value = g_hash_table_lookup (sc->settings, "enabled");
386         if (value)
387         {
388             *enabled = g_value_get_boolean (value);
389             return TRUE;
390         }
391     }
392     *enabled = FALSE;
393     return FALSE;
394 }
395
396 static gboolean
397 ag_account_changes_get_display_name (AgAccountChanges *changes,
398                                      const gchar **display_name)
399 {
400     AgServiceChanges *sc;
401     const GValue *value;
402
403     sc = g_hash_table_lookup (changes->services, SERVICE_GLOBAL);
404     if (sc)
405     {
406         value = g_hash_table_lookup (sc->settings, "name");
407         if (value)
408         {
409             *display_name = g_value_get_string (value);
410             return TRUE;
411         }
412     }
413     *display_name = NULL;
414     return FALSE;
415 }
416
417 static void
418 ag_service_changes_free (AgServiceChanges *sc)
419 {
420     g_free (sc->service_type);
421
422     if (sc->settings)
423         g_hash_table_unref (sc->settings);
424
425     if (sc->signatures)
426         g_hash_table_unref (sc->signatures);
427
428     g_slice_free (AgServiceChanges, sc);
429 }
430
431 void
432 _ag_account_changes_free (AgAccountChanges *changes)
433 {
434     if (G_LIKELY (changes))
435     {
436         g_hash_table_unref (changes->services);
437         g_slice_free (AgAccountChanges, changes);
438     }
439 }
440
441 static GList *
442 match_watch_with_key (AgAccount *account, GHashTable *watches,
443                       const gchar *key, GList *watch_list)
444 {
445     GHashTableIter iter;
446     AgAccountWatch watch;
447
448     g_hash_table_iter_init (&iter, watches);
449     while (g_hash_table_iter_next (&iter, NULL, (gpointer)&watch))
450     {
451         if (watch->key)
452         {
453             if (strcmp (key, watch->key) == 0)
454             {
455                 watch_list = g_list_prepend (watch_list, watch);
456             }
457         }
458         else /* match on the prefix */
459         {
460             if (g_str_has_prefix (key, watch->prefix))
461             {
462                 /* before addind the watch to the list, make sure it's not
463                  * already there */
464                 if (!g_list_find (watch_list, watch))
465                     watch_list = g_list_prepend (watch_list, watch);
466             }
467         }
468     }
469     return watch_list;
470 }
471
472 static void
473 update_settings (AgAccount *account, GHashTable *services)
474 {
475     AgAccountPrivate *priv = account->priv;
476     GHashTableIter iter;
477     AgServiceChanges *sc;
478     gchar *service_name;
479     GList *watch_list = NULL;
480
481     g_hash_table_iter_init (&iter, services);
482     while (g_hash_table_iter_next (&iter,
483                                    (gpointer)&service_name, (gpointer)&sc))
484     {
485         AgServiceSettings *ss;
486         GHashTableIter si;
487         gchar *key;
488         GValue *value;
489         GHashTable *watches = NULL;
490
491         if (priv->foreign)
492         {
493             /* If the account has been created from another instance
494              * (which might be in another process), the "changes" structure
495              * contains all the account settings for all services.
496              *
497              * Instead of discarding this precious information, we store all
498              * the settings in memory, to minimize future disk accesses.
499              */
500             ss = get_service_settings (priv, sc->service, TRUE);
501         }
502         else
503         {
504             /* if the changed service doesn't have a AgServiceSettings entry it
505              * means that the service was never selected on this account, so we
506              * don't need to update its settings. */
507             if (!priv->services) continue;
508             ss = g_hash_table_lookup (priv->services, service_name);
509         }
510         if (!ss) continue;
511
512         /* get the watches associated to this service */
513         if (priv->watches)
514             watches = g_hash_table_lookup (priv->watches, ss->service);
515
516         g_hash_table_iter_init (&si, sc->settings);
517         while (g_hash_table_iter_next (&si,
518                                        (gpointer)&key, (gpointer)&value))
519         {
520             if (ss->service == NULL)
521             {
522                 if (strcmp (key, "name") == 0)
523                 {
524                     g_free (priv->display_name);
525                     priv->display_name =
526                         value ? g_value_dup_string (value) : NULL;
527                     g_signal_emit (account, signals[DISPLAY_NAME_CHANGED], 0);
528                     continue;
529                 }
530                 else if (strcmp (key, "enabled") == 0)
531                 {
532                     priv->enabled =
533                         value ? g_value_get_boolean (value) : FALSE;
534                     g_signal_emit (account, signals[ENABLED], 0,
535                                    service_name, priv->enabled);
536                     continue;
537                 }
538             }
539
540             /* Move the key and value into the service settings (we can steal
541              * them from the hash table, as the AgServiceChanges structure is
542              * no longer needed after this */
543             g_hash_table_iter_steal (&si);
544
545             if (value)
546                 g_hash_table_replace (ss->settings, g_strdup (key), value);
547             else
548                 g_hash_table_remove (ss->settings, key);
549
550             /* check for installed watches to be invoked */
551             if (watches)
552                 watch_list = match_watch_with_key (account, watches, key,
553                                                    watch_list);
554
555             if (strcmp (key, "enabled") == 0)
556             {
557                 gboolean enabled =
558                     value ? g_value_get_boolean (value) : FALSE;
559                 g_signal_emit (account, signals[ENABLED], 0,
560                                service_name, enabled);
561             }
562             g_free(key);
563         }
564     }
565
566     /* invoke all watches */
567     while (watch_list)
568     {
569         AgAccountWatch watch = watch_list->data;
570
571         if (watch->key)
572             watch->callback (account, watch->key, watch->user_data);
573         else
574             watch->callback (account, watch->prefix, watch->user_data);
575         watch_list = g_list_delete_link (watch_list, watch_list);
576     }
577 }
578
579 void
580 _ag_account_store_completed (AgAccount *account, AgAccountChanges *changes,
581                              AgAccountStoreCb callback, const GError *error,
582                              gpointer user_data)
583 {
584     if (callback)
585         callback (account, error, user_data);
586
587     _ag_account_changes_free (changes);
588 }
589
590 /*
591  * _ag_account_done_changes:
592  *
593  * This function is called after a successful execution of a transaction, and
594  * must update the account data as with the contents of the AgAccountChanges
595  * structure.
596  */
597 void
598 _ag_account_done_changes (AgAccount *account, AgAccountChanges *changes)
599 {
600     AgAccountPrivate *priv = account->priv;
601
602     g_return_if_fail (changes != NULL);
603
604     if (changes->services)
605         update_settings (account, changes->services);
606
607     if (changes->deleted)
608     {
609         priv->deleted = TRUE;
610         g_signal_emit (account, signals[ENABLED], 0, NULL, FALSE);
611         g_signal_emit (account, signals[DELETED], 0);
612     }
613 }
614
615 static AgAccountChanges *
616 account_changes_get (AgAccountPrivate *priv)
617 {
618     if (!priv->changes)
619     {
620         priv->changes = g_slice_new0 (AgAccountChanges);
621         priv->changes->services =
622             g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
623                                    (GDestroyNotify)ag_service_changes_free);
624     }
625
626     return priv->changes;
627 }
628
629 static void
630 _ag_signatures_slice_free (AgSignature *sgn)
631 {
632     g_free (sgn->signature);
633     g_free (sgn->token);
634     g_slice_free (AgSignature, sgn);
635 }
636
637 static AgServiceChanges*
638 account_service_changes_get (AgAccountPrivate *priv, AgService *service,
639                              gboolean create_signatures)
640 {
641     AgAccountChanges *changes;
642     AgServiceChanges *sc;
643     gchar *service_name;
644     gchar *service_type;
645
646     changes = account_changes_get (priv);
647
648     service_name = service ? service->name : SERVICE_GLOBAL;
649     service_type = service ? service->type : SERVICE_GLOBAL_TYPE;
650
651     sc = g_hash_table_lookup (changes->services, service_name);
652     if (!sc)
653     {
654         sc = g_slice_new0 (AgServiceChanges);
655         sc->service = service;
656         sc->service_type = g_strdup (service_type);
657
658         sc->settings = g_hash_table_new_full
659             (g_str_hash, g_str_equal,
660             g_free, (GDestroyNotify)_ag_value_slice_free);
661         g_hash_table_insert (changes->services, service_name, sc);
662     }
663
664     if (create_signatures && !sc->signatures)
665         sc->signatures = g_hash_table_new_full
666             (g_str_hash, g_str_equal,
667              g_free, (GDestroyNotify)_ag_signatures_slice_free);
668
669     return sc;
670 }
671
672 static void
673 change_service_value (AgAccountPrivate *priv, AgService *service,
674                       const gchar *key, const GValue *value)
675 {
676     AgServiceChanges *sc;
677     sc = account_service_changes_get (priv, service, FALSE);
678     g_hash_table_insert (sc->settings,
679                          g_strdup (key), _ag_value_slice_dup (value));
680 }
681
682 static inline void
683 change_selected_service_value (AgAccountPrivate *priv,
684                                const gchar *key, const GValue *value)
685 {
686     change_service_value(priv, priv->service, key, value);
687 }
688
689 static void
690 ag_account_init (AgAccount *account)
691 {
692     account->priv = G_TYPE_INSTANCE_GET_PRIVATE (account, AG_TYPE_ACCOUNT,
693                                                  AgAccountPrivate);
694 }
695
696 static gboolean
697 got_account (sqlite3_stmt *stmt, AgAccountPrivate *priv)
698 {
699     g_assert (priv->display_name == NULL);
700     g_assert (priv->provider_name == NULL);
701     priv->display_name = g_strdup ((gchar *)sqlite3_column_text (stmt, 0));
702     priv->provider_name = g_strdup ((gchar *)sqlite3_column_text (stmt, 1));
703     priv->enabled = sqlite3_column_int (stmt, 2);
704     return TRUE;
705 }
706
707 static gboolean
708 ag_account_load (AgAccount *account)
709 {
710     AgAccountPrivate *priv = account->priv;
711     gchar sql[128];
712     gint rows;
713
714     g_snprintf (sql, sizeof (sql),
715                 "SELECT name, provider, enabled "
716                 "FROM Accounts WHERE id = %u", account->id);
717     rows = _ag_manager_exec_query (priv->manager,
718                                    (AgQueryCallback)got_account, priv, sql);
719     /* if the query succeeded but we didn't get a row, we must set the
720      * NOT_FOUND error */
721     if (_ag_manager_get_last_error (priv->manager) == NULL && rows != 1)
722     {
723         GError *error = g_error_new (AG_ERRORS, AG_ERROR_ACCOUNT_NOT_FOUND,
724                                      "Account %u not found in DB", account->id);
725         _ag_manager_take_error (priv->manager, error);
726     }
727
728     return rows == 1;
729 }
730
731 static GObject *
732 ag_account_constructor (GType type, guint n_params,
733                         GObjectConstructParam *params)
734 {
735     GObjectClass *object_class = (GObjectClass *)ag_account_parent_class;
736     GObject *object;
737     AgAccount *account;
738
739     object = object_class->constructor (type, n_params, params);
740     g_return_val_if_fail (AG_IS_ACCOUNT (object), NULL);
741
742     account = AG_ACCOUNT (object);
743     if (account->id)
744     {
745         if (account->priv->changes && account->priv->changes->created)
746         {
747             /* this is a new account and we should not load it */
748             _ag_account_changes_free (account->priv->changes);
749             account->priv->changes = NULL;
750         }
751         else if (!ag_account_load (account))
752         {
753             g_warning ("Unable to load account %u", account->id);
754             g_object_unref (object);
755             return NULL;
756         }
757     }
758
759     if (!account->priv->foreign)
760         ag_account_select_service (account, NULL);
761
762     return object;
763 }
764
765 static void
766 ag_account_set_property (GObject *object, guint property_id,
767                          const GValue *value, GParamSpec *pspec)
768 {
769     AgAccount *account = AG_ACCOUNT (object);
770     AgAccountPrivate *priv = account->priv;
771
772     switch (property_id)
773     {
774     case PROP_ID:
775         g_assert (account->id == 0);
776         account->id = g_value_get_uint (value);
777         break;
778     case PROP_MANAGER:
779         g_assert (priv->manager == NULL);
780         priv->manager = g_value_dup_object (value);
781         break;
782     case PROP_PROVIDER:
783         g_assert (priv->provider_name == NULL);
784         priv->provider_name = g_value_dup_string (value);
785         /* if this property is given, it means we are creating a new account */
786         if (priv->provider_name)
787         {
788             AgAccountChanges *changes = account_changes_get (priv);
789             changes->created = TRUE;
790         }
791         break;
792     case PROP_FOREIGN:
793         priv->foreign = g_value_get_boolean (value);
794         break;
795     default:
796         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
797         break;
798     }
799 }
800
801 static void
802 ag_account_dispose (GObject *object)
803 {
804     AgAccount *account = AG_ACCOUNT (object);
805     AgAccountPrivate *priv = account->priv;
806
807     if (priv->watches)
808     {
809         g_hash_table_destroy (priv->watches);
810         priv->watches = NULL;
811     }
812
813     if (priv->manager)
814     {
815         g_object_unref (priv->manager);
816         priv->manager = NULL;
817     }
818
819     G_OBJECT_CLASS (ag_account_parent_class)->dispose (object);
820 }
821
822 static void
823 ag_account_finalize (GObject *object)
824 {
825     AgAccountPrivate *priv = AG_ACCOUNT_PRIV (object);
826
827     g_free (priv->provider_name);
828     g_free (priv->display_name);
829
830     if (priv->services)
831         g_hash_table_unref (priv->services);
832
833     if (priv->changes)
834     {
835         DEBUG_INFO ("Finalizing account with uncommitted changes!");
836         _ag_account_changes_free (priv->changes);
837     }
838
839     G_OBJECT_CLASS (ag_account_parent_class)->finalize (object);
840 }
841
842 static void
843 ag_account_class_init (AgAccountClass *klass)
844 {
845     GObjectClass* object_class = G_OBJECT_CLASS (klass);
846
847     g_type_class_add_private (object_class, sizeof (AgAccountPrivate));
848
849     object_class->constructor = ag_account_constructor;
850     object_class->set_property = ag_account_set_property;
851     object_class->dispose = ag_account_dispose;
852     object_class->finalize = ag_account_finalize;
853
854     g_object_class_install_property
855         (object_class, PROP_ID,
856          g_param_spec_uint ("id", "id", "id",
857                             0, G_MAXUINT, 0,
858                             G_PARAM_STATIC_STRINGS |
859                             G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
860
861     g_object_class_install_property
862         (object_class, PROP_MANAGER,
863          g_param_spec_object ("manager", "manager", "manager",
864                               AG_TYPE_MANAGER,
865                               G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
866
867     g_object_class_install_property
868         (object_class, PROP_PROVIDER,
869          g_param_spec_string ("provider", "provider", "provider",
870                               NULL,
871                               G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
872                               G_PARAM_STATIC_STRINGS));
873
874     g_object_class_install_property
875         (object_class, PROP_FOREIGN,
876          g_param_spec_boolean ("foreign", "foreign", "foreign",
877                                FALSE,
878                                G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
879                                G_PARAM_STATIC_STRINGS));
880
881     /**
882      * AgAccount::enabled:
883      * @account: the #AgAccount.
884      * @service: the service which was enabled/disabled, or %NULL if the global
885      * enabledness of the account changed.
886      * @enabled: the new state of the @account.
887      *
888      * Emitted when the account "enabled" status was changed for one of its
889      * services, or for the account globally.
890      */
891     signals[ENABLED] = g_signal_new ("enabled",
892         G_TYPE_FROM_CLASS (klass),
893         G_SIGNAL_RUN_LAST,
894         0,
895         NULL, NULL,
896         ag_marshal_VOID__STRING_BOOLEAN,
897         G_TYPE_NONE,
898         2, G_TYPE_STRING, G_TYPE_BOOLEAN);
899
900     /**
901      * AgAccount::display-name-changed:
902      * @account: the #AgAccount.
903      *
904      * Emitted when the account display name has changed.
905      */
906     signals[DISPLAY_NAME_CHANGED] = g_signal_new ("display-name-changed",
907         G_TYPE_FROM_CLASS (klass),
908         G_SIGNAL_RUN_LAST,
909         0,
910         NULL, NULL,
911         g_cclosure_marshal_VOID__VOID,
912         G_TYPE_NONE,
913         0);
914
915     /**
916      * AgAccount::deleted:
917      * @account: the #AgAccount.
918      *
919      * Emitted when the account has been deleted.
920      */
921     signals[DELETED] = g_signal_new ("deleted",
922         G_TYPE_FROM_CLASS (klass),
923         G_SIGNAL_RUN_LAST,
924         0,
925         NULL, NULL,
926         g_cclosure_marshal_VOID__VOID,
927         G_TYPE_NONE, 0);
928
929 }
930
931 AgAccountChanges *
932 _ag_account_changes_from_dbus (AgManager *manager, DBusMessageIter *iter,
933                                gboolean created, gboolean deleted)
934 {
935     AgAccountChanges *changes;
936     AgServiceChanges *sc;
937     DBusMessageIter i_serv, i_struct, i_dict, i_list;
938     gchar *service_name;
939     gchar *service_type;
940     gint service_id;
941
942     changes = g_slice_new0 (AgAccountChanges);
943     changes->created = created;
944     changes->deleted = deleted;
945     changes->services =
946         g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
947                                (GDestroyNotify)ag_service_changes_free);
948
949     /* TODO: parse the settings */
950 #define EXPECT_TYPE(i, t) \
951     if (G_UNLIKELY (dbus_message_iter_get_arg_type (i) != t)) \
952     { \
953         DEBUG_INFO ("%s expected (%c), got (%c)", G_STRLOC, \
954                     t, dbus_message_iter_get_arg_type (i)); \
955         goto error; \
956     }
957
958     EXPECT_TYPE (iter, DBUS_TYPE_ARRAY);
959     dbus_message_iter_recurse (iter, &i_serv);
960
961     /* iterate the array of "ssa{sv}as", each one holds one service */
962     while (dbus_message_iter_get_arg_type (&i_serv) != DBUS_TYPE_INVALID)
963     {
964         EXPECT_TYPE (&i_serv, DBUS_TYPE_STRUCT);
965         dbus_message_iter_recurse (&i_serv, &i_struct);
966
967         EXPECT_TYPE (&i_struct, DBUS_TYPE_STRING);
968         dbus_message_iter_get_basic (&i_struct, &service_name);
969         dbus_message_iter_next (&i_struct);
970
971         EXPECT_TYPE (&i_struct, DBUS_TYPE_STRING);
972         dbus_message_iter_get_basic (&i_struct, &service_type);
973         dbus_message_iter_next (&i_struct);
974         
975         EXPECT_TYPE (&i_struct, DBUS_TYPE_UINT32);
976         dbus_message_iter_get_basic (&i_struct, &service_id);
977         dbus_message_iter_next (&i_struct);
978
979         sc = g_slice_new0 (AgServiceChanges);
980         if (service_name != NULL && strcmp (service_name, SERVICE_GLOBAL) == 0)
981             sc->service = NULL;
982         else
983             sc->service = _ag_manager_get_service_lazy (manager, service_name,
984                                                         service_type,
985                                                         service_id);
986         sc->service_type = g_strdup (service_type);
987
988         sc->settings = g_hash_table_new_full
989             (g_str_hash, g_str_equal,
990              g_free, (GDestroyNotify)_ag_value_slice_free);
991         g_hash_table_insert (changes->services, service_name, sc);
992
993         EXPECT_TYPE (&i_struct, DBUS_TYPE_ARRAY);
994         dbus_message_iter_recurse (&i_struct, &i_dict);
995
996         /* iterate the "a{sv}" of settings */
997         while (dbus_message_iter_get_arg_type (&i_dict) != DBUS_TYPE_INVALID)
998         {
999             const gchar *key;
1000             GValue value = { 0 };
1001
1002             EXPECT_TYPE (&i_dict, DBUS_TYPE_DICT_ENTRY);
1003             if (_ag_iter_get_dict_entry (&i_dict, &key, &value))
1004             {
1005                 g_hash_table_insert (sc->settings, g_strdup (key),
1006                                      g_slice_dup (GValue, &value));
1007             }
1008             dbus_message_iter_next (&i_dict);
1009         }
1010         dbus_message_iter_next (&i_struct);
1011
1012         EXPECT_TYPE (&i_struct, DBUS_TYPE_ARRAY);
1013         dbus_message_iter_recurse (&i_struct, &i_list);
1014
1015         /* iterate the "as" of removed settings */
1016         while (dbus_message_iter_get_arg_type (&i_list) != DBUS_TYPE_INVALID)
1017         {
1018             const gchar *key;
1019             EXPECT_TYPE (&i_list, DBUS_TYPE_STRING);
1020             dbus_message_iter_get_basic (&i_list, &key);
1021             g_hash_table_insert (sc->settings, g_strdup (key), NULL);
1022             dbus_message_iter_next (&i_list);
1023         }
1024
1025         dbus_message_iter_next (&i_serv);
1026     }
1027
1028 #undef EXPECT_TYPE
1029     return changes;
1030
1031 error:
1032     g_warning ("Wrong format of D-Bus message");
1033     g_slice_free(AgAccountChanges, changes);
1034     return NULL;
1035 }
1036
1037 static void
1038 add_service_type (GPtrArray *types, const gchar *service_type)
1039 {
1040     gboolean found = FALSE;
1041     gint i;
1042
1043     /* if the service type is not yet in the list, add it */
1044     for (i = 0; i < types->len; i++)
1045     {
1046         if (strcmp (service_type, g_ptr_array_index (types, i)) == 0)
1047         {
1048             found = TRUE;
1049             break;
1050         }
1051     }
1052
1053     if (!found)
1054         g_ptr_array_add (types, (gchar *)service_type);
1055 }
1056
1057 /**
1058  * _ag_account_changes_get_service_types:
1059  * @changes: the #AgAccountChanges structure.
1060  *
1061  * Gets the list of service types involved in the change. The list does not
1062  * contain duplicates.
1063  *
1064  * Returns: a newly-allocated GPtrArray (this must be freed, but not the
1065  * strings it holds!).
1066  */
1067 GPtrArray *
1068 _ag_account_changes_get_service_types (AgAccountChanges *changes)
1069 {
1070     GPtrArray *ret = g_ptr_array_sized_new (8);
1071
1072     if (changes->services)
1073     {
1074         GHashTableIter iter;
1075         AgServiceChanges *sc;
1076
1077         g_hash_table_iter_init (&iter, changes->services);
1078         while (g_hash_table_iter_next (&iter, NULL, (gpointer)&sc))
1079         {
1080             if (!sc->service_type) continue;
1081
1082             add_service_type (ret, sc->service_type);
1083         }
1084     }
1085
1086     /* if the account has been created or deleted, make sure that the global
1087      * service type is in the list */
1088     if (changes->created || changes->deleted)
1089         add_service_type (ret, SERVICE_GLOBAL_TYPE);
1090
1091     return ret;
1092 }
1093
1094 gboolean
1095 _ag_account_changes_have_service_type (AgAccountChanges *changes, gchar *service_type)
1096 {
1097     if (changes->services)
1098     {
1099         GHashTableIter iter;
1100         AgServiceChanges *sc;
1101
1102         g_hash_table_iter_init (&iter, changes->services);
1103         while (g_hash_table_iter_next (&iter,
1104                                        NULL, (gpointer)&sc))
1105         {
1106             if (g_strcmp0(sc->service_type, service_type) == 0)
1107                 return TRUE;
1108         }
1109     }
1110
1111     return FALSE;
1112 }
1113
1114 gboolean
1115 _ag_account_changes_have_enabled (AgAccountChanges *changes)
1116 {
1117     if (changes->services)
1118     {
1119         GHashTableIter iter;
1120         AgServiceChanges *sc;
1121
1122         g_hash_table_iter_init (&iter, changes->services);
1123         while (g_hash_table_iter_next (&iter,
1124                                        NULL, (gpointer)&sc))
1125         {
1126             const gchar *key = "enabled";
1127
1128             if (g_hash_table_lookup (sc->settings, (gpointer)key))
1129                 return TRUE;
1130         }
1131     }
1132
1133     return FALSE;
1134 }
1135
1136 static void
1137 ag_account_store_signature (AgAccount *account, AgServiceChanges *sc, GString *sql)
1138 {
1139     AgAccountId account_id;
1140     GHashTableIter i_signatures;
1141     gint service_id;
1142     gpointer ht_key, ht_value;
1143
1144     account_id = account->id;
1145     service_id = (sc->service != NULL) ? sc->service->id : 0;
1146
1147     g_hash_table_iter_init (&i_signatures, sc->signatures);
1148     while (g_hash_table_iter_next (&i_signatures, &ht_key, &ht_value))
1149     {
1150         const gchar *key = ht_key;
1151         AgSignature *sgn = ht_value;
1152
1153         if (sgn)
1154         {
1155             _ag_string_append_printf
1156                 (sql,
1157                  "INSERT OR REPLACE INTO Signatures"
1158                  "(account, service, key, signature, token)"
1159                  "VALUES (%d, %d, %Q, %Q, %Q);",
1160                  account_id, service_id, key, sgn->signature, sgn->token);
1161         }
1162     }
1163 }
1164
1165 static gchar *
1166 ag_account_get_store_sql (AgAccount *account, GError **error)
1167 {
1168     AgAccountPrivate *priv;
1169     AgAccountChanges *changes;
1170     GString *sql;
1171     gchar account_id_buffer[16];
1172     const gchar *account_id_str;
1173
1174     priv = account->priv;
1175
1176     if (G_UNLIKELY (priv->deleted))
1177     {
1178         *error = g_error_new (AG_ERRORS, AG_ERROR_DELETED,
1179                               "Account %s (id = %d) has been deleted",
1180                               priv->display_name, account->id);
1181         return NULL;
1182     }
1183
1184     changes = priv->changes;
1185
1186     if (G_UNLIKELY (!changes))
1187     {
1188         /* Nothing to do: return no SQL, and no error */
1189         return NULL;
1190     }
1191
1192     sql = g_string_sized_new (512);
1193     if (changes->deleted)
1194     {
1195         if (account->id != 0)
1196         {
1197             _ag_string_append_printf
1198                 (sql, "DELETE FROM Accounts WHERE id = %d;", account->id);
1199             _ag_string_append_printf
1200                 (sql, "DELETE FROM Settings WHERE account = %d;", account->id);
1201         }
1202         account_id_str = NULL; /* make the compiler happy */
1203     }
1204     else if (account->id == 0)
1205     {
1206         gboolean enabled;
1207         const gchar *display_name;
1208
1209         ag_account_changes_get_enabled (changes, &enabled);
1210         ag_account_changes_get_display_name (changes, &display_name);
1211         _ag_string_append_printf
1212             (sql,
1213              "INSERT INTO Accounts (name, provider, enabled) "
1214              "VALUES (%Q, %Q, %d);",
1215              display_name,
1216              priv->provider_name,
1217              enabled);
1218
1219         g_string_append (sql, "SELECT set_last_rowid_as_account_id();");
1220         account_id_str = "account_id()";
1221     }
1222     else
1223     {
1224         gboolean enabled, enabled_changed, display_name_changed;
1225         const gchar *display_name;
1226
1227         g_snprintf (account_id_buffer, sizeof (account_id_buffer),
1228                     "%u", account->id);
1229         account_id_str = account_id_buffer;
1230
1231         enabled_changed = ag_account_changes_get_enabled (changes, &enabled);
1232         display_name_changed =
1233             ag_account_changes_get_display_name (changes, &display_name);
1234
1235         if (display_name_changed || enabled_changed)
1236         {
1237             gboolean comma = FALSE;
1238             g_string_append (sql, "UPDATE Accounts SET ");
1239             if (display_name_changed)
1240             {
1241                 _ag_string_append_printf
1242                     (sql, "name = %Q", display_name);
1243                 comma = TRUE;
1244             }
1245
1246             if (enabled_changed)
1247             {
1248                 _ag_string_append_printf
1249                     (sql, "%cenabled = %d",
1250                      comma ? ',' : ' ', enabled);
1251             }
1252
1253             _ag_string_append_printf (sql, " WHERE id = %d;", account->id);
1254         }
1255     }
1256
1257     if (!changes->deleted)
1258     {
1259         GHashTableIter i_services;
1260         gpointer ht_key, ht_value;
1261
1262         g_hash_table_iter_init (&i_services, changes->services);
1263         while (g_hash_table_iter_next (&i_services, &ht_key, &ht_value))
1264         {
1265             AgServiceChanges *sc = ht_value;
1266             GHashTableIter i_settings;
1267             const gchar *service_id_str;
1268             gchar service_id_buffer[16];
1269
1270             if (sc->service)
1271             {
1272                 g_snprintf (service_id_buffer, sizeof (service_id_buffer),
1273                             "%d", sc->service->id);
1274                 service_id_str = service_id_buffer;
1275             }
1276             else
1277                 service_id_str = "0";
1278
1279             g_hash_table_iter_init (&i_settings, sc->settings);
1280             while (g_hash_table_iter_next (&i_settings, &ht_key, &ht_value))
1281             {
1282                 const gchar *key = ht_key;
1283                 const GValue *value = ht_value;
1284
1285                 if (value)
1286                 {
1287                     const gchar *value_str, *type_str;
1288
1289                     value_str = _ag_value_to_db (value);
1290                     type_str = _ag_type_from_g_type (G_VALUE_TYPE (value));
1291                     _ag_string_append_printf
1292                         (sql,
1293                          "INSERT OR REPLACE INTO Settings (account, service,"
1294                                                           "key, type, value) "
1295                          "VALUES (%s, %s, %Q, %Q, %Q);",
1296                          account_id_str, service_id_str, key,
1297                          type_str, value_str);
1298                 }
1299                 else if (account->id != 0)
1300                 {
1301                     _ag_string_append_printf
1302                         (sql,
1303                          "DELETE FROM Settings WHERE "
1304                          "account = %d AND "
1305                          "service = %Q AND "
1306                          "key = %Q;",
1307                          account->id, service_id_str, key);
1308                 }
1309             }
1310
1311             if (sc->signatures)
1312                 ag_account_store_signature (account, sc, sql);
1313         }
1314     }
1315
1316     return g_string_free (sql, FALSE);
1317 }
1318
1319 /**
1320  * ag_account_supports_service:
1321  * @account: the #AgAccount.
1322  *
1323  * Returns: a #gboolean which tells whether @account supports the service type
1324  * @service_type.
1325  */
1326 gboolean
1327 ag_account_supports_service (AgAccount *account, const gchar *service_type)
1328 {
1329     GList *services;
1330     gboolean ret = FALSE;
1331
1332     services = ag_account_list_services_by_type (account, service_type);
1333     if (services)
1334     {
1335         ag_service_list_free (services);
1336         ret = TRUE;
1337     }
1338     return ret;
1339 }
1340
1341 /**
1342  * ag_account_list_services:
1343  * @account: the #AgAccount.
1344  *
1345  * Returns: a #GList of #AgService items representing all the services
1346  * supported by this account. If the #AgManager was created with specified
1347  * service_type this will return only services with this service_type.
1348  * Must be free'd with ag_service_list_free().
1349  */
1350 GList *
1351 ag_account_list_services (AgAccount *account)
1352 {
1353     AgAccountPrivate *priv;
1354     GList *all_services, *list;
1355     GList *services = NULL;
1356
1357     g_return_val_if_fail (AG_IS_ACCOUNT (account), NULL);
1358     priv = account->priv;
1359
1360     if (!priv->provider_name)
1361         return NULL;
1362
1363     all_services = ag_manager_list_services (priv->manager);
1364     for (list = all_services; list != NULL; list = list->next)
1365     {
1366         AgService *service = list->data;
1367
1368         const gchar *provider = ag_service_get_provider (service);
1369         if (provider &&
1370             strcmp (provider, priv->provider_name) == 0)
1371         {
1372             services = g_list_prepend (services, service);
1373         }
1374         else
1375             ag_service_unref (service);
1376     }
1377     g_list_free (all_services);
1378     return services;
1379 }
1380
1381 /**
1382  * ag_account_list_services_by_type:
1383  * @account: the #AgAccount.
1384  * @service_type: the service type which all the returned services should
1385  * provide.
1386  *
1387  * Returns: a #GList of #AgService items representing all the services
1388  * supported by this account which provide @service_type. Must be free'd with
1389  * ag_service_list_free().
1390  */
1391 GList *
1392 ag_account_list_services_by_type (AgAccount *account,
1393                                   const gchar *service_type)
1394 {
1395     AgAccountPrivate *priv;
1396     GList *all_services, *list;
1397     GList *services = NULL;
1398
1399     g_return_val_if_fail (AG_IS_ACCOUNT (account), NULL);
1400     g_return_val_if_fail (service_type != NULL, NULL);
1401     priv = account->priv;
1402
1403     if (!priv->provider_name)
1404         return NULL;
1405
1406     all_services = ag_manager_list_services_by_type (priv->manager, service_type);
1407     for (list = all_services; list != NULL; list = list->next)
1408     {
1409         AgService *service = list->data;
1410         const gchar *provider = ag_service_get_provider (service);
1411         if (provider &&
1412             strcmp (provider, priv->provider_name) == 0)
1413         {
1414             services = g_list_prepend (services, service);
1415         }
1416         else
1417             ag_service_unref (service);
1418     }
1419     g_list_free (all_services);
1420     return services;
1421 }
1422
1423 static gboolean
1424 add_name_to_list (sqlite3_stmt *stmt, GList **plist)
1425 {
1426     gchar *name;
1427     name = g_strdup ((gchar *)sqlite3_column_text (stmt, 0));
1428
1429     *plist = g_list_prepend(*plist, name);
1430
1431     return TRUE;
1432 }
1433
1434 static inline GList *
1435 list_enabled_services_from_memory (AgAccountPrivate *priv,
1436                                    const gchar *service_type)
1437 {
1438     GHashTableIter iter;
1439     AgServiceSettings *ss;
1440     GList *list = NULL;
1441
1442     g_hash_table_iter_init (&iter, priv->services);
1443     while (g_hash_table_iter_next (&iter, NULL, (gpointer)&ss))
1444     {
1445         GValue *value;
1446
1447         if (ss->service == NULL) continue;
1448
1449         if (service_type != NULL &&
1450             g_strcmp0 (ag_service_get_service_type (ss->service), service_type) != 0)
1451                 continue;
1452
1453         value = g_hash_table_lookup (ss->settings, "enabled");
1454         if (value != NULL && g_value_get_boolean (value))
1455             list = g_list_prepend (list, ag_service_ref(ss->service));
1456     }
1457     return list;
1458 }
1459
1460 /**
1461  * ag_account_list_enabled_services:
1462  * @account: the #AgAccount.
1463  *
1464  * Returns: a #GList of #AgService items representing all the services
1465  * which are enabled. Must be free'd with ag_service_list_free().
1466  */
1467 GList *
1468 ag_account_list_enabled_services (AgAccount *account)
1469 {
1470     AgAccountPrivate *priv;
1471     GList *list = NULL;
1472     GList *iter;
1473     GList *services = NULL;
1474     const gchar *service_type;
1475     char sql[512];
1476
1477     g_return_val_if_fail (AG_IS_ACCOUNT (account), NULL);
1478     priv = account->priv;
1479
1480     service_type = ag_manager_get_service_type (priv->manager);
1481
1482     /* avoid accessing the DB, if possible */
1483     if (priv->foreign)
1484         return list_enabled_services_from_memory (priv, service_type);
1485
1486     if (service_type != NULL)
1487         sqlite3_snprintf (sizeof (sql), sql,
1488                           "SELECT DISTINCT Services.name FROM Services "
1489                           "JOIN Settings ON Settings.service = Services.id "
1490                           "WHERE Settings.key='enabled' "
1491                           "AND Settings.value='1' "
1492                           "AND Settings.account='%d' "
1493                           "AND Services.type = '%s';",
1494                            account->id,
1495                            service_type);
1496     else
1497         sqlite3_snprintf (sizeof (sql), sql,
1498                           "SELECT DISTINCT Services.name FROM Services "
1499                           "JOIN Settings ON Settings.service = Services.id "
1500                           "WHERE Settings.key='enabled' "
1501                           "AND Settings.value='1' "
1502                           "AND Settings.account='%d';",
1503                            account->id);
1504
1505     _ag_manager_exec_query (priv->manager, (AgQueryCallback)add_name_to_list,
1506                             &list, sql);
1507
1508     for (iter = list; iter != NULL; iter = iter->next)
1509     {
1510         gchar *service_name;
1511         AgService *service;
1512
1513         service_name = (gchar*)iter->data;
1514         service = ag_manager_get_service (priv->manager, service_name);
1515
1516         services = g_list_prepend (services, service);
1517         g_free (service_name);
1518     }
1519
1520     g_list_free (list);
1521
1522     return services;
1523 }
1524
1525 /**
1526  * ag_account_get_manager:
1527  * @account: the #AgAccount.
1528  *
1529  * Returns: the #AccountManager.
1530  */
1531 AgManager *
1532 ag_account_get_manager (AgAccount *account)
1533 {
1534     g_return_val_if_fail (AG_IS_ACCOUNT (account), NULL);
1535     return account->priv->manager;
1536 }
1537
1538 /**
1539  * ag_account_get_provider_name:
1540  * @account: the #AgAccount.
1541  *
1542  * Returns: the name of the provider of @account.
1543  */
1544 const gchar *
1545 ag_account_get_provider_name (AgAccount *account)
1546 {
1547     g_return_val_if_fail (AG_IS_ACCOUNT (account), NULL);
1548     return account->priv->provider_name;
1549 }
1550
1551 /**
1552  * ag_account_get_display_name:
1553  * @account: the #AgAccount.
1554  *
1555  * Returns: the display name for @account.
1556  */
1557 const gchar *
1558 ag_account_get_display_name (AgAccount *account)
1559 {
1560     g_return_val_if_fail (AG_IS_ACCOUNT (account), NULL);
1561     return account->priv->display_name;
1562 }
1563
1564 /**
1565  * ag_account_set_display_name:
1566  * @account: the #AgAccount.
1567  * @display_name: the display name to set.
1568  *
1569  * Changes the display name for @account to @display_name.
1570  */
1571 void
1572 ag_account_set_display_name (AgAccount *account, const gchar *display_name)
1573 {
1574     GValue value = { 0 };
1575
1576     g_return_if_fail (AG_IS_ACCOUNT (account));
1577
1578     g_value_init (&value, G_TYPE_STRING);
1579     g_value_set_static_string (&value, display_name);
1580     change_service_value (account->priv, NULL, "name", &value);
1581 }
1582
1583 /**
1584  * ag_account_select_service:
1585  * @account: the #AgAccount.
1586  * @service: the #AgService to select.
1587  *
1588  * Selects the configuration of service @service: from now on, all the
1589  * subsequent calls on the #AgAccount configuration will act on the @service.
1590  * If @service is %NULL, the global account configuration is selected.
1591  *
1592  * Note that if @account is being shared with other code one must take special
1593  * care to make sure the desired service is always selected.
1594  */
1595 void
1596 ag_account_select_service (AgAccount *account, AgService *service)
1597 {
1598     AgAccountPrivate *priv;
1599     gboolean load_settings = FALSE;
1600     AgServiceSettings *ss;
1601
1602     g_return_if_fail (AG_IS_ACCOUNT (account));
1603     priv = account->priv;
1604
1605     priv->service = service;
1606
1607     if (account->id != 0 &&
1608         !get_service_settings (priv, service, FALSE))
1609     {
1610         /* the settings for this service are not yet loaded: do it now */
1611         load_settings = TRUE;
1612     }
1613
1614     ss = get_service_settings (priv, service, TRUE);
1615
1616     if (load_settings)
1617     {
1618         guint service_id;
1619         gchar sql[128];
1620
1621         service_id = _ag_manager_get_service_id (priv->manager, service);
1622         g_snprintf (sql, sizeof (sql),
1623                     "SELECT key, type, value FROM Settings "
1624                     "WHERE account = %u AND service = %u",
1625                     account->id, service_id);
1626         _ag_manager_exec_query (priv->manager,
1627                                 (AgQueryCallback)got_account_setting,
1628                                 ss->settings, sql);
1629     }
1630 }
1631
1632 /**
1633  * ag_account_get_selected_service:
1634  * @account: the #AgAccount.
1635  *
1636  * Returns: the selected service, or %NULL if no service is selected.
1637  */
1638 AgService *
1639 ag_account_get_selected_service (AgAccount *account)
1640 {
1641     g_return_val_if_fail (AG_IS_ACCOUNT (account), NULL);
1642     return account->priv->service;
1643 }
1644
1645 /**
1646  * ag_account_get_enabled:
1647  * @account: the #AgAccount.
1648  *
1649  * Returns: a #gboolean which tells whether the selected service for @account is
1650  * enabled.
1651  */
1652 gboolean
1653 ag_account_get_enabled (AgAccount *account)
1654 {
1655     AgAccountPrivate *priv;
1656     gboolean ret = FALSE;
1657     AgServiceSettings *ss;
1658     GValue *val;
1659
1660     g_return_val_if_fail (AG_IS_ACCOUNT (account), FALSE);
1661     priv = account->priv;
1662
1663     if (priv->service == NULL)
1664     {
1665         ret = priv->enabled;
1666     }
1667     else
1668     {
1669         ss = get_service_settings (priv, priv->service, FALSE);
1670         if (ss)
1671         {
1672             val = g_hash_table_lookup (ss->settings, "enabled");
1673             ret = g_value_get_boolean (val);
1674         }
1675     }
1676     return ret;
1677 }
1678
1679 /**
1680  * ag_account_set_enabled:
1681  * @account: the #AgAccount.
1682  * @enabled: whether @account should be enabled.
1683  *
1684  * Sets the "enabled" flag on the selected service for @account.
1685  */
1686 void
1687 ag_account_set_enabled (AgAccount *account, gboolean enabled)
1688 {
1689     GValue value = { 0 };
1690
1691     g_return_if_fail (AG_IS_ACCOUNT (account));
1692
1693     g_value_init (&value, G_TYPE_BOOLEAN);
1694     g_value_set_boolean (&value, enabled);
1695     change_selected_service_value (account->priv, "enabled", &value);
1696 }
1697
1698 /**
1699  * ag_account_delete:
1700  * @account: the #AgAccount.
1701  *
1702  * Deletes the account. Call ag_account_store() in order to record the change
1703  * in the storage.
1704  */
1705 void
1706 ag_account_delete (AgAccount *account)
1707 {
1708     AgAccountChanges *changes;
1709
1710     g_return_if_fail (AG_IS_ACCOUNT (account));
1711
1712     changes = account_changes_get (account->priv);
1713     changes->deleted = TRUE;
1714 }
1715
1716 /**
1717  * ag_account_get_value:
1718  * @account: the #AgAccount.
1719  * @key: the name of the setting to retrieve.
1720  * @value: an initialized #GValue to receive the setting's value.
1721  *
1722  * Gets the value of the configuration setting @key: @value must be a
1723  * #GValue initialized to the type of the setting.
1724  *
1725  * Returns: one of <type>#AgSettingSource</type>: %AG_SETTING_SOURCE_NONE if the setting is
1726  * not present, %AG_SETTING_SOURCE_ACCOUNT if the setting comes from the
1727  * account configuration, or %AG_SETTING_SOURCE_PROFILE if the value comes as
1728  * predefined in the profile.
1729  */
1730 AgSettingSource
1731 ag_account_get_value (AgAccount *account, const gchar *key,
1732                       GValue *value)
1733 {
1734     AgAccountPrivate *priv;
1735     AgServiceSettings *ss;
1736     AgSettingSource source;
1737     const GValue *val = NULL;
1738
1739     g_return_val_if_fail (AG_IS_ACCOUNT (account), AG_SETTING_SOURCE_NONE);
1740     priv = account->priv;
1741
1742     ss = get_service_settings (priv, priv->service, FALSE);
1743     if (ss)
1744     {
1745         val = g_hash_table_lookup (ss->settings, key);
1746         source = AG_SETTING_SOURCE_ACCOUNT;
1747     }
1748
1749     if (!val && priv->service)
1750     {
1751         val = _ag_service_get_default_setting (priv->service, key);
1752         source = AG_SETTING_SOURCE_PROFILE;
1753     }
1754
1755     if (val)
1756     {
1757         if (G_VALUE_TYPE (val) == G_VALUE_TYPE (value))
1758             g_value_copy (val, value);
1759         else
1760             g_value_transform (val, value);
1761         return source;
1762     }
1763
1764     return AG_SETTING_SOURCE_NONE;
1765 }
1766
1767 /**
1768  * ag_account_set_value:
1769  * @account: the #AgAccount.
1770  * @key: the name of the setting to change.
1771  * @value: a #GValue holding the new setting's value.
1772  *
1773  * Sets the value of the configuration setting @key to the value @value.
1774  * If @value is %NULL, then the setting is unset.
1775  */
1776 void
1777 ag_account_set_value (AgAccount *account, const gchar *key,
1778                       const GValue *value)
1779 {
1780     AgAccountPrivate *priv;
1781
1782     g_return_if_fail (AG_IS_ACCOUNT (account));
1783     priv = account->priv;
1784
1785     change_selected_service_value (priv, key, value);
1786 }
1787
1788 /**
1789  * ag_account_settings_iter_init:
1790  * @account: the #AgAccount.
1791  * @iter: an uninitialized #AgAccountSettingIter structure.
1792  * @key_prefix: enumerate only the settings whose key starts with @key_prefix.
1793  *
1794  * Initializes @iter to iterate over the account settings. If @key_prefix is
1795  * not %NULL, only keys whose names start with @key_prefix will be iterated
1796  * over.
1797  */
1798 void
1799 ag_account_settings_iter_init (AgAccount *account,
1800                                AgAccountSettingIter *iter,
1801                                const gchar *key_prefix)
1802 {
1803     AgAccountPrivate *priv;
1804     AgServiceSettings *ss;
1805     RealIter *ri = (RealIter *)iter;
1806
1807     g_return_if_fail (AG_IS_ACCOUNT (account));
1808     g_return_if_fail (iter != NULL);
1809     priv = account->priv;
1810
1811     ri->account = account;
1812     ri->key_prefix = key_prefix;
1813     ri->stage = AG_ITER_STAGE_UNSET;
1814
1815     ss = get_service_settings (priv, priv->service, FALSE);
1816     if (ss)
1817     {
1818         g_hash_table_iter_init (&ri->iter, ss->settings);
1819         ri->stage = AG_ITER_STAGE_ACCOUNT;
1820     }
1821 }
1822
1823 /**
1824  * ag_account_settings_iter_next:
1825  * @iter: an initialized #AgAccountSettingIter structure.
1826  * @key: a pointer to a string receiving the key name.
1827  * @value: a pointer to a pointer to a #GValue, to receive the key value.
1828  *
1829  * Iterates over the account keys. @iter must be an iterator previously
1830  * initialized with ag_account_settings_iter_init().
1831  *
1832  * Returns: %TRUE if @key and @value have been set, %FALSE if we there are no
1833  * more account settings to iterate over.
1834  */
1835 gboolean
1836 ag_account_settings_iter_next (AgAccountSettingIter *iter,
1837                                const gchar **key, const GValue **value)
1838 {
1839     RealIter *ri = (RealIter *)iter;
1840     AgServiceSettings *ss;
1841     AgAccountPrivate *priv;
1842
1843     g_return_val_if_fail (iter != NULL, FALSE);
1844     g_return_val_if_fail (AG_IS_ACCOUNT (iter->account), FALSE);
1845     g_return_val_if_fail (key != NULL && value != NULL, FALSE);
1846     priv = iter->account->priv;
1847
1848     if (ri->stage == AG_ITER_STAGE_ACCOUNT)
1849     {
1850         while (g_hash_table_iter_next (&ri->iter,
1851                                        (gpointer *)key, (gpointer *)value))
1852         {
1853             if (ri->key_prefix && !g_str_has_prefix (*key, ri->key_prefix))
1854                 continue;
1855
1856             return TRUE;
1857         }
1858         ri->stage = AG_ITER_STAGE_UNSET;
1859     }
1860
1861     if (!priv->service) return FALSE;
1862
1863     if (ri->stage == AG_ITER_STAGE_UNSET)
1864     {
1865         GHashTable *settings;
1866
1867         settings = _ag_service_load_default_settings (priv->service);
1868         if (!settings) return FALSE;
1869
1870         g_hash_table_iter_init (&ri->iter, settings);
1871         ri->stage = AG_ITER_STAGE_SERVICE;
1872     }
1873
1874     ss = get_service_settings (priv, priv->service, FALSE);
1875     while (g_hash_table_iter_next (&ri->iter,
1876                                    (gpointer *)key, (gpointer *)value))
1877     {
1878         if (ri->key_prefix && !g_str_has_prefix (*key, ri->key_prefix))
1879             continue;
1880
1881         /* if the setting is also on the account, it is overriden and we must
1882          * not return it here */
1883         if (ss && g_hash_table_lookup (ss->settings, *key) != NULL)
1884             continue;
1885
1886         return TRUE;
1887     }
1888
1889     return FALSE;
1890 }
1891
1892 /**
1893  * AgAccountNotifyCb:
1894  * @account: the #AgAccount.
1895  * @key: the name of the key whose value has changed.
1896  * @user_data: the user data that was passed when installing this callback.
1897  *
1898  * This callback is invoked when the value of an account configuration setting
1899  * changes. If the callback was installed with ag_account_watch_key() then @key
1900  * is the name of the configuration setting which changed; if it was installed
1901  * with ag_account_watch_dir() then @key is the same key prefix that was used
1902  * when installing this callback.
1903  */
1904
1905 /**
1906  * ag_account_watch_key:
1907  * @account: the #AgAccount.
1908  * @key: the name of the key to watch.
1909  * @callback: a #AgAccountNotifyCb callback to be called.
1910  * @user_data: pointer to user data, to be passed to @callback.
1911  *
1912  * Installs a watch on @key: @callback will be invoked whenever the value of
1913  * @key changes (or the key is removed).
1914  *
1915  * Returns: a #AgAccountWatch, which can then be used to remove this watch.
1916  */
1917 AgAccountWatch
1918 ag_account_watch_key (AgAccount *account, const gchar *key,
1919                       AgAccountNotifyCb callback, gpointer user_data)
1920 {
1921     g_return_val_if_fail (AG_IS_ACCOUNT (account), NULL);
1922     g_return_val_if_fail (key != NULL, NULL);
1923     g_return_val_if_fail (callback != NULL, NULL);
1924
1925     return ag_account_watch_int (account, g_strdup (key), NULL,
1926                                  callback, user_data);
1927 }
1928
1929 /**
1930  * ag_account_watch_dir:
1931  * @account: the #AgAccount.
1932  * @key_prefix: the prefix of the keys to watch.
1933  * @callback: a #AgAccountNotifyCb callback to be called.
1934  * @user_data: pointer to user data, to be passed to @callback.
1935  *
1936  * Installs a watch on all the keys under @key_prefix: @callback will be
1937  * invoked whenever the value of any of these keys changes (or a key is
1938  * removed).
1939  *
1940  * Returns: a #AgAccountWatch, which can then be used to remove this watch.
1941  */
1942 AgAccountWatch
1943 ag_account_watch_dir (AgAccount *account, const gchar *key_prefix,
1944                       AgAccountNotifyCb callback, gpointer user_data)
1945 {
1946     g_return_val_if_fail (AG_IS_ACCOUNT (account), NULL);
1947     g_return_val_if_fail (key_prefix != NULL, NULL);
1948     g_return_val_if_fail (callback != NULL, NULL);
1949
1950     return ag_account_watch_int (account, NULL, g_strdup (key_prefix),
1951                                  callback, user_data);
1952 }
1953
1954 /**
1955  * ag_account_remove_watch:
1956  * @account: the #AgAccount.
1957  * @watch: the watch to remove.
1958  *
1959  * Removes the notification callback identified by @watch.
1960  */
1961 void
1962 ag_account_remove_watch (AgAccount *account, AgAccountWatch watch)
1963 {
1964     AgAccountPrivate *priv;
1965     GHashTable *service_watches;
1966
1967     g_return_if_fail (AG_IS_ACCOUNT (account));
1968     g_return_if_fail (watch != NULL);
1969     priv = account->priv;
1970
1971     if (G_LIKELY (priv->watches))
1972     {
1973         service_watches = g_hash_table_lookup (priv->watches, watch->service);
1974         if (G_LIKELY (service_watches &&
1975                       g_hash_table_remove (service_watches, watch)))
1976             return; /* success */
1977     }
1978
1979     g_warning ("Watch %p not found", watch);
1980 }
1981
1982 /**
1983  * AgAccountStoreCb:
1984  * @account: the #AgAccount.
1985  * @error: a #GError, or %NULL.
1986  * @user_data: the user data that was passed to ag_account_store().
1987  *
1988  * This callback is invoked when storing the account settings is completed. If
1989  * @error is not %NULL, then some error occurred and the data has most likely
1990  * not been written.
1991  */
1992
1993 /**
1994  * ag_account_store:
1995  * @account: the #AgAccount.
1996  * @callback: function to be called when the settings have been written.
1997  * @user_data: pointer to user data, to be passed to @callback.
1998  *
1999  * Store the account settings which have been changed into the account
2000  * database, and invoke @callback when the operation has been completed.
2001  */
2002 void
2003 ag_account_store (AgAccount *account, AgAccountStoreCb callback,
2004                   gpointer user_data)
2005 {
2006     AgAccountPrivate *priv;
2007     AgAccountChanges *changes;
2008     GError *error = NULL;
2009     gchar *sql;
2010
2011     g_return_if_fail (AG_IS_ACCOUNT (account));
2012     priv = account->priv;
2013
2014     sql = ag_account_get_store_sql (account, &error);
2015     if (G_UNLIKELY (error))
2016     {
2017         if (callback)
2018             callback (account, error, user_data);
2019         else
2020             g_warning ("%s: %s", G_STRFUNC, error->message);
2021
2022         g_error_free (error);
2023         return;
2024     }
2025
2026     changes = priv->changes;
2027     priv->changes = NULL;
2028
2029     if (G_UNLIKELY (!sql))
2030     {
2031         /* Nothing to do: invoke the callback immediately */
2032         if (callback)
2033             callback (account, NULL, user_data);
2034         return;
2035     }
2036
2037     _ag_manager_exec_transaction (priv->manager, sql, changes, account,
2038                                   callback, user_data);
2039     g_free (sql);
2040 }
2041
2042 /**
2043  * ag_account_store_blocking:
2044  * @account: the #AgAccount.
2045  * @error: pointer to receive the #GError, or %NULL.
2046  *
2047  * Store the account settings which have been changed into the account
2048  * database. This function does not return until the operation has completed.
2049  *
2050  * Returns: %TRUE on success, %FALSE on failure.
2051  */
2052 gboolean
2053 ag_account_store_blocking (AgAccount *account, GError **error)
2054 {
2055     AgAccountPrivate *priv;
2056     AgAccountChanges *changes;
2057     GError *error_int = NULL;
2058     gchar *sql;
2059
2060     g_return_val_if_fail (AG_IS_ACCOUNT (account), FALSE);
2061     priv = account->priv;
2062
2063     sql = ag_account_get_store_sql (account, &error_int);
2064     if (G_UNLIKELY (error_int))
2065     {
2066         g_warning ("%s: %s", G_STRFUNC, error_int->message);
2067         g_propagate_error (error, error_int);
2068         return FALSE;
2069     }
2070
2071     changes = priv->changes;
2072     priv->changes = NULL;
2073
2074     if (G_UNLIKELY (!sql))
2075     {
2076         /* Nothing to do: return immediately */
2077         return TRUE;
2078     }
2079
2080     _ag_manager_exec_transaction_blocking (priv->manager, sql,
2081                                            changes, account,
2082                                            &error_int);
2083     g_free (sql);
2084     _ag_account_changes_free (changes);
2085
2086     if (G_UNLIKELY (error_int))
2087     {
2088         g_warning ("%s: %s", G_STRFUNC, error_int->message);
2089         g_propagate_error (error, error_int);
2090         return FALSE;
2091     }
2092
2093     return TRUE;
2094 }
2095
2096 #ifdef HAVE_AEGISCRYPTO
2097 static gboolean
2098 store_data (gpointer key, gpointer value, gpointer data)
2099 {
2100     const gchar *str;
2101     str = _ag_value_to_db ((GValue *)value);
2102     g_string_append (data, (gchar *)key);
2103     g_string_append (data, str);
2104     return TRUE;
2105 }
2106
2107 static gchar*
2108 signature_data (AgAccount *account, const gchar *key)
2109 {
2110     GString *data;
2111     AgServiceChanges *sc;
2112     gboolean key_is_prefix = FALSE;
2113
2114     data = g_string_sized_new (512);
2115     sc = account_service_changes_get (account->priv, account->priv->service, FALSE);
2116
2117     g_string_append (data, key);
2118
2119     if (g_str_has_suffix (key, "/"))
2120         key_is_prefix = TRUE;
2121
2122     if (key_is_prefix)
2123     {
2124         AgAccountSettingIter iter;
2125         const gchar *str;
2126         const GValue *val;
2127
2128         GTree *tree;
2129         tree = g_tree_new ((GCompareFunc)g_strcmp0);
2130
2131         ag_account_settings_iter_init (account, &iter, key);
2132         while (ag_account_settings_iter_next (&iter, &str, &val))
2133         {
2134             g_tree_insert (tree, (gpointer)str, (gpointer)val);
2135         }
2136
2137         if (sc->settings)
2138         {
2139             gpointer p_key, p_val;
2140             GHashTableIter i_settings;
2141             g_hash_table_iter_init (&i_settings, sc->settings);
2142             while (g_hash_table_iter_next (&i_settings, &p_key, &p_val))
2143             {
2144                 g_tree_insert (tree, p_key, p_val);
2145             }
2146         }
2147
2148         g_tree_foreach (tree, store_data, data);
2149         g_tree_destroy (tree);
2150     }
2151     else
2152     {
2153         const gchar *val_str = NULL;
2154
2155         if (sc->settings)
2156         {
2157             gpointer value;
2158             value = g_hash_table_lookup (sc->settings, key);
2159             if (value)
2160             {
2161                 val_str = _ag_value_to_db ((GValue *)value);
2162                 g_string_append (data, val_str);
2163             }
2164         }
2165
2166         if (!val_str)
2167         {
2168             GValue val = { 0 };
2169
2170             g_value_init (&val, G_TYPE_STRING);
2171             ag_account_get_value (account, key, &val);
2172             val_str = _ag_value_to_db (&val);
2173             if (val_str)
2174                 g_string_append (data, val_str);
2175             g_value_unset (&val);
2176         }
2177     }
2178
2179     return g_string_free (data, FALSE);
2180 }
2181 #endif
2182
2183 /**
2184  * ag_account_sign:
2185  * @key: the name of the key or prefix of the keys to be signed.
2186  * @token: aegis token (NULL teminated string) or NULL in order to use the
2187            application aegis ID token, for creating the signature. The
2188            application must possess (request) the token.
2189  *
2190  * Creates signature of the @key with given @token. The account must be 
2191  * stored prior to calling this function.
2192  */
2193 void
2194 ag_account_sign (AgAccount *account, const gchar *key, const gchar *token)
2195 {
2196 #ifdef HAVE_AEGISCRYPTO
2197     AgSignature *sgn;
2198     AgAccountPrivate *priv;
2199     AgServiceChanges *sc;
2200     gchar *data;
2201     struct aegis_signature_t signature;
2202     gchar *signature_string;
2203
2204     g_return_if_fail (key != NULL);
2205     g_return_if_fail (token != NULL);
2206     g_return_if_fail (AG_IS_ACCOUNT (account));
2207
2208     data = signature_data (account, key);
2209
2210     g_return_if_fail (data != NULL);
2211
2212     aegis_crypto_result result_sign =
2213             aegis_crypto_sign (data,
2214                                strlen (data),
2215                                token,
2216                                &signature);
2217     g_free (data);
2218     g_return_if_fail (result_sign == aegis_crypto_ok);
2219
2220     aegis_crypto_signature_to_string (&signature,
2221                                       aegis_as_base64,
2222                                       token,
2223                                       &signature_string);
2224
2225     sgn = g_slice_new (AgSignature);
2226     sgn->signature = g_strdup (signature_string);
2227     aegis_crypto_free (signature_string);
2228     sgn->token = g_strdup (token);
2229
2230     priv = account->priv;
2231     sc = account_service_changes_get (priv, priv->service, TRUE);
2232
2233     g_hash_table_insert (sc->signatures,
2234                          g_strdup (key), sgn);
2235                          
2236     aegis_crypto_finish ();
2237 #else
2238     g_warning ("ag_account_sign: aegis-crypto not found! Unable to sign the key.");
2239 #endif
2240 }
2241
2242 /**
2243  * ag_account_verify:
2244  * @key: the name of the key or prefix of the keys to be verified.
2245  * @token: location to receive the pointer to aegis token.
2246  *
2247  * Verify if the key is signed and the signature matches the value
2248  * and provides the aegis token which was used for signing the @key.
2249  *
2250  * Returns: %TRUE if the key is signed and the signature matches
2251  * the value.
2252  */
2253 gboolean
2254 ag_account_verify (AgAccount *account, const gchar *key, const gchar **token)
2255 {
2256 #ifdef HAVE_AEGISCRYPTO
2257     AgAccountPrivate *priv;
2258     AgServiceSettings *ss;
2259     guint service_id;
2260     gchar *data;
2261     gchar *sql;
2262     AgSignature sgn;
2263     GString *sql_str;
2264     aegis_system_mode_t made_in_mode;
2265     aegis_crypto_result result_verify;
2266     aegis_crypto_result result_convert;
2267     struct aegis_signature_t signature;
2268     char *token_name;
2269
2270     g_return_val_if_fail (AG_IS_ACCOUNT (account), FALSE);
2271
2272     priv = account->priv;
2273
2274     ss = get_service_settings (priv, priv->service, FALSE);
2275     g_return_val_if_fail (ss != NULL, FALSE);
2276
2277     service_id = (priv->service != NULL) ? priv->service->id : 0;
2278
2279     sql_str = g_string_sized_new (512);
2280     _ag_string_append_printf (sql_str,
2281                               "SELECT signature, token FROM Signatures "
2282                               "WHERE account = %u AND service = %u AND key = %Q",
2283                               account->id, service_id, key);
2284     sql = g_string_free (sql_str, FALSE);
2285     _ag_manager_exec_query (priv->manager,
2286                             (AgQueryCallback)got_account_signature,
2287                             &sgn, sql);
2288
2289     g_free (sql);
2290     data = signature_data (account, key);
2291
2292     aegis_crypto_init();
2293
2294     token_name = NULL;
2295     result_convert =  aegis_crypto_string_to_signature (sgn.signature,
2296                                                         &signature,
2297                                                         &token_name);
2298
2299     if (result_convert != aegis_crypto_ok) {
2300         *token = NULL;
2301         aegis_crypto_finish ();
2302         g_free (data);
2303         return FALSE;
2304     }
2305
2306     result_verify = aegis_crypto_verify (&signature,
2307                                          token_name,
2308                                          data,
2309                                          strlen (data),
2310                                          &made_in_mode);
2311
2312     if (result_verify != aegis_crypto_ok) {
2313         *token = NULL;
2314         aegis_crypto_free (token_name);
2315         aegis_crypto_finish ();
2316         g_free (data);
2317         return FALSE;
2318     }
2319
2320     *token = g_strdup (token_name);
2321     if (token_name)
2322         aegis_crypto_free (token_name);
2323
2324     aegis_crypto_finish ();
2325
2326     g_free (data);
2327
2328     return TRUE;
2329 #else
2330     g_warning ("ag_account_verify: aegis-crypto not found! Unable to verify the key.");
2331     return FALSE;
2332 #endif
2333 }
2334
2335 /**
2336  * ag_account_verify_with_tokens:
2337  * @key: the name of the key or prefix of the keys to be verified.
2338  * @tokens: array of aegis tokens.
2339  *
2340  * Verify if the @key is signed with any of the tokens from the @tokens
2341  * and the signature is valid.
2342  *
2343  * Returns: %TRUE if the key is signed with any of the given tokens
2344  * and the signature is valid.
2345  */
2346 gboolean
2347 ag_account_verify_with_tokens (AgAccount *account, const gchar *key, const gchar **tokens)
2348 {
2349     g_return_val_if_fail (AG_IS_ACCOUNT (account), FALSE);
2350
2351     const gchar *tmp_token = NULL;
2352
2353     g_return_val_if_fail (tokens != NULL, FALSE);
2354
2355     if (ag_account_verify (account, key, &tmp_token))
2356     {
2357         g_return_val_if_fail (tmp_token != NULL, FALSE);
2358
2359         while (*tokens != NULL)
2360         {
2361             if (strcmp (tmp_token, *tokens) == 0)
2362             {
2363                 return TRUE;
2364             }
2365             tokens++;
2366         }
2367     }
2368
2369     return FALSE;
2370 }