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