]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact-manager.c
Merge branch 'undo-close-tab'
[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 <extensions/extensions.h>
32
33 #include "empathy-contact-manager.h"
34 #include "empathy-contact-monitor.h"
35 #include "empathy-contact-list.h"
36 #include "empathy-utils.h"
37
38 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
39 #include "empathy-debug.h"
40
41 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContactManager)
42 typedef struct {
43         GHashTable     *lists;
44         TpAccountManager *account_manager;
45         EmpathyContactMonitor *contact_monitor;
46         TpProxy *logger;
47         /* account object path (gchar *) => GHashTable containing favorite contacts
48          * (contact ID (gchar *) => TRUE) */
49         GHashTable *favourites;
50         TpProxySignalConnection *favourite_contacts_changed_signal;
51 } EmpathyContactManagerPriv;
52
53 static void contact_manager_iface_init         (EmpathyContactListIface    *iface);
54
55 G_DEFINE_TYPE_WITH_CODE (EmpathyContactManager, empathy_contact_manager, G_TYPE_OBJECT,
56                          G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST,
57                                                 contact_manager_iface_init));
58
59 static EmpathyContactManager *manager_singleton = NULL;
60
61 static void
62 contact_manager_members_changed_cb (EmpathyTpContactList  *list,
63                                     EmpathyContact        *contact,
64                                     EmpathyContact        *actor,
65                                     guint                  reason,
66                                     gchar                 *message,
67                                     gboolean               is_member,
68                                     EmpathyContactManager *manager)
69 {
70         g_signal_emit_by_name (manager, "members-changed",
71                                contact, actor, reason, message, is_member);
72 }
73
74 static void
75 contact_manager_pendings_changed_cb (EmpathyTpContactList  *list,
76                                      EmpathyContact        *contact,
77                                      EmpathyContact        *actor,
78                                      guint                  reason,
79                                      gchar                 *message,
80                                      gboolean               is_pending,
81                                      EmpathyContactManager *manager)
82 {
83         g_signal_emit_by_name (manager, "pendings-changed",
84                                contact, actor, reason, message, is_pending);
85 }
86
87 static void
88 contact_manager_groups_changed_cb (EmpathyTpContactList  *list,
89                                    EmpathyContact        *contact,
90                                    gchar                 *group,
91                                    gboolean               is_member,
92                                    EmpathyContactManager *manager)
93 {
94         g_signal_emit_by_name (manager, "groups-changed",
95                                contact, group, is_member);
96 }
97
98 static void
99 contact_manager_invalidated_cb (TpProxy *connection,
100                                 guint    domain,
101                                 gint     code,
102                                 gchar   *message,
103                                 EmpathyContactManager *manager)
104 {
105         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
106         EmpathyTpContactList *list;
107
108         DEBUG ("Removing connection: %s (%s)",
109                 tp_proxy_get_object_path (TP_PROXY (connection)),
110                 message);
111
112         list = g_hash_table_lookup (priv->lists, connection);
113         if (list) {
114                 empathy_tp_contact_list_remove_all (list);
115                 g_hash_table_remove (priv->lists, connection);
116         }
117 }
118
119 static void
120 contact_manager_disconnect_foreach (gpointer key,
121                                     gpointer value,
122                                     gpointer user_data)
123 {
124         TpConnection *connection = key;
125         EmpathyTpContactList  *list = value;
126         EmpathyContactManager *manager = user_data;
127
128         /* Disconnect signals from the list */
129         g_signal_handlers_disconnect_by_func (list,
130                                               contact_manager_members_changed_cb,
131                                               manager);
132         g_signal_handlers_disconnect_by_func (list,
133                                               contact_manager_pendings_changed_cb,
134                                               manager);
135         g_signal_handlers_disconnect_by_func (list,
136                                               contact_manager_groups_changed_cb,
137                                               manager);
138         g_signal_handlers_disconnect_by_func (connection,
139                                               contact_manager_invalidated_cb,
140                                               manager);
141 }
142
143 static void
144 contact_manager_status_changed_cb (TpAccount *account,
145                                    guint old_status,
146                                    guint new_status,
147                                    guint reason,
148                                    gchar *dbus_error_name,
149                                    GHashTable *details,
150                                    EmpathyContactManager *self)
151 {
152         EmpathyContactManagerPriv *priv = GET_PRIV (self);
153         EmpathyTpContactList      *list;
154         TpConnection              *connection;
155
156         if (new_status == TP_CONNECTION_STATUS_DISCONNECTED)
157                 /* No point to start tracking a connection which is about to die */
158                 return;
159
160         connection = tp_account_get_connection (account);
161
162         if (connection == NULL || g_hash_table_lookup (priv->lists, connection)) {
163                 return;
164         }
165
166         DEBUG ("Adding new connection: %s",
167                 tp_proxy_get_object_path (TP_PROXY (connection)));
168
169         list = empathy_tp_contact_list_new (connection);
170         g_hash_table_insert (priv->lists, g_object_ref (connection), list);
171         g_signal_connect (connection, "invalidated",
172                           G_CALLBACK (contact_manager_invalidated_cb),
173                           self);
174
175         /* Connect signals */
176         g_signal_connect (list, "members-changed",
177                           G_CALLBACK (contact_manager_members_changed_cb),
178                           self);
179         g_signal_connect (list, "pendings-changed",
180                           G_CALLBACK (contact_manager_pendings_changed_cb),
181                           self);
182         g_signal_connect (list, "groups-changed",
183                           G_CALLBACK (contact_manager_groups_changed_cb),
184                           self);
185 }
186
187 static void
188 contact_manager_validity_changed_cb (TpAccountManager *account_manager,
189                                      TpAccount *account,
190                                      gboolean valid,
191                                      EmpathyContactManager *manager)
192 {
193         if (valid) {
194                 empathy_signal_connect_weak (account, "status-changed",
195                             G_CALLBACK (contact_manager_status_changed_cb),
196                             G_OBJECT (manager));
197         }
198 }
199
200 static gboolean
201 contact_manager_is_favourite (EmpathyContactList *manager,
202                               EmpathyContact     *contact)
203 {
204         EmpathyContactManagerPriv *priv;
205         TpAccount *account;
206         const gchar *account_name;
207         GHashTable *contact_hash;
208
209         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), FALSE);
210         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
211
212         priv = GET_PRIV (manager);
213
214         account = empathy_contact_get_account (contact);
215         account_name = tp_proxy_get_object_path (TP_PROXY (account));
216         contact_hash = g_hash_table_lookup (priv->favourites, account_name);
217
218         if (contact_hash != NULL) {
219                 const gchar *contact_id = empathy_contact_get_id (contact);
220
221                 if (g_hash_table_lookup (contact_hash, contact_id) != NULL)
222                         return TRUE;
223         }
224
225         return FALSE;
226 }
227
228 static void
229 add_favourite_contact_cb (TpProxy *proxy,
230                           const GError *error,
231                           gpointer user_data,
232                           GObject *weak_object)
233 {
234         if (error != NULL)
235                 DEBUG ("AddFavouriteContact failed: %s", error->message);
236 }
237
238 static void
239 contact_manager_add_favourite (EmpathyContactList *manager,
240                                EmpathyContact *contact)
241 {
242         EmpathyContactManagerPriv *priv;
243         TpAccount *account;
244         const gchar *account_name;
245
246         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
247         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
248
249         priv = GET_PRIV (manager);
250
251         account = empathy_contact_get_account (contact);
252         account_name = tp_proxy_get_object_path (TP_PROXY (account));
253
254         emp_cli_logger_call_add_favourite_contact (priv->logger, -1,
255                                                    account_name,
256                                                    empathy_contact_get_id (contact),
257                                                    add_favourite_contact_cb, NULL, NULL, G_OBJECT (manager));
258 }
259
260 static void
261 remove_favourite_contact_cb (TpProxy *proxy,
262                              const GError *error,
263                              gpointer user_data,
264                              GObject *weak_object)
265 {
266         if (error != NULL)
267                 DEBUG ("RemoveFavouriteContact failed: %s", error->message);
268 }
269
270 static void
271 contact_manager_remove_favourite (EmpathyContactList *manager,
272                                   EmpathyContact *contact)
273 {
274         EmpathyContactManagerPriv *priv;
275         TpAccount *account;
276         const gchar *account_name;
277
278         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
279         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
280
281         priv = GET_PRIV (manager);
282
283         account = empathy_contact_get_account (contact);
284         account_name = tp_proxy_get_object_path (TP_PROXY (account));
285
286         emp_cli_logger_call_remove_favourite_contact (priv->logger, -1,
287                                                       account_name,
288                                                       empathy_contact_get_id (contact),
289                                                       remove_favourite_contact_cb, NULL, NULL, G_OBJECT (manager));
290 }
291
292 static void
293 add_contacts_to_favourites (EmpathyContactManager *self,
294                             const gchar *account,
295                             const gchar **contacts)
296 {
297         EmpathyContactManagerPriv *priv = GET_PRIV (self);
298         guint j;
299         GHashTable *contact_hash;
300
301         contact_hash = g_hash_table_lookup (priv->favourites, account);
302         if (contact_hash == NULL) {
303                 contact_hash = g_hash_table_new_full (g_str_hash,
304                                                       g_str_equal,
305                                                       g_free, NULL);
306
307                 g_hash_table_insert (priv->favourites,
308                                      g_strdup (account),
309                                      contact_hash);
310         }
311
312         for (j = 0; contacts && contacts[j] != NULL; j++) {
313                 g_hash_table_insert (contact_hash,
314                                      g_strdup (contacts[j]),
315                                      GINT_TO_POINTER (1));
316         }
317 }
318
319 static void
320 logger_favourite_contacts_add_from_value_array (GValueArray           *va,
321                                                 EmpathyContactManager *manager)
322 {
323         const gchar *account;
324         const gchar **contacts;
325
326         account = g_value_get_boxed (g_value_array_get_nth (va, 0));
327         contacts = g_value_get_boxed (g_value_array_get_nth (va, 1));
328
329         add_contacts_to_favourites (manager, account, contacts);
330 }
331
332 static void
333 logger_favourite_contacts_get_cb (TpProxy         *proxy,
334                                   const GPtrArray *result,
335                                   const GError    *error,
336                                   gpointer         user_data,
337                                   GObject         *weak_object)
338 {
339         EmpathyContactManager *manager = EMPATHY_CONTACT_MANAGER (weak_object);
340
341         if (error == NULL) {
342                 g_ptr_array_foreach ((GPtrArray *) result,
343                                 (GFunc)
344                                 logger_favourite_contacts_add_from_value_array,
345                                 manager);
346         } else {
347                 DEBUG ("Failed to get the FavouriteContacts property: %s",
348                                 error->message);
349         }
350 }
351
352 static void
353 logger_favourite_contacts_setup (EmpathyContactManager *manager)
354 {
355         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
356
357         emp_cli_logger_call_get_favourite_contacts (priv->logger, -1,
358                         logger_favourite_contacts_get_cb, NULL, NULL,
359                         G_OBJECT (manager));
360 }
361
362 static void
363 contact_manager_finalize (GObject *object)
364 {
365         EmpathyContactManagerPriv *priv = GET_PRIV (object);
366
367         tp_proxy_signal_connection_disconnect (priv->favourite_contacts_changed_signal);
368
369         if (priv->logger != NULL)
370                 g_object_unref (priv->logger);
371
372         g_hash_table_foreach (priv->lists,
373                               contact_manager_disconnect_foreach,
374                               object);
375         g_hash_table_destroy (priv->lists);
376         g_hash_table_destroy (priv->favourites);
377
378         g_object_unref (priv->account_manager);
379
380         if (priv->contact_monitor) {
381                 g_object_unref (priv->contact_monitor);
382         }
383 }
384
385 static GObject *
386 contact_manager_constructor (GType type,
387                              guint n_props,
388                              GObjectConstructParam *props)
389 {
390         GObject *retval;
391
392         if (manager_singleton) {
393                 retval = g_object_ref (manager_singleton);
394         } else {
395                 retval = G_OBJECT_CLASS (empathy_contact_manager_parent_class)->constructor
396                         (type, n_props, props);
397
398                 manager_singleton = EMPATHY_CONTACT_MANAGER (retval);
399                 g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
400         }
401
402         return retval;
403 }
404
405 /**
406  * empathy_contact_manager_initialized:
407  *
408  * Reports whether or not the singleton has already been created.
409  *
410  * There can be instances where you want to access the #EmpathyContactManager
411  * only if it has been set up for this process.
412  *
413  * Returns: %TRUE if the #EmpathyContactManager singleton has previously
414  * been initialized.
415  */
416 gboolean
417 empathy_contact_manager_initialized (void)
418 {
419         return (manager_singleton != NULL);
420 }
421
422 static void
423 empathy_contact_manager_class_init (EmpathyContactManagerClass *klass)
424 {
425         GObjectClass *object_class = G_OBJECT_CLASS (klass);
426
427         object_class->finalize = contact_manager_finalize;
428         object_class->constructor = contact_manager_constructor;
429
430         g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv));
431 }
432
433 static void
434 account_manager_prepared_cb (GObject *source_object,
435                              GAsyncResult *result,
436                              gpointer user_data)
437 {
438         GList *accounts, *l;
439         EmpathyContactManager *manager = user_data;
440         TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
441         GError *error = NULL;
442
443         if (!tp_account_manager_prepare_finish (account_manager, result, &error)) {
444                 DEBUG ("Failed to prepare account manager: %s", error->message);
445                 g_error_free (error);
446                 return;
447         }
448
449         accounts = tp_account_manager_get_valid_accounts (account_manager);
450
451         for (l = accounts; l != NULL; l = l->next) {
452                 TpAccount *account = l->data;
453                 TpConnection *conn = tp_account_get_connection (account);
454
455                 if (conn != NULL) {
456                         contact_manager_status_changed_cb (account, 0, 0, 0,
457                                                            NULL, NULL, manager);
458                 }
459
460                 empathy_signal_connect_weak (account, "status-changed",
461                     G_CALLBACK (contact_manager_status_changed_cb),
462                     G_OBJECT (manager));
463         }
464         g_list_free (accounts);
465
466         empathy_signal_connect_weak (account_manager, "account-validity-changed",
467                              G_CALLBACK (contact_manager_validity_changed_cb),
468                              G_OBJECT (manager));
469 }
470
471 static EmpathyContact *
472 contact_manager_lookup_contact (EmpathyContactManager *manager,
473                                 const gchar           *account_name,
474                                 const gchar           *contact_id)
475 {
476         EmpathyContact *retval = NULL;
477         GList *members, *l;
478
479         /* XXX: any more efficient way to do this (other than having to build
480          * and maintain a hash)? */
481         members = empathy_contact_list_get_members (
482                         EMPATHY_CONTACT_LIST (manager));
483         for (l = members; l; l = l->next) {
484                 EmpathyContact *contact = l->data;
485                 TpAccount *account = empathy_contact_get_account (contact);
486                 const gchar *id_cur;
487                 const gchar *name_cur;
488
489                 id_cur = empathy_contact_get_id (contact);
490                 name_cur = tp_proxy_get_object_path (TP_PROXY (account));
491
492                 if (!tp_strdiff (contact_id, id_cur) &&
493                         !tp_strdiff (account_name, name_cur)) {
494                         retval = contact;
495                 }
496
497                 g_object_unref (contact);
498         }
499
500         g_list_free (members);
501
502         return retval;
503 }
504
505 static void
506 logger_favourite_contacts_changed_cb (TpProxy      *proxy,
507                                       const gchar  *account_name,
508                                       const gchar **added,
509                                       const gchar **removed,
510                                       gpointer      user_data,
511                                       GObject      *weak_object)
512 {
513         EmpathyContactManagerPriv *priv;
514         EmpathyContactManager *manager = EMPATHY_CONTACT_MANAGER (weak_object);
515         GHashTable *contact_hash;
516         EmpathyContact *contact;
517         gint i;
518
519         priv = GET_PRIV (manager);
520
521         contact_hash = g_hash_table_lookup (priv->favourites, account_name);
522
523         /* XXX: note that, at the time of this comment, there will always be
524          * exactly one contact amongst added and removed, so the linear lookup
525          * of each contact isn't as painful as it appears */
526
527         add_contacts_to_favourites (manager, account_name, added);
528
529         for (i = 0; added && added[i]; i++) {
530                 contact = contact_manager_lookup_contact (manager, account_name,
531                                                           added[i]);
532                 if (contact != NULL)
533                         g_signal_emit_by_name (manager, "favourites-changed",
534                                                contact, TRUE);
535                 else
536                         DEBUG ("failed to find contact for account %s, contact "
537                                "id %s", account_name, added[i]);
538         }
539
540         for (i = 0; removed && removed[i]; i++) {
541                 contact_hash = g_hash_table_lookup (priv->favourites,
542                                                     account_name);
543
544                 if (contact_hash != NULL) {
545                         g_hash_table_remove (contact_hash, removed[i]);
546
547                         if (g_hash_table_size (contact_hash) < 1) {
548                                 g_hash_table_remove (priv->favourites,
549                                                      account_name);
550                         }
551                 }
552
553                 contact = contact_manager_lookup_contact (manager, account_name,
554                                                           removed[i]);
555                 if (contact != NULL)
556                         g_signal_emit_by_name (manager, "favourites-changed",
557                                                contact, FALSE);
558                 else
559                         DEBUG ("failed to find contact for account %s, contact "
560                                "id %s", account_name, removed[i]);
561         }
562 }
563
564 static void
565 empathy_contact_manager_init (EmpathyContactManager *manager)
566 {
567         EmpathyContactManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
568                 EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManagerPriv);
569         TpDBusDaemon *bus;
570         GError *error = NULL;
571
572         manager->priv = priv;
573         priv->lists = g_hash_table_new_full (empathy_proxy_hash,
574                                              empathy_proxy_equal,
575                                              (GDestroyNotify) g_object_unref,
576                                              (GDestroyNotify) g_object_unref);
577
578         priv->favourites = g_hash_table_new_full (g_str_hash, g_str_equal,
579                                                   (GDestroyNotify) g_free,
580                                                   (GDestroyNotify)
581                                                   g_hash_table_unref);
582
583         priv->account_manager = tp_account_manager_dup ();
584         priv->contact_monitor = NULL;
585
586         tp_account_manager_prepare_async (priv->account_manager, NULL,
587             account_manager_prepared_cb, manager);
588
589         bus = tp_dbus_daemon_dup (&error);
590
591         if (error == NULL) {
592                 priv->logger = g_object_new (TP_TYPE_PROXY,
593                                 "bus-name", "org.freedesktop.Telepathy.Logger",
594                                 "object-path",
595                                         "/org/freedesktop/Telepathy/Logger",
596                                 "dbus-daemon", bus,
597                                 NULL);
598                 g_object_unref (bus);
599
600                 tp_proxy_add_interface_by_id (priv->logger,
601                                 EMP_IFACE_QUARK_LOGGER);
602
603                 logger_favourite_contacts_setup (manager);
604
605                 priv->favourite_contacts_changed_signal =
606                         emp_cli_logger_connect_to_favourite_contacts_changed (
607                                 priv->logger,
608                                 logger_favourite_contacts_changed_cb, NULL,
609                                 NULL, G_OBJECT (manager), NULL);
610         } else {
611                 DEBUG ("Failed to get telepathy-logger proxy: %s",
612                                 error->message);
613                 g_clear_error (&error);
614         }
615 }
616
617 EmpathyContactManager *
618 empathy_contact_manager_dup_singleton (void)
619 {
620         return g_object_new (EMPATHY_TYPE_CONTACT_MANAGER, NULL);
621 }
622
623 EmpathyTpContactList *
624 empathy_contact_manager_get_list (EmpathyContactManager *manager,
625                                   TpConnection          *connection)
626 {
627         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
628
629         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
630         g_return_val_if_fail (TP_IS_CONNECTION (connection), NULL);
631
632         return g_hash_table_lookup (priv->lists, connection);
633 }
634
635 static void
636 contact_manager_add (EmpathyContactList *manager,
637                      EmpathyContact     *contact,
638                      const gchar        *message)
639 {
640         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
641         EmpathyContactList        *list;
642         TpConnection              *connection;
643
644         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
645
646         connection = empathy_contact_get_connection (contact);
647         list = g_hash_table_lookup (priv->lists, connection);
648
649         if (list) {
650                 empathy_contact_list_add (list, contact, message);
651         }
652 }
653
654 static void
655 contact_manager_remove (EmpathyContactList *manager,
656                         EmpathyContact     *contact,
657                         const gchar        *message)
658 {
659         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
660         EmpathyContactList        *list;
661         TpConnection              *connection;
662
663         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
664
665         connection = empathy_contact_get_connection (contact);
666         list = g_hash_table_lookup (priv->lists, connection);
667
668         if (list) {
669                 empathy_contact_list_remove (list, contact, message);
670         }
671 }
672
673 static void
674 contact_manager_get_members_foreach (TpConnection          *connection,
675                                      EmpathyTpContactList  *list,
676                                      GList                **contacts)
677 {
678         GList *l;
679
680         l = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (list));
681         *contacts = g_list_concat (*contacts, l);
682 }
683
684 static GList *
685 contact_manager_get_members (EmpathyContactList *manager)
686 {
687         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
688         GList                     *contacts = NULL;
689
690         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
691
692         g_hash_table_foreach (priv->lists,
693                               (GHFunc) contact_manager_get_members_foreach,
694                               &contacts);
695
696         return contacts;
697 }
698
699 static EmpathyContactMonitor *
700 contact_manager_get_monitor (EmpathyContactList *manager)
701 {
702         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
703
704         if (priv->contact_monitor == NULL) {
705                 priv->contact_monitor = empathy_contact_monitor_new_for_iface (manager);
706         }
707
708         return priv->contact_monitor;
709 }
710
711 static void
712 contact_manager_get_pendings_foreach (TpConnection          *connection,
713                                       EmpathyTpContactList  *list,
714                                       GList                **contacts)
715 {
716         GList *l;
717
718         l = empathy_contact_list_get_pendings (EMPATHY_CONTACT_LIST (list));
719         *contacts = g_list_concat (*contacts, l);
720 }
721
722 static GList *
723 contact_manager_get_pendings (EmpathyContactList *manager)
724 {
725         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
726         GList                     *contacts = NULL;
727
728         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
729
730         g_hash_table_foreach (priv->lists,
731                               (GHFunc) contact_manager_get_pendings_foreach,
732                               &contacts);
733
734         return contacts;
735 }
736
737 static void
738 contact_manager_get_all_groups_foreach (TpConnection          *connection,
739                                         EmpathyTpContactList  *list,
740                                         GList                **all_groups)
741 {
742         GList *groups, *l;
743
744         groups = empathy_contact_list_get_all_groups (EMPATHY_CONTACT_LIST (list));
745         for (l = groups; l; l = l->next) {
746                 if (!g_list_find_custom (*all_groups,
747                                          l->data,
748                                          (GCompareFunc) strcmp)) {
749                         *all_groups = g_list_prepend (*all_groups, l->data);
750                 } else {
751                         g_free (l->data);
752                 }
753         }
754
755         g_list_free (groups);
756 }
757
758 static GList *
759 contact_manager_get_all_groups (EmpathyContactList *manager)
760 {
761         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
762         GList                     *groups = NULL;
763
764         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
765
766         g_hash_table_foreach (priv->lists,
767                               (GHFunc) contact_manager_get_all_groups_foreach,
768                               &groups);
769
770         return groups;
771 }
772
773 static GList *
774 contact_manager_get_groups (EmpathyContactList *manager,
775                             EmpathyContact     *contact)
776 {
777         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
778         EmpathyContactList        *list;
779         TpConnection              *connection;
780
781         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
782
783         connection = empathy_contact_get_connection (contact);
784         list = g_hash_table_lookup (priv->lists, connection);
785
786         if (list) {
787                 return empathy_contact_list_get_groups (list, contact);
788         }
789
790         return NULL;
791 }
792
793 static void
794 contact_manager_add_to_group (EmpathyContactList *manager,
795                               EmpathyContact     *contact,
796                               const gchar        *group)
797 {
798         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
799         EmpathyContactList        *list;
800         TpConnection              *connection;
801
802         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
803
804         connection = empathy_contact_get_connection (contact);
805         list = g_hash_table_lookup (priv->lists, connection);
806
807         if (list) {
808                 empathy_contact_list_add_to_group (list, contact, group);
809         }
810 }
811
812 static void
813 contact_manager_remove_from_group (EmpathyContactList *manager,
814                                    EmpathyContact     *contact,
815                                    const gchar        *group)
816 {
817         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
818         EmpathyContactList        *list;
819         TpConnection              *connection;
820
821         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
822
823         connection = empathy_contact_get_connection (contact);
824         list = g_hash_table_lookup (priv->lists, connection);
825
826         if (list) {
827                 empathy_contact_list_remove_from_group (list, contact, group);
828         }
829 }
830
831 typedef struct {
832         const gchar *old_group;
833         const gchar *new_group;
834 } RenameGroupData;
835
836 static void
837 contact_manager_rename_group_foreach (TpConnection         *connection,
838                                       EmpathyTpContactList *list,
839                                       RenameGroupData      *data)
840 {
841         empathy_contact_list_rename_group (EMPATHY_CONTACT_LIST (list),
842                                            data->old_group,
843                                            data->new_group);
844 }
845
846 static void
847 contact_manager_rename_group (EmpathyContactList *manager,
848                               const gchar        *old_group,
849                               const gchar        *new_group)
850 {
851         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
852         RenameGroupData            data;
853
854         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
855
856         data.old_group = old_group;
857         data.new_group = new_group;
858         g_hash_table_foreach (priv->lists,
859                               (GHFunc) contact_manager_rename_group_foreach,
860                               &data);
861 }
862
863 static void contact_manager_remove_group_foreach (TpConnection         *connection,
864                                                   EmpathyTpContactList *list,
865                                                   const gchar *group)
866 {
867         empathy_contact_list_remove_group (EMPATHY_CONTACT_LIST (list),
868                                            group);
869 }
870
871 static void
872 contact_manager_remove_group (EmpathyContactList *manager,
873                               const gchar *group)
874 {
875         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
876
877         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
878
879         g_hash_table_foreach (priv->lists,
880                               (GHFunc) contact_manager_remove_group_foreach,
881                               (gpointer) group);
882 }
883
884 static void
885 contact_manager_iface_init (EmpathyContactListIface *iface)
886 {
887         iface->add               = contact_manager_add;
888         iface->remove            = contact_manager_remove;
889         iface->get_members       = contact_manager_get_members;
890         iface->get_monitor       = contact_manager_get_monitor;
891         iface->get_pendings      = contact_manager_get_pendings;
892         iface->get_all_groups    = contact_manager_get_all_groups;
893         iface->get_groups        = contact_manager_get_groups;
894         iface->add_to_group      = contact_manager_add_to_group;
895         iface->remove_from_group = contact_manager_remove_from_group;
896         iface->rename_group      = contact_manager_rename_group;
897         iface->remove_group      = contact_manager_remove_group;
898         iface->is_favourite      = contact_manager_is_favourite;
899         iface->remove_favourite  = contact_manager_remove_favourite;
900         iface->add_favourite     = contact_manager_add_favourite;
901 }
902
903 EmpathyContactListFlags
904 empathy_contact_manager_get_flags_for_connection (
905                                 EmpathyContactManager *manager,
906                                 TpConnection          *connection)
907 {
908         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
909         EmpathyContactList        *list;
910         EmpathyContactListFlags    flags;
911
912         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), FALSE);
913         g_return_val_if_fail (connection != NULL, FALSE);
914
915         list = g_hash_table_lookup (priv->lists, connection);
916         if (list == NULL) {
917                 return FALSE;
918         }
919         flags = empathy_contact_list_get_flags (list);
920
921         return flags;
922 }
923