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