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