]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact-manager.c
Merge remote-tracking branch 'jonny/ft'
[empathy.git] / libempathy / empathy-contact-manager.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007-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: Xavier Claessens <xclaesse@gmail.com>
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25
26 #include <telepathy-glib/account-manager.h>
27 #include <telepathy-glib/enums.h>
28 #include <telepathy-glib/proxy-subclass.h>
29 #include <telepathy-glib/util.h>
30
31 #include "empathy-contact-manager.h"
32 #include "empathy-contact-list.h"
33 #include "empathy-utils.h"
34
35 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
36 #include "empathy-debug.h"
37
38 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContactManager)
39 typedef struct {
40         /* Owned (TpConnection *) => Owned (TpContactList *)
41            The contact list associated with each connected connection */
42         GHashTable     *lists;
43         TpAccountManager *account_manager;
44 } EmpathyContactManagerPriv;
45
46 static void contact_manager_iface_init         (EmpathyContactListIface    *iface);
47
48 G_DEFINE_TYPE_WITH_CODE (EmpathyContactManager, empathy_contact_manager, G_TYPE_OBJECT,
49                          G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST,
50                                                 contact_manager_iface_init));
51
52 static EmpathyContactManager *manager_singleton = NULL;
53
54 static void
55 contact_manager_members_changed_cb (EmpathyTpContactList  *list,
56                                     EmpathyContact        *contact,
57                                     EmpathyContact        *actor,
58                                     guint                  reason,
59                                     gchar                 *message,
60                                     gboolean               is_member,
61                                     EmpathyContactManager *manager)
62 {
63         g_signal_emit_by_name (manager, "members-changed",
64                                contact, actor, reason, message, is_member);
65 }
66
67 static void
68 contact_manager_pendings_changed_cb (EmpathyTpContactList  *list,
69                                      EmpathyContact        *contact,
70                                      EmpathyContact        *actor,
71                                      guint                  reason,
72                                      gchar                 *message,
73                                      gboolean               is_pending,
74                                      EmpathyContactManager *manager)
75 {
76         g_signal_emit_by_name (manager, "pendings-changed",
77                                contact, actor, reason, message, is_pending);
78 }
79
80 static void
81 contact_manager_groups_changed_cb (EmpathyTpContactList  *list,
82                                    EmpathyContact        *contact,
83                                    gchar                 *group,
84                                    gboolean               is_member,
85                                    EmpathyContactManager *manager)
86 {
87         g_signal_emit_by_name (manager, "groups-changed",
88                                contact, group, is_member);
89 }
90
91 static void
92 contact_manager_invalidated_cb (TpProxy *connection,
93                                 guint    domain,
94                                 gint     code,
95                                 gchar   *message,
96                                 EmpathyContactManager *manager)
97 {
98         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
99         EmpathyTpContactList *list;
100
101         DEBUG ("Removing connection: %s (%s)",
102                 tp_proxy_get_object_path (TP_PROXY (connection)),
103                 message);
104
105         list = g_hash_table_lookup (priv->lists, connection);
106         if (list) {
107                 empathy_tp_contact_list_remove_all (list);
108                 g_hash_table_remove (priv->lists, connection);
109         }
110 }
111
112 static void
113 contact_manager_disconnect_foreach (gpointer key,
114                                     gpointer value,
115                                     gpointer user_data)
116 {
117         TpConnection *connection = key;
118         EmpathyTpContactList  *list = value;
119         EmpathyContactManager *manager = user_data;
120
121         /* Disconnect signals from the list */
122         g_signal_handlers_disconnect_by_func (list,
123                                               contact_manager_members_changed_cb,
124                                               manager);
125         g_signal_handlers_disconnect_by_func (list,
126                                               contact_manager_pendings_changed_cb,
127                                               manager);
128         g_signal_handlers_disconnect_by_func (list,
129                                               contact_manager_groups_changed_cb,
130                                               manager);
131         g_signal_handlers_disconnect_by_func (connection,
132                                               contact_manager_invalidated_cb,
133                                               manager);
134 }
135
136 static void
137 contact_manager_status_changed_cb (TpAccount *account,
138                                    guint old_status,
139                                    guint new_status,
140                                    guint reason,
141                                    gchar *dbus_error_name,
142                                    GHashTable *details,
143                                    EmpathyContactManager *self)
144 {
145         EmpathyContactManagerPriv *priv = GET_PRIV (self);
146         EmpathyTpContactList      *list;
147         TpConnection              *connection;
148
149         if (new_status == TP_CONNECTION_STATUS_DISCONNECTED)
150                 /* No point to start tracking a connection which is about to die */
151                 return;
152
153         connection = tp_account_get_connection (account);
154
155         if (connection == NULL || g_hash_table_lookup (priv->lists, connection)) {
156                 return;
157         }
158
159         DEBUG ("Adding new connection: %s",
160                 tp_proxy_get_object_path (TP_PROXY (connection)));
161
162         list = empathy_tp_contact_list_new (connection);
163         g_hash_table_insert (priv->lists, g_object_ref (connection), list);
164         g_signal_connect (connection, "invalidated",
165                           G_CALLBACK (contact_manager_invalidated_cb),
166                           self);
167
168         /* Connect signals */
169         g_signal_connect (list, "members-changed",
170                           G_CALLBACK (contact_manager_members_changed_cb),
171                           self);
172         g_signal_connect (list, "pendings-changed",
173                           G_CALLBACK (contact_manager_pendings_changed_cb),
174                           self);
175         g_signal_connect (list, "groups-changed",
176                           G_CALLBACK (contact_manager_groups_changed_cb),
177                           self);
178 }
179
180 static void
181 contact_manager_validity_changed_cb (TpAccountManager *account_manager,
182                                      TpAccount *account,
183                                      gboolean valid,
184                                      EmpathyContactManager *manager)
185 {
186         if (valid) {
187                 tp_g_signal_connect_object (account, "status-changed",
188                             G_CALLBACK (contact_manager_status_changed_cb),
189                             manager, 0);
190         }
191 }
192
193 static void
194 contact_manager_finalize (GObject *object)
195 {
196         EmpathyContactManagerPriv *priv = GET_PRIV (object);
197
198         g_hash_table_foreach (priv->lists,
199                               contact_manager_disconnect_foreach,
200                               object);
201         g_hash_table_unref (priv->lists);
202
203         g_object_unref (priv->account_manager);
204 }
205
206 static GObject *
207 contact_manager_constructor (GType type,
208                              guint n_props,
209                              GObjectConstructParam *props)
210 {
211         GObject *retval;
212
213         if (manager_singleton) {
214                 retval = g_object_ref (manager_singleton);
215         } else {
216                 retval = G_OBJECT_CLASS (empathy_contact_manager_parent_class)->constructor
217                         (type, n_props, props);
218
219                 manager_singleton = EMPATHY_CONTACT_MANAGER (retval);
220                 g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
221         }
222
223         return retval;
224 }
225
226 /**
227  * empathy_contact_manager_initialized:
228  *
229  * Reports whether or not the singleton has already been created.
230  *
231  * There can be instances where you want to access the #EmpathyContactManager
232  * only if it has been set up for this process.
233  *
234  * Returns: %TRUE if the #EmpathyContactManager singleton has previously
235  * been initialized.
236  */
237 gboolean
238 empathy_contact_manager_initialized (void)
239 {
240         return (manager_singleton != NULL);
241 }
242
243 static void
244 empathy_contact_manager_class_init (EmpathyContactManagerClass *klass)
245 {
246         GObjectClass *object_class = G_OBJECT_CLASS (klass);
247
248         object_class->finalize = contact_manager_finalize;
249         object_class->constructor = contact_manager_constructor;
250
251         g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv));
252 }
253
254 static void
255 account_manager_prepared_cb (GObject *source_object,
256                              GAsyncResult *result,
257                              gpointer user_data)
258 {
259         GList *accounts, *l;
260         EmpathyContactManager *manager = user_data;
261         TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
262         GError *error = NULL;
263
264         if (!tp_proxy_prepare_finish (account_manager, result, &error)) {
265                 DEBUG ("Failed to prepare account manager: %s", error->message);
266                 g_error_free (error);
267                 return;
268         }
269
270         accounts = tp_account_manager_get_valid_accounts (account_manager);
271
272         for (l = accounts; l != NULL; l = l->next) {
273                 TpAccount *account = l->data;
274                 TpConnection *conn = tp_account_get_connection (account);
275
276                 if (conn != NULL) {
277                         contact_manager_status_changed_cb (account, 0, 0, 0,
278                                                            NULL, NULL, manager);
279                 }
280
281                 tp_g_signal_connect_object (account, "status-changed",
282                     G_CALLBACK (contact_manager_status_changed_cb),
283                     manager, 0);
284         }
285         g_list_free (accounts);
286
287         tp_g_signal_connect_object (account_manager, "account-validity-changed",
288                              G_CALLBACK (contact_manager_validity_changed_cb),
289                              manager, 0);
290 }
291
292 static void
293 empathy_contact_manager_init (EmpathyContactManager *manager)
294 {
295         EmpathyContactManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
296                 EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManagerPriv);
297
298         manager->priv = priv;
299         priv->lists = g_hash_table_new_full (empathy_proxy_hash,
300                                              empathy_proxy_equal,
301                                              (GDestroyNotify) g_object_unref,
302                                              (GDestroyNotify) g_object_unref);
303
304         priv->account_manager = tp_account_manager_dup ();
305
306         tp_proxy_prepare_async (priv->account_manager, NULL,
307             account_manager_prepared_cb, manager);
308 }
309
310 EmpathyContactManager *
311 empathy_contact_manager_dup_singleton (void)
312 {
313         return g_object_new (EMPATHY_TYPE_CONTACT_MANAGER, NULL);
314 }
315
316 EmpathyTpContactList *
317 empathy_contact_manager_get_list (EmpathyContactManager *manager,
318                                   TpConnection          *connection)
319 {
320         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
321
322         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
323         g_return_val_if_fail (TP_IS_CONNECTION (connection), NULL);
324
325         return g_hash_table_lookup (priv->lists, connection);
326 }
327
328 static void
329 contact_manager_add (EmpathyContactList *manager,
330                      EmpathyContact     *contact,
331                      const gchar        *message)
332 {
333         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
334         EmpathyContactList        *list;
335         TpConnection              *connection;
336
337         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
338
339         connection = empathy_contact_get_connection (contact);
340         list = g_hash_table_lookup (priv->lists, connection);
341
342         if (list) {
343                 empathy_contact_list_add (list, contact, message);
344         }
345 }
346
347 static void
348 contact_manager_remove (EmpathyContactList *manager,
349                         EmpathyContact     *contact,
350                         const gchar        *message)
351 {
352         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
353         EmpathyContactList        *list;
354         TpConnection              *connection;
355
356         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
357
358         connection = empathy_contact_get_connection (contact);
359         list = g_hash_table_lookup (priv->lists, connection);
360
361         if (list) {
362                 empathy_contact_list_remove (list, contact, message);
363         }
364 }
365
366 static void
367 contact_manager_get_members_foreach (TpConnection          *connection,
368                                      EmpathyTpContactList  *list,
369                                      GList                **contacts)
370 {
371         GList *l;
372
373         l = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (list));
374         *contacts = g_list_concat (*contacts, l);
375 }
376
377 static GList *
378 contact_manager_get_members (EmpathyContactList *manager)
379 {
380         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
381         GList                     *contacts = NULL;
382
383         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
384
385         g_hash_table_foreach (priv->lists,
386                               (GHFunc) contact_manager_get_members_foreach,
387                               &contacts);
388
389         return contacts;
390 }
391
392 static void
393 contact_manager_get_pendings_foreach (TpConnection          *connection,
394                                       EmpathyTpContactList  *list,
395                                       GList                **contacts)
396 {
397         GList *l;
398
399         l = empathy_contact_list_get_pendings (EMPATHY_CONTACT_LIST (list));
400         *contacts = g_list_concat (*contacts, l);
401 }
402
403 static GList *
404 contact_manager_get_pendings (EmpathyContactList *manager)
405 {
406         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
407         GList                     *contacts = NULL;
408
409         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
410
411         g_hash_table_foreach (priv->lists,
412                               (GHFunc) contact_manager_get_pendings_foreach,
413                               &contacts);
414
415         return contacts;
416 }
417
418 static GList *
419 contact_manager_get_groups (EmpathyContactList *manager,
420                             EmpathyContact     *contact)
421 {
422         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
423         EmpathyContactList        *list;
424         TpConnection              *connection;
425
426         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
427
428         connection = empathy_contact_get_connection (contact);
429         list = g_hash_table_lookup (priv->lists, connection);
430
431         if (list) {
432                 return empathy_contact_list_get_groups (list, contact);
433         }
434
435         return NULL;
436 }
437
438 static void
439 contact_manager_add_to_group (EmpathyContactList *manager,
440                               EmpathyContact     *contact,
441                               const gchar        *group)
442 {
443         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
444         EmpathyContactList        *list;
445         TpConnection              *connection;
446
447         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
448
449         connection = empathy_contact_get_connection (contact);
450         list = g_hash_table_lookup (priv->lists, connection);
451
452         if (list) {
453                 empathy_contact_list_add_to_group (list, contact, group);
454         }
455 }
456
457 static void
458 contact_manager_remove_from_group (EmpathyContactList *manager,
459                                    EmpathyContact     *contact,
460                                    const gchar        *group)
461 {
462         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
463         EmpathyContactList        *list;
464         TpConnection              *connection;
465
466         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
467
468         connection = empathy_contact_get_connection (contact);
469         list = g_hash_table_lookup (priv->lists, connection);
470
471         if (list) {
472                 empathy_contact_list_remove_from_group (list, contact, group);
473         }
474 }
475
476 typedef struct {
477         const gchar *old_group;
478         const gchar *new_group;
479 } RenameGroupData;
480
481 static void
482 contact_manager_rename_group_foreach (TpConnection         *connection,
483                                       EmpathyTpContactList *list,
484                                       RenameGroupData      *data)
485 {
486         empathy_contact_list_rename_group (EMPATHY_CONTACT_LIST (list),
487                                            data->old_group,
488                                            data->new_group);
489 }
490
491 static void
492 contact_manager_rename_group (EmpathyContactList *manager,
493                               const gchar        *old_group,
494                               const gchar        *new_group)
495 {
496         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
497         RenameGroupData            data;
498
499         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
500
501         data.old_group = old_group;
502         data.new_group = new_group;
503         g_hash_table_foreach (priv->lists,
504                               (GHFunc) contact_manager_rename_group_foreach,
505                               &data);
506 }
507
508 static void contact_manager_remove_group_foreach (TpConnection         *connection,
509                                                   EmpathyTpContactList *list,
510                                                   const gchar *group)
511 {
512         empathy_contact_list_remove_group (EMPATHY_CONTACT_LIST (list),
513                                            group);
514 }
515
516 static void
517 contact_manager_remove_group (EmpathyContactList *manager,
518                               const gchar *group)
519 {
520         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
521
522         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
523
524         g_hash_table_foreach (priv->lists,
525                               (GHFunc) contact_manager_remove_group_foreach,
526                               (gpointer) group);
527 }
528
529 static void
530 contact_manager_iface_init (EmpathyContactListIface *iface)
531 {
532         iface->add               = contact_manager_add;
533         iface->remove            = contact_manager_remove;
534         iface->get_members       = contact_manager_get_members;
535         iface->get_pendings      = contact_manager_get_pendings;
536         iface->get_groups        = contact_manager_get_groups;
537         iface->add_to_group      = contact_manager_add_to_group;
538         iface->remove_from_group = contact_manager_remove_from_group;
539         iface->rename_group      = contact_manager_rename_group;
540         iface->remove_group      = contact_manager_remove_group;
541 }