use Azimuth object
[azimuth] / src / connection-watcher.c
1 /*
2  * connection-watcher.c - Source for ConnectionWatcher
3  * Copyright (C) 2010 Guillaume Desmottes
4  * @author Guillaume Desmottes <gdesmott@gnome.org>
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
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <telepathy-glib/account-manager.h>
26 #include <telepathy-glib/account.h>
27 #include <telepathy-glib/dbus.h>
28 #include <telepathy-glib/interfaces.h>
29 #include <telepathy-glib/gtypes.h>
30 #include <telepathy-glib/util.h>
31
32 #include "connection-watcher.h"
33
34 G_DEFINE_TYPE(ConnectionWatcher, connection_watcher, G_TYPE_OBJECT)
35
36 /* signal enum */
37 enum
38 {
39     CONNECTION_ADDED,
40     LAST_SIGNAL
41 };
42
43 static guint signals[LAST_SIGNAL] = {0};
44
45 /* private structure */
46 typedef struct _ConnectionWatcherPrivate ConnectionWatcherPrivate;
47
48 struct _ConnectionWatcherPrivate
49 {
50   TpAccountManager *account_mgr;
51   TpDBusDaemon *bus_daemon;
52   GList *accounts;
53
54   gboolean dispose_has_run;
55 };
56
57 #define CONNECTION_WATCHER_GET_PRIVATE(o)     (G_TYPE_INSTANCE_GET_PRIVATE ((o), CONNECTION_WATCHER_TYPE, ConnectionWatcherPrivate))
58
59 static void
60 connection_watcher_init (ConnectionWatcher *obj)
61 {
62   ConnectionWatcherPrivate *priv = CONNECTION_WATCHER_GET_PRIVATE (obj);
63
64   priv->bus_daemon = tp_dbus_daemon_dup (NULL);
65   g_assert (priv->bus_daemon != NULL);
66
67   priv->account_mgr = tp_account_manager_new (priv->bus_daemon);
68   priv->accounts = NULL;
69 }
70
71 static void connection_watcher_dispose (GObject *object);
72 static void connection_watcher_finalize (GObject *object);
73
74 static void
75 connection_watcher_class_init (ConnectionWatcherClass *connection_watcher_class)
76 {
77   GObjectClass *object_class = G_OBJECT_CLASS (connection_watcher_class);
78
79   g_type_class_add_private (connection_watcher_class, sizeof (ConnectionWatcherPrivate));
80
81   object_class->dispose = connection_watcher_dispose;
82   object_class->finalize = connection_watcher_finalize;
83
84   signals[CONNECTION_ADDED] = g_signal_new ("connection-added",
85     G_TYPE_FROM_CLASS (object_class),
86     G_SIGNAL_RUN_LAST,
87     0, NULL, NULL,
88     g_cclosure_marshal_VOID__OBJECT,
89     G_TYPE_NONE, 1, TP_TYPE_CONNECTION);
90 }
91
92 void
93 connection_watcher_dispose (GObject *object)
94 {
95   ConnectionWatcher *self = CONNECTION_WATCHER (object);
96   ConnectionWatcherPrivate *priv = CONNECTION_WATCHER_GET_PRIVATE (self);
97   GList *l;
98
99   if (priv->dispose_has_run)
100     return;
101
102   priv->dispose_has_run = TRUE;
103
104   g_object_unref (priv->account_mgr);
105
106   for (l = priv->accounts; l != NULL; l = g_list_next (l))
107     {
108       g_object_unref (l->data);
109     }
110
111   g_object_unref (priv->bus_daemon);
112
113   if (G_OBJECT_CLASS (connection_watcher_parent_class)->dispose)
114     G_OBJECT_CLASS (connection_watcher_parent_class)->dispose (object);
115 }
116
117 void
118 connection_watcher_finalize (GObject *object)
119 {
120   ConnectionWatcher *self = CONNECTION_WATCHER (object);
121   ConnectionWatcherPrivate *priv = CONNECTION_WATCHER_GET_PRIVATE (self);
122
123   g_list_free (priv->accounts);
124
125   G_OBJECT_CLASS (connection_watcher_parent_class)->finalize (object);
126 }
127
128 ConnectionWatcher *
129 connection_watcher_new (void)
130 {
131   return g_object_new (CONNECTION_WATCHER_TYPE,
132       NULL);
133 }
134
135 static void
136 conn_ready_cb (TpConnection *conn,
137     const GError *error,
138     gpointer user_data)
139 {
140   ConnectionWatcher *self = CONNECTION_WATCHER (user_data);
141
142   if (error != NULL)
143     {
144       g_print ("connection is not ready: %s\n", error->message);
145       goto out;
146     }
147
148   g_signal_emit (self, signals[CONNECTION_ADDED], 0, conn);
149
150 out:
151   g_object_unref (conn);
152 }
153
154 static void
155 account_invalidated_cb (TpProxy *proxy,
156     guint domain,
157     gint code,
158     gchar *message,
159     gpointer user_data)
160 {
161   ConnectionWatcher *self = CONNECTION_WATCHER (user_data);
162   ConnectionWatcherPrivate *priv = CONNECTION_WATCHER_GET_PRIVATE (self);
163
164   g_print ("remove invalidated account: %s\n",
165       tp_proxy_get_object_path (proxy));
166
167   priv->accounts = g_list_remove (priv->accounts, proxy);
168   g_object_unref (proxy);
169 }
170
171 static void
172 create_connection (ConnectionWatcher *self,
173     const gchar *path)
174 {
175   ConnectionWatcherPrivate *priv = CONNECTION_WATCHER_GET_PRIVATE (self);
176   TpConnection *conn;
177   GError *err = NULL;
178
179   if (path == NULL || !tp_strdiff (path, "/"))
180     return;
181
182   conn = tp_connection_new (priv->bus_daemon, NULL, path, &err);
183   if (conn == NULL)
184     {
185       g_print ("Failed to create TpConnection: %s\n", err->message);
186       g_error_free (err);
187       return;
188     }
189
190   tp_connection_call_when_ready (conn, conn_ready_cb, self);
191 }
192
193 static void
194 get_connection_cb (TpProxy *account,
195     const GValue *out_Value,
196     const GError *error,
197     gpointer user_data,
198     GObject *weak_object)
199 {
200   ConnectionWatcher *self = CONNECTION_WATCHER (weak_object);
201   ConnectionWatcherPrivate *priv = CONNECTION_WATCHER_GET_PRIVATE (self);
202
203   if (error != NULL)
204     {
205       g_print ("Failed to fetch Connection property: %s\n", error->message);
206       priv->accounts = g_list_remove (priv->accounts, account);
207       g_object_unref (account);
208       return;
209     }
210
211   create_connection (self, g_value_get_boxed (out_Value));
212 }
213
214 static void
215 account_property_changed_cb (TpAccount *account,
216     GHashTable *properties,
217     gpointer user_data,
218     GObject *weak_object)
219 {
220   ConnectionWatcher *self = CONNECTION_WATCHER (weak_object);
221
222   create_connection (self, tp_asv_get_object_path (properties, "Connection"));
223 }
224
225 static void
226 add_account (ConnectionWatcher *self,
227     TpAccount *account)
228 {
229   ConnectionWatcherPrivate *priv = CONNECTION_WATCHER_GET_PRIVATE (self);
230
231   priv->accounts = g_list_prepend (priv->accounts, account);
232
233   tp_cli_dbus_properties_call_get (account, -1,
234       TP_IFACE_ACCOUNT, "Connection", get_connection_cb,
235       self, NULL, G_OBJECT (self));
236
237   tp_cli_account_connect_to_account_property_changed (account,
238       account_property_changed_cb, self, NULL, G_OBJECT (self), NULL);
239
240   g_signal_connect (account, "invalidated",
241       G_CALLBACK (account_invalidated_cb), self);
242 }
243
244 static void
245 account_validity_changed_cb (TpAccountManager *account_mgr,
246     const gchar *account_path,
247     gboolean valid,
248     gpointer user_data,
249     GObject *weak_object)
250 {
251   ConnectionWatcher *self = CONNECTION_WATCHER (weak_object);
252   ConnectionWatcherPrivate *priv = CONNECTION_WATCHER_GET_PRIVATE (self);
253   GError *err = NULL;
254   TpAccount *account;
255
256   account = tp_account_new (priv->bus_daemon, account_path, &err);
257   if (account == NULL)
258     {
259       g_print ("Failed to create TpAccount: %s\n", err->message);
260       g_error_free (err);
261       return;
262     }
263
264   if (g_list_find (priv->accounts, account) != NULL)
265     return;
266
267   add_account (self, account);
268 }
269
270 static void
271 get_valid_accounts_cb (TpProxy *proxy,
272     const GValue *out_Value,
273     const GError *error,
274     gpointer user_data,
275     GObject *weak_object)
276 {
277   ConnectionWatcher *self = CONNECTION_WATCHER (weak_object);
278   ConnectionWatcherPrivate *priv = CONNECTION_WATCHER_GET_PRIVATE (self);
279   GPtrArray *valid_accounts;
280   guint i;
281
282   if (error != NULL)
283     {
284       g_print ("Failed to fetch ValidAccounts property: %s\n", error->message);
285       return;
286     }
287
288   valid_accounts = g_value_get_boxed (out_Value);
289   if (valid_accounts == NULL)
290     return;
291
292   for (i = 0; i < valid_accounts->len; i++)
293     {
294       const gchar *name;
295       TpAccount *account;
296       GError *err = NULL;
297
298       name = g_ptr_array_index (valid_accounts, i);
299       account = tp_account_new (priv->bus_daemon, name, &err);
300       if (account == NULL)
301         {
302           g_print ("Failed to create TpAccount: %s\n", err->message);
303           g_error_free (err);
304           continue;
305         }
306
307       add_account (self, account);
308     }
309 }
310
311 void
312 connection_watcher_start (ConnectionWatcher *self)
313 {
314   ConnectionWatcherPrivate *priv = CONNECTION_WATCHER_GET_PRIVATE (self);
315
316   tp_cli_dbus_properties_call_get (priv->account_mgr, -1,
317       TP_IFACE_ACCOUNT_MANAGER, "ValidAccounts", get_valid_accounts_cb,
318       self, NULL, G_OBJECT (self));
319
320   tp_cli_account_manager_connect_to_account_validity_changed (priv->account_mgr,
321       account_validity_changed_cb, NULL, NULL, G_OBJECT (self), NULL);
322 }