BugFix #203268 - accountListEnabled and accountList doesnot return the expected size
[accounts-sso:accounts-glib.git] / libaccounts-glib / ag-manager.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-manager
27  * @title: AgManager
28  * @short_description: The account manager object
29  *
30  * The #AgManager is the main object in this library.
31  */
32
33 #include "ag-manager.h"
34
35 #include "ag-errors.h"
36 #include "ag-internals.h"
37 #include "ag-util.h"
38 #include <dbus/dbus-glib-lowlevel.h>
39 #include <sched.h>
40 #include <sqlite3.h>
41 #include <string.h>
42 #include <unistd.h>
43
44 #ifndef DATABASE_DIR
45 #define DATABASE_DIR ".accounts"
46 #endif
47
48 enum
49 {
50     PROP_0,
51
52     PROP_SERVICE_TYPE,
53 };
54
55 enum
56 {
57     ACCOUNT_CREATED,
58     ACCOUNT_DELETED,
59     ACCOUNT_ENABLED,
60     ACCOUNT_UPDATED,
61     LAST_SIGNAL
62 };
63
64 static guint signals[LAST_SIGNAL] = { 0 };
65
66 struct _AgManagerPrivate {
67     sqlite3 *db;
68
69     sqlite3_stmt *begin_stmt;
70     sqlite3_stmt *commit_stmt;
71     sqlite3_stmt *rollback_stmt;
72
73     sqlite3_int64 last_service_id;
74     sqlite3_int64 last_account_id;
75
76     DBusConnection *dbus_conn;
77
78     /* Cache for AgService */
79     GHashTable *services;
80
81     /* Weak references to loaded accounts */
82     GHashTable *accounts;
83
84     /* list of StoreCbData awaiting for exclusive locks */
85     GList *locks;
86
87     /* list of EmittedSignalData for the signals emitted by this instance */
88     GList *emitted_signals;
89
90     /* list of ProcessedSignalData, to avoid processing signals twice */
91     GList *processed_signals;
92
93     /* D-Bus object paths we are listening to */
94     GPtrArray *object_paths;
95
96     GError *last_error;
97
98     guint db_timeout;
99
100     guint abort_on_db_timeout : 1;
101     guint is_disposed : 1;
102
103     gchar *service_type;
104 };
105
106 typedef struct {
107     AgManager *manager;
108     AgAccount *account;
109     gchar *sql;
110     AgAccountChanges *changes;
111     guint id;
112     AgAccountStoreCb callback;
113     gpointer user_data;
114 } StoreCbData;
115
116 typedef struct {
117     struct timespec ts;
118     gboolean must_process;
119 } EmittedSignalData;
120
121 typedef struct {
122     struct timespec ts;
123 } ProcessedSignalData;
124
125 G_DEFINE_TYPE (AgManager, ag_manager, G_TYPE_OBJECT);
126
127 #define AG_MANAGER_PRIV(obj) (AG_MANAGER(obj)->priv)
128
129 static void store_cb_data_free (StoreCbData *sd);
130 static void account_weak_notify (gpointer userdata, GObject *dead_account);
131
132 static void
133 set_error_from_db (AgManager *manager)
134 {
135     AgManagerPrivate *priv = manager->priv;
136     AgError code;
137     GError *error;
138
139     switch (sqlite3_errcode (priv->db))
140     {
141     case SQLITE_DONE:
142     case SQLITE_OK:
143         _ag_manager_take_error (manager, NULL);
144         return;
145     case SQLITE_BUSY:
146         code = AG_ERROR_DB_LOCKED;
147         if (priv->abort_on_db_timeout)
148             g_error ("Accounts DB timeout: causing application to abort.");
149         break;
150     default:
151         code = AG_ERROR_DB;
152         break;
153     }
154
155     error = g_error_new (AG_ERRORS, code, "SQLite error %d: %s",
156                          sqlite3_errcode (priv->db),
157                          sqlite3_errmsg (priv->db));
158     _ag_manager_take_error (manager, error);
159 }
160
161 static gboolean
162 timed_unref_account (gpointer account)
163 {
164     DEBUG_REFS ("Releasing temporary reference on account %u",
165                 AG_ACCOUNT (account)->id);
166     g_object_unref (account);
167     return FALSE;
168 }
169
170 static gboolean
171 parse_message_header (DBusMessageIter *iter,
172                       struct timespec *ts, AgAccountId *id,
173                       gboolean *created, gboolean *deleted,
174                       const gchar **provider_name)
175 {
176 #define EXPECT_TYPE(t) \
177     if (G_UNLIKELY (dbus_message_iter_get_arg_type (iter) != t)) return FALSE
178
179     EXPECT_TYPE (DBUS_TYPE_UINT32);
180     dbus_message_iter_get_basic (iter, &ts->tv_sec);
181     dbus_message_iter_next (iter);
182
183     EXPECT_TYPE (DBUS_TYPE_UINT32);
184     dbus_message_iter_get_basic (iter, &ts->tv_nsec);
185     dbus_message_iter_next (iter);
186
187     EXPECT_TYPE (DBUS_TYPE_UINT32);
188     dbus_message_iter_get_basic (iter, id);
189     dbus_message_iter_next (iter);
190
191     EXPECT_TYPE (DBUS_TYPE_BOOLEAN);
192     dbus_message_iter_get_basic (iter, created);
193     dbus_message_iter_next (iter);
194
195     EXPECT_TYPE (DBUS_TYPE_BOOLEAN);
196     dbus_message_iter_get_basic (iter, deleted);
197     dbus_message_iter_next (iter);
198
199     EXPECT_TYPE (DBUS_TYPE_STRING);
200     dbus_message_iter_get_basic (iter, provider_name);
201     dbus_message_iter_next (iter);
202
203 #undef EXPECT_TYPE
204     return TRUE;
205 }
206
207 static gboolean
208 ag_manager_must_emit_updated (AgManager *manager, AgAccountChanges *changes)
209 {
210     AgManagerPrivate *priv = manager->priv;
211
212     /* Don't emit the "updated" signal along with "created" or "deleted" */
213     if (changes->created || changes->deleted)
214         return FALSE;
215
216     /* The update-event is emitted whenever any value has been changed on
217      * particular service of account.
218      */
219     return (priv->service_type != NULL) ?
220         _ag_account_changes_have_service_type (changes, priv->service_type) : FALSE;
221 }
222
223 static gboolean
224 ag_manager_must_emit_enabled (AgManager *manager, AgAccountChanges *changes)
225 {
226     AgManagerPrivate *priv = manager->priv;
227
228     /* TODO: the enabled-event is emitted whenever enabled status has changed on
229      * any service or account. This has some possibility for optimization.
230      */
231     return (priv->service_type != NULL) ?
232         _ag_account_changes_have_enabled (changes) : FALSE;
233 }
234
235 static void
236 ag_manager_emit_signals (AgManager *manager, AgAccountId account_id,
237                          gboolean updated,
238                          gboolean enabled,
239                          gboolean created,
240                          gboolean deleted)
241 {
242     if (updated)
243         g_signal_emit_by_name (manager, "account-updated", account_id);
244
245     if (enabled)
246         g_signal_emit_by_name (manager, "enabled-event", account_id);
247
248     if (deleted)
249         g_signal_emit_by_name (manager, "account-deleted", account_id);
250
251     if (created)
252         g_signal_emit_by_name (manager, "account-created", account_id);
253 }
254
255 static gboolean
256 check_signal_processed (AgManagerPrivate *priv, struct timespec *ts)
257 {
258     ProcessedSignalData *psd;
259     GList *list;
260
261     for (list = priv->processed_signals; list != NULL; list = list->next)
262     {
263         psd = list->data;
264
265         if (psd->ts.tv_sec == ts->tv_sec &&
266             psd->ts.tv_nsec == ts->tv_nsec)
267         {
268             DEBUG_INFO ("Signal already processed: %lu-%lu",
269                         ts->tv_sec, ts->tv_nsec);
270             g_slice_free (ProcessedSignalData, psd);
271             priv->processed_signals =
272                 g_list_delete_link (priv->processed_signals, list);
273             return TRUE;
274         }
275     }
276
277     /* Add the signal to the list of processed ones; this is necessary if the
278      * manager was created for a specific service type, because in that case
279      * we are subscribing for DBus signals on two different object paths (the
280      * one for our service type, and one for the global settings), so we might
281      * get notified about the same signal twice.
282      */
283
284     /* Don't keep more than a very few elements in the list */
285     for (list = g_list_nth (priv->processed_signals, 2);
286          list != NULL;
287          list = g_list_nth (priv->processed_signals, 2))
288     {
289         priv->processed_signals = g_list_delete_link (priv->processed_signals,
290                                                       list);
291     }
292
293     psd = g_slice_new (ProcessedSignalData);
294     psd->ts = *ts;
295     priv->processed_signals = g_list_prepend (priv->processed_signals, psd);
296
297     return FALSE;
298 }
299
300 /**
301  * checks whether the sender of the message is listed in the object_paths array
302  */
303 static gboolean
304 message_is_from_interesting_object (DBusMessage *msg, GPtrArray *object_paths)
305 {
306     const gchar *msg_object_path;
307     gint i;
308
309     msg_object_path = dbus_message_get_path (msg);
310     if (G_UNLIKELY (msg_object_path == NULL))
311         return FALSE;
312
313     for (i = 0; i < object_paths->len; i++)
314     {
315         const gchar *object_path = g_ptr_array_index (object_paths, i);
316         if (strcmp (msg_object_path, object_path) == 0)
317             return TRUE;
318     }
319     return FALSE;
320 }
321
322 static DBusHandlerResult
323 dbus_filter_callback (DBusConnection *dbus_conn, DBusMessage *msg,
324                       void *user_data)
325 {
326     AgManager *manager = AG_MANAGER (user_data);
327     AgManagerPrivate *priv = manager->priv;
328     const gchar *provider_name = NULL;
329     AgAccountId account_id = 0;
330     AgAccount *account;
331     AgAccountChanges *changes;
332     struct timespec ts;
333     gboolean deleted, created;
334     gboolean ret;
335     gboolean ours = FALSE;
336     gboolean updated = FALSE;
337     gboolean enabled = FALSE;
338     gboolean must_instantiate = TRUE;
339     DBusMessageIter iter;
340     GList *list;
341
342     if (!dbus_message_is_signal (msg, AG_DBUS_IFACE, AG_DBUS_SIG_CHANGED))
343         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
344
345     if (!message_is_from_interesting_object(msg, priv->object_paths))
346         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
347
348     dbus_message_iter_init (msg, &iter);
349     ret = parse_message_header (&iter, &ts, &account_id,
350                                 &created, &deleted, &provider_name);
351     if (G_UNLIKELY (!ret))
352     {
353         g_warning ("%s: error in parsing signal arguments", G_STRFUNC);
354         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
355     }
356
357     DEBUG_INFO ("path = %s, time = %lu-%lu (%p)",
358                 dbus_message_get_path (msg), ts.tv_sec, ts.tv_nsec,
359                 manager);
360
361     /* Do not process the same signal more than once. */
362     if (check_signal_processed (priv, &ts))
363         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
364
365     for (list = priv->emitted_signals; list != NULL; list = list->next)
366     {
367         EmittedSignalData *esd = list->data;
368
369         if (esd->ts.tv_sec == ts.tv_sec &&
370             esd->ts.tv_nsec == ts.tv_nsec)
371         {
372             gboolean must_process = esd->must_process;
373             /* message is ours: we can ignore it, as the changes
374              * were already processed when the DB transaction succeeded. */
375             ours = TRUE;
376
377             DEBUG_INFO ("Signal is ours, must_process = %d", esd->must_process);
378             g_slice_free (EmittedSignalData, esd);
379             priv->emitted_signals = g_list_delete_link (priv->emitted_signals,
380                                                         list);
381             if (!must_process)
382                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
383         }
384     }
385
386     /* we must mark our emitted signals for reprocessing, because the current
387      * signal might modify some of the fields that were previously modified by
388      * us.
389      * This ensures that changes coming from different account manager
390      * instances are processed in the right order. */
391     for (list = priv->emitted_signals; list != NULL; list = list->next)
392     {
393         EmittedSignalData *esd = list->data;
394         DEBUG_INFO ("Marking pending signal for processing");
395         esd->must_process = TRUE;
396     }
397
398     changes = _ag_account_changes_from_dbus (manager, &iter, created, deleted);
399
400     /* check if the account is loaded */
401     account = g_hash_table_lookup (priv->accounts,
402                                    GUINT_TO_POINTER (account_id));
403
404     if (!account && !created && !deleted)
405         must_instantiate = FALSE;
406
407     if (ours && (deleted || created))
408         must_instantiate = FALSE;
409
410     if (!account && must_instantiate)
411     {
412         /* because of the checks above, this can happen if this is an account
413          * created or deleted from another instance.
414          * We must emit the signals, and cache the newly created account for a
415          * while, because the application is likely to inspect it */
416         account = g_object_new (AG_TYPE_ACCOUNT,
417                                 "manager", manager,
418                                 "provider", provider_name,
419                                 "id", account_id,
420                                 "foreign", created,
421                                 NULL);
422         g_return_val_if_fail (AG_IS_ACCOUNT (account),
423                               DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
424
425         g_object_weak_ref (G_OBJECT (account), account_weak_notify, manager);
426         g_hash_table_insert (priv->accounts, GUINT_TO_POINTER (account_id),
427                              account);
428         g_timeout_add_seconds (2, timed_unref_account, account);
429     }
430
431     if (changes)
432     {
433         updated = ag_manager_must_emit_updated (manager, changes);
434         enabled = ag_manager_must_emit_enabled (manager, changes);
435         if (account)
436             _ag_account_done_changes (account, changes);
437
438         _ag_account_changes_free (changes);
439     }
440
441     ag_manager_emit_signals (manager, account_id,
442                              updated,
443                              enabled,
444                              created,
445                              deleted);
446
447     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
448 }
449
450 static DBusMessage *
451 make_signal_for_service_type (DBusMessage *global_msg,
452                               const gchar *service_type)
453 {
454     gchar path[256];
455     gchar *escaped_type;
456     DBusMessage *msg;
457
458     escaped_type = _ag_dbus_escape_as_identifier (service_type);
459     g_snprintf (path, sizeof (path), "%s/%s",
460                 AG_DBUS_PATH_SERVICE, escaped_type);
461     g_free (escaped_type);
462
463     msg = dbus_message_copy (global_msg);
464     DEBUG_INFO("Setting path to %s", path);
465     if (!dbus_message_set_path (msg, path))
466         g_warning("setting path failed!");
467     return msg;
468 }
469
470 static void
471 signal_account_changes_on_service_types (AgManager *manager,
472                                          AgAccountChanges *changes,
473                                          DBusMessage *global_msg)
474 {
475     GPtrArray *service_types;
476     gint i;
477
478     service_types = _ag_account_changes_get_service_types (changes);
479     for (i = 0; i < service_types->len; i++)
480     {
481         const gchar *service_type;
482         DBusMessage *msg;
483         gboolean ret;
484
485         service_type = g_ptr_array_index(service_types, i);
486         msg = make_signal_for_service_type (global_msg, service_type);
487
488         ret = dbus_connection_send (manager->priv->dbus_conn, msg, NULL);
489         if (G_UNLIKELY (!ret))
490             g_warning ("Emission of DBus signal failed");
491         dbus_message_unref (msg);
492     }
493     g_ptr_array_free (service_types, TRUE);
494 }
495
496 static void
497 signal_account_changes (AgManager *manager, AgAccount *account,
498                         AgAccountChanges *changes)
499 {
500     AgManagerPrivate *priv = manager->priv;
501     DBusMessage *msg;
502     gboolean ret;
503     EmittedSignalData eds;
504
505     clock_gettime(CLOCK_MONOTONIC, &eds.ts);
506
507     msg = _ag_account_build_signal (account, changes, &eds.ts);
508     if (G_UNLIKELY (!msg))
509     {
510         g_warning ("Creation of D-Bus signal failed");
511         return;
512     }
513
514     ret = dbus_connection_send (priv->dbus_conn, msg, NULL);
515     if (G_UNLIKELY (!ret))
516     {
517         g_warning ("Emission of DBus signal failed");
518         goto finish;
519     }
520
521     /* emit the signal on all service-types */
522     signal_account_changes_on_service_types(manager, changes, msg);
523
524     dbus_connection_flush (priv->dbus_conn);
525     DEBUG_INFO ("Emitted signal, time: %lu-%lu", eds.ts.tv_sec, eds.ts.tv_nsec);
526
527     eds.must_process = FALSE;
528     priv->emitted_signals =
529         g_list_prepend (priv->emitted_signals,
530                         g_slice_dup (EmittedSignalData, &eds));
531
532 finish:
533     dbus_message_unref (msg);
534 }
535
536 static gboolean
537 got_service (sqlite3_stmt *stmt, AgService **p_service)
538 {
539     AgService *service;
540
541     g_assert (p_service != NULL);
542
543     service = _ag_service_new ();
544     service->id = sqlite3_column_int (stmt, 0);
545     service->display_name = g_strdup ((gchar *)sqlite3_column_text (stmt, 1));
546     service->provider = g_strdup ((gchar *)sqlite3_column_text (stmt, 2));
547     service->type = g_strdup ((gchar *)sqlite3_column_text (stmt, 3));
548
549     *p_service = service;
550     return TRUE;
551 }
552
553 static gboolean
554 got_service_id (sqlite3_stmt *stmt, AgService *service)
555 {
556     g_assert (service != NULL);
557
558     service->id = sqlite3_column_int (stmt, 0);
559     return TRUE;
560 }
561
562 static gboolean
563 add_service_to_db (AgManager *manager, AgService *service)
564 {
565     gchar *sql;
566
567     /* Add the service to the DB */
568     sql = sqlite3_mprintf ("INSERT INTO Services "
569                            "(name, display, provider, type) "
570                            "VALUES (%Q, %Q, %Q, %Q);",
571                            service->name,
572                            service->display_name,
573                            service->provider,
574                            service->type);
575     _ag_manager_exec_query (manager, NULL, NULL, sql);
576     sqlite3_free (sql);
577
578     /* The insert statement above might fail in the unlikely case
579      * that in the meantime the same service was inserted by some other
580      * process; so, instead of calling sqlite3_last_insert_rowid(), we
581      * just get the ID with another query. */
582     sql = sqlite3_mprintf ("SELECT id FROM Services WHERE name = %Q",
583                            service->name);
584     _ag_manager_exec_query (manager, (AgQueryCallback)got_service_id,
585                             service, sql);
586     sqlite3_free (sql);
587
588     return service->id != 0;
589 }
590
591 static gboolean
592 add_id_to_list (sqlite3_stmt *stmt, GList **plist)
593 {
594     gint id;
595
596     id = sqlite3_column_int (stmt, 0);
597     *plist = g_list_prepend (*plist, GINT_TO_POINTER (id));
598     return TRUE;
599 }
600
601 static void
602 account_weak_notify (gpointer userdata, GObject *dead_account)
603 {
604     AgManagerPrivate *priv = AG_MANAGER_PRIV (userdata);
605     GHashTableIter iter;
606     GObject *account;
607
608     DEBUG_REFS ("called for %p", dead_account);
609     g_hash_table_iter_init (&iter, priv->accounts);
610     while (g_hash_table_iter_next (&iter, NULL, (gpointer)&account))
611     {
612         if (account == dead_account)
613         {
614             g_hash_table_iter_steal (&iter);
615             break;
616         }
617     }
618 }
619
620 static void
621 account_weak_unref (GObject *account)
622 {
623     g_object_weak_unref (account, account_weak_notify,
624                          ag_account_get_manager (AG_ACCOUNT (account)));
625 }
626
627 /*
628  * exec_transaction:
629  *
630  * Executes a transaction, assuming that the exclusive lock has been obtained.
631  */
632 static void
633 exec_transaction (AgManager *manager, AgAccount *account,
634                   const gchar *sql, AgAccountChanges *changes,
635                   GError **error)
636 {
637     AgManagerPrivate *priv;
638     gchar *err_msg = NULL;
639     int ret;
640     gboolean updated, enabled;
641
642     DEBUG_LOCKS ("Accounts DB is now locked");
643     DEBUG_QUERIES ("called: %s", sql);
644     g_return_if_fail (AG_IS_MANAGER (manager));
645     priv = manager->priv;
646     g_return_if_fail (AG_IS_ACCOUNT (account));
647     g_return_if_fail (sql != NULL);
648     g_return_if_fail (priv->db != NULL);
649
650     ret = sqlite3_exec (priv->db, sql, NULL, NULL, &err_msg);
651     if (G_UNLIKELY (ret != SQLITE_OK))
652     {
653         *error = g_error_new (AG_ERRORS, AG_ERROR_DB, "%s", err_msg);
654         if (err_msg)
655             sqlite3_free (err_msg);
656
657         ret = sqlite3_step (priv->rollback_stmt);
658         if (G_UNLIKELY (ret != SQLITE_OK))
659             g_warning ("Rollback failed");
660         sqlite3_reset (priv->rollback_stmt);
661         DEBUG_LOCKS ("Accounts DB is now unlocked");
662         return;
663     }
664
665     ret = sqlite3_step (priv->commit_stmt);
666     if (G_UNLIKELY (ret != SQLITE_DONE))
667     {
668         *error = g_error_new_literal (AG_ERRORS, AG_ERROR_DB,
669                                       sqlite3_errmsg (priv->db));
670         sqlite3_reset (priv->commit_stmt);
671         return;
672     }
673     sqlite3_reset (priv->commit_stmt);
674
675     DEBUG_LOCKS ("Accounts DB is now unlocked");
676
677     /* everything went well; if this was a new account, we must update the
678      * local data structure */
679     if (account->id == 0)
680     {
681         account->id = priv->last_account_id;
682
683         /* insert the account into our cache */
684         g_object_weak_ref (G_OBJECT (account), account_weak_notify, manager);
685         g_hash_table_insert (priv->accounts, GUINT_TO_POINTER (account->id),
686                              account);
687     }
688
689     /* emit DBus signals to notify other processes */
690     signal_account_changes (manager, account, changes);
691
692     updated = ag_manager_must_emit_updated(manager, changes);
693
694     enabled = ag_manager_must_emit_enabled(manager, changes);
695     _ag_account_done_changes (account, changes);
696
697     ag_manager_emit_signals (manager, account->id,
698                              updated,
699                              enabled,
700                              changes->created,
701                              changes->deleted);
702 }
703
704 static void
705 lost_weak_ref (gpointer data, GObject *dead)
706 {
707     StoreCbData *sd = data;
708     AgManagerPrivate *priv;
709
710     GError error = { AG_ERRORS, AG_ERROR_DISPOSED, "Account disposed" };
711
712     g_assert ((GObject *)sd->account == dead);
713     _ag_account_store_completed (sd->account, sd->changes,
714                                  sd->callback, &error, sd->user_data);
715
716     priv = AG_MANAGER_PRIV (sd->manager);
717     priv->locks = g_list_remove (priv->locks, sd);
718     sd->account = NULL; /* so that the weak reference is not removed */
719     store_cb_data_free (sd);
720 }
721
722 static void
723 store_cb_data_free (StoreCbData *sd)
724 {
725     if (sd->account)
726         g_object_weak_unref (G_OBJECT (sd->account), lost_weak_ref, sd);
727     if (sd->id)
728         g_source_remove (sd->id);
729     g_free (sd->sql);
730     g_slice_free (StoreCbData, sd);
731 }
732
733 static gboolean
734 exec_transaction_idle (StoreCbData *sd)
735 {
736     AgManager *manager = sd->manager;
737     AgAccount *account = sd->account;
738     AgManagerPrivate *priv;
739     GError *error = NULL;
740     int ret;
741
742     g_return_val_if_fail (AG_IS_MANAGER (manager), FALSE);
743     priv = manager->priv;
744
745     g_return_val_if_fail (priv->begin_stmt != NULL, FALSE);
746     ret = sqlite3_step (priv->begin_stmt);
747     if (ret == SQLITE_BUSY)
748     {
749         sched_yield ();
750         return TRUE; /* call this callback again */
751     }
752
753     g_object_ref (manager);
754     g_object_ref (account);
755     if (ret == SQLITE_DONE)
756     {
757         exec_transaction (manager, account, sd->sql, sd->changes, &error);
758     }
759     else
760     {
761         error = g_error_new_literal (AG_ERRORS, AG_ERROR_DB, "Generic error");
762     }
763     _ag_account_store_completed (account, sd->changes,
764                                  sd->callback, error, sd->user_data);
765     if (error)
766         g_error_free (error);
767
768     priv->locks = g_list_remove (priv->locks, sd);
769     sd->id = 0;
770     store_cb_data_free (sd);
771     g_object_unref (account);
772     g_object_unref (manager);
773     return FALSE;
774 }
775
776 static int
777 prepare_transaction_statements (AgManagerPrivate *priv)
778 {
779     int ret;
780
781     if (G_UNLIKELY (!priv->begin_stmt))
782     {
783         ret = sqlite3_prepare_v2 (priv->db, "BEGIN EXCLUSIVE;", -1,
784                                   &priv->begin_stmt, NULL);
785         if (ret != SQLITE_OK) return ret;
786     }
787     else
788         sqlite3_reset (priv->begin_stmt);
789
790     if (G_UNLIKELY (!priv->commit_stmt))
791     {
792         ret = sqlite3_prepare_v2 (priv->db, "COMMIT;", -1,
793                                   &priv->commit_stmt, NULL);
794         if (ret != SQLITE_OK) return ret;
795     }
796     else
797         sqlite3_reset (priv->commit_stmt);
798
799     if (G_UNLIKELY (!priv->rollback_stmt))
800     {
801         ret = sqlite3_prepare_v2 (priv->db, "ROLLBACK;", -1,
802                                   &priv->rollback_stmt, NULL);
803         if (ret != SQLITE_OK) return ret;
804     }
805     else
806         sqlite3_reset (priv->rollback_stmt);
807
808     return SQLITE_OK;
809 }
810
811 static void
812 set_last_rowid_as_account_id (sqlite3_context *ctx,
813                               int argc, sqlite3_value **argv)
814 {
815     AgManagerPrivate *priv;
816
817     priv = sqlite3_user_data (ctx);
818     priv->last_account_id = sqlite3_last_insert_rowid (priv->db);
819     sqlite3_result_null (ctx);
820 }
821
822 static void
823 get_account_id (sqlite3_context *ctx, int argc, sqlite3_value **argv)
824 {
825     AgManagerPrivate *priv;
826
827     priv = sqlite3_user_data (ctx);
828     sqlite3_result_int64 (ctx, priv->last_account_id);
829 }
830
831 static void
832 create_functions (AgManagerPrivate *priv)
833 {
834     sqlite3_create_function (priv->db, "set_last_rowid_as_account_id", 0,
835                              SQLITE_ANY, priv,
836                              set_last_rowid_as_account_id, NULL, NULL);
837     sqlite3_create_function (priv->db, "account_id", 0,
838                              SQLITE_ANY, priv,
839                              get_account_id, NULL, NULL);
840 }
841
842 static void
843 setup_db_options (sqlite3 *db)
844 {
845     gchar *error;
846     int ret;
847
848     error = NULL;
849     ret = sqlite3_exec (db, "PRAGMA synchronous = 1", NULL, NULL, &error);
850     if (ret != SQLITE_OK)
851     {
852         g_warning ("%s: couldn't set synchronous mode (%s)",
853                    G_STRFUNC, error);
854         sqlite3_free (error);
855     }
856
857     error = NULL;
858     ret = sqlite3_exec (db, "PRAGMA journal_mode = TRUNCATE", NULL, NULL, &error);
859     if (ret != SQLITE_OK)
860     {
861         g_warning ("%s: couldn't set journal mode to TRUNCATE (%s)",
862                    G_STRFUNC, error);
863         sqlite3_free (error);
864     }
865 }
866
867 static gint
868 get_db_version (sqlite3 *db)
869 {
870     sqlite3_stmt *stmt;
871     gint version = 0, ret;
872
873     ret = sqlite3_prepare(db, "PRAGMA user_version", -1, &stmt, NULL);
874     if (G_UNLIKELY(ret != SQLITE_OK)) return 0;
875
876     ret = sqlite3_step(stmt);
877     if (G_LIKELY(ret == SQLITE_ROW))
878         version = sqlite3_column_int(stmt, 0);
879
880     sqlite3_finalize(stmt);
881     return version;
882 }
883
884 static gboolean
885 create_db (sqlite3 *db, guint timeout)
886 {
887     const gchar *sql;
888     gchar *error;
889     int ret;
890
891     sql = ""
892         "CREATE TABLE IF NOT EXISTS Accounts ("
893             "id INTEGER PRIMARY KEY AUTOINCREMENT,"
894             "name TEXT,"
895             "provider TEXT,"
896             "enabled INTEGER);"
897
898         "CREATE TABLE IF NOT EXISTS Services ("
899             "id INTEGER PRIMARY KEY AUTOINCREMENT,"
900             "name TEXT NOT NULL UNIQUE,"
901             "display TEXT NOT NULL,"
902             /* following fields are included for performance reasons */
903             "provider TEXT,"
904             "type TEXT);"
905         "CREATE INDEX IF NOT EXISTS idx_service ON Services(name);"
906
907         "CREATE TABLE IF NOT EXISTS Settings ("
908             "account INTEGER NOT NULL,"
909             "service INTEGER,"
910             "key TEXT NOT NULL,"
911             "type TEXT NOT NULL,"
912             "value BLOB);"
913         "CREATE UNIQUE INDEX IF NOT EXISTS idx_setting ON Settings "
914             "(account, service, key);"
915
916         "CREATE TRIGGER IF NOT EXISTS tg_delete_account "
917             "BEFORE DELETE ON Accounts FOR EACH ROW BEGIN "
918                 "DELETE FROM Settings WHERE account = OLD.id; "
919             "END;"
920
921         "CREATE TABLE IF NOT EXISTS Signatures ("
922             "account INTEGER NOT NULL,"
923             "service INTEGER,"
924             "key TEXT NOT NULL,"
925             "signature TEXT NOT NULL,"
926             "token TEXT NOT NULL);"
927         "CREATE UNIQUE INDEX IF NOT EXISTS idx_signatures ON Signatures "
928            "(account, service, key);"
929
930         "PRAGMA user_version = 1;";
931
932     error = NULL;
933     ret = sqlite3_exec (db, sql, NULL, NULL, &error);
934     if (ret == SQLITE_BUSY)
935     {
936         guint t;
937         for (t = 5; t < MAX_SQLITE_BUSY_LOOP_TIME_MS; t *= 2)
938         {
939             DEBUG_LOCKS ("Database locked, retrying...");
940             sched_yield ();
941             g_assert(error != NULL);
942             sqlite3_free (error);
943             ret = sqlite3_exec (db, sql, NULL, NULL, &error);
944             if (ret != SQLITE_BUSY) break;
945             usleep(t * 1000);
946         }
947     }
948
949     if (ret != SQLITE_OK)
950     {
951         g_warning ("Error initializing DB: %s", error);
952         sqlite3_free (error);
953         return FALSE;
954     }
955
956     return TRUE;
957 }
958
959 static gboolean
960 open_db (AgManager *manager)
961 {
962     AgManagerPrivate *priv = manager->priv;
963     const gchar *basedir;
964     gchar *filename, *pathname;
965     gint version;
966     gboolean ok = TRUE;
967     int ret;
968
969     basedir = g_getenv ("ACCOUNTS");
970     if (G_LIKELY (!basedir))
971     {
972         basedir = g_get_home_dir ();
973         pathname = g_build_path (G_DIR_SEPARATOR_S, basedir,
974             DATABASE_DIR, NULL);
975         if (G_UNLIKELY (g_mkdir_with_parents(pathname, 0755)))
976             g_warning ("Cannot create directory: %s", pathname);
977         filename = g_build_filename (pathname, "accounts.db", NULL);
978         g_free (pathname);
979     }
980     else
981     {
982         filename = g_build_filename (basedir, "accounts.db", NULL);
983     }
984     ret = sqlite3_open (filename, &priv->db);
985     g_free (filename);
986
987     if (ret != SQLITE_OK)
988     {
989         if (priv->db)
990         {
991             g_warning ("Error opening accounts DB: %s",
992                        sqlite3_errmsg (priv->db));
993             sqlite3_close (priv->db);
994             priv->db = NULL;
995         }
996         return FALSE;
997     }
998
999     /* TODO: busy handler */
1000
1001     version = get_db_version(priv->db);
1002     DEBUG_INFO ("DB version: %d", version);
1003     if (version < 1)
1004         ok = create_db(priv->db, priv->db_timeout);
1005     /* insert here code to upgrade the DB from older versions... */
1006
1007     if (G_UNLIKELY (!ok))
1008     {
1009         sqlite3_close (priv->db);
1010         priv->db = NULL;
1011         return FALSE;
1012     }
1013
1014     setup_db_options (priv->db);
1015     create_functions (priv);
1016
1017     return TRUE;
1018 }
1019
1020 static gboolean
1021 add_matches (AgManagerPrivate *priv)
1022 {
1023     gchar match[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
1024     DBusError error;
1025     gint i;
1026
1027     dbus_error_init (&error);
1028     for (i = 0; i < priv->object_paths->len; i++)
1029     {
1030         const gchar *path = g_ptr_array_index(priv->object_paths, i);
1031
1032         g_snprintf (match, sizeof (match),
1033                     "type='signal',interface='" AG_DBUS_IFACE "',path='%s'",
1034                     path);
1035         dbus_bus_add_match (priv->dbus_conn, match, &error);
1036         if (G_UNLIKELY (dbus_error_is_set (&error)))
1037         {
1038             g_warning ("Failed to add dbus filter (%s)", error.message);
1039             dbus_error_free (&error);
1040             return FALSE;
1041         }
1042     }
1043     return TRUE;
1044 }
1045
1046 static gboolean
1047 setup_dbus (AgManager *manager)
1048 {
1049     AgManagerPrivate *priv = manager->priv;
1050     DBusError error;
1051     gboolean ret;
1052
1053     dbus_error_init (&error);
1054     priv->dbus_conn = dbus_bus_get (DBUS_BUS_SESSION, &error);
1055     if (G_UNLIKELY (dbus_error_is_set (&error)))
1056     {
1057         g_warning ("Failed to get D-Bus connection (%s)", error.message);
1058         dbus_error_free (&error);
1059         return FALSE;
1060     }
1061
1062     ret = dbus_connection_add_filter (priv->dbus_conn,
1063                                       dbus_filter_callback,
1064                                       manager, NULL);
1065     if (G_UNLIKELY (!ret))
1066     {
1067         g_warning ("Failed to add dbus filter");
1068         return FALSE;
1069     }
1070
1071     if (priv->service_type == NULL)
1072     {
1073         /* listen to all changes */
1074         g_ptr_array_add (priv->object_paths, g_strdup (AG_DBUS_PATH));
1075     }
1076     else
1077     {
1078         gchar *escaped_type, *path;
1079
1080         /* listen for changes on our service type only */
1081         escaped_type = _ag_dbus_escape_as_identifier (priv->service_type);
1082         path = g_strdup_printf (AG_DBUS_PATH_SERVICE "/%s", escaped_type);
1083         g_free (escaped_type);
1084         g_ptr_array_add (priv->object_paths, path);
1085
1086         /* add also the global service type */
1087         g_ptr_array_add (priv->object_paths,
1088                          g_strdup (AG_DBUS_PATH_SERVICE_GLOBAL));
1089     }
1090
1091     ret = add_matches(priv);
1092     if (G_UNLIKELY (!ret)) return FALSE;
1093
1094     dbus_connection_setup_with_g_main (priv->dbus_conn, NULL);
1095     return TRUE;
1096 }
1097
1098 static void
1099 ag_manager_init (AgManager *manager)
1100 {
1101     AgManagerPrivate *priv;
1102
1103     manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, AG_TYPE_MANAGER,
1104                                                  AgManagerPrivate);
1105     priv = manager->priv;
1106
1107     priv->services =
1108         g_hash_table_new_full (g_str_hash, g_str_equal,
1109                                NULL, (GDestroyNotify)ag_service_unref);
1110     priv->accounts =
1111         g_hash_table_new_full (NULL, NULL,
1112                                NULL, (GDestroyNotify)account_weak_unref);
1113
1114     priv->db_timeout = MAX_SQLITE_BUSY_LOOP_TIME_MS; /* 5 seconds */
1115
1116     priv->object_paths = g_ptr_array_new_with_free_func (g_free);
1117 }
1118
1119 static GObject *
1120 ag_manager_constructor (GType type, guint n_params,
1121                         GObjectConstructParam *params)
1122 {
1123     GObjectClass *object_class = (GObjectClass *)ag_manager_parent_class;
1124     AgManager *manager;
1125     GObject *object;
1126
1127     object = object_class->constructor (type, n_params, params);
1128
1129     g_return_val_if_fail (object != NULL, NULL);
1130
1131     manager = AG_MANAGER (object);
1132     if (G_UNLIKELY (!open_db (manager) || !setup_dbus (manager)))
1133     {
1134         g_object_unref (object);
1135         return NULL;
1136     }
1137
1138     return object;
1139 }
1140
1141 static void
1142 ag_manager_set_property (GObject *object, guint property_id,
1143                          const GValue *value, GParamSpec *pspec)
1144 {
1145     AgManager *manager = AG_MANAGER (object);
1146     AgManagerPrivate *priv = manager->priv;
1147
1148     switch (property_id)
1149     {
1150     case PROP_SERVICE_TYPE:
1151         g_assert (priv->service_type == NULL);
1152         priv->service_type = g_value_dup_string (value);
1153         break;
1154     default:
1155         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1156         break;
1157     }
1158 }
1159
1160 static void
1161 ag_manager_dispose (GObject *object)
1162 {
1163     AgManagerPrivate *priv = AG_MANAGER_PRIV (object);
1164
1165     if (priv->is_disposed) return;
1166     priv->is_disposed = TRUE;
1167
1168     while (priv->locks)
1169     {
1170         store_cb_data_free (priv->locks->data);
1171         priv->locks = g_list_delete_link (priv->locks, priv->locks);
1172     }
1173
1174     G_OBJECT_CLASS (ag_manager_parent_class)->finalize (object);
1175 }
1176
1177 static void
1178 ag_manager_finalize (GObject *object)
1179 {
1180     AgManagerPrivate *priv = AG_MANAGER_PRIV (object);
1181
1182     if (priv->dbus_conn)
1183     {
1184         dbus_connection_remove_filter (priv->dbus_conn, dbus_filter_callback,
1185                                        object);
1186         dbus_connection_unref (priv->dbus_conn);
1187     }
1188
1189     g_ptr_array_free (priv->object_paths, TRUE);
1190
1191     while (priv->emitted_signals)
1192     {
1193         g_slice_free (EmittedSignalData, priv->emitted_signals->data);
1194         priv->emitted_signals = g_list_delete_link (priv->emitted_signals,
1195                                                     priv->emitted_signals);
1196     }
1197
1198     while (priv->processed_signals)
1199     {
1200         g_slice_free (ProcessedSignalData, priv->processed_signals->data);
1201         priv->processed_signals = g_list_delete_link (priv->processed_signals,
1202                                                       priv->processed_signals);
1203     }
1204
1205     if (priv->begin_stmt)
1206         sqlite3_finalize (priv->begin_stmt);
1207     if (priv->commit_stmt)
1208         sqlite3_finalize (priv->commit_stmt);
1209     if (priv->rollback_stmt)
1210         sqlite3_finalize (priv->rollback_stmt);
1211
1212     if (priv->services)
1213         g_hash_table_unref (priv->services);
1214
1215     if (priv->accounts)
1216         g_hash_table_unref (priv->accounts);
1217
1218     if (priv->db)
1219     {
1220         if (sqlite3_close (priv->db) != SQLITE_OK)
1221             g_warning ("Failed to close database: %s",
1222                        sqlite3_errmsg (priv->db));
1223         priv->db = NULL;
1224     }
1225     g_free (priv->service_type);
1226
1227     if (priv->last_error)
1228         g_error_free (priv->last_error);
1229
1230     G_OBJECT_CLASS (ag_manager_parent_class)->finalize (object);
1231 }
1232
1233 static void
1234 ag_manager_account_deleted (AgManager *manager, AgAccountId id)
1235 {
1236     g_return_if_fail (AG_IS_MANAGER (manager));
1237
1238     /* The weak reference is removed automatically when the account is removed
1239      * from the hash table */
1240     g_hash_table_remove (manager->priv->accounts, GUINT_TO_POINTER (id));
1241 }
1242
1243 static void
1244 ag_manager_class_init (AgManagerClass *klass)
1245 {
1246     GObjectClass* object_class = G_OBJECT_CLASS (klass);
1247
1248     g_type_class_add_private (object_class, sizeof (AgManagerPrivate));
1249
1250     klass->account_deleted = ag_manager_account_deleted;
1251     object_class->constructor = ag_manager_constructor;
1252     object_class->dispose = ag_manager_dispose;
1253     object_class->set_property = ag_manager_set_property;
1254     object_class->finalize = ag_manager_finalize;
1255
1256     g_object_class_install_property
1257         (object_class, PROP_SERVICE_TYPE,
1258          g_param_spec_string ("service-type", "service type", "Set service type",
1259                               NULL,
1260                               G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1261
1262     /**
1263      * AgManager::account-created:
1264      * @manager: the #AgManager.
1265      * @account_id: the #AgAccountId of the account that has been created.
1266      *
1267      * Emitted when a new account has been created; note that the account must
1268      * have been stored in the database: the signal is not emitted just in
1269      * response to ag_manager_create_account().
1270      */
1271     signals[ACCOUNT_CREATED] = g_signal_new ("account-created",
1272         G_TYPE_FROM_CLASS (klass),
1273         G_SIGNAL_RUN_LAST,
1274         0,
1275         NULL, NULL,
1276         g_cclosure_marshal_VOID__UINT,
1277         G_TYPE_NONE,
1278         1, G_TYPE_UINT);
1279
1280     /**
1281      * AgManager::account-enabled:
1282      * @manager: the #AgManager.
1283      * @account_id: the #AgAccountId of the account that has been enabled.
1284      *
1285      * If the manager has been created with ag_manager_new_for_service_type(), this
1286      * signal will be emitted when an account (identified by @account_id) has been
1287      * modified in such a way that the application might be interested to start/stop
1288      * using it: the "enabled" flag on the account or in some service supported by the
1289      * account and matching the #AgManager:service-type have changed.
1290      * In practice, this signal might be emitted more often than when strictly needed;
1291      * applications must call ag_account_list_enabled_services() or
1292      * ag_manager_list_enabled() to get the current state.
1293      */
1294     signals[ACCOUNT_ENABLED] = g_signal_new ("enabled-event",
1295         G_TYPE_FROM_CLASS (klass),
1296         G_SIGNAL_RUN_LAST,
1297         0,
1298         NULL, NULL,
1299         g_cclosure_marshal_VOID__UINT,
1300         G_TYPE_NONE,
1301         1, G_TYPE_UINT);
1302
1303     /**
1304      * AgManager::account-deleted:
1305      * @manager: the #AgManager.
1306      * @account_id: the #AgAccountId of the account that has been deleted.
1307      *
1308      * Emitted when an account has been deleted.
1309      * This signal is redundant with AgAccount::deleted, but it's convenient to
1310      * provide full change notification to #AgManager.
1311      */
1312     signals[ACCOUNT_DELETED] = g_signal_new ("account-deleted",
1313         G_TYPE_FROM_CLASS (klass),
1314         G_SIGNAL_RUN_LAST,
1315         G_STRUCT_OFFSET (AgManagerClass, account_deleted),
1316         NULL, NULL,
1317         g_cclosure_marshal_VOID__UINT,
1318         G_TYPE_NONE,
1319         1, G_TYPE_UINT);
1320
1321     /**
1322      * AgManager::account-updated:
1323      * @manager: the #AgManager.
1324      * @account_id: the #AgAccountId of the account that has been update.
1325      *
1326      * Emitted when particular service of an account has been updated.
1327      * This signal is redundant with AgAccount::deleted, but it's convenient to
1328      * provide full change notification to #AgManager.
1329      */
1330     signals[ACCOUNT_UPDATED] = g_signal_new ("account-updated",
1331          G_TYPE_FROM_CLASS (klass),
1332          G_SIGNAL_RUN_LAST,
1333          0,
1334          NULL, NULL,
1335          g_cclosure_marshal_VOID__UINT,
1336          G_TYPE_NONE,
1337          1, G_TYPE_UINT);
1338
1339     _ag_debug_init();
1340 }
1341
1342 /**
1343  * ag_manager_new:
1344  *
1345  * Returns: an instance of an #AgManager.
1346  */
1347 AgManager *
1348 ag_manager_new ()
1349 {
1350     return g_object_new (AG_TYPE_MANAGER, NULL);
1351 }
1352
1353 GList *
1354 _ag_manager_list_all (AgManager *manager)
1355 {
1356     GList *list = NULL;
1357     const gchar *sql;
1358
1359     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1360     sql = "SELECT id FROM Accounts;";
1361     _ag_manager_exec_query (manager, (AgQueryCallback)add_id_to_list,
1362                             &list, sql);
1363     return list;
1364 }
1365
1366 void
1367 _ag_manager_take_error (AgManager *manager, GError *error)
1368 {
1369     AgManagerPrivate *priv;
1370
1371     g_return_if_fail (AG_IS_MANAGER (manager));
1372     priv = manager->priv;
1373
1374     if (priv->last_error)
1375         g_error_free (priv->last_error);
1376     priv->last_error = error;
1377 }
1378
1379 const GError *
1380 _ag_manager_get_last_error (AgManager *manager)
1381 {
1382     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1383
1384     return manager->priv->last_error;
1385 }
1386
1387 /**
1388  * ag_manager_list:
1389  * @manager: the #AgManager.
1390  *
1391  * Lists the accounts. If the #AgManager is created with specified service_type
1392  * it will return only the accounts supporting this service_type.
1393  *
1394  * Returns: a #GList of #AgAccountId representing the accounts. Must
1395  * be free'd with ag_manager_list_free().
1396  */
1397 GList *
1398 ag_manager_list (AgManager *manager)
1399 {
1400     AgManagerPrivate *priv;
1401
1402     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1403     priv = manager->priv;
1404
1405     if (priv->service_type)
1406         return ag_manager_list_by_service_type (manager, priv->service_type);
1407
1408     return _ag_manager_list_all (manager);
1409 }
1410
1411 /**
1412  * ag_manager_list_by_service_type:
1413  * @manager: the #AgManager.
1414  *
1415  * Lists the accounts supporting the given service type.
1416  *
1417  * Returns: a #GList of #AgAccountId representing the accounts. Must
1418  * be free'd with ag_manager_list_free().
1419  */
1420 GList *
1421 ag_manager_list_by_service_type (AgManager *manager,
1422                                  const gchar *service_type)
1423 {
1424     GList *list = NULL;
1425     char sql[512];
1426
1427     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1428     sqlite3_snprintf (sizeof (sql), sql,
1429                       "SELECT DISTINCT account FROM Settings "
1430                       "JOIN Services ON Settings.service = Services.id "
1431                       "WHERE Services.type = %Q;",
1432                       service_type);
1433     _ag_manager_exec_query (manager, (AgQueryCallback)add_id_to_list,
1434                             &list, sql);
1435     return list;
1436 }
1437
1438 /**
1439  * ag_manager_list_enabled:
1440  * @manager: the #AgManager.
1441  *
1442  * Lists the enabled accounts.
1443  *
1444  * Returns: a #GList of the enabled #AgAccountId representing the accounts. Must
1445  * be free'd with ag_manager_list_free().
1446  */
1447 GList *
1448 ag_manager_list_enabled (AgManager *manager)
1449 {
1450     GList *list = NULL;
1451     char sql[512];
1452     AgManagerPrivate *priv;
1453
1454     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1455     priv = manager->priv;
1456     
1457     if (priv->service_type == NULL)
1458     {
1459         sqlite3_snprintf (sizeof (sql), sql,
1460                           "SELECT id FROM Accounts WHERE enabled=1;");
1461         _ag_manager_exec_query (manager, (AgQueryCallback)add_id_to_list,
1462                                 &list, sql);
1463     }
1464     else
1465     {
1466         list = ag_manager_list_enabled_by_service_type(manager, priv->service_type);
1467     }
1468     return list;
1469 }
1470
1471 /**
1472  * ag_manager_list_enabled_by_service_type:
1473  * @manager: the #AgManager.
1474  *
1475  * Lists the enabled accounts supporting the given service type.
1476  *
1477  * Returns: a #GList of the enabled #AgAccountId representing the accounts. Must
1478  * be free'd with ag_manager_list_free().
1479  */
1480 GList *
1481 ag_manager_list_enabled_by_service_type (AgManager *manager,
1482                                          const gchar *service_type)
1483 {
1484     GList *list = NULL;
1485     char sql[512];
1486
1487     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1488     g_return_val_if_fail (service_type != NULL, NULL);
1489     sqlite3_snprintf (sizeof (sql), sql,
1490                       "SELECT Settings.account FROM Settings "
1491                       "INNER JOIN Services ON Settings.service = Services.id "
1492                       "WHERE Settings.key='enabled' AND Settings.value='1' "
1493                       "AND Services.type = %Q AND Settings.account IN "
1494                       "(SELECT id FROM Accounts WHERE enabled=1);",
1495                       service_type);
1496     _ag_manager_exec_query (manager, (AgQueryCallback)add_id_to_list,
1497                             &list, sql);
1498     return list;
1499 }
1500
1501 /**
1502  * ag_manager_list_free:
1503  * @list: a #GList returned from some #AgManager method.
1504  *
1505  * Frees the memory taken by a #GList allocated by #AgManager.
1506  */
1507 void
1508 ag_manager_list_free (GList *list)
1509 {
1510     g_list_free (list);
1511 }
1512
1513 /**
1514  * ag_manager_get_account:
1515  * @manager: the #AgManager.
1516  * @account_id: the #AgAccountId of the account.
1517  *
1518  * Instantiates the object representing the account identified by
1519  * @account_id.
1520  *
1521  * Returns: an #AgAccount, on which the client must call g_object_unref()
1522  * when it's done with it, or %NULL if an error occurs.
1523  */
1524 AgAccount *
1525 ag_manager_get_account (AgManager *manager, AgAccountId account_id)
1526 {
1527     return ag_manager_load_account (manager, account_id, NULL);
1528 }
1529
1530 /**
1531  * ag_manager_load_account:
1532  * @manager: the #AgManager.
1533  * @account_id: the #AgAccountId of the account.
1534  * @error: pointer to a #GError, or %NULL.
1535  *
1536  * Instantiates the object representing the account identified by
1537  * @account_id.
1538  *
1539  * Returns: an #AgAccount, on which the client must call g_object_unref()
1540  * when it's done with it, or %NULL if an error occurs.
1541  */
1542 AgAccount *
1543 ag_manager_load_account (AgManager *manager, AgAccountId account_id,
1544                          GError **error)
1545 {
1546     AgManagerPrivate *priv;
1547     AgAccount *account;
1548
1549     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1550     g_return_val_if_fail (account_id != 0, NULL);
1551     priv = manager->priv;
1552
1553     account = g_hash_table_lookup (priv->accounts,
1554                                    GUINT_TO_POINTER (account_id));
1555     if (account)
1556         return g_object_ref (account);
1557
1558     /* the account is not loaded; do it now */
1559     account = g_object_new (AG_TYPE_ACCOUNT,
1560                             "manager", manager,
1561                             "id", account_id,
1562                             NULL);
1563     if (G_LIKELY (account))
1564     {
1565         g_object_weak_ref (G_OBJECT (account), account_weak_notify, manager);
1566         g_hash_table_insert (priv->accounts, GUINT_TO_POINTER (account_id),
1567                              account);
1568     }
1569     else if (priv->last_error != NULL)
1570     {
1571         g_set_error_literal (error,
1572                              priv->last_error->domain,
1573                              priv->last_error->code,
1574                              priv->last_error->message);
1575     }
1576     return account;
1577 }
1578
1579 /**
1580  * ag_manager_create_account:
1581  * @manager: the #AgManager.
1582  * @provider_name: name of the provider of the account.
1583  *
1584  * Create a new account. The account is not stored in the database until
1585  * ag_account_store() has successfully returned; the @id field in the
1586  * #AgAccount structure is also not meant to be valid till the account has been
1587  * stored.
1588  *
1589  * Returns: a new #AgAccount.
1590  */
1591 AgAccount *
1592 ag_manager_create_account (AgManager *manager, const gchar *provider_name)
1593 {
1594     AgAccount *account;
1595
1596     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1597
1598     account = g_object_new (AG_TYPE_ACCOUNT,
1599                             "manager", manager,
1600                             "provider", provider_name,
1601                             NULL);
1602     return account;
1603 }
1604
1605 /* This is called when creating AgService objects from inside the DBus
1606  * handler: we don't want to access the Db from there */
1607 AgService *
1608 _ag_manager_get_service_lazy (AgManager *manager, const gchar *service_name,
1609                               const gchar *service_type, const gint service_id)
1610 {
1611     AgManagerPrivate *priv;
1612     AgService *service;
1613
1614     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1615     g_return_val_if_fail (service_name != NULL, NULL);
1616     priv = manager->priv;
1617
1618     service = g_hash_table_lookup (priv->services, service_name);
1619     if (service)
1620     {
1621         if (service->id == 0)
1622             service->id = service_id;
1623         return ag_service_ref (service);
1624     }
1625
1626     service = _ag_service_new_from_memory (service_name, service_type, service_id);
1627
1628     g_hash_table_insert (priv->services, service->name, service);
1629     return ag_service_ref (service);
1630 }
1631
1632 /**
1633  * ag_manager_get_service:
1634  * @manager: the #AgManager.
1635  * @service_name: the name of the service.
1636  *
1637  * Loads the service identified by @service_name.
1638  *
1639  * Returns: an #AgService, which must be then free'd with ag_service_unref().
1640  */
1641 AgService *
1642 ag_manager_get_service (AgManager *manager, const gchar *service_name)
1643 {
1644     AgManagerPrivate *priv;
1645     AgService *service;
1646     gchar *sql;
1647     gint rows;
1648
1649
1650     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1651     g_return_val_if_fail (service_name != NULL, NULL);
1652     priv = manager->priv;
1653
1654     service = g_hash_table_lookup (priv->services, service_name);
1655     if (service)
1656         return ag_service_ref (service);
1657
1658     /* First, check if the service is in the DB */
1659     sql = sqlite3_mprintf ("SELECT id, display, provider, type "
1660                            "FROM Services WHERE name = %Q", service_name);
1661     rows = _ag_manager_exec_query (manager, (AgQueryCallback)got_service,
1662                                    &service, sql);
1663     sqlite3_free (sql);
1664
1665     if (service)
1666     {
1667         /* the basic server data have been loaded from the DB; the service name
1668          * is still missing, though */
1669         service->name = g_strdup (service_name);
1670     }
1671     else
1672     {
1673         /* The service is not in the DB: it must be loaded */
1674         service = _ag_service_new_from_file (service_name);
1675
1676         if (service && !add_service_to_db (manager, service))
1677         {
1678             g_warning ("Error in adding service %s to DB!", service_name);
1679             ag_service_unref (service);
1680             service = NULL;
1681         }
1682     }
1683
1684     if (G_UNLIKELY (!service)) return NULL;
1685
1686     g_hash_table_insert (priv->services, service->name, service);
1687     return ag_service_ref (service);
1688 }
1689
1690 guint
1691 _ag_manager_get_service_id (AgManager *manager, AgService *service)
1692 {
1693     g_return_val_if_fail (AG_IS_MANAGER (manager), 0);
1694
1695     if (service == NULL) return 0; /* global service */
1696
1697     if (service->id == 0)
1698     {
1699         gchar *sql;
1700         gint rows;
1701
1702         /* We got this service name from another process; load the id from the
1703          * DB - it must already exist */
1704         sql = sqlite3_mprintf ("SELECT id FROM Services WHERE name = %Q",
1705                                service->name);
1706         rows = _ag_manager_exec_query (manager, (AgQueryCallback)got_service_id,
1707                                        service, sql);
1708         sqlite3_free (sql);
1709         if (G_UNLIKELY (rows != 1))
1710         {
1711             g_warning ("%s: got %d rows when asking for service %s",
1712                        G_STRFUNC, rows, service->name);
1713         }
1714     }
1715
1716     return service->id;
1717 }
1718
1719 /**
1720  * ag_manager_list_services:
1721  * @manager: the #AgManager.
1722  *
1723  * Gets a list of all the installed services.
1724  * If the #AgManager is created with specified service_type
1725  * it will return only the installed services supporting this service_type.
1726  *
1727  * Returns: a list of #AgService, which must be then free'd with
1728  * ag_service_list_free().
1729  */
1730 GList *
1731 ag_manager_list_services (AgManager *manager)
1732 {
1733     AgManagerPrivate *priv;
1734
1735     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1736     priv = manager->priv;
1737
1738     if (priv->service_type)
1739         return ag_manager_list_services_by_type (manager, priv->service_type);
1740
1741     return _ag_services_list (manager);
1742 }
1743
1744 /**
1745  * ag_manager_list_services_by_type:
1746  * @manager: the #AgManager.
1747  * @service_type: the type of the service.
1748  *
1749  * Gets a list of all the installed services of type @service_type.
1750  *
1751  * Returns: a list of #AgService, which must be then free'd with
1752  * ag_service_list_free().
1753  */
1754 GList *
1755 ag_manager_list_services_by_type (AgManager *manager, const gchar *service_type)
1756 {
1757     GList *all_services, *list;
1758     GList *services = NULL;
1759
1760     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1761     g_return_val_if_fail (service_type != NULL, NULL);
1762
1763     /* if we kept the DB Service table always up-to-date with all known
1764      * services, then we could just run a query over it. But while we are not,
1765      * it's simpler to implement the function by reusing the output from
1766      * _ag_services_list(manager). */
1767     all_services = _ag_services_list (manager);
1768     for (list = all_services; list != NULL; list = list->next)
1769     {
1770         AgService *service = list->data;
1771         const gchar *serviceType = ag_service_get_service_type (service);
1772         if (serviceType && strcmp (serviceType, service_type) == 0)
1773         {
1774             services = g_list_prepend (services, service);
1775         }
1776         else
1777             ag_service_unref (service);
1778     }
1779     g_list_free (all_services);
1780
1781     return services;
1782 }
1783
1784 void
1785 _ag_manager_exec_transaction (AgManager *manager, const gchar *sql,
1786                               AgAccountChanges *changes, AgAccount *account,
1787                               AgAccountStoreCb callback, gpointer user_data)
1788 {
1789     AgManagerPrivate *priv = manager->priv;
1790     GError *error = NULL;
1791     int ret;
1792
1793     ret = prepare_transaction_statements (priv);
1794     if (G_UNLIKELY (ret != SQLITE_OK))
1795     {
1796         error = g_error_new (AG_ERRORS, AG_ERROR_DB, "Got error: %s (%d)",
1797                              sqlite3_errmsg (priv->db), ret);
1798         goto finish;
1799     }
1800
1801     ret = sqlite3_step (priv->begin_stmt);
1802     if (ret == SQLITE_BUSY)
1803     {
1804         if (callback)
1805         {
1806             StoreCbData *sd;
1807
1808             sd = g_slice_new (StoreCbData);
1809             sd->manager = manager;
1810             sd->account = account;
1811             sd->changes = changes;
1812             sd->callback = callback;
1813             sd->user_data = user_data;
1814             sd->sql = g_strdup (sql);
1815             sd->id = g_idle_add ((GSourceFunc)exec_transaction_idle, sd);
1816             priv->locks = g_list_prepend (priv->locks, sd);
1817             g_object_weak_ref (G_OBJECT (account), lost_weak_ref, sd);
1818         }
1819         return;
1820     }
1821
1822     if (ret != SQLITE_DONE)
1823     {
1824         error = g_error_new (AG_ERRORS, AG_ERROR_DB, "Got error: %s (%d)",
1825                              sqlite3_errmsg (priv->db), ret);
1826         goto finish;
1827     }
1828
1829     exec_transaction (manager, account, sql, changes, &error);
1830
1831 finish:
1832     _ag_account_store_completed (account, changes,
1833                                  callback, error, user_data);
1834     if (error)
1835         g_error_free (error);
1836 }
1837
1838 void
1839 _ag_manager_exec_transaction_blocking (AgManager *manager, const gchar *sql,
1840                                        AgAccountChanges *changes,
1841                                        AgAccount *account,
1842                                        GError **error)
1843 {
1844     AgManagerPrivate *priv = manager->priv;
1845     gint sleep_ms = 200;
1846     int ret;
1847
1848     ret = prepare_transaction_statements (priv);
1849     if (G_UNLIKELY (ret != SQLITE_OK))
1850     {
1851         *error = g_error_new (AG_ERRORS, AG_ERROR_DB, "Got error: %s (%d)",
1852                               sqlite3_errmsg (priv->db), ret);
1853         return;
1854     }
1855
1856     ret = sqlite3_step (priv->begin_stmt);
1857     while (ret == SQLITE_BUSY)
1858     {
1859         /* TODO: instead of this loop, use a semaphore or some other non
1860          * polling mechanism */
1861         if (sleep_ms > 30000)
1862         {
1863             DEBUG_LOCKS ("Database locked for more than 30 seconds; "
1864                          "giving up!");
1865             break;
1866         }
1867         DEBUG_LOCKS ("Database locked, sleeping for %ums", sleep_ms);
1868         g_usleep (sleep_ms * 1000);
1869         sleep_ms *= 2;
1870         ret = sqlite3_step (priv->begin_stmt);
1871     }
1872
1873     if (ret != SQLITE_DONE)
1874     {
1875         *error = g_error_new (AG_ERRORS, AG_ERROR_DB, "Got error: %s (%d)",
1876                               sqlite3_errmsg (priv->db), ret);
1877         return;
1878     }
1879
1880     exec_transaction (manager, account, sql, changes, error);
1881 }
1882
1883 static guint
1884 timespec_diff_ms(struct timespec *ts1, struct timespec *ts0)
1885 {
1886     return (ts1->tv_sec - ts0->tv_sec) * 1000 +
1887         (ts1->tv_nsec - ts0->tv_nsec) / 1000000;
1888 }
1889
1890 /* Executes an SQL statement, and optionally calls
1891  * the callback for every row of the result.
1892  * Returns the number of rows fetched.
1893  */
1894 gint
1895 _ag_manager_exec_query (AgManager *manager,
1896                         AgQueryCallback callback, gpointer user_data,
1897                         const gchar *sql)
1898 {
1899     sqlite3 *db;
1900     int ret;
1901     sqlite3_stmt *stmt;
1902     struct timespec ts0, ts1;
1903     gint rows = 0;
1904
1905     g_return_val_if_fail (AG_IS_MANAGER (manager), 0);
1906     db = manager->priv->db;
1907
1908     g_return_val_if_fail (db != NULL, 0);
1909
1910     ret = sqlite3_prepare_v2 (db, sql, -1, &stmt, NULL);
1911     if (ret != SQLITE_OK)
1912     {
1913         g_warning ("%s: can't compile SQL statement \"%s\": %s", G_STRFUNC, sql,
1914                    sqlite3_errmsg (db));
1915         return 0;
1916     }
1917
1918     DEBUG_QUERIES ("about to run:\n%s", sql);
1919
1920     /* get the current time, to abort the operation in case the DB is locked
1921      * for longer than db_timeout. */
1922     clock_gettime(CLOCK_MONOTONIC, &ts0);
1923
1924     do
1925     {
1926         ret = sqlite3_step (stmt);
1927
1928         switch (ret)
1929         {
1930             case SQLITE_DONE:
1931                 break;
1932
1933             case SQLITE_ROW:
1934                 if (callback == NULL || callback (stmt, user_data))
1935                 {
1936                     rows++;
1937                 }
1938                 break;
1939
1940             case SQLITE_BUSY:
1941                 clock_gettime(CLOCK_MONOTONIC, &ts1);
1942                 if (timespec_diff_ms(&ts1, &ts0) < manager->priv->db_timeout)
1943                 {
1944                     /* If timeout was specified and table is locked,
1945                      * wait instead of executing default runtime
1946                      * error action. Otherwise, fall through to it. */
1947                     sched_yield ();
1948                     break;
1949                 }
1950
1951             default:
1952                 set_error_from_db (manager);
1953                 g_warning ("%s: runtime error while executing \"%s\": %s",
1954                            G_STRFUNC, sql, sqlite3_errmsg (db));
1955                 sqlite3_finalize (stmt);
1956                 return rows;
1957         }
1958     } while (ret != SQLITE_DONE);
1959
1960     sqlite3_finalize (stmt);
1961
1962     return rows;
1963 }
1964
1965 /**
1966  * ag_manager_get_provider:
1967  * @manager: the #AgManager.
1968  * @provider_name: the name of the provider.
1969  *
1970  * Loads the provider identified by @provider_name.
1971  *
1972  * Returns: an #AgProvider, which must be then free'd with ag_provider_unref().
1973  */
1974 AgProvider *
1975 ag_manager_get_provider (AgManager *manager, const gchar *provider_name)
1976 {
1977     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1978     g_return_val_if_fail (provider_name != NULL, NULL);
1979
1980     /* We don't implement any caching mechanism for AgProvider structures: they
1981      * shouldn't be loaded that often. */
1982     return _ag_provider_new_from_file (provider_name);
1983 }
1984
1985 /**
1986  * ag_manager_list_providers:
1987  * @manager: the #AgManager.
1988  *
1989  * Gets a list of all the installed providers.
1990  *
1991  * Returns: a list of #AgProvider, which must be then free'd with
1992  * ag_provider_list_free().
1993  */
1994 GList *
1995 ag_manager_list_providers (AgManager *manager)
1996 {
1997     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
1998
1999     return _ag_providers_list (manager);
2000 }
2001
2002 /**
2003  * ag_manager_new_for_service_type:
2004  * @service_type: the name of a service type
2005  *
2006  * Returns: an instance of an #AgManager with specified service type.
2007  */
2008 AgManager *
2009 ag_manager_new_for_service_type (const gchar *service_type)
2010 {
2011     AgManager *manager;
2012
2013     g_return_val_if_fail (service_type != NULL, NULL);
2014
2015     manager = g_object_new (AG_TYPE_MANAGER, "service-type", service_type, NULL);
2016     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
2017
2018     return manager;
2019 }
2020
2021 const gchar *
2022 ag_manager_get_service_type (AgManager *manager)
2023 {
2024     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
2025
2026     return manager->priv->service_type;
2027 }
2028
2029 /**
2030  * ag_manager_set_db_timeout:
2031  * @manager: the #AgManager.
2032  * @timeout_ms: the new timeout, in milliseconds.
2033  *
2034  * Sets the timeout for database operations. This tells the library how long
2035  * it is allowed to block while waiting for a locked DB to become accessible.
2036  * Higher values mean a higher chance of successful reads, but also mean that
2037  * the execution might be blocked for a longer time.
2038  * The default is 5 seconds.
2039  */
2040 void
2041 ag_manager_set_db_timeout (AgManager *manager, guint timeout_ms)
2042 {
2043     g_return_if_fail (AG_IS_MANAGER (manager));
2044     manager->priv->db_timeout = timeout_ms;
2045 }
2046
2047 /**
2048  * ag_manager_get_db_timeout:
2049  * @manager: the #AgManager.
2050  *
2051  * Returns: the timeout (in milliseconds) for database operations.
2052  */
2053 guint
2054 ag_manager_get_db_timeout (AgManager *manager)
2055 {
2056     g_return_val_if_fail (AG_IS_MANAGER (manager), 0);
2057     return manager->priv->db_timeout;
2058 }
2059
2060 /**
2061  * ag_manager_set_abort_on_db_timeout:
2062  * @manager: the #AgManager.
2063  * @abort: whether to abort when a DB timeout occurs.
2064  *
2065  * Tells libaccounts whether it should make the client application abort when
2066  * a timeout error occurs. The default is %FALSE.
2067  */
2068 void
2069 ag_manager_set_abort_on_db_timeout (AgManager *manager, gboolean abort)
2070 {
2071     g_return_if_fail (AG_IS_MANAGER (manager));
2072     manager->priv->abort_on_db_timeout = abort;
2073 }
2074
2075 /**
2076  * ag_manager_get_abort_on_db_timeout:
2077  * @manager: the #AgManager.
2078  *
2079  * Returns: whether the library will abort when a timeout error occurs.
2080  */
2081 gboolean
2082 ag_manager_get_abort_on_db_timeout (AgManager *manager)
2083 {
2084     g_return_val_if_fail (AG_IS_MANAGER (manager), FALSE);
2085     return manager->priv->abort_on_db_timeout;
2086 }
2087
2088 /**
2089  * ag_manager_load_service_type:
2090  * @manager: the #AgManager.
2091  * @service_type: the name of the service type.
2092  *
2093  * Instantiate the service type @service_type.
2094  *
2095  * Returns: an #AgServiceType, which must be then free'd with
2096  * ag_service_type_unref().
2097  */
2098 AgServiceType *
2099 ag_manager_load_service_type (AgManager *manager, const gchar *service_type)
2100 {
2101     g_return_val_if_fail (AG_IS_MANAGER (manager), NULL);
2102
2103     /* Given the small size of the service type file, and the unlikely need to
2104      * load them more than once, we don't cache them in the manager. But this
2105      * might change in the future.
2106      */
2107     return _ag_service_type_new_from_file (service_type);
2108 }
2109