]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact-manager.c
d0ba13054753a78a9dcd4f69f91150973bee5ead
[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         guint i;
324
325         for (i = 0; i < va->n_values; i++) {
326                 GValue *account_value;
327                 const gchar *account;
328                 GValue *contacts_value;
329                 const gchar **contacts;
330
331                 account_value = g_value_array_get_nth (va, 0);
332                 contacts_value = g_value_array_get_nth (va, 1);
333
334                 account = g_value_get_boxed (account_value);
335                 contacts = g_value_get_boxed (contacts_value);
336
337                 add_contacts_to_favourites (manager, account, contacts);
338         }
339 }
340
341 static void
342 logger_favourite_contacts_get_cb (TpProxy         *proxy,
343                                   const GPtrArray *result,
344                                   const GError    *error,
345                                   gpointer         user_data,
346                                   GObject         *weak_object)
347 {
348         EmpathyContactManager *manager = EMPATHY_CONTACT_MANAGER (user_data);
349
350         if (error == NULL) {
351                 g_ptr_array_foreach ((GPtrArray *) result,
352                                 (GFunc)
353                                 logger_favourite_contacts_add_from_value_array,
354                                 manager);
355         } else {
356                 DEBUG ("Failed to get the FavouriteContacts property: %s",
357                                 error->message);
358         }
359 }
360
361 static void
362 logger_favourite_contacts_setup (EmpathyContactManager *manager)
363 {
364         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
365
366         emp_cli_logger_call_get_favourite_contacts (priv->logger, -1,
367                         logger_favourite_contacts_get_cb, manager, NULL,
368                         G_OBJECT (manager));
369 }
370
371 static void
372 contact_manager_finalize (GObject *object)
373 {
374         EmpathyContactManagerPriv *priv = GET_PRIV (object);
375
376         tp_proxy_signal_connection_disconnect (priv->favourite_contacts_changed_signal);
377
378         g_object_unref (priv->logger);
379
380         g_hash_table_foreach (priv->lists,
381                               contact_manager_disconnect_foreach,
382                               object);
383         g_hash_table_destroy (priv->lists);
384         g_hash_table_destroy (priv->favourites);
385
386         g_object_unref (priv->account_manager);
387
388         if (priv->contact_monitor) {
389                 g_object_unref (priv->contact_monitor);
390         }
391 }
392
393 static GObject *
394 contact_manager_constructor (GType type,
395                              guint n_props,
396                              GObjectConstructParam *props)
397 {
398         GObject *retval;
399
400         if (manager_singleton) {
401                 retval = g_object_ref (manager_singleton);
402         } else {
403                 retval = G_OBJECT_CLASS (empathy_contact_manager_parent_class)->constructor
404                         (type, n_props, props);
405
406                 manager_singleton = EMPATHY_CONTACT_MANAGER (retval);
407                 g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
408         }
409
410         return retval;
411 }
412
413 /**
414  * empathy_contact_manager_initialized:
415  *
416  * Reports whether or not the singleton has already been created.
417  *
418  * There can be instances where you want to access the #EmpathyContactManager
419  * only if it has been set up for this process.
420  *
421  * Returns: %TRUE if the #EmpathyContactManager singleton has previously
422  * been initialized.
423  */
424 gboolean
425 empathy_contact_manager_initialized (void)
426 {
427         return (manager_singleton != NULL);
428 }
429
430 static void
431 empathy_contact_manager_class_init (EmpathyContactManagerClass *klass)
432 {
433         GObjectClass *object_class = G_OBJECT_CLASS (klass);
434
435         object_class->finalize = contact_manager_finalize;
436         object_class->constructor = contact_manager_constructor;
437
438         g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv));
439 }
440
441 static void
442 account_manager_prepared_cb (GObject *source_object,
443                              GAsyncResult *result,
444                              gpointer user_data)
445 {
446         GList *accounts, *l;
447         EmpathyContactManager *manager = user_data;
448         TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
449         GError *error = NULL;
450
451         if (!tp_account_manager_prepare_finish (account_manager, result, &error)) {
452                 DEBUG ("Failed to prepare account manager: %s", error->message);
453                 g_error_free (error);
454                 return;
455         }
456
457         accounts = tp_account_manager_get_valid_accounts (account_manager);
458
459         for (l = accounts; l != NULL; l = l->next) {
460                 TpAccount *account = l->data;
461                 TpConnection *conn = tp_account_get_connection (account);
462
463                 if (conn != NULL) {
464                         contact_manager_status_changed_cb (account, 0, 0, 0,
465                                                            NULL, NULL, manager);
466                 }
467
468                 empathy_signal_connect_weak (account, "status-changed",
469                     G_CALLBACK (contact_manager_status_changed_cb),
470                     G_OBJECT (manager));
471         }
472         g_list_free (accounts);
473
474         empathy_signal_connect_weak (account_manager, "account-validity-changed",
475                              G_CALLBACK (contact_manager_validity_changed_cb),
476                              G_OBJECT (manager));
477 }
478
479 static EmpathyContact *
480 contact_manager_lookup_contact (EmpathyContactManager *manager,
481                                 const gchar           *account_name,
482                                 const gchar           *contact_id)
483 {
484         EmpathyContact *retval = NULL;
485         GList *members, *l;
486
487         /* XXX: any more efficient way to do this (other than having to build
488          * and maintain a hash)? */
489         members = empathy_contact_list_get_members (
490                         EMPATHY_CONTACT_LIST (manager));
491         for (l = members; l; l = l->next) {
492                 EmpathyContact *contact = l->data;
493                 TpAccount *account = empathy_contact_get_account (contact);
494                 const gchar *id_cur;
495                 const gchar *name_cur;
496
497                 id_cur = empathy_contact_get_id (contact);
498                 name_cur = tp_proxy_get_object_path (TP_PROXY (account));
499
500                 if (!tp_strdiff (contact_id, id_cur) &&
501                         !tp_strdiff (account_name, name_cur)) {
502                         retval = contact;
503                 }
504
505                 g_object_unref (contact);
506         }
507
508         g_list_free (members);
509
510         return retval;
511 }
512
513 static void
514 logger_favourite_contacts_changed_cb (TpProxy      *proxy,
515                                       const gchar  *account_name,
516                                       const gchar **added,
517                                       const gchar **removed,
518                                       gpointer      user_data,
519                                       GObject      *weak_object)
520 {
521         EmpathyContactManagerPriv *priv;
522         EmpathyContactManager *manager = EMPATHY_CONTACT_MANAGER (weak_object);
523         GHashTable *contact_hash;
524         EmpathyContact *contact;
525         gint i;
526
527         priv = GET_PRIV (manager);
528
529         contact_hash = g_hash_table_lookup (priv->favourites, account_name);
530
531         /* XXX: note that, at the time of this comment, there will always be
532          * exactly one contact amongst added and removed, so the linear lookup
533          * of each contact isn't as painful as it appears */
534
535         add_contacts_to_favourites (manager, account_name, added);
536
537         for (i = 0; added && added[i]; i++) {
538                 contact = contact_manager_lookup_contact (manager, account_name,
539                                                           added[i]);
540                 if (contact != NULL)
541                         g_signal_emit_by_name (manager, "favourites-changed",
542                                                contact, TRUE);
543                 else
544                         DEBUG ("failed to find contact for account %s, contact "
545                                "id %s", account_name, added[i]);
546         }
547
548         for (i = 0; removed && removed[i]; i++) {
549                 contact_hash = g_hash_table_lookup (priv->favourites,
550                                                     account_name);
551
552                 if (contact_hash != NULL) {
553                         g_hash_table_remove (contact_hash, removed[i]);
554
555                         if (g_hash_table_size (contact_hash) < 1) {
556                                 g_hash_table_remove (priv->favourites,
557                                                      account_name);
558                         }
559                 }
560
561                 contact = contact_manager_lookup_contact (manager, account_name,
562                                                           removed[i]);
563                 if (contact != NULL)
564                         g_signal_emit_by_name (manager, "favourites-changed",
565                                                contact, FALSE);
566                 else
567                         DEBUG ("failed to find contact for account %s, contact "
568                                "id %s", account_name, removed[i]);
569         }
570 }
571
572 static void
573 empathy_contact_manager_init (EmpathyContactManager *manager)
574 {
575         EmpathyContactManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
576                 EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManagerPriv);
577         TpDBusDaemon *bus;
578         GError *error = NULL;
579
580         manager->priv = priv;
581         priv->lists = g_hash_table_new_full (empathy_proxy_hash,
582                                              empathy_proxy_equal,
583                                              (GDestroyNotify) g_object_unref,
584                                              (GDestroyNotify) g_object_unref);
585
586         priv->favourites = g_hash_table_new_full (g_str_hash, g_str_equal,
587                                                   (GDestroyNotify) g_free,
588                                                   (GDestroyNotify)
589                                                   g_hash_table_unref);
590
591         priv->account_manager = tp_account_manager_dup ();
592         priv->contact_monitor = NULL;
593
594         tp_account_manager_prepare_async (priv->account_manager, NULL,
595             account_manager_prepared_cb, manager);
596
597         bus = tp_dbus_daemon_dup (&error);
598
599         if (error == NULL) {
600                 priv->logger = g_object_new (TP_TYPE_PROXY,
601                                 "bus-name", "org.freedesktop.Telepathy.Logger",
602                                 "object-path",
603                                         "/org/freedesktop/Telepathy/Logger",
604                                 "dbus-daemon", bus,
605                                 NULL);
606                 g_object_unref (bus);
607
608                 tp_proxy_add_interface_by_id (priv->logger,
609                                 EMP_IFACE_QUARK_LOGGER);
610
611                 logger_favourite_contacts_setup (manager);
612
613                 priv->favourite_contacts_changed_signal =
614                         emp_cli_logger_connect_to_favourite_contacts_changed (
615                                 priv->logger,
616                                 logger_favourite_contacts_changed_cb, NULL,
617                                 NULL, G_OBJECT (manager), NULL);
618         } else {
619                 DEBUG ("Failed to get telepathy-logger proxy: %s",
620                                 error->message);
621                 g_clear_error (&error);
622         }
623 }
624
625 EmpathyContactManager *
626 empathy_contact_manager_dup_singleton (void)
627 {
628         return g_object_new (EMPATHY_TYPE_CONTACT_MANAGER, NULL);
629 }
630
631 EmpathyTpContactList *
632 empathy_contact_manager_get_list (EmpathyContactManager *manager,
633                                   TpConnection          *connection)
634 {
635         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
636
637         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
638         g_return_val_if_fail (TP_IS_CONNECTION (connection), NULL);
639
640         return g_hash_table_lookup (priv->lists, connection);
641 }
642
643 static void
644 contact_manager_add (EmpathyContactList *manager,
645                      EmpathyContact     *contact,
646                      const gchar        *message)
647 {
648         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
649         EmpathyContactList        *list;
650         TpConnection              *connection;
651
652         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
653
654         connection = empathy_contact_get_connection (contact);
655         list = g_hash_table_lookup (priv->lists, connection);
656
657         if (list) {
658                 empathy_contact_list_add (list, contact, message);
659         }
660 }
661
662 static void
663 contact_manager_remove (EmpathyContactList *manager,
664                         EmpathyContact     *contact,
665                         const gchar        *message)
666 {
667         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
668         EmpathyContactList        *list;
669         TpConnection              *connection;
670
671         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
672
673         connection = empathy_contact_get_connection (contact);
674         list = g_hash_table_lookup (priv->lists, connection);
675
676         if (list) {
677                 empathy_contact_list_remove (list, contact, message);
678         }
679 }
680
681 static void
682 contact_manager_get_members_foreach (TpConnection          *connection,
683                                      EmpathyTpContactList  *list,
684                                      GList                **contacts)
685 {
686         GList *l;
687
688         l = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (list));
689         *contacts = g_list_concat (*contacts, l);
690 }
691
692 static GList *
693 contact_manager_get_members (EmpathyContactList *manager)
694 {
695         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
696         GList                     *contacts = NULL;
697
698         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
699
700         g_hash_table_foreach (priv->lists,
701                               (GHFunc) contact_manager_get_members_foreach,
702                               &contacts);
703
704         return contacts;
705 }
706
707 static EmpathyContactMonitor *
708 contact_manager_get_monitor (EmpathyContactList *manager)
709 {
710         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
711
712         if (priv->contact_monitor == NULL) {
713                 priv->contact_monitor = empathy_contact_monitor_new_for_iface (manager);
714         }
715
716         return priv->contact_monitor;
717 }
718
719 static void
720 contact_manager_get_pendings_foreach (TpConnection          *connection,
721                                       EmpathyTpContactList  *list,
722                                       GList                **contacts)
723 {
724         GList *l;
725
726         l = empathy_contact_list_get_pendings (EMPATHY_CONTACT_LIST (list));
727         *contacts = g_list_concat (*contacts, l);
728 }
729
730 static GList *
731 contact_manager_get_pendings (EmpathyContactList *manager)
732 {
733         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
734         GList                     *contacts = NULL;
735
736         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
737
738         g_hash_table_foreach (priv->lists,
739                               (GHFunc) contact_manager_get_pendings_foreach,
740                               &contacts);
741
742         return contacts;
743 }
744
745 static void
746 contact_manager_get_all_groups_foreach (TpConnection          *connection,
747                                         EmpathyTpContactList  *list,
748                                         GList                **all_groups)
749 {
750         GList *groups, *l;
751
752         groups = empathy_contact_list_get_all_groups (EMPATHY_CONTACT_LIST (list));
753         for (l = groups; l; l = l->next) {
754                 if (!g_list_find_custom (*all_groups,
755                                          l->data,
756                                          (GCompareFunc) strcmp)) {
757                         *all_groups = g_list_prepend (*all_groups, l->data);
758                 } else {
759                         g_free (l->data);
760                 }
761         }
762
763         g_list_free (groups);
764 }
765
766 static GList *
767 contact_manager_get_all_groups (EmpathyContactList *manager)
768 {
769         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
770         GList                     *groups = NULL;
771
772         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
773
774         g_hash_table_foreach (priv->lists,
775                               (GHFunc) contact_manager_get_all_groups_foreach,
776                               &groups);
777
778         return groups;
779 }
780
781 static GList *
782 contact_manager_get_groups (EmpathyContactList *manager,
783                             EmpathyContact     *contact)
784 {
785         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
786         EmpathyContactList        *list;
787         TpConnection              *connection;
788
789         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
790
791         connection = empathy_contact_get_connection (contact);
792         list = g_hash_table_lookup (priv->lists, connection);
793
794         if (list) {
795                 return empathy_contact_list_get_groups (list, contact);
796         }
797
798         return NULL;
799 }
800
801 static void
802 contact_manager_add_to_group (EmpathyContactList *manager,
803                               EmpathyContact     *contact,
804                               const gchar        *group)
805 {
806         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
807         EmpathyContactList        *list;
808         TpConnection              *connection;
809
810         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
811
812         connection = empathy_contact_get_connection (contact);
813         list = g_hash_table_lookup (priv->lists, connection);
814
815         if (list) {
816                 empathy_contact_list_add_to_group (list, contact, group);
817         }
818 }
819
820 static void
821 contact_manager_remove_from_group (EmpathyContactList *manager,
822                                    EmpathyContact     *contact,
823                                    const gchar        *group)
824 {
825         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
826         EmpathyContactList        *list;
827         TpConnection              *connection;
828
829         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
830
831         connection = empathy_contact_get_connection (contact);
832         list = g_hash_table_lookup (priv->lists, connection);
833
834         if (list) {
835                 empathy_contact_list_remove_from_group (list, contact, group);
836         }
837 }
838
839 typedef struct {
840         const gchar *old_group;
841         const gchar *new_group;
842 } RenameGroupData;
843
844 static void
845 contact_manager_rename_group_foreach (TpConnection         *connection,
846                                       EmpathyTpContactList *list,
847                                       RenameGroupData      *data)
848 {
849         empathy_contact_list_rename_group (EMPATHY_CONTACT_LIST (list),
850                                            data->old_group,
851                                            data->new_group);
852 }
853
854 static void
855 contact_manager_rename_group (EmpathyContactList *manager,
856                               const gchar        *old_group,
857                               const gchar        *new_group)
858 {
859         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
860         RenameGroupData            data;
861
862         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
863
864         data.old_group = old_group;
865         data.new_group = new_group;
866         g_hash_table_foreach (priv->lists,
867                               (GHFunc) contact_manager_rename_group_foreach,
868                               &data);
869 }
870
871 static void contact_manager_remove_group_foreach (TpConnection         *connection,
872                                                   EmpathyTpContactList *list,
873                                                   const gchar *group)
874 {
875         empathy_contact_list_remove_group (EMPATHY_CONTACT_LIST (list),
876                                            group);
877 }
878
879 static void
880 contact_manager_remove_group (EmpathyContactList *manager,
881                               const gchar *group)
882 {
883         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
884
885         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
886
887         g_hash_table_foreach (priv->lists,
888                               (GHFunc) contact_manager_remove_group_foreach,
889                               (gpointer) group);
890 }
891
892 static void
893 contact_manager_iface_init (EmpathyContactListIface *iface)
894 {
895         iface->add               = contact_manager_add;
896         iface->remove            = contact_manager_remove;
897         iface->get_members       = contact_manager_get_members;
898         iface->get_monitor       = contact_manager_get_monitor;
899         iface->get_pendings      = contact_manager_get_pendings;
900         iface->get_all_groups    = contact_manager_get_all_groups;
901         iface->get_groups        = contact_manager_get_groups;
902         iface->add_to_group      = contact_manager_add_to_group;
903         iface->remove_from_group = contact_manager_remove_from_group;
904         iface->rename_group      = contact_manager_rename_group;
905         iface->remove_group      = contact_manager_remove_group;
906         iface->is_favourite      = contact_manager_is_favourite;
907         iface->remove_favourite  = contact_manager_remove_favourite;
908         iface->add_favourite     = contact_manager_add_favourite;
909 }
910
911 EmpathyContactListFlags
912 empathy_contact_manager_get_flags_for_connection (
913                                 EmpathyContactManager *manager,
914                                 TpConnection          *connection)
915 {
916         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
917         EmpathyContactList        *list;
918         EmpathyContactListFlags    flags;
919
920         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), FALSE);
921         g_return_val_if_fail (connection != NULL, FALSE);
922
923         list = g_hash_table_lookup (priv->lists, connection);
924         if (list == NULL) {
925                 return FALSE;
926         }
927         flags = empathy_contact_list_get_flags (list);
928
929         return flags;
930 }
931