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