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