]> git.0d.be Git - empathy.git/blob - libempathy/empathy-connection-aggregator.c
include telepathy-glib.h
[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 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
26 #include "empathy-debug.h"
27 #include "empathy-utils.h"
28
29
30 #include "extensions/extensions.h"
31
32 G_DEFINE_TYPE (EmpathyConnectionAggregator, empathy_connection_aggregator,
33     G_TYPE_OBJECT);
34
35 enum {
36   EVENT_CONTACT_LIST_CHANGED,
37   LAST_SIGNAL
38 };
39
40 static guint signals[LAST_SIGNAL];
41
42 struct _EmpathyConnectionAggregatorPriv {
43   TpAccountManager *mgr;
44
45   /* List of owned TpConnection */
46   GList *conns;
47 };
48
49 static void
50 empathy_connection_aggregator_dispose (GObject *object)
51 {
52   EmpathyConnectionAggregator *self = (EmpathyConnectionAggregator *) object;
53
54   g_clear_object (&self->priv->mgr);
55
56   g_list_free_full (self->priv->conns, g_object_unref);
57   self->priv->conns = NULL;
58
59   G_OBJECT_CLASS (empathy_connection_aggregator_parent_class)->dispose (object);
60 }
61
62 static void
63 empathy_connection_aggregator_class_init (
64     EmpathyConnectionAggregatorClass *klass)
65 {
66   GObjectClass *oclass = G_OBJECT_CLASS (klass);
67
68   oclass->dispose = empathy_connection_aggregator_dispose;
69
70   signals[EVENT_CONTACT_LIST_CHANGED] =
71     g_signal_new ("contact-list-changed",
72       G_TYPE_FROM_CLASS (klass),
73       G_SIGNAL_RUN_LAST,
74       0,
75       NULL, NULL,
76       g_cclosure_marshal_generic,
77       G_TYPE_NONE,
78       2, G_TYPE_PTR_ARRAY, G_TYPE_PTR_ARRAY);
79
80   g_type_class_add_private (klass, sizeof (EmpathyConnectionAggregatorPriv));
81 }
82
83 static void
84 contact_list_changed_cb (TpConnection *conn,
85     GPtrArray *added,
86     GPtrArray *removed,
87     EmpathyConnectionAggregator *self)
88 {
89   g_signal_emit (self, signals[EVENT_CONTACT_LIST_CHANGED], 0, added, removed);
90 }
91
92 static void
93 conn_invalidated_cb (TpConnection *conn,
94     guint domain,
95     gint code,
96     gchar *message,
97     EmpathyConnectionAggregator *self)
98 {
99   self->priv->conns = g_list_remove (self->priv->conns, conn);
100
101   g_object_unref (conn);
102 }
103
104 static void
105 check_connection (EmpathyConnectionAggregator *self,
106     TpConnection *conn)
107 {
108   GPtrArray *contacts;
109
110   if (g_list_find (self->priv->conns, conn) != NULL)
111     return;
112
113   self->priv->conns = g_list_prepend (self->priv->conns,
114       g_object_ref (conn));
115
116   tp_g_signal_connect_object (conn, "contact-list-changed",
117       G_CALLBACK (contact_list_changed_cb), self, 0);
118
119   contacts = tp_connection_dup_contact_list (conn);
120   if (contacts != NULL)
121     {
122       GPtrArray *empty;
123
124       empty = g_ptr_array_new ();
125
126       contact_list_changed_cb (conn, contacts, empty, self);
127       g_ptr_array_unref (empty);
128     }
129   g_ptr_array_unref (contacts);
130
131   tp_g_signal_connect_object (conn, "invalidated",
132       G_CALLBACK (conn_invalidated_cb), self, 0);
133 }
134
135 static void
136 check_account (EmpathyConnectionAggregator *self,
137     TpAccount *account)
138 {
139   TpConnection *conn;
140
141   conn = tp_account_get_connection (account);
142   if (conn != NULL)
143     check_connection (self, conn);
144 }
145
146 static void
147 account_conn_changed_cb (TpAccount *account,
148     GParamSpec *spec,
149     EmpathyConnectionAggregator *self)
150 {
151   check_account (self, account);
152 }
153
154 static void
155 add_account (EmpathyConnectionAggregator *self,
156     TpAccount *account)
157 {
158   check_account (self, account);
159
160   tp_g_signal_connect_object (account, "notify::connection",
161       G_CALLBACK (account_conn_changed_cb), self, 0);
162 }
163
164 static void
165 account_validity_changed_cb (TpAccountManager *manager,
166     TpAccount *account,
167     gboolean valid,
168     EmpathyConnectionAggregator *self)
169 {
170   if (valid)
171     add_account (self, account);
172 }
173
174 static void
175 am_prepare_cb (GObject *source,
176     GAsyncResult *result,
177     gpointer user_data)
178 {
179   EmpathyConnectionAggregator *self = EMPATHY_CONNECTION_AGGREGATOR (user_data);
180   GError *error = NULL;
181   GList *accounts, *l;
182
183   if (!tp_proxy_prepare_finish (source, result, &error))
184     {
185       DEBUG ("Failed to prepare account manager: %s", error->message);
186       g_error_free (error);
187       goto out;
188     }
189
190   accounts = tp_account_manager_dup_valid_accounts (self->priv->mgr);
191   for (l = accounts; l != NULL; l = g_list_next (l))
192     {
193       TpAccount *account = l->data;
194
195       add_account (self, account);
196     }
197
198   tp_g_signal_connect_object (self->priv->mgr, "account-validity-changed",
199       G_CALLBACK (account_validity_changed_cb), self, 0);
200
201   g_list_free_full (accounts, g_object_unref);
202
203 out:
204   g_object_unref (self);
205 }
206
207 static void
208 empathy_connection_aggregator_init (EmpathyConnectionAggregator *self)
209 {
210   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
211       EMPATHY_TYPE_CONNECTION_AGGREGATOR, EmpathyConnectionAggregatorPriv);
212
213   self->priv->mgr = tp_account_manager_dup ();
214
215   tp_proxy_prepare_async (self->priv->mgr, NULL, am_prepare_cb,
216       g_object_ref (self));
217 }
218
219 EmpathyConnectionAggregator *
220 empathy_connection_aggregator_dup_singleton (void)
221 {
222   static EmpathyConnectionAggregator *aggregator = NULL;
223
224   if (G_LIKELY (aggregator != NULL))
225       return g_object_ref (aggregator);
226
227   aggregator = g_object_new (EMPATHY_TYPE_CONNECTION_AGGREGATOR, NULL);
228
229   g_object_add_weak_pointer (G_OBJECT (aggregator), (gpointer *) &aggregator);
230   return aggregator;
231 }
232
233 /* (transfer container) */
234 GList *
235 empathy_connection_aggregator_get_all_groups (EmpathyConnectionAggregator *self)
236 {
237   GList *keys, *l;
238   GHashTable *set;
239
240   set = g_hash_table_new (g_str_hash, g_str_equal);
241
242   for (l = self->priv->conns; l != NULL; l = g_list_next (l))
243     {
244       TpConnection *conn = l->data;
245       const gchar * const *groups;
246       guint i;
247
248       groups = tp_connection_get_contact_groups (conn);
249       if (groups == NULL)
250         continue;
251
252       for (i = 0; groups[i] != NULL; i++)
253         g_hash_table_insert (set, (gchar *) groups[i], GUINT_TO_POINTER (TRUE));
254     }
255
256   keys = g_hash_table_get_keys (set);
257   g_hash_table_unref (set);
258
259   return keys;
260 }
261
262 GPtrArray *
263 empathy_connection_aggregator_dup_all_contacts (
264     EmpathyConnectionAggregator *self)
265 {
266   GPtrArray *result;
267   GList *l;
268
269   result = g_ptr_array_new_with_free_func (g_object_unref);
270
271   for (l = self->priv->conns; l != NULL; l = g_list_next (l))
272     {
273       TpConnection *conn = l->data;
274       GPtrArray *contacts;
275
276       contacts = tp_connection_dup_contact_list (conn);
277       if (contacts == NULL)
278         continue;
279
280       tp_g_ptr_array_extend (result, contacts);
281
282       /* tp_g_ptr_array_extend() doesn't give us an extra ref */
283       g_ptr_array_foreach (contacts, (GFunc) g_object_ref, NULL);
284
285       g_ptr_array_unref (contacts);
286     }
287
288   return result;
289 }
290
291 static void
292 rename_group_cb (GObject *source,
293     GAsyncResult *result,
294     gpointer user_data)
295 {
296   GError *error = NULL;
297
298   if (!tp_connection_rename_group_finish (TP_CONNECTION (source), result,
299         &error))
300     {
301       DEBUG ("Failed to rename group on %s: %s",
302           tp_proxy_get_object_path (source), error->message);
303       g_error_free (error);
304     }
305 }
306
307 void
308 empathy_connection_aggregator_rename_group (EmpathyConnectionAggregator *self,
309     const gchar *old_name,
310     const gchar *new_name)
311 {
312   GList *l;
313
314   for (l = self->priv->conns; l != NULL; l = g_list_next (l))
315     {
316       TpConnection *conn = l->data;
317       const gchar * const *groups;
318
319       groups = tp_connection_get_contact_groups (conn);
320
321       if (!tp_strv_contains (groups, old_name))
322         continue;
323
324       DEBUG ("Rename group '%s' to '%s' on %s", old_name, new_name,
325           tp_proxy_get_object_path (conn));
326
327       tp_connection_rename_group_async (conn, old_name, new_name,
328           rename_group_cb, NULL);
329     }
330 }