]> git.0d.be Git - empathy.git/blob - src/empathy-call-factory.c
Move should_create_salut_account to local-xmpp-assistant-widget
[empathy.git] / src / empathy-call-factory.c
1 /*
2  * empathy-call-factory.c - Source for EmpathyCallFactory
3  * Copyright (C) 2008 Collabora Ltd.
4  * @author Sjoerd Simons <sjoerd.simons@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
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <telepathy-glib/account-channel-request.h>
26 #include <telepathy-glib/simple-handler.h>
27 #include <telepathy-glib/interfaces.h>
28 #include <telepathy-glib/util.h>
29
30 #include <telepathy-yell/telepathy-yell.h>
31
32 #include <libempathy/empathy-client-factory.h>
33 #include <libempathy/empathy-request-util.h>
34 #include <libempathy/empathy-tp-contact-factory.h>
35 #include <libempathy/empathy-utils.h>
36
37 #include "empathy-call-factory.h"
38 #include "empathy-call-handler.h"
39
40 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
41 #include <libempathy/empathy-debug.h>
42
43 G_DEFINE_TYPE(EmpathyCallFactory, empathy_call_factory, TP_TYPE_BASE_CLIENT)
44
45 static void handle_channels (TpBaseClient *client,
46     TpAccount *account,
47     TpConnection *connection,
48     GList *channels,
49     GList *requests_satisfied,
50     gint64 user_action_time,
51     TpHandleChannelsContext *context);
52
53 static void approve_channels (TpBaseClient *client,
54     TpAccount *account,
55     TpConnection *connection,
56     GList *channels,
57     TpChannelDispatchOperation *dispatch_operation,
58     TpAddDispatchOperationContext *context);
59
60 /* signal enum */
61 enum
62 {
63     NEW_CALL_HANDLER,
64     INCOMING_CALL,
65     LAST_SIGNAL
66 };
67
68 static guint signals[LAST_SIGNAL] = {0};
69
70 static GObject *call_factory = NULL;
71
72 static void
73 empathy_call_factory_init (EmpathyCallFactory *obj)
74 {
75   TpBaseClient *client = (TpBaseClient *) obj;
76
77   tp_base_client_take_approver_filter (client, tp_asv_new (
78         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
79           TPY_IFACE_CHANNEL_TYPE_CALL,
80         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
81           G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
82         NULL));
83
84   tp_base_client_take_handler_filter (client, tp_asv_new (
85         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
86           TPY_IFACE_CHANNEL_TYPE_CALL,
87         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
88           G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
89         NULL));
90
91   tp_base_client_take_handler_filter (client, tp_asv_new (
92         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
93           TPY_IFACE_CHANNEL_TYPE_CALL,
94         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
95           G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
96         TPY_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, G_TYPE_BOOLEAN, TRUE,
97         NULL));
98
99   tp_base_client_take_handler_filter (client, tp_asv_new (
100         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
101           TPY_IFACE_CHANNEL_TYPE_CALL,
102         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
103           G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
104         TPY_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, G_TYPE_BOOLEAN, TRUE,
105         NULL));
106
107   tp_base_client_add_handler_capabilities_varargs (client,
108     "org.freedesktop.Telepathy.Channel.Interface.MediaSignalling/ice-udp",
109     "org.freedesktop.Telepathy.Channel.Interface.MediaSignalling/gtalk-p2p",
110     "org.freedesktop.Telepathy.Channel.Interface.MediaSignalling/video/h264",
111     NULL);
112 }
113
114 static GObject *
115 empathy_call_factory_constructor (GType type, guint n_construct_params,
116   GObjectConstructParam *construct_params)
117 {
118   g_return_val_if_fail (call_factory == NULL, NULL);
119
120   call_factory = G_OBJECT_CLASS (empathy_call_factory_parent_class)->constructor
121           (type, n_construct_params, construct_params);
122   g_object_add_weak_pointer (call_factory, (gpointer)&call_factory);
123
124   return call_factory;
125 }
126
127 static void
128 empathy_call_factory_class_init (EmpathyCallFactoryClass *klass)
129 {
130   GObjectClass *object_class = G_OBJECT_CLASS (klass);
131   TpBaseClientClass *base_clt_cls = TP_BASE_CLIENT_CLASS (klass);
132
133   object_class->constructor = empathy_call_factory_constructor;
134
135   base_clt_cls->handle_channels = handle_channels;
136   base_clt_cls->add_dispatch_operation = approve_channels;
137
138   signals[NEW_CALL_HANDLER] =
139     g_signal_new ("new-call-handler",
140       G_TYPE_FROM_CLASS (klass),
141       G_SIGNAL_RUN_LAST, 0,
142       NULL, NULL,
143       g_cclosure_marshal_generic,
144       G_TYPE_NONE,
145       2, EMPATHY_TYPE_CALL_HANDLER, G_TYPE_BOOLEAN);
146
147   signals[INCOMING_CALL] =
148     g_signal_new ("incoming-call",
149       G_TYPE_FROM_CLASS (klass),
150       G_SIGNAL_RUN_LAST, 0,
151       NULL, NULL,
152       g_cclosure_marshal_generic,
153       G_TYPE_BOOLEAN,
154       4, G_TYPE_UINT, TPY_TYPE_CALL_CHANNEL,
155       TP_TYPE_CHANNEL_DISPATCH_OPERATION,
156       TP_TYPE_ADD_DISPATCH_OPERATION_CONTEXT);
157 }
158
159 EmpathyCallFactory *
160 empathy_call_factory_initialise (void)
161 {
162   EmpathyCallFactory *self;
163   EmpathyClientFactory *factory;
164   TpAccountManager *am;
165
166   g_return_val_if_fail (call_factory == NULL, NULL);
167
168   am = tp_account_manager_dup ();
169   factory = empathy_client_factory_dup ();
170
171   self = EMPATHY_CALL_FACTORY (g_object_new (EMPATHY_TYPE_CALL_FACTORY,
172       "account-manager", am,
173       "factory", factory,
174       "name", EMPATHY_CALL_BUS_NAME_SUFFIX,
175       NULL));
176
177   g_object_unref (am);
178   g_object_unref (factory);
179
180   return self;
181 }
182
183 EmpathyCallFactory *
184 empathy_call_factory_get (void)
185 {
186   g_return_val_if_fail (call_factory != NULL, NULL);
187
188   return EMPATHY_CALL_FACTORY (call_factory);
189 }
190
191 static void
192 call_channel_got_contact (TpConnection *connection,
193   EmpathyContact *contact,
194   const GError *error,
195   gpointer user_data,
196   GObject *weak_object)
197 {
198   EmpathyCallFactory *factory = EMPATHY_CALL_FACTORY (weak_object);
199   EmpathyCallHandler *handler;
200   TpyCallChannel *call = TPY_CALL_CHANNEL (user_data);
201
202   if (contact == NULL)
203     {
204       /* FIXME use hangup with an appropriate error */
205       tp_channel_close_async (TP_CHANNEL (call), NULL, NULL);
206       return;
207     }
208
209   handler = empathy_call_handler_new_for_channel (call, contact);
210
211   g_signal_emit (factory, signals[NEW_CALL_HANDLER], 0,
212     handler, FALSE);
213
214   g_object_unref (handler);
215 }
216
217 static void
218 call_channel_ready (EmpathyCallFactory *factory,
219   TpyCallChannel *call)
220 {
221   TpChannel *channel = TP_CHANNEL (call);
222   const gchar *id;
223
224   id = tp_channel_get_identifier (channel);
225
226   /* The ready callback has a reference, so pass that on */
227   empathy_tp_contact_factory_get_from_id (
228     tp_channel_borrow_connection (channel),
229     id,
230     call_channel_got_contact,
231     channel,
232     g_object_unref,
233     (GObject *) factory);
234 }
235
236 static void
237 call_channel_ready_cb (TpyCallChannel *call,
238   GParamSpec *spec,
239   EmpathyCallFactory *factory)
240 {
241   gboolean ready;
242
243   g_object_get (call, "ready", &ready, NULL);
244   if (!ready)
245     return;
246
247   call_channel_ready (factory, call);
248 }
249
250
251 static void
252 handle_channels (TpBaseClient *client,
253     TpAccount *account,
254     TpConnection *connection,
255     GList *channels,
256     GList *requests_satisfied,
257     gint64 user_action_time,
258     TpHandleChannelsContext *context)
259 {
260   EmpathyCallFactory *self = EMPATHY_CALL_FACTORY (client);
261   GList *l;
262
263   for (l = channels; l != NULL; l = g_list_next (l))
264     {
265       TpChannel *channel = l->data;
266       TpyCallChannel *call;
267       gboolean ready;
268
269       if (tp_proxy_get_invalidated (channel) != NULL)
270         continue;
271
272       if (tp_channel_get_channel_type_id (channel) !=
273           TPY_IFACE_QUARK_CHANNEL_TYPE_CALL)
274         continue;
275
276       if (!TPY_IS_CALL_CHANNEL (channel))
277         continue;
278
279       call = TPY_CALL_CHANNEL (channel);
280
281       /* Take a ref to keep while hopping through the async callbacks */
282       g_object_ref (call);
283       g_object_get (call, "ready", &ready, NULL);
284
285       if (!ready)
286         tp_g_signal_connect_object (call, "notify::ready",
287           G_CALLBACK (call_channel_ready_cb), self, 0);
288       else
289         call_channel_ready (self, call);
290     }
291
292   tp_handle_channels_context_accept (context);
293 }
294
295 static TpyCallChannel *
296 find_call_channel (GList *channels)
297 {
298   GList *l;
299
300   for (l = channels; l != NULL; l = g_list_next (l))
301     {
302       TpChannel *channel = l->data;
303       GQuark channel_type;
304
305       if (tp_proxy_get_invalidated (channel) != NULL)
306         continue;
307
308       channel_type = tp_channel_get_channel_type_id (channel);
309
310       if (channel_type == TPY_IFACE_QUARK_CHANNEL_TYPE_CALL)
311         return TPY_CALL_CHANNEL (channel);
312     }
313
314   return NULL;
315 }
316
317 static void
318 approve_channels (TpBaseClient *client,
319     TpAccount *account,
320     TpConnection *connection,
321     GList *channels,
322     TpChannelDispatchOperation *dispatch_operation,
323     TpAddDispatchOperationContext *context)
324 {
325   EmpathyCallFactory *self = EMPATHY_CALL_FACTORY (client);
326   TpyCallChannel *channel;
327   guint handle;
328   GError error = { TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "" };
329   gboolean handled = FALSE;
330
331   channel = find_call_channel (channels);
332
333   if (channel == NULL)
334     {
335       DEBUG ("Failed to find the main channel; ignoring");
336       error.message = "Unknown channel";
337       goto out;
338     }
339
340   handle = tp_channel_get_handle (TP_CHANNEL (channel), NULL);
341
342   if (handle == 0)
343     {
344       DEBUG ("Unknown handle, ignoring");
345       error.code = TP_ERROR_INVALID_HANDLE;
346       error.message = "Unknown handle";
347       goto out;
348     }
349
350   g_signal_emit (self, signals[INCOMING_CALL], 0,
351       handle, channel, dispatch_operation, context,
352       &handled);
353
354   if (handled)
355     return;
356
357   /* There was no call window so the context wasn't handled. */
358   DEBUG ("Call with a contact for which there's no existing "
359     "call window, ignoring");
360   error.message = "No call window with this contact";
361
362  out:
363   tp_add_dispatch_operation_context_fail (context, &error);
364 }
365
366 gboolean
367 empathy_call_factory_register (EmpathyCallFactory *self,
368     GError **error)
369 {
370   return tp_base_client_register (TP_BASE_CLIENT (self), error);
371 }