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