Fix indentation Fix not returning the contact in tp_contact_list_find()
[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 "gossip-utils.h"
32 #include "gossip-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         gboolean        setup;
43 };
44
45 typedef struct {
46         const gchar *old_group;
47         const gchar *new_group;
48 } ContactManagerRenameGroupData;
49
50 typedef struct {
51         GossipContact *contact;
52         const gchar   *id;
53 } ContactManagerFindData;
54
55 static void           empathy_contact_manager_class_init   (EmpathyContactManagerClass      *klass);
56 static void           contact_manager_iface_init           (EmpathyContactListIface         *iface);
57 static void           empathy_contact_manager_init         (EmpathyContactManager           *manager);
58 static void           contact_manager_finalize             (GObject                         *object);
59 static void           contact_manager_setup                (EmpathyContactList              *manager);
60 static GossipContact *contact_manager_find                 (EmpathyContactList              *manager,
61                                                             const gchar                     *id);
62 static void           contact_manager_add                  (EmpathyContactList              *manager,
63                                                             GossipContact                   *contact,
64                                                             const gchar                     *message);
65 static void           contact_manager_remove               (EmpathyContactList              *manager,
66                                                             GossipContact                   *contact,
67                                                             const gchar                     *message);
68 static GList *        contact_manager_get_contacts         (EmpathyContactList              *manager);
69 static void           contact_manager_setup_foreach        (McAccount                       *account,
70                                                             EmpathyTpContactList            *list,
71                                                             EmpathyContactManager           *manager);
72 static gboolean       contact_manager_find_foreach         (McAccount                       *account,
73                                                             EmpathyTpContactList            *list,
74                                                             ContactManagerFindData          *data);
75 static void           contact_manager_add_account          (EmpathyContactManager           *manager,
76                                                             McAccount                       *account);
77 static void           contact_manager_added_cb             (EmpathyTpContactList            *list,
78                                                             GossipContact                   *contact,
79                                                             EmpathyContactManager           *manager);
80 static void           contact_manager_removed_cb           (EmpathyTpContactList            *list,
81                                                             GossipContact                   *contact,
82                                                             EmpathyContactManager           *manager);
83 static void           contact_manager_destroy_cb           (EmpathyTpContactList            *list,
84                                                             EmpathyContactManager           *manager);
85 static void           contact_manager_rename_group_foreach (McAccount                       *account,
86                                                             EmpathyTpContactList            *list,
87                                                             ContactManagerRenameGroupData   *data);
88 static void           contact_manager_get_groups_foreach   (McAccount                       *account,
89                                                             EmpathyTpContactList            *list,
90                                                             GList                          **all_groups);
91 static void           contact_manager_get_contacts_foreach (McAccount                       *account,
92                                                             EmpathyTpContactList            *list,
93                                                             GList                          **contacts);
94 static void           contact_manager_status_changed_cb    (MissionControl                  *mc,
95                                                             TelepathyConnectionStatus        status,
96                                                             McPresence                       presence,
97                                                             TelepathyConnectionStatusReason  reason,
98                                                             const gchar                     *unique_name,
99                                                             EmpathyContactManager           *manager);
100
101 G_DEFINE_TYPE_WITH_CODE (EmpathyContactManager, empathy_contact_manager, G_TYPE_OBJECT,
102                          G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST,
103                                                 contact_manager_iface_init));
104
105 static void
106 empathy_contact_manager_class_init (EmpathyContactManagerClass *klass)
107 {
108         GObjectClass *object_class = G_OBJECT_CLASS (klass);
109
110         object_class->finalize = contact_manager_finalize;
111
112         g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv));
113 }
114
115 static void
116 contact_manager_iface_init (EmpathyContactListIface *iface)
117 {
118         iface->setup = contact_manager_setup;
119         iface->find = contact_manager_find;
120         iface->add = contact_manager_add;
121         iface->remove = contact_manager_remove;
122         iface->get_contacts = contact_manager_get_contacts;
123 }
124
125 static void
126 empathy_contact_manager_init (EmpathyContactManager *manager)
127 {
128         EmpathyContactManagerPriv *priv;
129         GSList                    *accounts, *l;
130
131         priv = GET_PRIV (manager);
132
133         priv->lists = g_hash_table_new_full (gossip_account_hash,
134                                              gossip_account_equal,
135                                              (GDestroyNotify) g_object_unref,
136                                              (GDestroyNotify) g_object_unref);
137
138         priv->mc = gossip_mission_control_new ();
139
140         dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
141                                      "AccountStatusChanged",
142                                      G_CALLBACK (contact_manager_status_changed_cb),
143                                      manager, NULL);
144
145         /* Get ContactList for existing connections */
146         accounts = mission_control_get_online_connections (priv->mc, NULL);
147         for (l = accounts; l; l = l->next) {
148                 McAccount *account;
149
150                 account = l->data;
151                 contact_manager_add_account (manager, account);
152                 
153                 g_object_unref (account);
154         }
155         g_slist_free (accounts);
156 }
157
158 static void
159 contact_manager_finalize (GObject *object)
160 {
161         EmpathyContactManagerPriv *priv;
162
163         priv = GET_PRIV (object);
164
165         g_hash_table_destroy (priv->lists);
166         g_object_unref (priv->mc);
167 }
168
169 EmpathyContactManager *
170 empathy_contact_manager_new (void)
171 {
172         static EmpathyContactManager *manager = NULL;
173
174         if (!manager) {
175                 manager = g_object_new (EMPATHY_TYPE_CONTACT_MANAGER, NULL);
176                 g_object_add_weak_pointer (G_OBJECT (manager), (gpointer) &manager);
177         } else {
178                 g_object_ref (manager);
179         }
180
181         return manager;
182 }
183
184 static void
185 contact_manager_setup (EmpathyContactList *manager)
186 {
187         EmpathyContactManagerPriv *priv;
188
189         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
190
191         priv = GET_PRIV (manager);
192
193         if (priv->setup) {
194                 /* Already done */
195                 return;
196         }
197
198         g_hash_table_foreach (priv->lists,
199                               (GHFunc) contact_manager_setup_foreach,
200                               manager);
201
202         priv->setup = TRUE;
203 }
204
205 static GossipContact *
206 contact_manager_find (EmpathyContactList *manager,
207                       const gchar        *id)
208 {
209         EmpathyContactManagerPriv *priv;
210         ContactManagerFindData     data;
211
212         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
213         g_return_val_if_fail (id != NULL, NULL);
214
215         priv = GET_PRIV (manager);
216
217         data.contact = NULL;
218         data.id = id;
219
220         g_hash_table_find (priv->lists,
221                            (GHRFunc) contact_manager_find_foreach,
222                            &data);
223
224         return data.contact;
225 }
226
227 static void
228 contact_manager_add (EmpathyContactList *manager,
229                      GossipContact      *contact,
230                      const gchar        *message)
231 {
232         EmpathyContactManagerPriv *priv;
233         EmpathyContactList        *list;
234         McAccount                 *account;
235
236         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
237         g_return_if_fail (GOSSIP_IS_CONTACT (contact));
238
239         priv = GET_PRIV (manager);
240
241         account = gossip_contact_get_account (contact);
242         list = g_hash_table_lookup (priv->lists, account);
243
244         if (list) {
245                 empathy_contact_list_add (list, contact, message);
246         }
247 }
248
249 static void
250 contact_manager_remove (EmpathyContactList *manager,
251                         GossipContact      *contact,
252                         const gchar        *message)
253 {
254         EmpathyContactManagerPriv *priv;
255         EmpathyContactList        *list;
256         McAccount                 *account;
257
258         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
259         g_return_if_fail (GOSSIP_IS_CONTACT (contact));
260
261         priv = GET_PRIV (manager);
262
263         account = gossip_contact_get_account (contact);
264         list = g_hash_table_lookup (priv->lists, account);
265
266         if (list) {
267                 empathy_contact_list_remove (list, contact, message);
268         }
269 }
270
271 static GList *
272 contact_manager_get_contacts (EmpathyContactList *manager)
273 {
274         EmpathyContactManagerPriv *priv;
275         GList                     *contacts = NULL;
276
277         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
278
279         priv = GET_PRIV (manager);
280
281         g_hash_table_foreach (priv->lists,
282                               (GHFunc) contact_manager_get_contacts_foreach,
283                               &contacts);
284
285         return contacts;
286 }
287
288 EmpathyTpContactList *
289 empathy_contact_manager_get_list (EmpathyContactManager *manager,
290                                   McAccount             *account)
291 {
292         EmpathyContactManagerPriv *priv;
293
294         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
295         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
296
297         priv = GET_PRIV (manager);
298
299         return g_hash_table_lookup (priv->lists, account);
300 }
301
302 GossipContact *
303 empathy_contact_manager_get_user (EmpathyContactManager *manager,
304                                   McAccount             *account)
305 {
306         EmpathyContactManagerPriv *priv;
307         EmpathyTpContactList        *list;
308
309         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
310         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
311
312         priv = GET_PRIV (manager);
313
314         list = g_hash_table_lookup (priv->lists, account);
315         
316         if (!list) {
317                 return NULL;
318         }
319
320         return empathy_tp_contact_list_get_user (list);
321 }
322
323 GossipContact *
324 empathy_contact_manager_create (EmpathyContactManager *manager,
325                                 McAccount             *account,
326                                 const gchar           *id)
327 {
328         EmpathyContactManagerPriv *priv;
329         EmpathyTpContactList      *list;
330
331         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
332         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
333         g_return_val_if_fail (id != NULL, NULL);
334
335         priv = GET_PRIV (manager);
336
337         list = g_hash_table_lookup (priv->lists, account);
338         
339         if (!list) {
340                 return NULL;
341         }
342
343         return empathy_tp_contact_list_get_from_id (list, id);
344 }
345
346 void
347 empathy_contact_manager_rename_group (EmpathyContactManager *manager,
348                                       const gchar           *old_group,
349                                       const gchar           *new_group)
350 {
351         EmpathyContactManagerPriv   *priv;
352         ContactManagerRenameGroupData  data;
353
354         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
355         g_return_if_fail (old_group != NULL);
356         g_return_if_fail (new_group != NULL);
357
358         priv = GET_PRIV (manager);
359
360         data.old_group = old_group;
361         data.new_group = new_group;
362
363         g_hash_table_foreach (priv->lists,
364                               (GHFunc) contact_manager_rename_group_foreach,
365                               &data);
366 }
367
368 GList *
369 empathy_contact_manager_get_groups (EmpathyContactManager *manager)
370 {
371         EmpathyContactManagerPriv *priv;
372         GList                     *groups = NULL;
373
374         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
375
376         priv = GET_PRIV (manager);
377
378         g_hash_table_foreach (priv->lists,
379                               (GHFunc) contact_manager_get_groups_foreach,
380                               &groups);
381
382         return groups;
383 }
384
385 static void
386 contact_manager_setup_foreach (McAccount             *account,
387                                EmpathyTpContactList  *list,
388                                EmpathyContactManager *manager)
389 {
390         empathy_contact_list_setup (EMPATHY_CONTACT_LIST (list));
391 }
392
393 static gboolean
394 contact_manager_find_foreach (McAccount              *account,
395                               EmpathyTpContactList   *list,
396                               ContactManagerFindData *data)
397 {
398         data->contact = empathy_contact_list_find (EMPATHY_CONTACT_LIST (list),
399                                                    data->id);
400
401         if (data->contact) {
402                 return TRUE;
403         }
404
405         return FALSE;
406 }
407
408 static void
409 contact_manager_add_account (EmpathyContactManager *manager,
410                              McAccount             *account)
411 {
412         EmpathyContactManagerPriv *priv;
413         EmpathyTpContactList        *list;
414
415         priv = GET_PRIV (manager);
416
417         if (g_hash_table_lookup (priv->lists, account)) {
418                 return;
419         }
420
421         gossip_debug (DEBUG_DOMAIN, "Adding new account: %s",
422                       mc_account_get_display_name (account));
423
424         list = empathy_tp_contact_list_new (account);
425         if (!list) {
426                 return;
427         }
428
429         g_hash_table_insert (priv->lists, g_object_ref (account), list);
430
431         /* Connect signals */
432         g_signal_connect (list, "contact-added",
433                           G_CALLBACK (contact_manager_added_cb),
434                           manager);
435         g_signal_connect (list, "contact-removed",
436                           G_CALLBACK (contact_manager_removed_cb),
437                           manager);
438         g_signal_connect (list, "destroy",
439                           G_CALLBACK (contact_manager_destroy_cb),
440                           manager);
441
442         if (priv->setup) {
443                 empathy_contact_list_setup (EMPATHY_CONTACT_LIST (list));
444         }
445 }
446
447 static void
448 contact_manager_added_cb (EmpathyTpContactList  *list,
449                           GossipContact         *contact,
450                           EmpathyContactManager *manager)
451 {
452         g_signal_emit_by_name (manager, "contact-added", contact);
453 }
454
455 static void
456 contact_manager_removed_cb (EmpathyTpContactList  *list,
457                             GossipContact         *contact,
458                             EmpathyContactManager *manager)
459 {
460         g_signal_emit_by_name (manager, "contact-removed", contact);
461 }
462
463 static void
464 contact_manager_destroy_cb (EmpathyTpContactList  *list,
465                             EmpathyContactManager *manager)
466 {
467         EmpathyContactManagerPriv *priv;
468         McAccount                 *account;
469
470         priv = GET_PRIV (manager);
471
472         account = empathy_tp_contact_list_get_account (list);
473
474         gossip_debug (DEBUG_DOMAIN, "Removing account: %s",
475                       mc_account_get_display_name (account));
476
477         /* Disconnect signals from the list */
478         g_signal_handlers_disconnect_by_func (list,
479                                               contact_manager_added_cb,
480                                               manager);
481         g_signal_handlers_disconnect_by_func (list,
482                                               contact_manager_removed_cb,
483                                               manager);
484         g_signal_handlers_disconnect_by_func (list,
485                                               contact_manager_destroy_cb,
486                                               manager);
487
488         g_hash_table_remove (priv->lists, account);
489 }
490
491 static void
492 contact_manager_rename_group_foreach (McAccount                     *account,
493                                       EmpathyTpContactList          *list,
494                                       ContactManagerRenameGroupData *data)
495 {
496         empathy_tp_contact_list_rename_group (list,
497                                               data->old_group,
498                                               data->new_group);
499 }
500
501 static void
502 contact_manager_get_groups_foreach (McAccount             *account,
503                                     EmpathyTpContactList  *list,
504                                     GList                **all_groups)
505 {
506         GList *groups, *l;
507
508         groups = empathy_tp_contact_list_get_groups (list);
509         for (l = groups; l; l = l->next) {
510                 if (!g_list_find_custom (*all_groups,
511                                          l->data,
512                                          (GCompareFunc) strcmp)) {
513                         *all_groups = g_list_append (*all_groups,
514                                                      g_strdup (l->data));
515                 }
516                 g_free (l->data);
517         }
518
519         g_list_free (groups);
520 }
521
522 static void
523 contact_manager_get_contacts_foreach (McAccount             *account,
524                                       EmpathyTpContactList  *list,
525                                       GList                **contacts)
526 {
527         GList *l;
528
529         l = empathy_contact_list_get_contacts (EMPATHY_CONTACT_LIST (list));
530         *contacts = g_list_concat (*contacts, l);
531 }
532
533 static void
534 contact_manager_status_changed_cb (MissionControl                  *mc,
535                                    TelepathyConnectionStatus        status,
536                                    McPresence                       presence,
537                                    TelepathyConnectionStatusReason  reason,
538                                    const gchar                     *unique_name,
539                                    EmpathyContactManager           *manager)
540 {
541         EmpathyContactManagerPriv *priv;
542         McAccount                 *account;
543
544         priv = GET_PRIV (manager);
545
546         if (status != TP_CONN_STATUS_CONNECTED) {
547                 /* We only care about newly connected accounts */
548                 return;
549         }
550
551         account = mc_account_lookup (unique_name);
552         contact_manager_add_account (manager, account);
553
554         g_object_unref (account);
555 }
556