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