]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact-manager.c
Fix ref count error for account objects.
[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 Collabora Ltd.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program 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  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  * 
20  * Authors: Xavier Claessens <xclaesse@gmail.com>
21  */
22
23 #include <config.h>
24
25 #include <string.h>
26
27 #include <libtelepathy/tp-constants.h>
28
29 #include "empathy-contact-manager.h"
30 #include "empathy-contact-list.h"
31 #include "empathy-utils.h"
32 #include "empathy-debug.h"
33
34 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
35                        EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManagerPriv))
36
37 #define DEBUG_DOMAIN "ContactManager"
38
39 struct _EmpathyContactManagerPriv {
40         GHashTable     *lists;
41         MissionControl *mc;
42 };
43
44 static void empathy_contact_manager_class_init (EmpathyContactManagerClass *klass);
45 static void empathy_contact_manager_init       (EmpathyContactManager      *manager);
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 void
53 contact_manager_members_changed_cb (EmpathyTpContactList  *list,
54                                     EmpathyContact        *contact,
55                                     EmpathyContact        *actor,
56                                     guint                  reason,
57                                     gchar                 *message,
58                                     gboolean               is_member,
59                                     EmpathyContactManager *manager)
60 {
61         g_signal_emit_by_name (manager, "members-changed",
62                                contact, actor, reason, message, is_member);
63 }
64
65 static void
66 contact_manager_pendings_changed_cb (EmpathyTpContactList  *list,
67                                      EmpathyContact        *contact,
68                                      EmpathyContact        *actor,
69                                      guint                  reason,
70                                      gchar                 *message,
71                                      gboolean               is_pending,
72                                      EmpathyContactManager *manager)
73 {
74         g_signal_emit_by_name (manager, "pendings-changed",
75                                contact, actor, reason, message, is_pending);
76 }
77
78 static void
79 contact_manager_groups_changed_cb (EmpathyTpContactList  *list,
80                                    EmpathyContact        *contact,
81                                    gchar                 *group,
82                                    gboolean               is_member,
83                                    EmpathyContactManager *manager)
84 {
85         g_signal_emit_by_name (manager, "groups-changed",
86                                contact, group, is_member);
87 }
88
89 static void contact_manager_destroy_cb (EmpathyTpContactList  *list,
90                                         EmpathyContactManager *manager);
91
92 static void
93 contact_manager_disconnect_foreach (gpointer key,
94                                     gpointer value,
95                                     gpointer user_data)
96 {
97         EmpathyTpContactList  *list = value;
98         EmpathyContactManager *manager = user_data;
99
100         /* Disconnect signals from the list */
101         g_signal_handlers_disconnect_by_func (list,
102                                               contact_manager_members_changed_cb,
103                                               manager);
104         g_signal_handlers_disconnect_by_func (list,
105                                               contact_manager_pendings_changed_cb,
106                                               manager);
107         g_signal_handlers_disconnect_by_func (list,
108                                               contact_manager_groups_changed_cb,
109                                               manager);
110         g_signal_handlers_disconnect_by_func (list,
111                                               contact_manager_destroy_cb,
112                                               manager);
113 }
114
115 static void
116 contact_manager_destroy_cb (EmpathyTpContactList  *list,
117                             EmpathyContactManager *manager)
118 {
119         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
120         McAccount                 *account;
121
122         account = empathy_tp_contact_list_get_account (list);
123
124         empathy_debug (DEBUG_DOMAIN, "Removing account: %s",
125                        mc_account_get_display_name (account));
126
127         contact_manager_disconnect_foreach (account, list, manager);
128         g_hash_table_remove (priv->lists, account);
129 }
130
131 static void
132 contact_manager_add_account (EmpathyContactManager *manager,
133                              McAccount             *account)
134 {
135         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
136         EmpathyTpContactList      *list;
137
138         if (g_hash_table_lookup (priv->lists, account)) {
139                 return;
140         }
141
142         empathy_debug (DEBUG_DOMAIN, "Adding new account: %s",
143                        mc_account_get_display_name (account));
144
145         list = empathy_tp_contact_list_new (account);
146         if (!list) {
147                 return;
148         }
149
150         g_hash_table_insert (priv->lists, g_object_ref (account), list);
151
152         /* Connect signals */
153         g_signal_connect (list, "members-changed",
154                           G_CALLBACK (contact_manager_members_changed_cb),
155                           manager);
156         g_signal_connect (list, "pendings-changed",
157                           G_CALLBACK (contact_manager_pendings_changed_cb),
158                           manager);
159         g_signal_connect (list, "groups-changed",
160                           G_CALLBACK (contact_manager_groups_changed_cb),
161                           manager);
162         g_signal_connect (list, "destroy",
163                           G_CALLBACK (contact_manager_destroy_cb),
164                           manager);
165 }
166
167 static void
168 contact_manager_status_changed_cb (MissionControl                  *mc,
169                                    TelepathyConnectionStatus        status,
170                                    McPresence                       presence,
171                                    TelepathyConnectionStatusReason  reason,
172                                    const gchar                     *unique_name,
173                                    EmpathyContactManager           *manager)
174 {
175         McAccount *account;
176
177         if (status != TP_CONN_STATUS_CONNECTED) {
178                 /* We only care about newly connected accounts */
179                 return;
180         }
181
182         account = mc_account_lookup (unique_name);
183         contact_manager_add_account (manager, account);
184         g_object_unref (account);
185 }
186
187 static void
188 contact_manager_finalize (GObject *object)
189 {
190         EmpathyContactManagerPriv *priv = GET_PRIV (object);
191
192         dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc),
193                                         "AccountStatusChanged",
194                                         G_CALLBACK (contact_manager_status_changed_cb),
195                                         object);
196
197         g_hash_table_foreach (priv->lists,
198                               contact_manager_disconnect_foreach,
199                               object);
200         g_hash_table_destroy (priv->lists);
201         g_object_unref (priv->mc);
202 }
203
204 static void
205 empathy_contact_manager_class_init (EmpathyContactManagerClass *klass)
206 {
207         GObjectClass *object_class = G_OBJECT_CLASS (klass);
208
209         object_class->finalize = contact_manager_finalize;
210
211         g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv));
212 }
213
214 static void
215 empathy_contact_manager_init (EmpathyContactManager *manager)
216 {
217         EmpathyContactManagerPriv *priv;
218         GSList                    *accounts, *l;
219
220         priv = GET_PRIV (manager);
221
222         priv->lists = g_hash_table_new_full (empathy_account_hash,
223                                              empathy_account_equal,
224                                              (GDestroyNotify) g_object_unref,
225                                              (GDestroyNotify) g_object_unref);
226
227         priv->mc = empathy_mission_control_new ();
228
229         dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
230                                      "AccountStatusChanged",
231                                      G_CALLBACK (contact_manager_status_changed_cb),
232                                      manager, NULL);
233
234         /* Get ContactList for existing connections */
235         accounts = mission_control_get_online_connections (priv->mc, NULL);
236         for (l = accounts; l; l = l->next) {
237                 contact_manager_add_account (manager, l->data);
238                 g_object_unref (l->data);
239         }
240         g_slist_free (accounts);
241 }
242
243 EmpathyContactManager *
244 empathy_contact_manager_new (void)
245 {
246         static EmpathyContactManager *manager = NULL;
247
248         if (!manager) {
249                 manager = g_object_new (EMPATHY_TYPE_CONTACT_MANAGER, NULL);
250                 g_object_add_weak_pointer (G_OBJECT (manager), (gpointer) &manager);
251         } else {
252                 g_object_ref (manager);
253         }
254
255         return manager;
256 }
257
258 EmpathyTpContactList *
259 empathy_contact_manager_get_list (EmpathyContactManager *manager,
260                                   McAccount             *account)
261 {
262         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
263
264         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
265         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
266
267         return g_hash_table_lookup (priv->lists, account);
268 }
269
270 static void
271 contact_manager_add (EmpathyContactList *manager,
272                      EmpathyContact     *contact,
273                      const gchar        *message)
274 {
275         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
276         EmpathyContactList        *list;
277         McAccount                 *account;
278
279         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
280
281         account = empathy_contact_get_account (contact);
282         list = g_hash_table_lookup (priv->lists, account);
283
284         if (list) {
285                 empathy_contact_list_add (list, contact, message);
286         }
287 }
288
289 static void
290 contact_manager_remove (EmpathyContactList *manager,
291                         EmpathyContact      *contact,
292                         const gchar        *message)
293 {
294         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
295         EmpathyContactList        *list;
296         McAccount                 *account;
297
298         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
299
300         account = empathy_contact_get_account (contact);
301         list = g_hash_table_lookup (priv->lists, account);
302
303         if (list) {
304                 empathy_contact_list_remove (list, contact, message);
305         }
306 }
307
308 static void
309 contact_manager_get_members_foreach (McAccount             *account,
310                                      EmpathyTpContactList  *list,
311                                      GList                **contacts)
312 {
313         GList *l;
314
315         l = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (list));
316         *contacts = g_list_concat (*contacts, l);
317 }
318
319 static GList *
320 contact_manager_get_members (EmpathyContactList *manager)
321 {
322         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
323         GList                     *contacts = NULL;
324
325         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
326
327         g_hash_table_foreach (priv->lists,
328                               (GHFunc) contact_manager_get_members_foreach,
329                               &contacts);
330
331         return contacts;
332 }
333
334 static void
335 contact_manager_get_pendings_foreach (McAccount             *account,
336                                       EmpathyTpContactList  *list,
337                                       GList                **contacts)
338 {
339         GList *l;
340
341         l = empathy_contact_list_get_pendings (EMPATHY_CONTACT_LIST (list));
342         *contacts = g_list_concat (*contacts, l);
343 }
344
345 static GList *
346 contact_manager_get_pendings (EmpathyContactList *manager)
347 {
348         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
349         GList                     *contacts = NULL;
350
351         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
352
353         g_hash_table_foreach (priv->lists,
354                               (GHFunc) contact_manager_get_pendings_foreach,
355                               &contacts);
356
357         return contacts;
358 }
359
360 static void
361 contact_manager_get_all_groups_foreach (McAccount             *account,
362                                         EmpathyTpContactList  *list,
363                                         GList                **all_groups)
364 {
365         GList *groups, *l;
366
367         groups = empathy_contact_list_get_all_groups (EMPATHY_CONTACT_LIST (list));
368         for (l = groups; l; l = l->next) {
369                 if (!g_list_find_custom (*all_groups,
370                                          l->data,
371                                          (GCompareFunc) strcmp)) {
372                         *all_groups = g_list_prepend (*all_groups, l->data);
373                 } else {
374                         g_free (l->data);
375                 }
376         }
377
378         g_list_free (groups);
379 }
380
381 static GList *
382 contact_manager_get_all_groups (EmpathyContactList *manager)
383 {
384         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
385         GList                     *groups = NULL;
386
387         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
388
389         g_hash_table_foreach (priv->lists,
390                               (GHFunc) contact_manager_get_all_groups_foreach,
391                               &groups);
392
393         return groups;
394 }
395
396 static GList *
397 contact_manager_get_groups (EmpathyContactList *manager,
398                             EmpathyContact     *contact)
399 {
400         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
401         EmpathyContactList        *list;
402         McAccount                 *account;
403
404         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
405
406         account = empathy_contact_get_account (contact);
407         list = g_hash_table_lookup (priv->lists, account);
408
409         if (list) {
410                 return empathy_contact_list_get_groups (list, contact);
411         }
412
413         return NULL;
414 }
415
416 static void
417 contact_manager_add_to_group (EmpathyContactList *manager,
418                               EmpathyContact     *contact,
419                               const gchar        *group)
420 {
421         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
422         EmpathyContactList        *list;
423         McAccount                 *account;
424
425         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
426
427         account = empathy_contact_get_account (contact);
428         list = g_hash_table_lookup (priv->lists, account);
429
430         if (list) {
431                 empathy_contact_list_add_to_group (list, contact, group);
432         }
433 }
434
435 static void
436 contact_manager_remove_from_group (EmpathyContactList *manager,
437                                    EmpathyContact     *contact,
438                                    const gchar        *group)
439 {
440         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
441         EmpathyContactList        *list;
442         McAccount                 *account;
443
444         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
445
446         account = empathy_contact_get_account (contact);
447         list = g_hash_table_lookup (priv->lists, account);
448
449         if (list) {
450                 empathy_contact_list_remove_from_group (list, contact, group);
451         }
452 }
453
454 typedef struct {
455         const gchar *old_group;
456         const gchar *new_group;
457 } RenameGroupData;
458
459 static void
460 contact_manager_rename_group_foreach (McAccount            *account,
461                                       EmpathyTpContactList *list,
462                                       RenameGroupData      *data)
463 {
464         empathy_contact_list_rename_group (EMPATHY_CONTACT_LIST (list),
465                                            data->old_group,
466                                            data->new_group);
467 }
468
469 static void
470 contact_manager_rename_group (EmpathyContactList *manager,
471                               const gchar        *old_group,
472                               const gchar        *new_group)
473 {
474         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
475         RenameGroupData            data;
476
477         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
478
479         data.old_group = old_group;
480         data.new_group = new_group;
481         g_hash_table_foreach (priv->lists,
482                               (GHFunc) contact_manager_rename_group_foreach,
483                               &data);
484 }
485
486 static void
487 contact_manager_iface_init (EmpathyContactListIface *iface)
488 {
489         iface->add               = contact_manager_add;
490         iface->remove            = contact_manager_remove;
491         iface->get_members       = contact_manager_get_members;
492         iface->get_pendings      = contact_manager_get_pendings;
493         iface->get_all_groups    = contact_manager_get_all_groups;
494         iface->get_groups        = contact_manager_get_groups;
495         iface->add_to_group      = contact_manager_add_to_group;
496         iface->remove_from_group = contact_manager_remove_from_group;
497         iface->rename_group      = contact_manager_rename_group;
498 }
499