]> git.0d.be Git - empathy.git/blob - libempathy/empathy-account-manager.c
Insert a comment claryfing reference ownership.
[empathy.git] / libempathy / empathy-account-manager.c
1 /*
2  * Copyright (C) 2008 Collabora Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  * 
18  * Authors: Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
19  */
20
21 #include "config.h"
22
23 #include <libmissioncontrol/mc-account-monitor.h>
24
25 #include "empathy-account-manager.h"
26 #include "empathy-marshal.h"
27 #include "empathy-utils.h"
28
29 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountManager)
30
31 typedef struct {
32   McAccountMonitor *monitor;
33   MissionControl   *mc;
34
35   GHashTable       *accounts;
36   int               connected;
37   int               connecting;
38   gboolean          dispose_run;
39 } EmpathyAccountManagerPriv;
40
41 typedef struct {
42   McPresence presence;
43   TpConnectionStatus connection;
44   gboolean is_enabled;
45
46   guint source_id;
47 } AccountData;
48
49 enum {
50   ACCOUNT_CREATED,
51   ACCOUNT_DELETED,
52   ACCOUNT_ENABLED,
53   ACCOUNT_DISABLED,
54   ACCOUNT_CHANGED,
55   ACCOUNT_CONNECTION_CHANGED,
56   ACCOUNT_PRESENCE_CHANGED,
57   LAST_SIGNAL
58 };
59
60 static guint signals[LAST_SIGNAL];
61 static EmpathyAccountManager *manager_singleton = NULL;
62
63 G_DEFINE_TYPE (EmpathyAccountManager, empathy_account_manager, G_TYPE_OBJECT);
64
65 static AccountData *
66 account_data_new (McPresence presence,
67                   TpConnectionStatus connection,
68                   gboolean is_enabled)
69 {
70   AccountData *retval;
71
72   retval = g_slice_new0 (AccountData);
73   retval->presence = presence;
74   retval->connection = connection;
75   retval->is_enabled = is_enabled;
76   retval->source_id = 0;
77
78   return retval;
79 }
80
81 static AccountData *
82 account_data_new_default (MissionControl *mc,
83                           McAccount *account)
84 {
85   McPresence actual_p;
86   TpConnectionStatus actual_c;
87   GError *err = NULL;
88
89   actual_p = mission_control_get_presence_actual (mc, &err);
90   if (err != NULL)
91     {
92       actual_p = MC_PRESENCE_UNSET;
93       g_clear_error (&err);
94     }
95
96   actual_c = mission_control_get_connection_status (mc, account, &err);
97
98   if (err != NULL)
99     {
100       actual_c = TP_CONNECTION_STATUS_DISCONNECTED;
101       g_error_free (err);
102     }
103
104   return account_data_new (actual_p, actual_c, mc_account_is_enabled (account));
105 }
106
107 static void
108 account_data_free (AccountData *data)
109 {
110   if (data->source_id > 0)
111     {
112       g_source_remove (data->source_id);
113       data->source_id = 0;
114     }
115
116   g_slice_free (AccountData, data);
117 }
118
119 static void
120 account_created_cb (McAccountMonitor *mon,
121                     gchar *account_name,
122                     EmpathyAccountManager *manager)
123 {
124   McAccount *account;
125   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
126
127   account = mc_account_lookup (account_name);
128
129   if (account)
130     {
131       AccountData *data;
132
133       data = account_data_new_default (priv->mc, account);
134
135       /* the reference returned by mc_account_lookup is owned by the
136        * hash table.
137        */
138       g_hash_table_insert (priv->accounts, account, data);
139
140       g_signal_emit (manager, signals[ACCOUNT_CREATED], 0, account);
141     }
142 }
143
144 static void
145 account_deleted_cb (McAccountMonitor *mon,
146                     gchar *account_name,
147                     EmpathyAccountManager *manager)
148 {
149   McAccount *account;
150   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
151
152   account = mc_account_lookup (account_name);
153
154   if (account)
155     {
156       g_signal_emit (manager, signals[ACCOUNT_DELETED], 0, account);
157
158       g_hash_table_remove (priv->accounts, account);
159       g_object_unref (account);
160     }
161 }
162
163 static void
164 account_changed_cb (McAccountMonitor *mon,
165                     gchar *account_name,
166                     EmpathyAccountManager *manager)
167 {
168   McAccount *account;
169
170   account = mc_account_lookup (account_name);
171
172   if (account)
173     {
174       g_signal_emit (manager, signals[ACCOUNT_CHANGED], 0, account);
175       g_object_unref (account);
176     }
177 }
178
179 static void
180 account_disabled_cb (McAccountMonitor *mon,
181                      gchar *account_name,
182                      EmpathyAccountManager *manager)
183 {
184   McAccount *account;
185   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
186   AccountData *data;
187
188   account = mc_account_lookup (account_name);
189
190   if (account)
191     {
192       data = g_hash_table_lookup (priv->accounts, account);
193       g_assert (data);
194       data->is_enabled = FALSE;
195
196       g_signal_emit (manager, signals[ACCOUNT_DISABLED], 0, account);
197       g_object_unref (account);
198     }
199 }
200
201 static void
202 account_enabled_cb (McAccountMonitor *mon,
203                     gchar *account_name,
204                     EmpathyAccountManager *manager)
205 {
206   McAccount *account;
207   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
208   AccountData *data;
209
210   account = mc_account_lookup (account_name);
211
212   if (account)
213     {
214       data = g_hash_table_lookup (priv->accounts, account);
215       g_assert (data);
216       data->is_enabled = TRUE;
217
218       g_signal_emit (manager, signals[ACCOUNT_ENABLED], 0, account);
219       g_object_unref (account);
220     }
221 }
222
223 static void
224 update_connection_numbers (EmpathyAccountManager *manager,
225                            TpConnectionStatus conn,
226                            TpConnectionStatus old_c)
227 {
228   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
229
230   if (conn == TP_CONNECTION_STATUS_CONNECTED)
231     {
232       priv->connected++;
233       if (old_c == TP_CONNECTION_STATUS_CONNECTING)
234         priv->connecting--;
235     }
236
237   if (conn == TP_CONNECTION_STATUS_CONNECTING)
238     {
239       priv->connecting++;
240       if (old_c == TP_CONNECTION_STATUS_CONNECTED)
241         priv->connected--;
242     }
243
244   if (conn == TP_CONNECTION_STATUS_DISCONNECTED)
245     {
246       if (old_c == TP_CONNECTION_STATUS_CONNECTED)
247         priv->connected--;
248
249       if (old_c == TP_CONNECTION_STATUS_CONNECTING)
250         priv->connecting--;
251     }
252 }
253
254 static gboolean
255 remove_data_timeout (gpointer _data)
256 {
257   AccountData *data = _data;
258
259   data->source_id = 0;
260
261   return FALSE;
262 }
263
264 static void
265 account_status_changed_cb (MissionControl *mc,
266                            TpConnectionStatus connection,
267                            McPresence presence,
268                            TpConnectionStatusReason reason,
269                            const gchar *unique_name,
270                            EmpathyAccountManager *manager)
271 {
272   McAccount *account;
273   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
274   AccountData *data;
275   McPresence old_p;
276   TpConnectionStatus old_c;
277   gboolean emit_presence = FALSE, emit_connection = FALSE;
278
279   account = mc_account_lookup (unique_name);
280
281   if (account)
282     {
283       data = g_hash_table_lookup (priv->accounts, account);
284       g_assert (data);
285
286       old_p = data->presence;
287       old_c = data->connection;
288
289       if (old_p != presence)
290         {
291           data->presence = presence;
292           emit_presence = TRUE;
293         }
294
295       if (old_c != connection)
296         {
297           data->connection = connection;
298           update_connection_numbers (manager, connection, old_c);
299
300           if (old_c == TP_CONNECTION_STATUS_CONNECTING &&
301               connection == TP_CONNECTION_STATUS_CONNECTED)
302             {
303                 if (data->source_id > 0) {
304                   g_source_remove (data->source_id);
305                   data->source_id = 0;
306                 }
307
308                 data->source_id = g_timeout_add_seconds (10,
309                                                          remove_data_timeout,
310                                                          data);
311             }
312           emit_connection = TRUE;
313         }
314
315       if (emit_presence)
316         g_signal_emit (manager, signals[ACCOUNT_PRESENCE_CHANGED], 0,
317                        account, presence, old_p);
318
319       if (emit_connection)
320         g_signal_emit (manager, signals[ACCOUNT_CONNECTION_CHANGED], 0,
321                        account, reason, connection, old_c);
322
323       g_object_unref (account);
324     }
325 }
326
327 static void
328 empathy_account_manager_init (EmpathyAccountManager *manager)
329 {
330   EmpathyAccountManagerPriv *priv =
331       G_TYPE_INSTANCE_GET_PRIVATE (manager,
332                                    EMPATHY_TYPE_ACCOUNT_MANAGER, EmpathyAccountManagerPriv);
333   GList *mc_accounts, *l;
334   guint initial_connection;
335   AccountData *data;
336
337   manager->priv = priv;
338   priv->monitor = mc_account_monitor_new ();
339   priv->mc = empathy_mission_control_new ();
340   priv->connected = priv->connecting = 0;
341   priv->dispose_run = FALSE;
342
343   priv->accounts = g_hash_table_new_full (empathy_account_hash,
344                                           empathy_account_equal,
345                                           g_object_unref, 
346                                           (GDestroyNotify) account_data_free);
347
348   mc_accounts = mc_accounts_list ();
349
350   for (l = mc_accounts; l; l = l->next)
351     {
352       data = account_data_new_default (priv->mc, l->data);
353
354       initial_connection = mission_control_get_connection_status (priv->mc,
355                                                                   l->data, NULL);
356       if (initial_connection == TP_CONNECTION_STATUS_CONNECTED) {
357         priv->connected++;
358       } else if (initial_connection == TP_CONNECTION_STATUS_CONNECTING) {
359         priv->connecting++;
360       }
361
362       /* no need to g_object_ref () the account here, as mc_accounts_list ()
363        * already increases the refcount.
364        */
365       g_hash_table_insert (priv->accounts, l->data, data);
366     }
367
368   g_signal_connect (priv->monitor, "account-created",
369                     G_CALLBACK (account_created_cb), manager);
370   g_signal_connect (priv->monitor, "account-deleted",
371                     G_CALLBACK (account_deleted_cb), manager);
372   g_signal_connect (priv->monitor, "account-disabled",
373                     G_CALLBACK (account_disabled_cb), manager);
374   g_signal_connect (priv->monitor, "account-enabled",
375                     G_CALLBACK (account_enabled_cb), manager);
376   g_signal_connect (priv->monitor, "account-changed",
377                     G_CALLBACK (account_changed_cb), manager);
378
379   dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc), "AccountStatusChanged",
380                                G_CALLBACK (account_status_changed_cb),
381                                manager, NULL);
382
383   g_list_free (mc_accounts);
384 }
385
386 static void
387 disconnect_monitor_signals (McAccountMonitor *monitor,
388                             GObject *obj)
389 {
390   g_signal_handlers_disconnect_by_func (monitor,
391                                         account_created_cb, obj);
392   g_signal_handlers_disconnect_by_func (monitor,
393                                         account_deleted_cb, obj);
394   g_signal_handlers_disconnect_by_func (monitor,
395                                         account_disabled_cb, obj);
396   g_signal_handlers_disconnect_by_func (monitor,
397                                         account_enabled_cb, obj);
398   g_signal_handlers_disconnect_by_func (monitor,
399                                         account_changed_cb, obj);
400 }
401
402 static void
403 do_finalize (GObject *obj)
404 {
405   EmpathyAccountManager *manager = EMPATHY_ACCOUNT_MANAGER (obj);
406   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
407
408   g_hash_table_unref (priv->accounts);
409
410   G_OBJECT_CLASS (empathy_account_manager_parent_class)->finalize (obj);
411 }
412
413 static void
414 do_dispose (GObject *obj)
415 {
416   EmpathyAccountManager *manager = EMPATHY_ACCOUNT_MANAGER (obj);
417   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
418
419   if (priv->dispose_run)
420     return;
421
422   priv->dispose_run = TRUE;
423
424   dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc),
425                                   "AccountStatusChanged",
426                                   G_CALLBACK (account_status_changed_cb),
427                                   obj);
428
429   disconnect_monitor_signals (priv->monitor, obj);
430
431   if (priv->monitor)
432     {
433       g_object_unref (priv->monitor);
434       priv->monitor = NULL;
435     }
436
437   if (priv->mc)
438     g_object_unref (priv->mc);
439
440   g_hash_table_remove_all (priv->accounts);
441
442   G_OBJECT_CLASS (empathy_account_manager_parent_class)->dispose (obj);
443 }
444
445 static GObject*
446 do_constructor (GType type,
447                 guint n_construct_params,
448                 GObjectConstructParam *construct_params)
449 {
450   GObject *retval;
451
452   if (!manager_singleton)
453     {
454       retval = G_OBJECT_CLASS (empathy_account_manager_parent_class)->constructor (type,
455                                                                                    n_construct_params,
456                                                                                    construct_params);
457       g_object_add_weak_pointer (retval, (gpointer *) &retval);
458       manager_singleton = EMPATHY_ACCOUNT_MANAGER (retval);
459     } 
460   else
461     {
462       retval = g_object_ref (manager_singleton);
463     }
464
465   return retval;
466 }
467
468 static void
469 empathy_account_manager_class_init (EmpathyAccountManagerClass *klass)
470 {
471   GObjectClass *oclass = G_OBJECT_CLASS (klass);
472
473   oclass->finalize = do_finalize;
474   oclass->dispose = do_dispose;
475   oclass->constructor = do_constructor;
476
477   signals[ACCOUNT_CREATED] =
478     g_signal_new ("account-created",
479                   G_TYPE_FROM_CLASS (klass),
480                   G_SIGNAL_RUN_LAST,
481                   0,
482                   NULL, NULL,
483                   g_cclosure_marshal_VOID__OBJECT,
484                   G_TYPE_NONE,
485                   1, MC_TYPE_ACCOUNT);
486
487   signals[ACCOUNT_DELETED] =
488     g_signal_new ("account-deleted",
489                   G_TYPE_FROM_CLASS (klass),
490                   G_SIGNAL_RUN_LAST,
491                   0,
492                   NULL, NULL,
493                   g_cclosure_marshal_VOID__OBJECT,
494                   G_TYPE_NONE,
495                   1, MC_TYPE_ACCOUNT);
496
497   signals[ACCOUNT_ENABLED] =
498     g_signal_new ("account-enabled",
499                   G_TYPE_FROM_CLASS (klass),
500                   G_SIGNAL_RUN_LAST,
501                   0,
502                   NULL, NULL,
503                   g_cclosure_marshal_VOID__OBJECT,
504                   G_TYPE_NONE,
505                   1, MC_TYPE_ACCOUNT);
506
507   signals[ACCOUNT_DISABLED] =
508     g_signal_new ("account-disabled",
509                   G_TYPE_FROM_CLASS (klass),
510                   G_SIGNAL_RUN_LAST,
511                   0,
512                   NULL, NULL,
513                   g_cclosure_marshal_VOID__OBJECT,
514                   G_TYPE_NONE,
515                   1, MC_TYPE_ACCOUNT);
516
517   signals[ACCOUNT_CHANGED] =
518     g_signal_new ("account-changed",
519                   G_TYPE_FROM_CLASS (klass),
520                   G_SIGNAL_RUN_LAST,
521                   0,
522                   NULL, NULL,
523                   g_cclosure_marshal_VOID__OBJECT,
524                   G_TYPE_NONE,
525                   1, MC_TYPE_ACCOUNT);
526
527   signals[ACCOUNT_CONNECTION_CHANGED] =
528     g_signal_new ("account-connection-changed",
529                   G_TYPE_FROM_CLASS (klass),
530                   G_SIGNAL_RUN_LAST,
531                   0,
532                   NULL, NULL,
533                   _empathy_marshal_VOID__OBJECT_INT_UINT_UINT,
534                   G_TYPE_NONE,
535                   4, MC_TYPE_ACCOUNT,
536                   G_TYPE_INT,   /* reason */
537                   G_TYPE_UINT,  /* actual connection */
538                   G_TYPE_UINT); /* previous connection */
539
540   signals[ACCOUNT_PRESENCE_CHANGED] =
541     g_signal_new ("account-presence-changed",
542                   G_TYPE_FROM_CLASS (klass),
543                   G_SIGNAL_RUN_LAST,
544                   0,
545                   NULL, NULL,
546                   _empathy_marshal_VOID__OBJECT_INT_INT,
547                   G_TYPE_NONE,
548                   3, MC_TYPE_ACCOUNT,
549                   G_TYPE_INT,  /* actual presence */
550                   G_TYPE_INT); /* previous presence */
551   
552   g_type_class_add_private (oclass, sizeof (EmpathyAccountManagerPriv));
553 }
554
555 /* public methods */
556
557 EmpathyAccountManager *
558 empathy_account_manager_dup_singleton (void)
559 {
560   return g_object_new (EMPATHY_TYPE_ACCOUNT_MANAGER, NULL);
561 }
562
563 int
564 empathy_account_manager_get_connected_accounts (EmpathyAccountManager *manager)
565 {
566   EmpathyAccountManagerPriv *priv;
567
568   g_assert (EMPATHY_IS_ACCOUNT_MANAGER (manager));
569
570   priv = GET_PRIV (manager);
571
572   return priv->connected;
573 }
574
575 int
576 empathy_account_manager_get_connecting_accounts (EmpathyAccountManager *manager)
577 {
578   EmpathyAccountManagerPriv *priv;
579
580   g_assert (EMPATHY_IS_ACCOUNT_MANAGER (manager));
581
582   priv = GET_PRIV (manager);
583
584   return priv->connecting;
585 }
586
587 gboolean
588 empathy_account_manager_is_account_just_connected (EmpathyAccountManager *manager,
589                                                    McAccount *account)
590 {
591   EmpathyAccountManagerPriv *priv;
592   AccountData *data;
593
594   g_assert (EMPATHY_IS_ACCOUNT_MANAGER (manager));
595
596   priv = GET_PRIV (manager);
597   data = g_hash_table_lookup (priv->accounts, account);
598
599   g_assert (data);
600
601   return (data->source_id > 0);
602 }
603