]> git.0d.be Git - empathy.git/blob - src/empathy-filter.c
initialize connection_status
[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         gchar                 *service;
393         guint                  type;
394         guint                  id;
395 } FilterTubesData;
396
397 static void
398 filter_tubes_dispatch (EmpathyFilter *filter,
399                        gpointer       user_data)
400 {
401         static TpDBusDaemon *daemon = NULL;
402         FilterTubesData     *data = user_data;
403         gchar               *thandler_bus_name;
404         gchar               *thandler_object_path;
405         gboolean             activatable = FALSE;
406         gchar              **names = NULL;
407         GError              *error = NULL;
408
409         /* Build the bus-name and object-path where the handler for this tube
410          * is supposed to be. */
411         thandler_bus_name = empathy_tube_handler_build_bus_name (data->type,
412                                                                  data->service);
413         thandler_object_path = empathy_tube_handler_build_object_path (data->type,
414                                                                        data->service);
415
416         /* Check if that bus-name is activatable, if not that means the
417          * application needed to handle this tube isn't installed. */
418         if (!daemon) {
419                 daemon = tp_dbus_daemon_new (tp_get_bus ());
420         }
421
422         if (!tp_cli_dbus_daemon_run_list_activatable_names (daemon, -1,
423                                                             &names, &error,
424                                                             NULL)) {
425                 empathy_debug (DEBUG_DOMAIN, "Error listing activatable names: %s",
426                                error->message);
427                 g_clear_error (&error);
428         } else {
429                 gchar **name;
430
431                 for (name = names; *name; name++) {
432                         if (!tp_strdiff (*name, thandler_bus_name)) {
433                                 activatable = TRUE;
434                                 break;
435                         }
436                 }
437                 g_strfreev (names);
438         }
439
440         if (activatable) {
441                 TpProxy *connection;
442                 TpProxy *thandler;
443                 gchar   *object_path;
444                 guint    handle_type;
445                 guint    handle;
446
447                 /* Create the proxy for the tube handler */
448                 thandler = g_object_new (TP_TYPE_PROXY,
449                                          "dbus-connection", tp_get_bus (),
450                                          "bus-name", thandler_bus_name,
451                                          "object-path", thandler_object_path,
452                                          NULL);
453                 tp_proxy_add_interface_by_id (thandler, EMP_IFACE_QUARK_TUBE_HANDLER);
454
455                 /* Give the tube to the handler */
456                 g_object_get (data->channel,
457                               "connection", &connection,
458                               "object-path", &object_path,
459                               "handle_type", &handle_type,
460                               "handle", &handle,
461                               NULL);
462
463                 empathy_debug (DEBUG_DOMAIN, "Dispatching tube");
464                 emp_cli_tube_handler_call_handle_tube (thandler, -1,
465                                                        connection->bus_name,
466                                                        connection->object_path,
467                                                        object_path, handle_type,
468                                                        handle, data->id,
469                                                        filter_tubes_async_cb,
470                                                        "handling tube", NULL,
471                                                        G_OBJECT (filter));
472
473                 g_object_unref (thandler);
474                 g_object_unref (connection);
475                 g_free (object_path);
476         } else {
477                 GtkWidget *dialog;
478                 gchar     *str;
479
480                 /* Tell the user that the tube can't be handled */
481                 str = g_strdup_printf (_("%s invited you to play %s but you don't "
482                                          "have it installed."),
483                                        empathy_contact_get_name (data->initiator),
484                                        data->service);
485                 dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
486                                                  GTK_MESSAGE_ERROR,
487                                                  GTK_BUTTONS_OK, str);
488                 g_free (str);
489                 str = g_strdup_printf (_("%s Invitation"), data->service);
490                 gtk_window_set_title (GTK_WINDOW (dialog), str);
491                 g_free (str);
492
493                 g_signal_connect (dialog, "destroy",
494                                   G_CALLBACK (gtk_widget_destroy),
495                                   NULL);
496
497                 empathy_debug (DEBUG_DOMAIN, "Tube can't be handled, closing");
498                 tp_cli_channel_type_tubes_call_close_tube (data->channel, -1,
499                                                            data->id,
500                                                            NULL, NULL, NULL,
501                                                            NULL);
502         }
503
504         g_free (thandler_bus_name);
505         g_free (thandler_object_path);
506         g_free (data->service);
507         g_object_unref (data->channel);
508         g_object_unref (data->initiator);
509         g_object_unref (data->factory);
510         g_slice_free (FilterTubesData, data);
511 }
512
513 static void
514 filter_tubes_new_tube_cb (TpChannel   *channel,
515                           guint        id,
516                           guint        initiator,
517                           guint        type,
518                           const gchar *service,
519                           GHashTable  *parameters,
520                           guint        state,
521                           gpointer     user_data,
522                           GObject     *filter)
523 {
524         EmpathyFilterPriv *priv = GET_PRIV (filter);
525         FilterTubesData   *data;
526         McAccount         *account;
527         guint              number;
528         gchar             *msg;
529
530         /* Increase tube count */
531         number = GPOINTER_TO_UINT (g_hash_table_lookup (priv->tubes, channel));
532         g_hash_table_replace (priv->tubes, g_object_ref (channel),
533                               GUINT_TO_POINTER (++number));
534         empathy_debug (DEBUG_DOMAIN, "Increased tube count for channel %p: %d",
535                        channel, number);
536
537         /* We dispatch only local pending tubes */
538         if (state != TP_TUBE_STATE_LOCAL_PENDING) {
539                 return;
540         }
541
542         account = empathy_channel_get_account (channel);
543         data = g_slice_new (FilterTubesData);
544         data->type = type;
545         data->id = id;
546         data->service = g_strdup (service);
547         data->channel = g_object_ref (channel);
548         data->factory = empathy_contact_factory_new ();
549         data->initiator = empathy_contact_factory_get_from_handle (data->factory,
550                                                                    account,
551                                                                    initiator);
552
553         empathy_contact_run_until_ready (data->initiator,
554                                          EMPATHY_CONTACT_READY_NAME, NULL);
555
556         msg = g_strdup_printf (_("%s is offering you a tube for application %s"),
557                                empathy_contact_get_name (data->initiator),
558                                service);
559
560         filter_emit_event (EMPATHY_FILTER (filter), GTK_STOCK_DIALOG_QUESTION,
561                            msg, filter_tubes_dispatch, data);
562
563         g_free (msg);
564         g_object_unref (account);
565 }
566
567 static void
568 filter_tubes_list_tubes_cb (TpChannel       *channel,
569                             const GPtrArray *tubes,
570                             const GError    *error,
571                             gpointer         user_data,
572                             GObject         *filter)
573 {
574         guint i;
575
576         if (error) {
577                 empathy_debug (DEBUG_DOMAIN, "Error listing tubes: %s",
578                                error->message);
579                 return;
580         }
581
582         for (i = 0; i < tubes->len; i++) {
583                 GValueArray *values;
584
585                 values = g_ptr_array_index (tubes, i);
586                 filter_tubes_new_tube_cb (channel,
587                                           g_value_get_uint (g_value_array_get_nth (values, 0)),
588                                           g_value_get_uint (g_value_array_get_nth (values, 1)),
589                                           g_value_get_uint (g_value_array_get_nth (values, 2)),
590                                           g_value_get_string (g_value_array_get_nth (values, 3)),
591                                           g_value_get_boxed (g_value_array_get_nth (values, 4)),
592                                           g_value_get_uint (g_value_array_get_nth (values, 5)),
593                                           user_data, filter);
594         }
595 }
596
597 static void
598 filter_tubes_channel_invalidated_cb (TpProxy       *proxy,
599                                      guint          domain,
600                                      gint           code,
601                                      gchar         *message,
602                                      EmpathyFilter *filter)
603 {
604         EmpathyFilterPriv *priv = GET_PRIV (filter);
605
606         empathy_debug (DEBUG_DOMAIN, "Channel %p invalidated: %s", proxy, message);
607
608         g_hash_table_remove (priv->tubes, proxy);
609 }
610
611 static void
612 filter_tubes_tube_closed_cb (TpChannel *channel,
613                              guint      id,
614                              gpointer   user_data,
615                              GObject   *filter)
616 {
617         EmpathyFilterPriv *priv = GET_PRIV (filter);
618         guint              number;
619
620         number = GPOINTER_TO_UINT (g_hash_table_lookup (priv->tubes, channel));
621         if (number == 1) {
622                 empathy_debug (DEBUG_DOMAIN, "Ended tube count for channel %p, "
623                                "closing channel", channel);
624                 tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL);
625         }
626         else if (number > 1) {
627                 empathy_debug (DEBUG_DOMAIN, "Decrease tube count for channel %p: %d",
628                                channel, number);
629                 g_hash_table_replace (priv->tubes, g_object_ref (channel),
630                                       GUINT_TO_POINTER (--number));
631         }
632 }
633
634 static void
635 filter_tubes_handle_channel (EmpathyFilter *filter,
636                              TpChannel     *channel,
637                              gboolean       is_incoming)
638 {
639         EmpathyFilterPriv *priv = GET_PRIV (filter);
640
641         if (g_hash_table_lookup (priv->tubes, channel)) {
642                 return;
643         }
644
645         empathy_debug (DEBUG_DOMAIN, "Handling new channel");
646
647         g_hash_table_insert (priv->tubes, g_object_ref (channel),
648                              GUINT_TO_POINTER (0));
649
650         g_signal_connect (channel, "invalidated",
651                           G_CALLBACK (filter_tubes_channel_invalidated_cb),
652                           filter);
653
654         tp_cli_channel_type_tubes_connect_to_tube_closed (channel,
655                                                           filter_tubes_tube_closed_cb,
656                                                           NULL, NULL,
657                                                           G_OBJECT (filter), NULL);
658         tp_cli_channel_type_tubes_connect_to_new_tube (channel,
659                                                        filter_tubes_new_tube_cb,
660                                                        NULL, NULL,
661                                                        G_OBJECT (filter), NULL);
662         tp_cli_channel_type_tubes_call_list_tubes (channel, -1,
663                                                    filter_tubes_list_tubes_cb,
664                                                    NULL, NULL,
665                                                    G_OBJECT (filter));
666 }
667
668 static void
669 filter_contact_list_destroy_cb (EmpathyTpGroup *group,
670                                 EmpathyFilter  *filter)
671 {
672         g_object_unref (group);
673 }
674
675 static void
676 filter_contact_list_handle_channel (EmpathyFilter *filter,
677                                     TpChannel     *channel,
678                                     gboolean       is_incoming)
679 {
680         EmpathyTpGroup *group;
681
682         group = empathy_tp_group_new (channel);
683         g_signal_connect (group, "notify::ready",
684                           G_CALLBACK (filter_contact_list_ready_cb),
685                           filter);      
686         g_signal_connect (group, "destroy",
687                           G_CALLBACK (filter_contact_list_destroy_cb),
688                           filter);
689 }
690
691 static void
692 filter_connection_invalidated_cb (TpConnection  *connection,
693                                   guint          domain,
694                                   gint           code,
695                                   gchar         *message,
696                                   EmpathyFilter *filter)
697 {
698         EmpathyFilterPriv *priv = GET_PRIV (filter);
699         GHashTableIter     iter;
700         gpointer           key, value;
701
702         empathy_debug (DEBUG_DOMAIN, "connection invalidated: %s", message);
703
704         g_hash_table_iter_init (&iter, priv->accounts);
705         while (g_hash_table_iter_next (&iter, &key, &value)) {
706                 if (value == connection) {
707                         g_hash_table_remove (priv->accounts, key);
708                         break;
709                 }
710         }
711 }
712
713 typedef void (*HandleChannelFunc) (EmpathyFilter *filter,
714                                    TpChannel     *channel,
715                                    gboolean       is_incoming);
716
717 static void
718 filter_conection_new_channel_cb (TpConnection *connection,
719                                  const gchar  *object_path,
720                                  const gchar  *channel_type,
721                                  guint         handle_type,
722                                  guint         handle,
723                                  gboolean      suppress_handler,
724                                  gpointer      user_data,
725                                  GObject      *filter)
726 {
727         HandleChannelFunc  func = NULL;
728         TpChannel         *channel;
729         gpointer           had_channels;
730         
731         had_channels = g_object_get_data (G_OBJECT (connection), "had-channels");
732         if (had_channels == NULL) {
733                 return;
734         }
735
736         if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT)) {
737                 func = filter_chat_handle_channel;
738         }
739 #ifdef HAVE_VOIP
740         else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)) {
741                 func = filter_call_handle_channel;
742         }
743 #endif
744         else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST)) {
745                 func = filter_contact_list_handle_channel;
746         }
747         else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES)) {
748                 func = filter_tubes_handle_channel;
749         } else {
750                 empathy_debug (DEBUG_DOMAIN, "Unknown channel type %s",
751                                channel_type);
752                 return;
753         }
754
755         channel = tp_channel_new (connection, object_path, channel_type,
756                                   handle_type, handle, NULL);
757         tp_channel_run_until_ready (channel, NULL, NULL);
758
759         /* We abuse of suppress_handler, TRUE means OUTGOING */
760         func (EMPATHY_FILTER (filter), channel, suppress_handler);
761
762         g_object_unref (channel);
763 }
764
765 static void
766 filter_connection_list_channels_cb (TpConnection    *connection,
767                                     const GPtrArray *channels,
768                                     const GError    *error,
769                                     gpointer         user_data,
770                                     GObject         *filter)
771 {
772         guint i;
773
774         g_object_set_data (G_OBJECT (connection), "had-channels",
775                            GUINT_TO_POINTER (1));
776
777         for (i = 0; i < channels->len; i++) {
778                 GValueArray *values;
779
780                 values = g_ptr_array_index (channels, i);
781                 filter_conection_new_channel_cb (connection,
782                         g_value_get_boxed (g_value_array_get_nth (values, 0)),
783                         g_value_get_string (g_value_array_get_nth (values, 1)),
784                         g_value_get_uint (g_value_array_get_nth (values, 2)),
785                         g_value_get_uint (g_value_array_get_nth (values, 3)),
786                         TRUE, user_data, filter);
787         }
788 }
789
790 #ifdef HAVE_VOIP
791 static void
792 filter_connection_advertise_capabilities_cb (TpConnection    *connection,
793                                              const GPtrArray *capabilities,
794                                              const GError    *error,
795                                              gpointer         user_data,
796                                              GObject         *filter)
797 {
798         if (error) {
799                 empathy_debug (DEBUG_DOMAIN, "Error advertising capabilities: %s",
800                                error->message);
801         }
802 }
803 #endif
804
805 static void
806 filter_connection_ready_cb (TpConnection  *connection,
807                             gpointer       unused,
808                             EmpathyFilter *filter)
809 {
810 #ifdef HAVE_VOIP
811         GPtrArray   *capabilities;
812         GType        cap_type;
813         GValue       cap = {0, };
814         const gchar *remove = NULL;
815 #endif
816
817         empathy_debug (DEBUG_DOMAIN, "Connection ready, accepting new channels");
818
819         tp_cli_connection_connect_to_new_channel (connection,
820                                                   filter_conection_new_channel_cb,
821                                                   NULL, NULL,
822                                                   G_OBJECT (filter), NULL);
823         tp_cli_connection_call_list_channels (connection, -1,
824                                               filter_connection_list_channels_cb,
825                                               NULL, NULL,
826                                               G_OBJECT (filter));
827
828 #ifdef HAVE_VOIP
829         /* Advertise VoIP capabilities */
830         capabilities = g_ptr_array_sized_new (1);
831         cap_type = dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING,
832                                            G_TYPE_UINT, G_TYPE_INVALID);
833         g_value_init (&cap, cap_type);
834         g_value_take_boxed (&cap, dbus_g_type_specialized_construct (cap_type));
835         dbus_g_type_struct_set (&cap,
836                                 0, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
837                                 1, TP_CHANNEL_MEDIA_CAPABILITY_AUDIO |
838                                    TP_CHANNEL_MEDIA_CAPABILITY_VIDEO |
839                                    TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_STUN  |
840                                    TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_GTALK_P2P,
841                                 G_MAXUINT);
842         g_ptr_array_add (capabilities, g_value_get_boxed (&cap));
843
844         tp_cli_connection_interface_capabilities_call_advertise_capabilities (
845                 connection, -1,
846                 capabilities, &remove,
847                 filter_connection_advertise_capabilities_cb,
848                 NULL, NULL, G_OBJECT (filter));
849 #endif
850 }
851
852 static void
853 filter_update_account (EmpathyFilter *filter,
854                        McAccount     *account)
855 {
856         EmpathyFilterPriv *priv = GET_PRIV (filter);
857         TpConnection      *connection;
858         gboolean           ready;
859
860         connection = g_hash_table_lookup (priv->accounts, account);
861         if (connection) {
862                 return;
863         }
864
865         connection = mission_control_get_tpconnection (priv->mc, account, NULL);
866         if (!connection) {
867                 return;
868         }
869
870         g_hash_table_insert (priv->accounts, g_object_ref (account), connection);
871         g_signal_connect (connection, "invalidated",
872                           G_CALLBACK (filter_connection_invalidated_cb),
873                           filter);
874
875         g_object_get (connection, "connection-ready", &ready, NULL);
876         if (ready) {
877                 filter_connection_ready_cb (connection, NULL, filter);
878         } else {
879                 g_signal_connect (connection, "notify::connection-ready",
880                                   G_CALLBACK (filter_connection_ready_cb),
881                                   filter);
882         }
883 }
884
885 static void
886 filter_status_changed_cb (MissionControl           *mc,
887                           TpConnectionStatus        status,
888                           McPresence                presence,
889                           TpConnectionStatusReason  reason,
890                           const gchar              *unique_name,
891                           EmpathyFilter            *filter)
892 {
893         McAccount *account;
894
895         account = mc_account_lookup (unique_name);
896         filter_update_account (filter, account);
897         g_object_unref (account);
898 }
899
900 static void
901 filter_finalize (GObject *object)
902 {
903         EmpathyFilterPriv *priv = GET_PRIV (object);
904
905         empathy_disconnect_account_status_changed (priv->token);
906         g_object_unref (priv->mc);
907
908         g_slist_foreach (priv->events, (GFunc) filter_event_free, NULL);
909         g_slist_free (priv->events);
910
911         g_hash_table_destroy (priv->accounts);
912         g_hash_table_destroy (priv->tubes);
913 }
914
915 static void
916 filter_get_property (GObject    *object,
917                      guint       param_id,
918                      GValue     *value,
919                      GParamSpec *pspec)
920 {
921         EmpathyFilterPriv *priv = GET_PRIV (object);
922
923         switch (param_id) {
924         case PROP_TOP_EVENT:
925                 g_value_set_pointer (value, priv->events ? priv->events->data : NULL);
926                 break;
927         default:
928                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
929                 break;
930         };
931 }
932
933 static void
934 empathy_filter_class_init (EmpathyFilterClass *klass)
935 {
936         GObjectClass *object_class = G_OBJECT_CLASS (klass);
937
938         object_class->finalize = filter_finalize;
939         object_class->get_property = filter_get_property;
940
941         g_object_class_install_property (object_class,
942                                          PROP_TOP_EVENT,
943                                          g_param_spec_pointer ("top-event",
944                                                                "The top event",
945                                                                "The first event in the events list",
946                                                                G_PARAM_READABLE));
947
948         g_type_class_add_private (object_class, sizeof (EmpathyFilterPriv));
949 }
950
951 static void
952 empathy_filter_init (EmpathyFilter *filter)
953 {
954         EmpathyFilterPriv *priv = GET_PRIV (filter);
955         GList             *accounts, *l;
956
957         priv->tubes = g_hash_table_new_full (filter_channel_hash,
958                                              filter_channel_equal,
959                                              g_object_unref, NULL);
960
961         priv->mc = empathy_mission_control_new ();
962         priv->token = empathy_connect_to_account_status_changed (priv->mc,
963                 G_CALLBACK (filter_status_changed_cb),
964                 filter, NULL);
965
966         priv->accounts = g_hash_table_new_full (empathy_account_hash,
967                                                 empathy_account_equal,
968                                                 g_object_unref,
969                                                 g_object_unref);
970         accounts = mc_accounts_list_by_enabled (TRUE);
971         for (l = accounts; l; l = l->next) {
972                 filter_update_account (filter, l->data);
973                 g_object_unref (l->data);
974         }
975         g_list_free (accounts);
976 }
977
978 EmpathyFilter *
979 empathy_filter_new (void)
980 {
981         static EmpathyFilter *filter = NULL;
982
983         if (!filter) {
984                 filter = g_object_new (EMPATHY_TYPE_FILTER, NULL);
985                 g_object_add_weak_pointer (G_OBJECT (filter), (gpointer) &filter);
986         } else {
987                 g_object_ref (filter);
988         }
989
990         return filter;
991 }
992