]> git.0d.be Git - empathy.git/blob - src/empathy-filter.c
Keep a priv pointer in the object struct instead of using G_TYPE_INSTANCE_GET_PRIVATE...
[empathy.git] / src / empathy-filter.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007-2008 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  * 
19  * Authors: Xavier Claessens <xclaesse@gmail.com>
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25
26 #include <glib/gi18n.h>
27
28 #include <telepathy-glib/enums.h>
29 #include <telepathy-glib/channel.h>
30 #include <telepathy-glib/connection.h>
31 #include <telepathy-glib/util.h>
32 #include <telepathy-glib/dbus.h>
33 #include <telepathy-glib/proxy-subclass.h>
34
35 #include <libmissioncontrol/mission-control.h>
36 #include <libmissioncontrol/mc-account.h>
37
38 #include <extensions/extensions.h>
39
40 #include <libempathy/empathy-tp-chat.h>
41 #include <libempathy/empathy-tp-call.h>
42 #include <libempathy/empathy-tp-group.h>
43 #include <libempathy/empathy-utils.h>
44 #include <libempathy/empathy-tube-handler.h>
45 #include <libempathy/empathy-contact-factory.h>
46
47 #include <libempathy-gtk/empathy-chat.h>
48 #include <libempathy-gtk/empathy-images.h>
49 #include <libempathy-gtk/empathy-contact-dialogs.h>
50
51 #include "empathy-filter.h"
52 #include "empathy-chat-window.h"
53 #include "empathy-call-window.h"
54
55 #define DEBUG_FLAG EMPATHY_DEBUG_FILTER
56 #include <libempathy/empathy-debug.h>
57
58 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyFilter)
59 typedef struct {
60         GSList         *events;
61         GHashTable     *accounts;
62         gpointer        token;
63         MissionControl *mc;
64         GHashTable     *tubes;
65 } EmpathyFilterPriv;
66
67 G_DEFINE_TYPE (EmpathyFilter, empathy_filter, G_TYPE_OBJECT);
68
69 enum {
70         PROP_0,
71         PROP_TOP_EVENT,
72 };
73
74 typedef void (*FilterFunc) (EmpathyFilter *filter,
75                             gpointer       user_data);
76
77 typedef struct {
78         EmpathyFilterEvent public;
79         FilterFunc         func;
80         gpointer           user_data;
81 } EmpathyFilterEventExt;
82
83 static guint
84 filter_channel_hash (gconstpointer key)
85 {
86         TpProxy *channel = TP_PROXY (key);
87
88         return g_str_hash (channel->object_path);
89 }
90
91 static gboolean
92 filter_channel_equal (gconstpointer a,
93                       gconstpointer b)
94 {
95         TpProxy *channel_a = TP_PROXY (a);
96         TpProxy *channel_b = TP_PROXY (b);
97
98         return g_str_equal (channel_a->object_path, channel_b->object_path);
99 }
100
101 static void
102 filter_event_free (EmpathyFilterEventExt *event)
103 {
104         g_free (event->public.icon_name);
105         g_free (event->public.message);
106         g_slice_free (EmpathyFilterEventExt, event);
107 }
108
109 static void
110 filter_emit_event (EmpathyFilter *filter,
111                    const gchar   *icon_name,
112                    const gchar   *message,
113                    FilterFunc     func,
114                    gpointer       user_data)
115 {
116         EmpathyFilterPriv     *priv = GET_PRIV (filter);
117         EmpathyFilterEventExt *event;
118
119         DEBUG ("Emit event, icon_name=%s message='%s'", icon_name, message);
120
121         event = g_slice_new0 (EmpathyFilterEventExt);
122         event->func = func;
123         event->user_data = user_data;
124         event->public.icon_name = g_strdup (icon_name);
125         event->public.message = g_strdup (message);
126
127         priv->events = g_slist_append (priv->events, event);
128         if (priv->events->data == event) {
129                 g_object_notify (G_OBJECT (filter), "top-event");
130         }
131 }
132
133 void
134 empathy_filter_activate_event (EmpathyFilter      *filter,
135                                EmpathyFilterEvent *event)
136 {
137         EmpathyFilterPriv     *priv = GET_PRIV (filter);
138         EmpathyFilterEventExt *event_ext;
139         GSList                *l;
140         gboolean               is_top;
141
142         g_return_if_fail (EMPATHY_IS_FILTER (filter));
143         g_return_if_fail (event != NULL);
144
145         if (!(l = g_slist_find (priv->events, event))) {
146                 return;
147         }
148
149         DEBUG ("Activating event");
150
151         is_top = (l == priv->events);
152         priv->events = g_slist_delete_link (priv->events, l);
153         if (is_top) {
154                 g_object_notify (G_OBJECT (filter), "top-event");
155         }
156
157         event_ext = (EmpathyFilterEventExt*) event;
158         if (event_ext->func) {
159                 event_ext->func (filter, event_ext->user_data);
160         }
161
162         filter_event_free (event_ext);
163 }
164
165 EmpathyFilterEvent *
166 empathy_filter_get_top_event (EmpathyFilter *filter)
167 {
168         EmpathyFilterPriv *priv = GET_PRIV (filter);
169
170         g_return_val_if_fail (EMPATHY_IS_FILTER (filter), NULL);
171
172         return priv->events ? priv->events->data : NULL;
173 }
174
175 static void
176 filter_chat_dispatch (EmpathyFilter *filter,
177                       gpointer       user_data)
178 {
179         EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
180         EmpathyChat   *chat = NULL;
181         const gchar   *id;
182
183         id = empathy_tp_chat_get_id (tp_chat);
184         if (!id) {
185                 EmpathyContact *contact;
186
187                 contact = empathy_tp_chat_get_remote_contact (tp_chat);
188                 if (contact) {
189                         id = empathy_contact_get_id (contact);
190                 }
191         }
192
193         if (id) {
194                 McAccount *account;
195
196                 account = empathy_tp_chat_get_account (tp_chat);
197                 chat = empathy_chat_window_find_chat (account, id);
198         }
199
200         if (chat) {
201                 empathy_chat_set_tp_chat (chat, tp_chat);
202         } else {
203                 chat = empathy_chat_new (tp_chat);
204         }
205
206         empathy_chat_window_present_chat (chat);
207         g_object_unref (tp_chat);
208 }
209
210 static void
211 filter_chat_message_received_cb (EmpathyTpChat   *tp_chat,
212                                  EmpathyMessage  *message,
213                                  EmpathyFilter   *filter)
214 {
215         EmpathyContact  *sender;
216         gchar           *msg;
217
218         g_signal_handlers_disconnect_by_func (tp_chat,
219                                               filter_chat_message_received_cb,
220                                               filter);
221
222         sender = empathy_message_get_sender (message);
223         msg = g_strdup_printf (_("New message from %s:\n%s"),
224                                empathy_contact_get_name (sender),
225                                empathy_message_get_body (message));
226
227         filter_emit_event (filter, EMPATHY_IMAGE_NEW_MESSAGE, msg,
228                            filter_chat_dispatch, tp_chat);
229
230         g_free (msg);
231 }
232
233 static void
234 filter_chat_handle_channel (EmpathyFilter *filter,
235                             TpChannel     *channel,
236                             gboolean       is_incoming)
237 {
238         EmpathyTpChat *tp_chat;
239
240         DEBUG ("New text channel to be filtered: %p", channel);
241
242         tp_chat = empathy_tp_chat_new (channel, FALSE);
243         empathy_run_until_ready (tp_chat);
244         if (is_incoming) {
245                 filter_chat_dispatch (filter, tp_chat);
246         } else {
247                 g_signal_connect (tp_chat, "message-received",
248                                   G_CALLBACK (filter_chat_message_received_cb),
249                                   filter);
250         }
251 }
252
253 static void
254 filter_call_dispatch (EmpathyFilter *filter,
255                       gpointer       user_data)
256 {
257         EmpathyTpCall *call = EMPATHY_TP_CALL (user_data);
258
259         empathy_call_window_new (call);
260         g_object_unref (call);
261 }
262
263 static void
264 filter_call_contact_notify_cb (EmpathyTpCall *call,
265                                gpointer       unused,
266                                EmpathyFilter *filter)
267 {
268         EmpathyContact *contact;
269         gchar          *msg;
270
271         g_object_get (call, "contact", &contact, NULL);
272         if (!contact) {
273                 return;
274         }
275
276         empathy_contact_run_until_ready (contact,
277                                          EMPATHY_CONTACT_READY_NAME,
278                                          NULL);
279
280         msg = g_strdup_printf (_("Incoming call from %s"),
281                                empathy_contact_get_name (contact));
282
283         filter_emit_event (filter, EMPATHY_IMAGE_VOIP, msg,
284                            filter_call_dispatch, call);
285
286         g_free (msg);
287         g_object_unref (contact);
288 }
289
290 static void
291 filter_call_handle_channel (EmpathyFilter *filter,
292                             TpChannel     *channel,
293                             gboolean       is_incoming)
294 {
295         EmpathyTpCall *call;
296
297         DEBUG ("New media channel to be filtered: %p", channel);
298
299         call = empathy_tp_call_new (channel);
300         if (is_incoming) {
301                 filter_call_dispatch (filter, call);
302         } else {
303                 g_signal_connect (call, "notify::contact",
304                                   G_CALLBACK (filter_call_contact_notify_cb),
305                                   filter);      
306         }
307 }
308
309 static void
310 filter_contact_list_subscribe (EmpathyFilter *filter,
311                                gpointer       user_data)
312 {
313         EmpathyContact *contact = EMPATHY_CONTACT (user_data);
314
315         empathy_subscription_dialog_show (contact, NULL);
316         g_object_unref (contact);
317 }
318
319 static void
320 filter_contact_list_local_pending_cb (EmpathyTpGroup *group,
321                                       EmpathyContact *contact,
322                                       EmpathyContact *actor,
323                                       guint           reason,
324                                       gchar          *message,
325                                       EmpathyFilter  *filter)
326 {
327         GString *str;
328
329         DEBUG ("New local pending contact");
330
331         empathy_contact_run_until_ready (contact,
332                                          EMPATHY_CONTACT_READY_NAME,
333                                          NULL);
334
335         str = g_string_new (NULL);
336         g_string_printf (str, _("Subscription requested by %s"),
337                          empathy_contact_get_name (contact));   
338         if (!G_STR_EMPTY (message)) {
339                 g_string_append_printf (str, _("\nMessage: %s"), message);
340         }
341
342         filter_emit_event (filter, GTK_STOCK_DIALOG_QUESTION, str->str,
343                            filter_contact_list_subscribe,
344                            g_object_ref (contact));
345
346         g_string_free (str, TRUE);
347 }
348
349 static void
350 filter_contact_list_ready_cb (EmpathyTpGroup *group,
351                               gpointer        unused,
352                               EmpathyFilter  *filter)
353 {
354         GList *pendings, *l;
355
356         if (tp_strdiff ("publish", empathy_tp_group_get_name (group))) {
357                 g_object_unref (group);
358                 return;
359         }
360
361         DEBUG ("Publish contact list ready");
362
363         g_signal_connect (group, "local-pending",
364                           G_CALLBACK (filter_contact_list_local_pending_cb),
365                           filter);
366
367         pendings = empathy_tp_group_get_local_pendings (group);
368         for (l = pendings; l; l = l->next) {
369                 EmpathyPendingInfo *info = l->data;
370
371                 filter_contact_list_local_pending_cb (group, info->member,
372                                                       info->actor, info->reason,
373                                                       info->message, filter);
374                 empathy_pending_info_free (info);
375         }
376         g_list_free (pendings);
377 }
378
379 static void
380 filter_tubes_async_cb (TpProxy      *channel,
381                        const GError *error,
382                        gpointer      user_data,
383                        GObject      *filter)
384 {
385         if (error) {
386                 DEBUG ("Error %s: %s", (gchar*) user_data, error->message);
387         }
388 }
389
390 typedef struct {
391         EmpathyContactFactory *factory;
392         EmpathyContact        *initiator;
393         TpChannel             *channel;
394         guint                  id;
395         gchar                 *bus_name;
396         gchar                 *object_path;
397         gboolean               activatable;
398 } FilterTubesData;
399
400 static void
401 filter_tubes_dispatch (EmpathyFilter *filter,
402                        gpointer       user_data)
403 {
404         FilterTubesData *data = user_data;
405
406         if (data->activatable) {
407                 TpProxy *connection;
408                 TpProxy *thandler;
409                 gchar   *object_path;
410                 guint    handle_type;
411                 guint    handle;
412
413                 /* Create the proxy for the tube handler */
414                 thandler = g_object_new (TP_TYPE_PROXY,
415                                          "dbus-connection", tp_get_bus (),
416                                          "bus-name", data->bus_name,
417                                          "object-path", data->object_path,
418                                          NULL);
419                 tp_proxy_add_interface_by_id (thandler, EMP_IFACE_QUARK_TUBE_HANDLER);
420
421                 /* Give the tube to the handler */
422                 g_object_get (data->channel,
423                               "connection", &connection,
424                               "object-path", &object_path,
425                               "handle_type", &handle_type,
426                               "handle", &handle,
427                               NULL);
428
429                 DEBUG ("Dispatching tube");
430                 emp_cli_tube_handler_call_handle_tube (thandler, -1,
431                                                        connection->bus_name,
432                                                        connection->object_path,
433                                                        object_path, handle_type,
434                                                        handle, data->id,
435                                                        filter_tubes_async_cb,
436                                                        "handling tube", NULL,
437                                                        G_OBJECT (filter));
438
439                 g_object_unref (thandler);
440                 g_object_unref (connection);
441                 g_free (object_path);
442         } else {
443                 GtkWidget *dialog;
444                 gchar     *str;
445
446                 /* Tell the user that the tube can't be handled */
447                 str = g_strdup_printf (_("%s offered you an invitation, but "
448                                          "you don't have the needed external "
449                                          "application to handle it."),
450                                        empathy_contact_get_name (data->initiator));
451
452                 dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
453                                                  GTK_MESSAGE_ERROR,
454                                                  GTK_BUTTONS_OK, str);
455                 gtk_window_set_title (GTK_WINDOW (dialog),
456                                       _("Invitation Error"));
457                 g_free (str);
458
459                 gtk_widget_show (dialog);
460                 g_signal_connect (dialog, "response",
461                                   G_CALLBACK (gtk_widget_destroy),
462                                   NULL);
463
464                 DEBUG ("Tube can't be handled, closing");
465                 tp_cli_channel_type_tubes_call_close_tube (data->channel, -1,
466                                                            data->id,
467                                                            NULL, NULL, NULL,
468                                                            NULL);
469         }
470
471         g_free (data->bus_name);
472         g_free (data->object_path);
473         g_object_unref (data->channel);
474         g_object_unref (data->initiator);
475         g_object_unref (data->factory);
476         g_slice_free (FilterTubesData, data);
477 }
478
479 static void
480 filter_tubes_new_tube_cb (TpChannel   *channel,
481                           guint        id,
482                           guint        initiator,
483                           guint        type,
484                           const gchar *service,
485                           GHashTable  *parameters,
486                           guint        state,
487                           gpointer     user_data,
488                           GObject     *filter)
489 {
490         EmpathyFilterPriv *priv = GET_PRIV (filter);
491         static TpDBusDaemon *daemon = NULL;
492         FilterTubesData   *data;
493         McAccount         *account;
494         guint              number;
495         gchar             *msg;
496         gchar            **names;
497         gboolean           running = FALSE;
498         const gchar       *icon_name;
499         GError            *error = NULL;
500
501         /* Increase tube count */
502         number = GPOINTER_TO_UINT (g_hash_table_lookup (priv->tubes, channel));
503         g_hash_table_replace (priv->tubes, g_object_ref (channel),
504                               GUINT_TO_POINTER (++number));
505         DEBUG ("Increased tube count for channel %p: %d", channel, number);
506
507         /* We dispatch only local pending tubes */
508         if (state != TP_TUBE_STATE_LOCAL_PENDING) {
509                 return;
510         }
511
512         if (!daemon) {
513                 daemon = tp_dbus_daemon_new (tp_get_bus ());
514         }
515
516         account = empathy_channel_get_account (channel);
517         data = g_slice_new (FilterTubesData);
518         data->activatable = FALSE;
519         data->id = id;
520         data->channel = g_object_ref (channel);
521         data->factory = empathy_contact_factory_new ();
522         data->initiator = empathy_contact_factory_get_from_handle (data->factory,
523                                                                    account,
524                                                                    initiator);
525         data->bus_name = empathy_tube_handler_build_bus_name (type, service);
526         data->object_path = empathy_tube_handler_build_object_path (type, service);
527         g_object_unref (account);
528
529         /* Check if that bus-name has an owner, if it has one that means the
530          * app is already running and we can directly give the channel. */
531         tp_cli_dbus_daemon_run_name_has_owner (daemon, -1, data->bus_name,
532                                                &running, NULL, NULL);
533         if (running) {
534                 DEBUG ("Tube handler running");
535                 data->activatable = TRUE;
536                 filter_tubes_dispatch (EMPATHY_FILTER (filter), data);
537                 return;
538         }
539
540         /* Check if that bus-name is activatable, if not that means the
541          * application needed to handle this tube isn't installed. */
542         if (!tp_cli_dbus_daemon_run_list_activatable_names (daemon, -1,
543                                                             &names, &error,
544                                                             NULL)) {
545                 DEBUG ("Error listing activatable names: %s", error->message);
546                 g_clear_error (&error);
547         } else {
548                 gchar **name;
549
550                 for (name = names; *name; name++) {
551                         if (!tp_strdiff (*name, data->bus_name)) {
552                                 data->activatable = TRUE;
553                                 break;
554                         }
555                 }
556                 g_strfreev (names);
557         }
558
559         empathy_contact_run_until_ready (data->initiator,
560                                          EMPATHY_CONTACT_READY_NAME, NULL);
561
562         if (data->activatable) {
563                 icon_name = GTK_STOCK_EXECUTE;
564                 msg = g_strdup_printf (_("%s is offering you an invitation. An external "
565                                          "application will be started to handle it."),
566                                        empathy_contact_get_name (data->initiator));
567         } else {
568                 icon_name = GTK_STOCK_DIALOG_ERROR;
569                 msg = g_strdup_printf (_("%s is offering you an invitation, but "
570                                          "you don't have the needed external "
571                                          "application to handle it."),
572                                        empathy_contact_get_name (data->initiator));
573         }
574
575         filter_emit_event (EMPATHY_FILTER (filter), icon_name, msg,
576                            filter_tubes_dispatch, data);
577
578         g_free (msg);
579 }
580
581 static void
582 filter_tubes_list_tubes_cb (TpChannel       *channel,
583                             const GPtrArray *tubes,
584                             const GError    *error,
585                             gpointer         user_data,
586                             GObject         *filter)
587 {
588         guint i;
589
590         if (error) {
591                 DEBUG ("Error listing tubes: %s", error->message);
592                 return;
593         }
594
595         for (i = 0; i < tubes->len; i++) {
596                 GValueArray *values;
597
598                 values = g_ptr_array_index (tubes, i);
599                 filter_tubes_new_tube_cb (channel,
600                                           g_value_get_uint (g_value_array_get_nth (values, 0)),
601                                           g_value_get_uint (g_value_array_get_nth (values, 1)),
602                                           g_value_get_uint (g_value_array_get_nth (values, 2)),
603                                           g_value_get_string (g_value_array_get_nth (values, 3)),
604                                           g_value_get_boxed (g_value_array_get_nth (values, 4)),
605                                           g_value_get_uint (g_value_array_get_nth (values, 5)),
606                                           user_data, filter);
607         }
608 }
609
610 static void
611 filter_tubes_channel_invalidated_cb (TpProxy       *proxy,
612                                      guint          domain,
613                                      gint           code,
614                                      gchar         *message,
615                                      EmpathyFilter *filter)
616 {
617         EmpathyFilterPriv *priv = GET_PRIV (filter);
618
619         DEBUG ("Channel %p invalidated: %s", proxy, message);
620
621         g_hash_table_remove (priv->tubes, proxy);
622 }
623
624 static void
625 filter_tubes_tube_closed_cb (TpChannel *channel,
626                              guint      id,
627                              gpointer   user_data,
628                              GObject   *filter)
629 {
630         EmpathyFilterPriv *priv = GET_PRIV (filter);
631         guint              number;
632
633         number = GPOINTER_TO_UINT (g_hash_table_lookup (priv->tubes, channel));
634         if (number == 1) {
635                 DEBUG ("Ended tube count for channel %p, closing channel",
636                         channel);
637                 tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL);
638         }
639         else if (number > 1) {
640                 DEBUG ("Decrease tube count for channel %p: %d", channel, number);
641                 g_hash_table_replace (priv->tubes, g_object_ref (channel),
642                                       GUINT_TO_POINTER (--number));
643         }
644 }
645
646 static void
647 filter_tubes_handle_channel (EmpathyFilter *filter,
648                              TpChannel     *channel,
649                              gboolean       is_incoming)
650 {
651         EmpathyFilterPriv *priv = GET_PRIV (filter);
652
653         if (g_hash_table_lookup (priv->tubes, channel)) {
654                 return;
655         }
656
657         DEBUG ("Handling new channel");
658
659         g_hash_table_insert (priv->tubes, g_object_ref (channel),
660                              GUINT_TO_POINTER (0));
661
662         g_signal_connect (channel, "invalidated",
663                           G_CALLBACK (filter_tubes_channel_invalidated_cb),
664                           filter);
665
666         tp_cli_channel_type_tubes_connect_to_tube_closed (channel,
667                                                           filter_tubes_tube_closed_cb,
668                                                           NULL, NULL,
669                                                           G_OBJECT (filter), NULL);
670         tp_cli_channel_type_tubes_connect_to_new_tube (channel,
671                                                        filter_tubes_new_tube_cb,
672                                                        NULL, NULL,
673                                                        G_OBJECT (filter), NULL);
674         tp_cli_channel_type_tubes_call_list_tubes (channel, -1,
675                                                    filter_tubes_list_tubes_cb,
676                                                    NULL, NULL,
677                                                    G_OBJECT (filter));
678 }
679
680 static void
681 filter_contact_list_destroy_cb (EmpathyTpGroup *group,
682                                 EmpathyFilter  *filter)
683 {
684         g_object_unref (group);
685 }
686
687 static void
688 filter_contact_list_handle_channel (EmpathyFilter *filter,
689                                     TpChannel     *channel,
690                                     gboolean       is_incoming)
691 {
692         EmpathyTpGroup *group;
693
694         group = empathy_tp_group_new (channel);
695         g_signal_connect (group, "notify::ready",
696                           G_CALLBACK (filter_contact_list_ready_cb),
697                           filter);      
698         g_signal_connect (group, "destroy",
699                           G_CALLBACK (filter_contact_list_destroy_cb),
700                           filter);
701 }
702
703 static void
704 filter_connection_invalidated_cb (TpConnection  *connection,
705                                   guint          domain,
706                                   gint           code,
707                                   gchar         *message,
708                                   EmpathyFilter *filter)
709 {
710         EmpathyFilterPriv *priv = GET_PRIV (filter);
711         GHashTableIter     iter;
712         gpointer           key, value;
713
714         DEBUG ("connection invalidated: %s", message);
715
716         g_hash_table_iter_init (&iter, priv->accounts);
717         while (g_hash_table_iter_next (&iter, &key, &value)) {
718                 if (value == connection) {
719                         g_hash_table_remove (priv->accounts, key);
720                         break;
721                 }
722         }
723 }
724
725 typedef void (*HandleChannelFunc) (EmpathyFilter *filter,
726                                    TpChannel     *channel,
727                                    gboolean       is_incoming);
728
729 static void
730 filter_conection_new_channel_cb (TpConnection *connection,
731                                  const gchar  *object_path,
732                                  const gchar  *channel_type,
733                                  guint         handle_type,
734                                  guint         handle,
735                                  gboolean      suppress_handler,
736                                  gpointer      user_data,
737                                  GObject      *filter)
738 {
739         HandleChannelFunc  func = NULL;
740         TpChannel         *channel;
741         gpointer           had_channels;
742         
743         had_channels = g_object_get_data (G_OBJECT (connection), "had-channels");
744         if (had_channels == NULL) {
745                 return;
746         }
747
748         if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT)) {
749                 func = filter_chat_handle_channel;
750         }
751         else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)) {
752                 func = filter_call_handle_channel;
753         }
754         else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST)) {
755                 func = filter_contact_list_handle_channel;
756         }
757         else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES)) {
758                 func = filter_tubes_handle_channel;
759         } else {
760                 DEBUG ("Unknown channel type %s", channel_type);
761                 return;
762         }
763
764         channel = tp_channel_new (connection, object_path, channel_type,
765                                   handle_type, handle, NULL);
766         tp_channel_run_until_ready (channel, NULL, NULL);
767
768         /* We abuse of suppress_handler, TRUE means OUTGOING */
769         func (EMPATHY_FILTER (filter), channel, suppress_handler);
770
771         g_object_unref (channel);
772 }
773
774 static void
775 filter_connection_list_channels_cb (TpConnection    *connection,
776                                     const GPtrArray *channels,
777                                     const GError    *error,
778                                     gpointer         user_data,
779                                     GObject         *filter)
780 {
781         guint i;
782
783         if (error) {
784                 DEBUG ("Error listing channels: %s", error->message);
785                 return;
786         }
787
788         g_object_set_data (G_OBJECT (connection), "had-channels",
789                            GUINT_TO_POINTER (1));
790
791         for (i = 0; i < channels->len; i++) {
792                 GValueArray *values;
793
794                 values = g_ptr_array_index (channels, i);
795                 filter_conection_new_channel_cb (connection,
796                         g_value_get_boxed (g_value_array_get_nth (values, 0)),
797                         g_value_get_string (g_value_array_get_nth (values, 1)),
798                         g_value_get_uint (g_value_array_get_nth (values, 2)),
799                         g_value_get_uint (g_value_array_get_nth (values, 3)),
800                         TRUE, user_data, filter);
801         }
802 }
803
804 static void
805 filter_connection_advertise_capabilities_cb (TpConnection    *connection,
806                                              const GPtrArray *capabilities,
807                                              const GError    *error,
808                                              gpointer         user_data,
809                                              GObject         *filter)
810 {
811         if (error) {
812                 DEBUG ("Error advertising capabilities: %s", error->message);
813         }
814 }
815
816 static void
817 filter_connection_ready_cb (TpConnection  *connection,
818                             gpointer       unused,
819                             EmpathyFilter *filter)
820 {
821         GPtrArray   *capabilities;
822         GType        cap_type;
823         GValue       cap = {0, };
824         const gchar *remove = NULL;
825
826         DEBUG ("Connection ready, accepting new channels");
827
828         tp_cli_connection_connect_to_new_channel (connection,
829                                                   filter_conection_new_channel_cb,
830                                                   NULL, NULL,
831                                                   G_OBJECT (filter), NULL);
832         tp_cli_connection_call_list_channels (connection, -1,
833                                               filter_connection_list_channels_cb,
834                                               NULL, NULL,
835                                               G_OBJECT (filter));
836
837         /* Advertise VoIP capabilities */
838         capabilities = g_ptr_array_sized_new (1);
839         cap_type = dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING,
840                                            G_TYPE_UINT, G_TYPE_INVALID);
841         g_value_init (&cap, cap_type);
842         g_value_take_boxed (&cap, dbus_g_type_specialized_construct (cap_type));
843         dbus_g_type_struct_set (&cap,
844                                 0, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
845                                 1, TP_CHANNEL_MEDIA_CAPABILITY_AUDIO |
846                                    TP_CHANNEL_MEDIA_CAPABILITY_VIDEO |
847                                    TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_STUN  |
848                                    TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_GTALK_P2P,
849                                 G_MAXUINT);
850         g_ptr_array_add (capabilities, g_value_get_boxed (&cap));
851
852         tp_cli_connection_interface_capabilities_call_advertise_capabilities (
853                 connection, -1,
854                 capabilities, &remove,
855                 filter_connection_advertise_capabilities_cb,
856                 NULL, NULL, G_OBJECT (filter));
857 }
858
859 static void
860 filter_update_account (EmpathyFilter *filter,
861                        McAccount     *account)
862 {
863         EmpathyFilterPriv *priv = GET_PRIV (filter);
864         TpConnection      *connection;
865         gboolean           ready;
866
867         connection = g_hash_table_lookup (priv->accounts, account);
868         if (connection) {
869                 return;
870         }
871
872         connection = mission_control_get_tpconnection (priv->mc, account, NULL);
873         if (!connection) {
874                 return;
875         }
876
877         g_hash_table_insert (priv->accounts, g_object_ref (account), connection);
878         g_signal_connect (connection, "invalidated",
879                           G_CALLBACK (filter_connection_invalidated_cb),
880                           filter);
881
882         g_object_get (connection, "connection-ready", &ready, NULL);
883         if (ready) {
884                 filter_connection_ready_cb (connection, NULL, filter);
885         } else {
886                 g_signal_connect (connection, "notify::connection-ready",
887                                   G_CALLBACK (filter_connection_ready_cb),
888                                   filter);
889         }
890 }
891
892 static void
893 filter_status_changed_cb (MissionControl           *mc,
894                           TpConnectionStatus        status,
895                           McPresence                presence,
896                           TpConnectionStatusReason  reason,
897                           const gchar              *unique_name,
898                           EmpathyFilter            *filter)
899 {
900         McAccount *account;
901
902         account = mc_account_lookup (unique_name);
903         filter_update_account (filter, account);
904         g_object_unref (account);
905 }
906
907 static void
908 filter_finalize (GObject *object)
909 {
910         EmpathyFilterPriv *priv = GET_PRIV (object);
911
912         empathy_disconnect_account_status_changed (priv->token);
913         g_object_unref (priv->mc);
914
915         g_slist_foreach (priv->events, (GFunc) filter_event_free, NULL);
916         g_slist_free (priv->events);
917
918         g_hash_table_destroy (priv->accounts);
919         g_hash_table_destroy (priv->tubes);
920 }
921
922 static void
923 filter_get_property (GObject    *object,
924                      guint       param_id,
925                      GValue     *value,
926                      GParamSpec *pspec)
927 {
928         EmpathyFilterPriv *priv = GET_PRIV (object);
929
930         switch (param_id) {
931         case PROP_TOP_EVENT:
932                 g_value_set_pointer (value, priv->events ? priv->events->data : NULL);
933                 break;
934         default:
935                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
936                 break;
937         };
938 }
939
940 static void
941 empathy_filter_class_init (EmpathyFilterClass *klass)
942 {
943         GObjectClass *object_class = G_OBJECT_CLASS (klass);
944
945         object_class->finalize = filter_finalize;
946         object_class->get_property = filter_get_property;
947
948         g_object_class_install_property (object_class,
949                                          PROP_TOP_EVENT,
950                                          g_param_spec_pointer ("top-event",
951                                                                "The top event",
952                                                                "The first event in the events list",
953                                                                G_PARAM_READABLE));
954
955         g_type_class_add_private (object_class, sizeof (EmpathyFilterPriv));
956 }
957
958 static void
959 empathy_filter_init (EmpathyFilter *filter)
960 {
961         GList             *accounts, *l;
962         EmpathyFilterPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (filter,
963                 EMPATHY_TYPE_FILTER, EmpathyFilterPriv);
964
965         filter->priv = priv;
966         priv->tubes = g_hash_table_new_full (filter_channel_hash,
967                                              filter_channel_equal,
968                                              g_object_unref, NULL);
969
970         priv->mc = empathy_mission_control_new ();
971         priv->token = empathy_connect_to_account_status_changed (priv->mc,
972                 G_CALLBACK (filter_status_changed_cb),
973                 filter, NULL);
974
975         priv->accounts = g_hash_table_new_full (empathy_account_hash,
976                                                 empathy_account_equal,
977                                                 g_object_unref,
978                                                 g_object_unref);
979         accounts = mc_accounts_list_by_enabled (TRUE);
980         for (l = accounts; l; l = l->next) {
981                 filter_update_account (filter, l->data);
982                 g_object_unref (l->data);
983         }
984         g_list_free (accounts);
985 }
986
987 EmpathyFilter *
988 empathy_filter_new (void)
989 {
990         static EmpathyFilter *filter = NULL;
991
992         if (!filter) {
993                 filter = g_object_new (EMPATHY_TYPE_FILTER, NULL);
994                 g_object_add_weak_pointer (G_OBJECT (filter), (gpointer) &filter);
995         } else {
996                 g_object_ref (filter);
997         }
998
999         return filter;
1000 }
1001