]> git.0d.be Git - empathy.git/blob - libempathy/empathy-account-manager.c
dc15651cb3b7717a98762470a1eb8d72f2829f24
[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  *          Sjoerd Simons <sjoerd.simons@collabora.co.uk>
20  */
21
22 #include "config.h"
23
24 #include <telepathy-glib/util.h>
25 #include <telepathy-glib/account-manager.h>
26 #include <telepathy-glib/enums.h>
27 #include <telepathy-glib/defs.h>
28 #include <telepathy-glib/interfaces.h>
29
30 #include "empathy-account-manager.h"
31 #include "empathy-marshal.h"
32 #include "empathy-utils.h"
33
34 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
35 #include <libempathy/empathy-debug.h>
36
37 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountManager)
38
39 #define MC5_BUS_NAME "org.freedesktop.Telepathy.MissionControl5"
40
41 typedef struct {
42   /* (owned) unique name -> (reffed) EmpathyAccount */
43   GHashTable       *accounts;
44   int               connected;
45   int               connecting;
46   gboolean          dispose_run;
47   TpProxySignalConnection *proxy_signal;
48   TpAccountManager *tp_manager;
49   TpDBusDaemon *dbus;
50
51   /* global presence */
52   EmpathyAccount *global_account;
53
54   TpConnectionPresenceType global_presence;
55   gchar *global_status;
56   gchar *global_status_message;
57 } EmpathyAccountManagerPriv;
58
59 enum {
60   ACCOUNT_CREATED,
61   ACCOUNT_DELETED,
62   ACCOUNT_ENABLED,
63   ACCOUNT_DISABLED,
64   ACCOUNT_CHANGED,
65   ACCOUNT_CONNECTION_CHANGED,
66   GLOBAL_PRESENCE_CHANGED,
67   NEW_CONNECTION,
68   LAST_SIGNAL
69 };
70
71 static guint signals[LAST_SIGNAL];
72 static EmpathyAccountManager *manager_singleton = NULL;
73
74 G_DEFINE_TYPE (EmpathyAccountManager, empathy_account_manager, G_TYPE_OBJECT);
75
76 static void
77 emp_account_connection_cb (EmpathyAccount *account,
78   GParamSpec *spec,
79   gpointer manager)
80 {
81   TpConnection *connection = empathy_account_get_connection (account);
82
83   DEBUG ("Signalling connection %p of account %s",
84       connection, empathy_account_get_unique_name (account));
85
86   if (connection != NULL)
87     g_signal_emit (manager, signals[NEW_CONNECTION], 0, connection);
88 }
89
90 static void
91 emp_account_enabled_cb (EmpathyAccount *account,
92   GParamSpec *spec,
93   gpointer manager)
94 {
95   if (empathy_account_is_enabled (account))
96     g_signal_emit (manager, signals[ACCOUNT_ENABLED], 0, account);
97   else
98     g_signal_emit (manager, signals[ACCOUNT_DISABLED], 0, account);
99 }
100
101 static void
102 emp_account_status_changed_cb (EmpathyAccount *account,
103   TpConnectionStatus old,
104   TpConnectionStatus new,
105   TpConnectionStatusReason reason,
106   gpointer user_data)
107 {
108   EmpathyAccountManager *manager = EMPATHY_ACCOUNT_MANAGER (user_data);
109   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
110
111   switch (old)
112     {
113       case TP_CONNECTION_STATUS_CONNECTING:
114         priv->connecting--;
115         break;
116       case TP_CONNECTION_STATUS_CONNECTED:
117         priv->connected--;
118         break;
119       default:
120         break;
121     }
122
123   switch (new)
124     {
125       case TP_CONNECTION_STATUS_CONNECTING:
126         priv->connecting++;
127         break;
128       case TP_CONNECTION_STATUS_CONNECTED:
129         priv->connected++;
130         break;
131       default:
132         break;
133     }
134
135   g_signal_emit (manager, signals[ACCOUNT_CONNECTION_CHANGED], 0,
136     account, reason, new, old);
137 }
138
139 static void
140 emp_account_manager_update_global_presence (EmpathyAccountManager *manager)
141 {
142   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
143   TpConnectionPresenceType presence = TP_CONNECTION_PRESENCE_TYPE_UNSET;
144   EmpathyAccount *account = NULL;
145   GHashTableIter iter;
146   gpointer value;
147
148   g_hash_table_iter_init (&iter, priv->accounts);
149   while (g_hash_table_iter_next (&iter, NULL, &value))
150     {
151       EmpathyAccount *a = EMPATHY_ACCOUNT (value);
152       TpConnectionPresenceType p;
153
154       g_object_get (a, "presence", &p, NULL);
155
156       if (tp_connection_presence_type_cmp_availability (p, presence) > 0)
157         {
158           account = a;
159           presence = p;
160         }
161     }
162
163   priv->global_account = account;
164   g_free (priv->global_status);
165   g_free (priv->global_status_message);
166
167   if (account == NULL)
168     {
169       priv->global_status = NULL;
170       priv->global_status_message = NULL;
171       return;
172     }
173
174   g_object_get (account,
175     "presence", &priv->global_presence,
176     "status", &priv->global_status,
177     "status-message", &priv->global_status_message,
178     NULL);
179 }
180
181 static void
182 emp_account_presence_changed_cb (EmpathyAccount *account,
183   TpConnectionPresenceType presence,
184   const gchar *status,
185   const gchar *status_message,
186   gpointer user_data)
187 {
188   EmpathyAccountManager *manager = EMPATHY_ACCOUNT_MANAGER (user_data);
189   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
190
191   if (tp_connection_presence_type_cmp_availability (presence,
192       priv->global_presence) > 0)
193     {
194       priv->global_account = account;
195
196       priv->global_presence = presence;
197
198       g_free (priv->global_status);
199       priv->global_status = g_strdup (status);
200
201       g_free (priv->global_status_message);
202       priv->global_status_message = g_strdup (status_message);
203
204       goto signal;
205     }
206   else if (priv->global_account == account)
207     {
208       emp_account_manager_update_global_presence (manager);
209       goto signal;
210     }
211
212   return;
213 signal:
214     g_signal_emit (manager, signals[GLOBAL_PRESENCE_CHANGED], 0,
215       priv->global_presence, priv->global_status, priv->global_status_message);
216 }
217
218 static void
219 emp_account_ready_cb (GObject *obj, GParamSpec *spec, gpointer user_data)
220 {
221   EmpathyAccountManager *manager = EMPATHY_ACCOUNT_MANAGER (user_data);
222   EmpathyAccount *account = EMPATHY_ACCOUNT (obj);
223   gboolean ready;
224
225   g_object_get (account, "ready", &ready, NULL);
226
227   if (!ready)
228     return;
229
230   g_signal_emit (manager, signals[ACCOUNT_CREATED], 0, account);
231
232   g_signal_connect (account, "notify::connection",
233     G_CALLBACK (emp_account_connection_cb), manager);
234
235   g_signal_connect (account, "notify::enabled",
236     G_CALLBACK (emp_account_enabled_cb), manager);
237
238   g_signal_connect (account, "status-changed",
239     G_CALLBACK (emp_account_status_changed_cb), manager);
240
241   g_signal_connect (account, "presence-changed",
242     G_CALLBACK (emp_account_presence_changed_cb), manager);
243 }
244
245 static void
246 account_manager_got_all_cb (TpProxy *proxy,
247     GHashTable *properties,
248     const GError *error,
249     gpointer user_data,
250     GObject *weak_object)
251 {
252   EmpathyAccountManager *manager = EMPATHY_ACCOUNT_MANAGER (weak_object);
253   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
254   GPtrArray *accounts;
255   int i;
256
257   if (error != NULL)
258     {
259       DEBUG ("Failed to get account manager properties: %s", error->message);
260       return;
261     }
262
263   accounts = tp_asv_get_boxed (properties, "ValidAccounts",
264     EMPATHY_ARRAY_TYPE_OBJECT);
265
266
267   for (i = 0; i < accounts->len; i++)
268     {
269       EmpathyAccount *account;
270       gchar *name = g_ptr_array_index (accounts, i);
271
272       account = empathy_account_new (priv->dbus, name);
273       g_hash_table_insert (priv->accounts, g_strdup (name), account);
274
275       g_signal_connect (account, "notify::ready",
276         G_CALLBACK (emp_account_ready_cb), manager);
277     }
278 }
279
280 static void
281 account_manager_name_owner_changed_cb (TpDBusDaemon *proxy,
282     const gchar *arg0,
283     const gchar *arg1,
284     const gchar *arg2,
285     gpointer user_data,
286     GObject *weak_object)
287 {
288   EmpathyAccountManager *manager = EMPATHY_ACCOUNT_MANAGER (weak_object);
289   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
290
291   tp_proxy_signal_connection_disconnect (priv->proxy_signal);
292   priv->proxy_signal = NULL;
293
294   priv->tp_manager = tp_account_manager_new (priv->dbus);
295   tp_cli_dbus_properties_call_get_all (priv->tp_manager, -1,
296     TP_IFACE_ACCOUNT_MANAGER,
297     account_manager_got_all_cb,
298     NULL,
299     NULL,
300     G_OBJECT (manager));
301 }
302
303 static void
304 empathy_account_manager_init (EmpathyAccountManager *manager)
305 {
306   EmpathyAccountManagerPriv *priv;
307   TpProxy *mc5_proxy;
308
309   priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
310       EMPATHY_TYPE_ACCOUNT_MANAGER, EmpathyAccountManagerPriv);
311
312   manager->priv = priv;
313   priv->connected = priv->connecting = 0;
314   priv->global_presence = TP_CONNECTION_PRESENCE_TYPE_UNSET;
315
316   priv->accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
317       g_free, (GDestroyNotify) g_object_unref);
318
319   priv->dbus = tp_dbus_daemon_dup (NULL);
320
321   priv->proxy_signal = tp_cli_dbus_daemon_connect_to_name_owner_changed (
322       priv->dbus,
323       account_manager_name_owner_changed_cb,
324       TP_ACCOUNT_MANAGER_BUS_NAME,
325       NULL,
326       G_OBJECT (manager),
327       NULL);
328
329   /* trigger MC5 starting */
330   mc5_proxy = g_object_new (TP_TYPE_PROXY,
331     "dbus-daemon", priv->dbus,
332     "dbus-connection", tp_proxy_get_dbus_connection (TP_PROXY (priv->dbus)),
333     "bus-name", MC5_BUS_NAME,
334     "object-path", "/",
335     NULL);
336
337   tp_cli_dbus_peer_call_ping (mc5_proxy, -1, NULL, NULL, NULL, NULL);
338
339   g_object_unref (mc5_proxy);
340 }
341
342 static void
343 do_finalize (GObject *obj)
344 {
345   EmpathyAccountManager *manager = EMPATHY_ACCOUNT_MANAGER (obj);
346   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
347
348   g_hash_table_destroy (priv->accounts);
349
350   G_OBJECT_CLASS (empathy_account_manager_parent_class)->finalize (obj);
351 }
352
353 static void
354 do_dispose (GObject *obj)
355 {
356   EmpathyAccountManager *manager = EMPATHY_ACCOUNT_MANAGER (obj);
357   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
358
359   if (priv->dispose_run)
360     return;
361
362   priv->dispose_run = TRUE;
363
364   if (priv->dbus == NULL)
365     g_object_unref (priv->dbus);
366   priv->dbus = NULL;
367
368   G_OBJECT_CLASS (empathy_account_manager_parent_class)->dispose (obj);
369 }
370
371 static GObject *
372 do_constructor (GType type,
373                 guint n_construct_params,
374                 GObjectConstructParam *construct_params)
375 {
376   GObject *retval;
377
378   if (!manager_singleton)
379     {
380       retval = G_OBJECT_CLASS (empathy_account_manager_parent_class)->constructor (type,
381                                                                                    n_construct_params,
382                                                                                    construct_params);
383       manager_singleton = EMPATHY_ACCOUNT_MANAGER (retval);
384       g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
385     }
386   else
387     {
388       retval = g_object_ref (manager_singleton);
389     }
390
391   return retval;
392 }
393
394 static void
395 empathy_account_manager_class_init (EmpathyAccountManagerClass *klass)
396 {
397   GObjectClass *oclass = G_OBJECT_CLASS (klass);
398
399   oclass->finalize = do_finalize;
400   oclass->dispose = do_dispose;
401   oclass->constructor = do_constructor;
402
403   signals[ACCOUNT_CREATED] =
404     g_signal_new ("account-created",
405                   G_TYPE_FROM_CLASS (klass),
406                   G_SIGNAL_RUN_LAST,
407                   0,
408                   NULL, NULL,
409                   g_cclosure_marshal_VOID__OBJECT,
410                   G_TYPE_NONE,
411                   1, EMPATHY_TYPE_ACCOUNT);
412
413   signals[ACCOUNT_DELETED] =
414     g_signal_new ("account-deleted",
415                   G_TYPE_FROM_CLASS (klass),
416                   G_SIGNAL_RUN_LAST,
417                   0,
418                   NULL, NULL,
419                   g_cclosure_marshal_VOID__OBJECT,
420                   G_TYPE_NONE,
421                   1, EMPATHY_TYPE_ACCOUNT);
422
423   signals[ACCOUNT_ENABLED] =
424     g_signal_new ("account-enabled",
425                   G_TYPE_FROM_CLASS (klass),
426                   G_SIGNAL_RUN_LAST,
427                   0,
428                   NULL, NULL,
429                   g_cclosure_marshal_VOID__OBJECT,
430                   G_TYPE_NONE,
431                   1, EMPATHY_TYPE_ACCOUNT);
432
433   signals[ACCOUNT_DISABLED] =
434     g_signal_new ("account-disabled",
435                   G_TYPE_FROM_CLASS (klass),
436                   G_SIGNAL_RUN_LAST,
437                   0,
438                   NULL, NULL,
439                   g_cclosure_marshal_VOID__OBJECT,
440                   G_TYPE_NONE,
441                   1, EMPATHY_TYPE_ACCOUNT);
442
443   signals[ACCOUNT_CHANGED] =
444     g_signal_new ("account-changed",
445                   G_TYPE_FROM_CLASS (klass),
446                   G_SIGNAL_RUN_LAST,
447                   0,
448                   NULL, NULL,
449                   g_cclosure_marshal_VOID__OBJECT,
450                   G_TYPE_NONE,
451                   1, EMPATHY_TYPE_ACCOUNT);
452
453   signals[ACCOUNT_CONNECTION_CHANGED] =
454     g_signal_new ("account-connection-changed",
455                   G_TYPE_FROM_CLASS (klass),
456                   G_SIGNAL_RUN_LAST,
457                   0,
458                   NULL, NULL,
459                   _empathy_marshal_VOID__OBJECT_INT_UINT_UINT,
460                   G_TYPE_NONE,
461                   4, EMPATHY_TYPE_ACCOUNT,
462                   G_TYPE_INT,   /* reason */
463                   G_TYPE_UINT,  /* actual connection */
464                   G_TYPE_UINT); /* previous connection */
465
466   signals[GLOBAL_PRESENCE_CHANGED] =
467     g_signal_new ("global-presence-changed",
468                   G_TYPE_FROM_CLASS (klass),
469                   G_SIGNAL_RUN_LAST,
470                   0,
471                   NULL, NULL,
472                   _empathy_marshal_VOID__UINT_STRING_STRING,
473                   G_TYPE_NONE,
474                   3, G_TYPE_UINT, /* Presence type */
475                   G_TYPE_STRING,  /* status */
476                   G_TYPE_STRING); /* stauts message*/
477
478   signals[NEW_CONNECTION] =
479     g_signal_new ("new-connection",
480                   G_TYPE_FROM_CLASS (klass),
481                   G_SIGNAL_RUN_LAST,
482                   0,
483                   NULL, NULL,
484                   g_cclosure_marshal_VOID__OBJECT,
485                   G_TYPE_NONE,
486                   1, TP_TYPE_CONNECTION);
487
488   g_type_class_add_private (oclass, sizeof (EmpathyAccountManagerPriv));
489 }
490
491 /* public methods */
492
493 EmpathyAccountManager *
494 empathy_account_manager_dup_singleton (void)
495 {
496   return g_object_new (EMPATHY_TYPE_ACCOUNT_MANAGER, NULL);
497 }
498
499 EmpathyAccount *
500 empathy_account_manager_create (EmpathyAccountManager *manager,
501         const gchar *connection_manager,
502         const gchar *protocol,
503         const gchar *display_name)
504 {
505   /* FIXME */
506   return NULL;
507 }
508
509 int
510 empathy_account_manager_get_connected_accounts (EmpathyAccountManager *manager)
511 {
512   EmpathyAccountManagerPriv *priv;
513
514   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_MANAGER (manager), 0);
515
516   priv = GET_PRIV (manager);
517
518   return priv->connected;
519 }
520
521 int
522 empathy_account_manager_get_connecting_accounts (EmpathyAccountManager *manager)
523 {
524   EmpathyAccountManagerPriv *priv;
525
526   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_MANAGER (manager), 0);
527
528   priv = GET_PRIV (manager);
529
530   return priv->connecting;
531 }
532
533 /**
534  * empathy_account_manager_get_count:
535  * @manager: a #EmpathyAccountManager
536  *
537  * Get the number of accounts.
538  *
539  * Returns: the number of accounts.
540  **/
541 int
542 empathy_account_manager_get_count (EmpathyAccountManager *manager)
543 {
544   EmpathyAccountManagerPriv *priv;
545
546   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_MANAGER (manager), 0);
547
548   priv = GET_PRIV (manager);
549
550   return g_hash_table_size (priv->accounts);
551 }
552
553 EmpathyAccount *
554 empathy_account_manager_get_account (EmpathyAccountManager *manager,
555                                      TpConnection          *connection)
556 {
557   EmpathyAccountManagerPriv *priv;
558   GHashTableIter iter;
559   gpointer value;
560
561   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_MANAGER (manager), 0);
562
563   priv = GET_PRIV (manager);
564
565   g_hash_table_iter_init (&iter, priv->accounts);
566   while (g_hash_table_iter_next (&iter, NULL, &value))
567     {
568       EmpathyAccount *account = EMPATHY_ACCOUNT (value);
569
570       if (connection == empathy_account_get_connection (account))
571           return account;
572     }
573
574   return NULL;
575 }
576
577 EmpathyAccount *
578 empathy_account_manager_lookup (EmpathyAccountManager *manager,
579     const gchar *unique_name)
580 {
581   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
582   EmpathyAccount *account;
583
584   account = g_hash_table_lookup (priv->accounts, unique_name);
585
586   if (account != NULL)
587     g_object_ref (account);
588
589   return account;
590 }
591
592 GList *
593 empathy_account_manager_dup_accounts (EmpathyAccountManager *manager)
594 {
595   EmpathyAccountManagerPriv *priv;
596   GList *ret;
597
598   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_MANAGER (manager), NULL);
599
600   priv = GET_PRIV (manager);
601
602   ret = g_hash_table_get_values (priv->accounts);
603   g_list_foreach (ret, (GFunc) g_object_ref, NULL);
604
605   return ret;
606 }
607
608 /**
609  * empathy_account_manager_dup_connections:
610  * @manager: a #EmpathyAccountManager
611  *
612  * Get a #GList of all ready #TpConnection. The list must be freed with
613  * g_list_free, and its elements must be unreffed.
614  *
615  * Returns: the list of connections
616  **/
617 GList *
618 empathy_account_manager_dup_connections (EmpathyAccountManager *manager)
619 {
620   EmpathyAccountManagerPriv *priv;
621   GHashTableIter iter;
622   gpointer value;
623   GList *ret = NULL;
624
625   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_MANAGER (manager), NULL);
626
627   priv = GET_PRIV (manager);
628
629   g_hash_table_iter_init (&iter, priv->accounts);
630   while (g_hash_table_iter_next (&iter, NULL, &value))
631     {
632       EmpathyAccount *account = EMPATHY_ACCOUNT (value);
633       TpConnection *connection;
634
635       connection = empathy_account_get_connection (account);
636       if (connection != NULL)
637         ret = g_list_prepend (ret, g_object_ref (connection));
638     }
639
640   return ret;
641 }
642
643 void
644 empathy_account_manager_remove (EmpathyAccountManager *manager,
645     EmpathyAccount *account)
646 {
647   /* FIXME */
648 }
649
650
651 void
652 empathy_account_manager_request_global_presence (
653   EmpathyAccountManager *manager,
654   TpConnectionPresenceType type,
655   const gchar *status,
656   const gchar *message)
657 {
658   /* FIXME should remember requested presence and set it on new accounts
659      as well */
660   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
661   GHashTableIter iter;
662   gpointer value;
663
664   g_hash_table_iter_init (&iter, priv->accounts);
665   while (g_hash_table_iter_next (&iter, NULL, &value))
666     {
667       EmpathyAccount *account = EMPATHY_ACCOUNT (value);
668       gboolean ready;
669
670       g_object_get (account, "ready", &ready, NULL);
671
672       if (ready)
673         empathy_account_request_presence (account, type, status, message);
674     }
675 }
676
677 TpConnectionPresenceType
678 empathy_account_manager_get_global_presence (
679   EmpathyAccountManager *manager,
680   gchar **status,
681   gchar **message)
682 {
683   EmpathyAccountManagerPriv *priv = GET_PRIV (manager);
684
685   if (status != NULL)
686     *status = g_strdup (priv->global_status);
687   if (message != NULL)
688     *message = g_strdup (priv->global_status_message);
689
690   return priv->global_presence;
691 }
692