]> git.0d.be Git - empathy.git/blob - libempathy/empathy-connection-aggregator.c
5253203067a94d57277bac968127f32b5bc5e02f
[empathy.git] / libempathy / empathy-connection-aggregator.c
1 /*
2  * empathy-connection-aggregator.c - Source for EmpathyConnectionAggregator
3  * Copyright (C) 2010 Collabora Ltd.
4  * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 #include <config.h>
22
23 #include "empathy-connection-aggregator.h"
24
25 #include <telepathy-glib/telepathy-glib.h>
26
27 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
28 #include "empathy-debug.h"
29 #include "empathy-utils.h"
30
31
32 #include "extensions/extensions.h"
33
34 G_DEFINE_TYPE (EmpathyConnectionAggregator, empathy_connection_aggregator,
35     G_TYPE_OBJECT);
36
37 enum {
38   EVENT_CONTACT_LIST_CHANGED,
39   LAST_SIGNAL
40 };
41
42 static guint signals[LAST_SIGNAL];
43
44 struct _EmpathyConnectionAggregatorPriv {
45   TpAccountManager *mgr;
46
47   /* List of owned TpConnection */
48   GList *conns;
49 };
50
51 static void
52 empathy_connection_aggregator_dispose (GObject *object)
53 {
54   EmpathyConnectionAggregator *self = (EmpathyConnectionAggregator *) object;
55
56   g_clear_object (&self->priv->mgr);
57
58   g_list_free_full (self->priv->conns, g_object_unref);
59   self->priv->conns = NULL;
60
61   G_OBJECT_CLASS (empathy_connection_aggregator_parent_class)->dispose (object);
62 }
63
64 static void
65 empathy_connection_aggregator_class_init (
66     EmpathyConnectionAggregatorClass *klass)
67 {
68   GObjectClass *oclass = G_OBJECT_CLASS (klass);
69
70   oclass->dispose = empathy_connection_aggregator_dispose;
71
72   signals[EVENT_CONTACT_LIST_CHANGED] =
73     g_signal_new ("contact-list-changed",
74       G_TYPE_FROM_CLASS (klass),
75       G_SIGNAL_RUN_LAST,
76       0,
77       NULL, NULL,
78       g_cclosure_marshal_generic,
79       G_TYPE_NONE,
80       3, TP_TYPE_CONNECTION, G_TYPE_PTR_ARRAY, G_TYPE_PTR_ARRAY);
81
82   g_type_class_add_private (klass, sizeof (EmpathyConnectionAggregatorPriv));
83 }
84
85 static void
86 contact_list_changed_cb (TpConnection *conn,
87     GPtrArray *added,
88     GPtrArray *removed,
89     EmpathyConnectionAggregator *self)
90 {
91   g_signal_emit (self, signals[EVENT_CONTACT_LIST_CHANGED], 0, conn,
92       added, removed);
93 }
94
95 static void
96 conn_invalidated_cb (TpConnection *conn,
97     guint domain,
98     gint code,
99     gchar *message,
100     EmpathyConnectionAggregator *self)
101 {
102   self->priv->conns = g_list_remove (self->priv->conns, conn);
103
104   g_object_unref (conn);
105 }
106
107 static void
108 check_connection (EmpathyConnectionAggregator *self,
109     TpConnection *conn)
110 {
111   GPtrArray *contacts;
112
113   if (g_list_find (self->priv->conns, conn) != NULL)
114     return;
115
116   self->priv->conns = g_list_prepend (self->priv->conns,
117       g_object_ref (conn));
118
119   tp_g_signal_connect_object (conn, "contact-list-changed",
120       G_CALLBACK (contact_list_changed_cb), self, 0);
121
122   contacts = tp_connection_dup_contact_list (conn);
123   if (contacts != NULL)
124     {
125       GPtrArray *empty;
126
127       empty = g_ptr_array_new ();
128
129       contact_list_changed_cb (conn, contacts, empty, self);
130       g_ptr_array_unref (empty);
131     }
132
133   tp_g_signal_connect_object (conn, "invalidated",
134       G_CALLBACK (conn_invalidated_cb), self, 0);
135 }
136
137 static void
138 check_account (EmpathyConnectionAggregator *self,
139     TpAccount *account)
140 {
141   TpConnection *conn;
142
143   conn = tp_account_get_connection (account);
144   if (conn != NULL)
145     check_connection (self, conn);
146 }
147
148 static void
149 account_conn_changed_cb (TpAccount *account,
150     GParamSpec *spec,
151     EmpathyConnectionAggregator *self)
152 {
153   check_account (self, account);
154 }
155
156 static void
157 add_account (EmpathyConnectionAggregator *self,
158     TpAccount *account)
159 {
160   check_account (self, account);
161
162   tp_g_signal_connect_object (account, "notify::connection",
163       G_CALLBACK (account_conn_changed_cb), self, 0);
164 }
165
166 static void
167 account_validity_changed_cb (TpAccountManager *manager,
168     TpAccount *account,
169     gboolean valid,
170     EmpathyConnectionAggregator *self)
171 {
172   if (valid)
173     add_account (self, account);
174 }
175
176 static void
177 am_prepare_cb (GObject *source,
178     GAsyncResult *result,
179     gpointer user_data)
180 {
181   EmpathyConnectionAggregator *self = EMPATHY_CONNECTION_AGGREGATOR (user_data);
182   GError *error = NULL;
183   GList *accounts, *l;
184
185   if (!tp_proxy_prepare_finish (source, result, &error))
186     {
187       DEBUG ("Failed to prepare account manager: %s", error->message);
188       g_error_free (error);
189       goto out;
190     }
191
192   accounts = tp_account_manager_get_valid_accounts (self->priv->mgr);
193   for (l = accounts; l != NULL; l = g_list_next (l))
194     {
195       TpAccount *account = l->data;
196
197       add_account (self, account);
198     }
199
200   tp_g_signal_connect_object (self->priv->mgr, "account-validity-changed",
201       G_CALLBACK (account_validity_changed_cb), self, 0);
202
203   g_list_free (accounts);
204
205 out:
206   g_object_unref (self);
207 }
208
209 static void
210 empathy_connection_aggregator_init (EmpathyConnectionAggregator *self)
211 {
212   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
213       EMPATHY_TYPE_CONNECTION_AGGREGATOR, EmpathyConnectionAggregatorPriv);
214
215   self->priv->mgr = tp_account_manager_dup ();
216
217   tp_proxy_prepare_async (self->priv->mgr, NULL, am_prepare_cb,
218       g_object_ref (self));
219 }
220
221 EmpathyConnectionAggregator *
222 empathy_connection_aggregator_dup_singleton (void)
223 {
224   static EmpathyConnectionAggregator *aggregator = NULL;
225
226   if (G_LIKELY (aggregator != NULL))
227       return g_object_ref (aggregator);
228
229   aggregator = g_object_new (EMPATHY_TYPE_CONNECTION_AGGREGATOR, NULL);
230
231   g_object_add_weak_pointer (G_OBJECT (aggregator), (gpointer *) &aggregator);
232   return aggregator;
233 }
234
235 GList *
236 empathy_connection_aggregator_get_all_groups (EmpathyConnectionAggregator *self)
237 {
238   GList *keys, *l;
239   GHashTable *set;
240
241   set = g_hash_table_new (g_str_hash, g_str_equal);
242
243   for (l = self->priv->conns; l != NULL; l = g_list_next (l))
244     {
245       TpConnection *conn = l->data;
246       const gchar * const *groups;
247       guint i;
248
249       groups = tp_connection_get_contact_groups (conn);
250       if (groups == NULL)
251         continue;
252
253       for (i = 0; groups[i] != NULL; i++)
254         g_hash_table_insert (set, (gchar *) groups[i], GUINT_TO_POINTER (TRUE));
255     }
256
257   keys = g_hash_table_get_keys (set);
258   g_hash_table_unref (set);
259
260   return keys;
261 }
262
263 GPtrArray *
264 empathy_connection_aggregator_dup_all_contacts (
265     EmpathyConnectionAggregator *self)
266 {
267   GPtrArray *result;
268   GList *l;
269
270   result = g_ptr_array_new_with_free_func (g_object_unref);
271
272   for (l = self->priv->conns; l != NULL; l = g_list_next (l))
273     {
274       TpConnection *conn = l->data;
275       GPtrArray *contacts;
276
277       contacts = tp_connection_dup_contact_list (conn);
278       if (contacts == NULL)
279         continue;
280
281       tp_g_ptr_array_extend (result, contacts);
282
283       /* tp_g_ptr_array_extend() doesn't give us an extra ref */
284       g_ptr_array_foreach (contacts, (GFunc) g_object_ref, NULL);
285
286       g_ptr_array_unref (contacts);
287     }
288
289   return result;
290 }