1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
3 /*
4 * This file is part of mission-control
5 *
6 * Copyright © 2008-2009 Nokia Corporation.
7 * Copyright © 2009-2010 Collabora Ltd.
8 *
9 * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * version 2.1 as published by the Free Software Foundation.
14 *
15 * This library is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23 * 02110-1301 USA
24 *
25 */
26 #include "config.h"
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <glib/gstdio.h>
31
32 #include <dbus/dbus-glib-lowlevel.h>
33
34 #include <telepathy-glib/telepathy-glib.h>
35 #include <telepathy-glib/telepathy-glib-dbus.h>
36
37 #include "mcd-account.h"
38 #include "mcd-account-priv.h"
39 #include "mcd-account-manager.h"
40 #include "mcd-dispatcher-priv.h"
41 #include "mcd-channel-priv.h"
42 #include "mcd-misc.h"
43 #include "request.h"
44
45 static void
online_request_cb(McdAccount * account,gpointer userdata,const GError * error)46 online_request_cb (McdAccount *account, gpointer userdata, const GError *error)
47 {
48 McdChannel *channel = MCD_CHANNEL (userdata);
49 McdConnection *connection;
50
51 if (error)
52 {
53 g_warning ("got error: %s", error->message);
54 mcd_channel_take_error (channel, g_error_copy (error));
55 g_object_unref (channel);
56 return;
57 }
58 DEBUG ("called");
59 connection = mcd_account_get_connection (account);
60 g_return_if_fail (connection != NULL);
61 g_return_if_fail (mcd_account_get_connection_status (account)
62 == TP_CONNECTION_STATUS_CONNECTED);
63
64 if (mcd_channel_get_status (channel) == MCD_CHANNEL_STATUS_FAILED)
65 {
66 DEBUG ("channel %p is failed", channel);
67 g_object_unref (channel);
68 return;
69 }
70
71 /* the connection will take ownership of the channel if and only if it
72 * has no parent; we expect it to have no parent, and the connection will
73 * become its parent */
74 g_assert (mcd_mission_get_parent ((McdMission *) channel) == NULL);
75 mcd_connection_request_channel (connection, channel);
76 }
77
78 static void mcd_account_channel_request_disconnect (McdRequest *request);
79
80 static void
on_request_succeeded_with_channel(McdRequest * request,const gchar * conn_path,GHashTable * conn_props,const gchar * chan_path,GHashTable * chan_props,McdChannel * channel)81 on_request_succeeded_with_channel (McdRequest *request,
82 const gchar *conn_path,
83 GHashTable *conn_props,
84 const gchar *chan_path,
85 GHashTable *chan_props,
86 McdChannel *channel)
87 {
88 mcd_account_channel_request_disconnect (request);
89 }
90
91 static void
on_request_failed(McdRequest * request,const gchar * err_string,const gchar * message,McdChannel * channel)92 on_request_failed (McdRequest *request,
93 const gchar *err_string,
94 const gchar *message,
95 McdChannel *channel)
96 {
97 g_warning ("Channel request %s failed, error: %s",
98 _mcd_request_get_object_path (request), message);
99
100 mcd_account_channel_request_disconnect (request);
101 }
102
103 static void ready_to_request_cb (McdRequest *request, McdChannel *channel);
104
105 static void
mcd_account_channel_request_disconnect(McdRequest * request)106 mcd_account_channel_request_disconnect (McdRequest *request)
107 {
108 g_signal_handlers_disconnect_matched (request, G_SIGNAL_MATCH_FUNC,
109 0, /* signal_id ignored */
110 0, /* detail ignored */
111 NULL, /* closure ignored */
112 on_request_failed,
113 NULL /* user data ignored */);
114 g_signal_handlers_disconnect_matched (request, G_SIGNAL_MATCH_FUNC,
115 0, /* signal_id ignored */
116 0, /* detail ignored */
117 NULL, /* closure ignored */
118 on_request_succeeded_with_channel,
119 NULL /* user data ignored */);
120 g_signal_handlers_disconnect_matched (request, G_SIGNAL_MATCH_FUNC,
121 0, /* signal_id ignored */
122 0, /* detail ignored */
123 NULL, /* closure ignored */
124 ready_to_request_cb,
125 NULL /* user data ignored */);
126 }
127
128 McdChannel *
_mcd_account_create_request(McdClientRegistry * clients,McdAccount * account,GHashTable * properties,gint64 user_time,const gchar * preferred_handler,GHashTable * hints,gboolean use_existing,McdRequest ** request_out,GError ** error)129 _mcd_account_create_request (McdClientRegistry *clients,
130 McdAccount *account, GHashTable *properties,
131 gint64 user_time, const gchar *preferred_handler,
132 GHashTable *hints, gboolean use_existing,
133 McdRequest **request_out, GError **error)
134 {
135 McdChannel *channel;
136 GHashTable *props;
137 McdRequest *request;
138
139 if (!mcd_account_check_request (account, properties, error))
140 {
141 return NULL;
142 }
143
144 /* We MUST deep-copy the hash-table, as we don't know how dbus-glib will
145 * free it */
146 props = _mcd_deepcopy_asv (properties);
147 request = _mcd_request_new (clients, use_existing, account, props,
148 user_time, preferred_handler, hints);
149 g_assert (request != NULL);
150 g_hash_table_unref (props);
151
152 channel = _mcd_channel_new_request (request);
153
154 /* FIXME: this isn't ideal - if the account is deleted, Proceed will fail,
155 * whereas what we want to happen is that Proceed will succeed but
156 * immediately cause a failure to be signalled. It'll do for now though. */
157
158 /* This can't actually be emitted until Proceed() is called; it'll always
159 * come before succeeded-with-channel or failed */
160 g_signal_connect_data (request,
161 "ready-to-request",
162 G_CALLBACK (ready_to_request_cb),
163 g_object_ref (channel),
164 (GClosureNotify) g_object_unref,
165 0);
166
167 /* we use connect_after, to make sure that other signals (such as
168 * RemoveRequest) are emitted before the Failed signal */
169 g_signal_connect_data (request,
170 "succeeded-with-channel",
171 G_CALLBACK (on_request_succeeded_with_channel),
172 g_object_ref (channel),
173 (GClosureNotify) g_object_unref,
174 G_CONNECT_AFTER);
175 g_signal_connect_data (request,
176 "failed",
177 G_CALLBACK (on_request_failed),
178 g_object_ref (channel),
179 (GClosureNotify) g_object_unref,
180 G_CONNECT_AFTER);
181
182 if (request_out != NULL)
183 {
184 *request_out = g_object_ref (request);
185 }
186
187 return channel;
188 }
189
190 static void
ready_to_request_cb(McdRequest * request,McdChannel * channel)191 ready_to_request_cb (McdRequest *request,
192 McdChannel *channel)
193 {
194 GError *error = _mcd_request_dup_failure (request);
195
196 /* if we didn't ref the channel, disconnecting the signal could
197 * destroy it */
198 g_object_ref (channel);
199 g_signal_handlers_disconnect_by_func (request, ready_to_request_cb,
200 channel);
201
202 if (error != NULL)
203 {
204 g_message ("request denied by plugin: %s", error->message);
205 mcd_channel_take_error (channel, error);
206 }
207 else
208 {
209 DEBUG ("Starting online request");
210 /* Put the account online if necessary, and when that's finished,
211 * make the actual request. (The callback releases this reference.) */
212 _mcd_account_online_request (_mcd_request_get_account (request),
213 online_request_cb,
214 g_object_ref (channel));
215 }
216
217 g_object_unref (channel);
218 }
219
220 gboolean
mcd_account_check_request(McdAccount * account,GHashTable * request,GError ** error)221 mcd_account_check_request (McdAccount *account, GHashTable *request,
222 GError **error)
223 {
224 gboolean (*impl) (McdAccount *account, GHashTable *request,
225 GError **error);
226
227 g_return_val_if_fail (MCD_IS_ACCOUNT (account), FALSE);
228 g_return_val_if_fail (request != NULL, FALSE);
229
230 impl = MCD_ACCOUNT_GET_CLASS (account)->check_request;
231
232 if (impl == NULL)
233 return TRUE;
234
235 return impl (account, request, error);
236 }
237
238 /* Default implementation of check_request */
239 gboolean
_mcd_account_check_request_real(McdAccount * account,GHashTable * request,GError ** error)240 _mcd_account_check_request_real (McdAccount *account, GHashTable *request,
241 GError **error)
242 {
243 return TRUE;
244 }
245