]> git.0d.be Git - empathy.git/blob - src/empathy-event-manager.c
Add Answer with video to the default dialogs
[empathy.git] / src / empathy-event-manager.c
1 /*
2  * Copyright (C) 2007-2008 Collabora Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * Authors: Xavier Claessens <xclaesse@gmail.com>
19  *          Sjoerd Simons <sjoerd.simons@collabora.co.uk>
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25 #include <glib/gi18n.h>
26
27 #include <telepathy-glib/account-manager.h>
28 #include <telepathy-glib/util.h>
29 #include <telepathy-glib/interfaces.h>
30 #include <telepathy-glib/simple-approver.h>
31
32 #include <telepathy-yell/telepathy-yell.h>
33
34 #include <libempathy/empathy-channel-factory.h>
35 #include <libempathy/empathy-presence-manager.h>
36 #include <libempathy/empathy-tp-contact-factory.h>
37 #include <libempathy/empathy-contact-manager.h>
38 #include <libempathy/empathy-tp-chat.h>
39 #include <libempathy/empathy-tp-streamed-media.h>
40 #include <libempathy/empathy-tp-file.h>
41 #include <libempathy/empathy-utils.h>
42 #include <libempathy/empathy-gsettings.h>
43
44 #include <extensions/extensions.h>
45
46 #include <libempathy-gtk/empathy-images.h>
47 #include <libempathy-gtk/empathy-contact-dialogs.h>
48 #include <libempathy-gtk/empathy-sound-manager.h>
49 #include <libempathy-gtk/empathy-ui-utils.h>
50
51 #include "empathy-event-manager.h"
52 #include "empathy-main-window.h"
53
54 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
55 #include <libempathy/empathy-debug.h>
56
57 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyEventManager)
58
59 #define NOTIFICATION_TIMEOUT 2 /* seconds */
60
61 #define ACCEPT_WITHOUT_VIDEO 1
62
63 /* The time interval in milliseconds between 2 incoming rings */
64 #define MS_BETWEEN_RING 500
65
66 typedef struct {
67   EmpathyEventManager *manager;
68   TpChannelDispatchOperation *operation;
69   gulong invalidated_handler;
70   /* Remove contact if applicable */
71   EmpathyContact *contact;
72   /* option signal handler and it's instance */
73   gulong handler;
74   GObject *handler_instance;
75   /* optional accept widget */
76   GtkWidget *dialog;
77   /* Channel of the CDO that will be used during the approval */
78   TpChannel *main_channel;
79   gboolean auto_approved;
80 } EventManagerApproval;
81
82 typedef struct {
83   TpBaseClient *approver;
84   TpBaseClient *auth_approver;
85   EmpathyContactManager *contact_manager;
86   GSList *events;
87   /* Ongoing approvals */
88   GSList *approvals;
89
90   gint ringing;
91
92   GSettings *gsettings_notif;
93   GSettings *gsettings_ui;
94
95   EmpathySoundManager *sound_mgr;
96 } EmpathyEventManagerPriv;
97
98 typedef struct _EventPriv EventPriv;
99 typedef void (*EventFunc) (EventPriv *event);
100
101 struct _EventPriv {
102   EmpathyEvent public;
103   EmpathyEventManager *manager;
104   EventManagerApproval *approval;
105   EventFunc func;
106   gboolean inhibit;
107   gpointer user_data;
108   guint autoremove_timeout_id;
109 };
110
111 enum {
112   EVENT_ADDED,
113   EVENT_REMOVED,
114   EVENT_UPDATED,
115   LAST_SIGNAL
116 };
117
118 static guint signals[LAST_SIGNAL];
119
120 G_DEFINE_TYPE (EmpathyEventManager, empathy_event_manager, G_TYPE_OBJECT);
121
122 static EmpathyEventManager * manager_singleton = NULL;
123
124 static EventManagerApproval *
125 event_manager_approval_new (EmpathyEventManager *manager,
126   TpChannelDispatchOperation *operation,
127   TpChannel *main_channel)
128 {
129   EventManagerApproval *result = g_slice_new0 (EventManagerApproval);
130   result->operation = g_object_ref (operation);
131   result->manager = manager;
132   result->main_channel = g_object_ref (main_channel);
133
134   return result;
135 }
136
137 static void
138 event_manager_approval_free (EventManagerApproval *approval)
139 {
140   g_signal_handler_disconnect (approval->operation,
141     approval->invalidated_handler);
142   g_object_unref (approval->operation);
143
144   g_object_unref (approval->main_channel);
145
146   if (approval->handler != 0)
147     g_signal_handler_disconnect (approval->handler_instance,
148       approval->handler);
149
150   if (approval->handler_instance != NULL)
151     g_object_unref (approval->handler_instance);
152
153   if (approval->contact != NULL)
154     g_object_unref (approval->contact);
155
156   if (approval->dialog != NULL)
157     {
158       gtk_widget_destroy (approval->dialog);
159     }
160
161   g_slice_free (EventManagerApproval, approval);
162 }
163
164 static void
165 event_free (EventPriv *event)
166 {
167   g_free (event->public.icon_name);
168   g_free (event->public.header);
169   g_free (event->public.message);
170
171   if (event->autoremove_timeout_id != 0)
172     g_source_remove (event->autoremove_timeout_id);
173
174   tp_clear_object (&(event->public.contact));
175   tp_clear_object (&(event->public.account));
176
177   g_slice_free (EventPriv, event);
178 }
179
180 static void
181 event_remove (EventPriv *event)
182 {
183   EmpathyEventManagerPriv *priv = GET_PRIV (event->manager);
184
185   DEBUG ("Removing event %p", event);
186
187   priv->events = g_slist_remove (priv->events, event);
188   g_signal_emit (event->manager, signals[EVENT_REMOVED], 0, event);
189   event_free (event);
190 }
191
192 void
193 empathy_event_remove (EmpathyEvent *event_public)
194 {
195   EventPriv *event = (EventPriv *) event_public;
196
197   event_remove (event);
198 }
199
200 static gboolean
201 autoremove_event_timeout_cb (EventPriv *event)
202 {
203   event->autoremove_timeout_id = 0;
204   event_remove (event);
205   return FALSE;
206 }
207
208 static gboolean
209 display_notify_area (EmpathyEventManager *self)
210 {
211   EmpathyEventManagerPriv *priv = GET_PRIV (self);
212
213   return g_settings_get_boolean (priv->gsettings_ui,
214       EMPATHY_PREFS_UI_EVENTS_NOTIFY_AREA);
215 }
216
217 static void
218 event_manager_add (EmpathyEventManager *manager,
219     TpAccount *account,
220     EmpathyContact *contact,
221     EmpathyEventType type,
222     const gchar *icon_name,
223     const gchar *header,
224     const gchar *message,
225     EventManagerApproval *approval,
226     EventFunc func,
227     gpointer user_data)
228 {
229   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
230   EventPriv               *event;
231
232   event = g_slice_new0 (EventPriv);
233   event->public.account = account != NULL ? g_object_ref (account) : NULL;
234   event->public.contact = contact ? g_object_ref (contact) : NULL;
235   event->public.type = type;
236   event->public.icon_name = g_strdup (icon_name);
237   event->public.header = g_strdup (header);
238   event->public.message = g_strdup (message);
239   event->public.must_ack = (func != NULL);
240   event->inhibit = FALSE;
241   event->func = func;
242   event->user_data = user_data;
243   event->manager = manager;
244   event->approval = approval;
245
246   DEBUG ("Adding event %p", event);
247   priv->events = g_slist_prepend (priv->events, event);
248
249   if (!display_notify_area (manager))
250     {
251       /* Don't fire the 'event-added' signal as we activate the event now */
252       if (approval != NULL)
253         approval->auto_approved = TRUE;
254
255       empathy_event_activate (&event->public);
256       return;
257     }
258
259   g_signal_emit (event->manager, signals[EVENT_ADDED], 0, event);
260
261   if (!event->public.must_ack)
262     {
263       event->autoremove_timeout_id = g_timeout_add_seconds (
264           NOTIFICATION_TIMEOUT, (GSourceFunc) autoremove_event_timeout_cb,
265           event);
266     }
267 }
268
269 static void
270 handle_with_cb (GObject *source,
271     GAsyncResult *result,
272     gpointer user_data)
273 {
274   TpChannelDispatchOperation *cdo = TP_CHANNEL_DISPATCH_OPERATION (source);
275   GError *error = NULL;
276
277   if (!tp_channel_dispatch_operation_handle_with_finish (cdo, result, &error))
278     {
279       DEBUG ("HandleWith failed: %s\n", error->message);
280       g_error_free (error);
281     }
282 }
283
284 static void
285 handle_with_time_cb (GObject *source,
286     GAsyncResult *result,
287     gpointer user_data)
288 {
289   TpChannelDispatchOperation *cdo = TP_CHANNEL_DISPATCH_OPERATION (source);
290   GError *error = NULL;
291
292   if (!tp_channel_dispatch_operation_handle_with_time_finish (cdo, result,
293         &error))
294     {
295       if (g_error_matches (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED))
296         {
297           EventManagerApproval *approval = user_data;
298
299           DEBUG ("HandleWithTime() is not implemented, falling back to "
300               "HandleWith(). Please upgrade to telepathy-mission-control "
301               "5.5.0 or later");
302
303           tp_channel_dispatch_operation_handle_with_async (approval->operation,
304               NULL, handle_with_cb, approval);
305         }
306       else
307         {
308           DEBUG ("HandleWithTime failed: %s\n", error->message);
309         }
310       g_error_free (error);
311     }
312 }
313
314 static void
315 event_manager_approval_approve (EventManagerApproval *approval)
316 {
317   gint64 timestamp;
318
319   if (approval->auto_approved)
320     {
321       timestamp = TP_USER_ACTION_TIME_NOT_USER_ACTION;
322     }
323   else
324     {
325       timestamp = empathy_get_current_action_time ();
326     }
327
328   g_assert (approval->operation != NULL);
329
330   tp_channel_dispatch_operation_handle_with_time_async (approval->operation,
331       NULL, timestamp, handle_with_time_cb, approval);
332 }
333
334 static void
335 event_channel_process_func (EventPriv *event)
336 {
337   event_manager_approval_approve (event->approval);
338 }
339
340 static void
341 event_text_channel_process_func (EventPriv *event)
342 {
343   EmpathyTpChat *tp_chat;
344
345   if (event->approval->handler != 0)
346     {
347       tp_chat = EMPATHY_TP_CHAT (event->approval->handler_instance);
348
349       g_signal_handler_disconnect (tp_chat, event->approval->handler);
350       event->approval->handler = 0;
351     }
352
353   event_manager_approval_approve (event->approval);
354 }
355
356 static EventPriv *
357 event_lookup_by_approval (EmpathyEventManager *manager,
358   EventManagerApproval *approval)
359 {
360   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
361   GSList *l;
362   EventPriv *retval = NULL;
363
364   for (l = priv->events; l; l = l->next)
365     {
366       EventPriv *event = l->data;
367
368       if (event->approval == approval)
369         {
370           retval = event;
371           break;
372         }
373     }
374
375   return retval;
376 }
377
378 static void
379 event_update (EmpathyEventManager *manager, EventPriv *event,
380   const char *icon_name, const char *header, const char *msg)
381 {
382   g_free (event->public.icon_name);
383   g_free (event->public.header);
384   g_free (event->public.message);
385
386   event->public.icon_name = g_strdup (icon_name);
387   event->public.header = g_strdup (header);
388   event->public.message = g_strdup (msg);
389
390   g_signal_emit (manager, signals[EVENT_UPDATED], 0, event);
391 }
392
393 static void
394 reject_channel_claim_cb (GObject *source,
395     GAsyncResult *result,
396     gpointer user_data)
397 {
398   TpChannelDispatchOperation *cdo = TP_CHANNEL_DISPATCH_OPERATION (source);
399   GError *error = NULL;
400
401   if (!tp_channel_dispatch_operation_claim_with_finish (cdo, result, &error))
402     {
403       DEBUG ("Failed to claim channel: %s", error->message);
404
405       g_error_free (error);
406       goto out;
407     }
408
409   if (EMPATHY_IS_TP_STREAMED_MEDIA (user_data))
410     {
411       empathy_tp_streamed_media_close (user_data);
412     }
413   else if (TPY_IS_CALL_CHANNEL (user_data))
414     {
415       tpy_call_channel_hangup_async (user_data,
416           TPY_CALL_STATE_CHANGE_REASON_USER_REQUESTED,
417           "", "", NULL, NULL);
418       tp_channel_close_async (user_data, NULL, NULL);
419     }
420   else if (EMPATHY_IS_TP_CHAT (user_data))
421     {
422       empathy_tp_chat_leave (user_data, "");
423     }
424   else if (EMPATHY_IS_TP_FILE (user_data))
425     {
426       empathy_tp_file_close (user_data);
427     }
428
429 out:
430   g_object_unref (user_data);
431 }
432
433 static void
434 reject_auth_channel_claim_cb (GObject *source,
435     GAsyncResult *result,
436     gpointer user_data)
437 {
438   TpChannelDispatchOperation *cdo = TP_CHANNEL_DISPATCH_OPERATION (source);
439   GError *error = NULL;
440
441   if (!tp_channel_dispatch_operation_claim_with_finish (cdo, result, &error))
442     {
443       DEBUG ("Failed to claim channel: %s", error->message);
444
445       g_error_free (error);
446       return;
447     }
448
449   tp_cli_channel_call_close (TP_CHANNEL (user_data), -1,
450       NULL, NULL, NULL, NULL);
451 }
452
453 static void
454 reject_approval (EventManagerApproval *approval)
455 {
456   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
457
458   /* We have to claim the channel before closing it */
459
460   /* Unfortunately, we need to special case the auth channels for the
461    * time being as they don't have a wrapper object handler in
462    * approval->handler_instance as they're not actually handled by
463    * this process, so we can just use a noddy callback to call Close()
464    * directly. */
465   if (approval->handler_instance != NULL)
466     {
467       tp_channel_dispatch_operation_claim_with_async (approval->operation,
468           priv->approver, reject_channel_claim_cb,
469           g_object_ref (approval->handler_instance));
470     }
471   else if (tp_channel_get_channel_type_id (approval->main_channel)
472       == TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION)
473     {
474       tp_channel_dispatch_operation_claim_with_async (approval->operation,
475           priv->auth_approver, reject_auth_channel_claim_cb,
476           approval->main_channel);
477     }
478 }
479
480 static void
481 event_manager_call_window_confirmation_dialog_response_cb (GtkDialog *dialog,
482   gint response, gpointer user_data)
483 {
484   EventManagerApproval *approval = user_data;
485
486   gtk_widget_destroy (approval->dialog);
487   approval->dialog = NULL;
488
489   if (response == GTK_RESPONSE_ACCEPT)
490     {
491       event_manager_approval_approve (approval);
492     }
493   else if (response == ACCEPT_WITHOUT_VIDEO)
494     {
495       tpy_call_channel_send_video (TPY_CALL_CHANNEL (approval->main_channel),
496         FALSE);
497       event_manager_approval_approve (approval);
498     }
499   else
500     {
501       reject_approval (approval);
502     }
503 }
504
505 static void
506 event_channel_process_voip_func (EventPriv *event)
507 {
508   GtkWidget *dialog;
509   GtkWidget *button;
510   GtkWidget *image;
511   gboolean video;
512   gchar *title;
513   EmpathyEventType etype = event->public.type;
514
515   if (event->approval->dialog != NULL)
516     {
517       gtk_window_present (GTK_WINDOW (event->approval->dialog));
518       return;
519     }
520
521   if (etype == EMPATHY_EVENT_TYPE_VOIP)
522     {
523       EmpathyTpStreamedMedia *call;
524       call = EMPATHY_TP_STREAMED_MEDIA (event->approval->handler_instance);
525       video = empathy_tp_streamed_media_has_initial_video (call);
526     }
527   else if (etype == EMPATHY_EVENT_TYPE_CALL)
528     {
529       TpyCallChannel *call;
530       call = TPY_CALL_CHANNEL (event->approval->handler_instance);
531       g_object_get (G_OBJECT (call), "initial-video", &video, NULL);
532     }
533   else
534     {
535       g_warning ("Unknown event type: %d", event->public.type);
536       return;
537     }
538
539   dialog = gtk_message_dialog_new (NULL, 0,
540       GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
541       video ? _("Incoming video call"): _("Incoming call"));
542
543   gtk_message_dialog_format_secondary_text (
544     GTK_MESSAGE_DIALOG (dialog), video ?
545       _("%s is video calling you. Do you want to answer?"):
546       _("%s is calling you. Do you want to answer?"),
547       empathy_contact_get_alias (event->approval->contact));
548
549   title = g_strdup_printf (_("Incoming call from %s"),
550       empathy_contact_get_alias (event->approval->contact));
551
552   gtk_window_set_title (GTK_WINDOW (dialog), title);
553   g_free (title);
554
555   /* Set image of the dialog */
556   if (video)
557     {
558       image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_VIDEO_CALL,
559           GTK_ICON_SIZE_DIALOG);
560     }
561   else
562     {
563       image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_VOIP,
564           GTK_ICON_SIZE_DIALOG);
565     }
566
567   gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), image);
568   gtk_widget_show (image);
569
570   gtk_dialog_set_default_response (GTK_DIALOG (dialog),
571       GTK_RESPONSE_OK);
572
573   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
574       _("_Reject"), GTK_RESPONSE_REJECT);
575   image = gtk_image_new_from_icon_name ("call-stop",
576     GTK_ICON_SIZE_BUTTON);
577   gtk_button_set_image (GTK_BUTTON (button), image);
578
579   if (video && etype == EMPATHY_EVENT_TYPE_CALL)
580     {
581       button = gtk_dialog_add_button (GTK_DIALOG (dialog),
582         _("_Answer"), ACCEPT_WITHOUT_VIDEO);
583
584       image = gtk_image_new_from_icon_name ("call-start",
585         GTK_ICON_SIZE_BUTTON);
586       gtk_button_set_image (GTK_BUTTON (button), image);
587     }
588
589   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
590     video ? _("_Answer with video") : _("_Answer"), GTK_RESPONSE_ACCEPT);
591
592   image = gtk_image_new_from_icon_name ("call-start",
593         GTK_ICON_SIZE_BUTTON);
594   gtk_button_set_image (GTK_BUTTON (button), image);
595
596   g_signal_connect (dialog, "response",
597     G_CALLBACK (event_manager_call_window_confirmation_dialog_response_cb),
598     event->approval);
599
600   gtk_widget_show (dialog);
601
602   event->approval->dialog = dialog;
603 }
604
605 static void
606 event_manager_chat_message_received_cb (EmpathyTpChat *tp_chat,
607   EmpathyMessage *message,
608   EventManagerApproval *approval)
609 {
610   GtkWidget       *window;
611   EmpathyContact  *sender;
612   const gchar     *header;
613   const gchar     *msg;
614   EventPriv       *event;
615   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
616
617   /* try to update the event if it's referring to a chat which is already in the
618    * queue. */
619   event = event_lookup_by_approval (approval->manager, approval);
620
621   sender = empathy_message_get_sender (message);
622
623   /* We only want to show incoming messages */
624   if (empathy_contact_is_user (sender))
625     return;
626
627   header = empathy_contact_get_alias (sender);
628   msg = empathy_message_get_body (message);
629
630   if (event != NULL)
631     event_update (approval->manager, event, EMPATHY_IMAGE_NEW_MESSAGE, header,
632         msg);
633   else
634     event_manager_add (approval->manager, NULL, sender,
635         EMPATHY_EVENT_TYPE_CHAT, EMPATHY_IMAGE_NEW_MESSAGE, header, msg,
636         approval, event_text_channel_process_func, NULL);
637
638   window = empathy_main_window_dup ();
639
640   empathy_sound_manager_play (priv->sound_mgr, window,
641       EMPATHY_SOUND_CONVERSATION_NEW);
642
643   g_object_unref (window);
644 }
645
646 static void
647 event_manager_approval_done (EventManagerApproval *approval)
648 {
649   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
650   GSList                  *l;
651
652   if (approval->operation != NULL)
653     {
654       GQuark channel_type;
655
656       channel_type = tp_channel_get_channel_type_id (approval->main_channel);
657
658       if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA ||
659           channel_type == TPY_IFACE_QUARK_CHANNEL_TYPE_CALL)
660         {
661           priv->ringing--;
662           if (priv->ringing == 0)
663             empathy_sound_manager_stop (priv->sound_mgr,
664                 EMPATHY_SOUND_PHONE_INCOMING);
665         }
666     }
667
668   priv->approvals = g_slist_remove (priv->approvals, approval);
669
670   for (l = priv->events; l; l = l->next)
671     {
672       EventPriv *event = l->data;
673
674       if (event->approval == approval)
675         {
676           event_remove (event);
677           break;
678         }
679     }
680
681   event_manager_approval_free (approval);
682 }
683
684 static void
685 cdo_invalidated_cb (TpProxy *cdo,
686     guint domain,
687     gint code,
688     gchar *message,
689     EventManagerApproval *approval)
690 {
691   DEBUG ("ChannelDispatchOperation has been invalidated: %s", message);
692
693   event_manager_approval_done (approval);
694 }
695
696 static void
697 event_manager_call_state_changed_cb (TpyCallChannel *call,
698   TpyCallState state,
699   TpyCallFlags flags,
700    const GValueArray *call_state_reason,
701   GHashTable *call_state_details,
702   EventManagerApproval *approval)
703 {
704   if (state == TPY_CALL_STATE_ENDED)
705     {
706       DEBUG ("Call ended, seems we missed it :/");
707       reject_approval (approval);
708     }
709 }
710
711 static void
712 event_manager_call_channel_got_contact_cb (TpConnection *connection,
713                                  EmpathyContact *contact,
714                                  const GError *error,
715                                  gpointer user_data,
716                                  GObject *object)
717 {
718   EventManagerApproval *approval = (EventManagerApproval *) user_data;
719   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
720   GtkWidget *window;
721   TpyCallChannel *call;
722   gchar *header;
723   gboolean video;
724
725   call = TPY_CALL_CHANNEL (approval->handler_instance);
726
727   if (error != NULL)
728     {
729       DEBUG ("Can't get the contact for the call.. Rejecting?");
730       reject_approval (approval);
731       return;
732     }
733
734   if (tpy_call_channel_get_state (call, NULL, NULL) == TPY_CALL_STATE_ENDED)
735     {
736       DEBUG ("Call already ended, seems we missed it :/");
737       reject_approval (approval);
738       return;
739     }
740
741   approval->handler = g_signal_connect (call, "state-changed",
742     G_CALLBACK (event_manager_call_state_changed_cb), approval);
743
744   window = empathy_main_window_dup ();
745   approval->contact = g_object_ref (contact);
746
747   g_object_get (G_OBJECT (call), "initial-video", &video, NULL);
748
749   header = g_strdup_printf (
750     video ? _("Incoming video call from %s") :_("Incoming call from %s"),
751     empathy_contact_get_alias (approval->contact));
752
753   event_manager_add (approval->manager, NULL,
754       approval->contact, EMPATHY_EVENT_TYPE_CALL,
755       video ? EMPATHY_IMAGE_VIDEO_CALL : EMPATHY_IMAGE_VOIP,
756       header, NULL, approval,
757       event_channel_process_voip_func, NULL);
758
759   g_free (header);
760
761   priv->ringing++;
762   if (priv->ringing == 1)
763     empathy_sound_manager_start_playing (priv->sound_mgr, window,
764         EMPATHY_SOUND_PHONE_INCOMING, MS_BETWEEN_RING);
765
766   g_object_unref (window);
767 }
768
769 static void
770 event_manager_media_channel_got_contact (EventManagerApproval *approval)
771 {
772   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
773   GtkWidget *window = empathy_main_window_dup ();
774   gchar *header;
775   EmpathyTpStreamedMedia *call;
776   gboolean video;
777
778   call = EMPATHY_TP_STREAMED_MEDIA (approval->handler_instance);
779
780   video = empathy_tp_streamed_media_has_initial_video (call);
781
782   header = g_strdup_printf (
783     video ? _("Incoming video call from %s") :_("Incoming call from %s"),
784     empathy_contact_get_alias (approval->contact));
785
786   event_manager_add (approval->manager, NULL,
787       approval->contact, EMPATHY_EVENT_TYPE_VOIP,
788       video ? EMPATHY_IMAGE_VIDEO_CALL : EMPATHY_IMAGE_VOIP,
789       header, NULL, approval,
790       event_channel_process_voip_func, NULL);
791
792   g_free (header);
793
794   priv->ringing++;
795   if (priv->ringing == 1)
796     empathy_sound_manager_start_playing (priv->sound_mgr, window,
797         EMPATHY_SOUND_PHONE_INCOMING, MS_BETWEEN_RING);
798
799   g_object_unref (window);
800 }
801
802 static void
803 event_manager_media_channel_contact_changed_cb (EmpathyTpStreamedMedia *call,
804   GParamSpec *param, EventManagerApproval *approval)
805 {
806   EmpathyContact *contact;
807
808   g_object_get (G_OBJECT (call), "contact", &contact, NULL);
809
810   if (contact == NULL)
811     return;
812
813   approval->contact = contact;
814   event_manager_media_channel_got_contact (approval);
815 }
816
817 static void
818 invite_dialog_response_cb (GtkDialog *dialog,
819                            gint response,
820                            EventManagerApproval *approval)
821 {
822   gtk_widget_destroy (GTK_WIDGET (approval->dialog));
823   approval->dialog = NULL;
824
825   if (response != GTK_RESPONSE_OK)
826     {
827       /* close channel */
828       DEBUG ("Muc invitation rejected");
829
830       reject_approval (approval);
831
832       return;
833     }
834
835   DEBUG ("Muc invitation accepted");
836
837   /* We'll join the room when handling the channel */
838   event_manager_approval_approve (approval);
839 }
840
841 static void
842 event_room_channel_process_func (EventPriv *event)
843 {
844   GtkWidget *dialog, *button, *image;
845   TpChannel *channel = event->approval->main_channel;
846   gchar *title;
847
848   if (event->approval->dialog != NULL)
849     {
850       gtk_window_present (GTK_WINDOW (event->approval->dialog));
851       return;
852     }
853
854   /* create dialog */
855   dialog = gtk_message_dialog_new (NULL, 0,
856       GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Room invitation"));
857
858   title = g_strdup_printf (_("Invitation to join %s"),
859       tp_channel_get_identifier (channel));
860
861   gtk_window_set_title (GTK_WINDOW (dialog), title);
862   g_free (title);
863
864   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
865       _("%s is inviting you to join %s"),
866       empathy_contact_get_alias (event->approval->contact),
867       tp_channel_get_identifier (channel));
868
869   gtk_dialog_set_default_response (GTK_DIALOG (dialog),
870       GTK_RESPONSE_OK);
871
872   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
873       _("_Decline"), GTK_RESPONSE_CANCEL);
874   image = gtk_image_new_from_icon_name (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
875   gtk_button_set_image (GTK_BUTTON (button), image);
876
877   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
878       _("_Join"), GTK_RESPONSE_OK);
879   image = gtk_image_new_from_icon_name (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
880   gtk_button_set_image (GTK_BUTTON (button), image);
881
882   g_signal_connect (dialog, "response",
883       G_CALLBACK (invite_dialog_response_cb), event->approval);
884
885   gtk_widget_show (dialog);
886
887   event->approval->dialog = dialog;
888 }
889
890 static void
891 display_invite_room_dialog (EventManagerApproval *approval)
892 {
893   GtkWidget *window = empathy_main_window_dup ();
894   const gchar *invite_msg;
895   gchar *msg;
896   TpHandle self_handle;
897   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
898
899   self_handle = tp_channel_group_get_self_handle (approval->main_channel);
900   tp_channel_group_get_local_pending_info (approval->main_channel, self_handle,
901       NULL, NULL, &invite_msg);
902
903   if (approval->contact != NULL)
904     {
905       msg = g_strdup_printf (_("%s invited you to join %s"),
906           empathy_contact_get_alias (approval->contact),
907           tp_channel_get_identifier (approval->main_channel));
908     }
909   else
910     {
911       msg = g_strdup_printf (_("You have been invited to join %s"),
912           tp_channel_get_identifier (approval->main_channel));
913     }
914
915   event_manager_add (approval->manager, NULL,
916       approval->contact, EMPATHY_EVENT_TYPE_INVITATION,
917       EMPATHY_IMAGE_GROUP_MESSAGE, msg, invite_msg, approval,
918       event_room_channel_process_func, NULL);
919
920   empathy_sound_manager_play (priv->sound_mgr, window,
921       EMPATHY_SOUND_CONVERSATION_NEW);
922
923   g_free (msg);
924   g_object_unref (window);
925 }
926
927 static void
928 event_manager_muc_invite_got_contact_cb (TpConnection *connection,
929                                          EmpathyContact *contact,
930                                          const GError *error,
931                                          gpointer user_data,
932                                          GObject *object)
933 {
934   EventManagerApproval *approval = (EventManagerApproval *) user_data;
935
936   if (error != NULL)
937     {
938       DEBUG ("Error: %s", error->message);
939     }
940   else
941     {
942       approval->contact = g_object_ref (contact);
943     }
944
945   display_invite_room_dialog (approval);
946 }
947
948 static void
949 event_manager_ft_got_contact_cb (TpConnection *connection,
950                                  EmpathyContact *contact,
951                                  const GError *error,
952                                  gpointer user_data,
953                                  GObject *object)
954 {
955   EventManagerApproval *approval = (EventManagerApproval *) user_data;
956   GtkWidget *window = empathy_main_window_dup ();
957   char *header;
958   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
959
960   approval->contact = g_object_ref (contact);
961
962   header = g_strdup_printf (_("Incoming file transfer from %s"),
963                             empathy_contact_get_alias (approval->contact));
964
965   event_manager_add (approval->manager, NULL,
966       approval->contact, EMPATHY_EVENT_TYPE_TRANSFER,
967       EMPATHY_IMAGE_DOCUMENT_SEND, header, NULL,
968       approval, event_channel_process_func, NULL);
969
970   /* FIXME better sound for incoming file transfers ?*/
971   empathy_sound_manager_play (priv->sound_mgr, window,
972       EMPATHY_SOUND_CONVERSATION_NEW);
973
974   g_free (header);
975   g_object_unref (window);
976 }
977
978 static void
979 event_manager_auth_process_func (EventPriv *event)
980 {
981   empathy_event_approve ((EmpathyEvent *) event);
982 }
983
984 /* If there is a file-transfer, media, or auth channel consider it as
985  * the main one. */
986 static TpChannel *
987 find_main_channel (GList *channels)
988 {
989   GList *l;
990   TpChannel *text = NULL;
991
992   for (l = channels; l != NULL; l = g_list_next (l))
993     {
994       TpChannel *channel = l->data;
995       GQuark channel_type;
996
997       if (tp_proxy_get_invalidated (channel) != NULL)
998         continue;
999
1000       channel_type = tp_channel_get_channel_type_id (channel);
1001
1002       if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA ||
1003           channel_type == TPY_IFACE_QUARK_CHANNEL_TYPE_CALL ||
1004           channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER ||
1005           channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION)
1006         return channel;
1007
1008       else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
1009         text = channel;
1010     }
1011
1012   return text;
1013 }
1014
1015 static void
1016 approve_channels (TpSimpleApprover *approver,
1017     TpAccount *account,
1018     TpConnection *connection,
1019     GList *channels,
1020     TpChannelDispatchOperation *dispatch_operation,
1021     TpAddDispatchOperationContext *context,
1022     gpointer user_data)
1023 {
1024   EmpathyEventManager *self = user_data;
1025   EmpathyEventManagerPriv *priv = GET_PRIV (self);
1026   TpChannel *channel;
1027   EventManagerApproval *approval;
1028   GQuark channel_type;
1029
1030   channel = find_main_channel (channels);
1031   if (channel == NULL)
1032     {
1033       GError error = { TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
1034           "Unknown channel type" };
1035
1036       DEBUG ("Failed to find the main channel; ignoring");
1037
1038       tp_add_dispatch_operation_context_fail (context, &error);
1039       return;
1040     }
1041
1042   approval = event_manager_approval_new (self, dispatch_operation, channel);
1043   priv->approvals = g_slist_prepend (priv->approvals, approval);
1044
1045   approval->invalidated_handler = g_signal_connect (dispatch_operation,
1046       "invalidated", G_CALLBACK (cdo_invalidated_cb), approval);
1047
1048   channel_type = tp_channel_get_channel_type_id (channel);
1049
1050   if (TP_IS_TEXT_CHANNEL (channel))
1051     {
1052       EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (channel);
1053       GList *messages, *l;
1054
1055       approval->handler_instance = g_object_ref (tp_chat);
1056
1057       if (tp_proxy_has_interface (channel, TP_IFACE_CHANNEL_INTERFACE_GROUP))
1058         {
1059           /* Are we in local-pending ? */
1060           TpHandle inviter;
1061
1062           if (empathy_tp_chat_is_invited (tp_chat, &inviter))
1063             {
1064               /* We are invited to a room */
1065               DEBUG ("Have been invited to %s. Ask user if he wants to accept",
1066                   tp_channel_get_identifier (channel));
1067
1068               if (inviter != 0)
1069                 {
1070                   empathy_tp_contact_factory_get_from_handle (connection,
1071                       inviter, event_manager_muc_invite_got_contact_cb,
1072                       approval, NULL, G_OBJECT (self));
1073                 }
1074               else
1075                 {
1076                   display_invite_room_dialog (approval);
1077                 }
1078
1079               goto out;
1080             }
1081
1082           /* We are not invited, approve the channel right now */
1083           tp_add_dispatch_operation_context_accept (context);
1084
1085           approval->auto_approved = TRUE;
1086           event_manager_approval_approve (approval);
1087           return;
1088         }
1089
1090       /* 1-1 text channel, wait for the first message */
1091       approval->handler = g_signal_connect (tp_chat, "message-received-empathy",
1092         G_CALLBACK (event_manager_chat_message_received_cb), approval);
1093
1094       messages = (GList *) empathy_tp_chat_get_pending_messages (tp_chat);
1095       for (l = messages; l != NULL; l = g_list_next (l))
1096         {
1097           EmpathyMessage *msg = l->data;
1098
1099           event_manager_chat_message_received_cb (tp_chat, msg, approval);
1100         }
1101     }
1102   else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
1103     {
1104       EmpathyContact *contact;
1105       EmpathyTpStreamedMedia *call = empathy_tp_streamed_media_new (account,
1106         channel);
1107
1108       approval->handler_instance = G_OBJECT (call);
1109
1110       g_object_get (G_OBJECT (call), "contact", &contact, NULL);
1111
1112       if (contact == NULL)
1113         {
1114           g_signal_connect (call, "notify::contact",
1115             G_CALLBACK (event_manager_media_channel_contact_changed_cb),
1116             approval);
1117         }
1118       else
1119         {
1120           approval->contact = contact;
1121           event_manager_media_channel_got_contact (approval);
1122         }
1123
1124     }
1125   else if (channel_type == TPY_IFACE_QUARK_CHANNEL_TYPE_CALL)
1126     {
1127       TpyCallChannel *call = TPY_CALL_CHANNEL (channel);
1128       const gchar *id;
1129
1130       approval->handler_instance = g_object_ref (call);
1131
1132       id = tp_channel_get_identifier (channel);
1133
1134       empathy_tp_contact_factory_get_from_id (connection, id,
1135         event_manager_call_channel_got_contact_cb,
1136         approval, NULL, G_OBJECT (self));
1137     }
1138   else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER)
1139     {
1140       TpHandle handle;
1141       EmpathyTpFile *tp_file = empathy_tp_file_new (channel);
1142
1143       approval->handler_instance = G_OBJECT (tp_file);
1144
1145       handle = tp_channel_get_handle (channel, NULL);
1146
1147       empathy_tp_contact_factory_get_from_handle (connection, handle,
1148         event_manager_ft_got_contact_cb, approval, NULL, G_OBJECT (self));
1149     }
1150   else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION)
1151     {
1152       event_manager_add (approval->manager, account, NULL, EMPATHY_EVENT_TYPE_AUTH,
1153           GTK_STOCK_DIALOG_AUTHENTICATION, tp_account_get_display_name (account),
1154           _("Password required"), approval,
1155           event_manager_auth_process_func, NULL);
1156     }
1157   else
1158     {
1159       GError error = { TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
1160           "Invalid channel type" };
1161
1162       DEBUG ("Unknown channel type (%s), ignoring..",
1163           g_quark_to_string (channel_type));
1164
1165       tp_add_dispatch_operation_context_fail (context, &error);
1166       return;
1167     }
1168
1169 out:
1170   tp_add_dispatch_operation_context_accept (context);
1171 }
1172
1173 static void
1174 event_pending_subscribe_func (EventPriv *event)
1175 {
1176   empathy_subscription_dialog_show (event->public.contact, event->public.header,
1177       NULL);
1178   event_remove (event);
1179 }
1180
1181 static void
1182 event_manager_pendings_changed_cb (EmpathyContactList  *list,
1183   EmpathyContact *contact, EmpathyContact *actor,
1184   guint reason, gchar *message, gboolean is_pending,
1185   EmpathyEventManager *manager)
1186 {
1187   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
1188   gchar                   *header, *event_msg;
1189
1190   if (!is_pending)
1191     {
1192       GSList *l;
1193
1194       for (l = priv->events; l; l = l->next)
1195         {
1196           EventPriv *event = l->data;
1197
1198           if (event->public.contact == contact &&
1199               event->func == event_pending_subscribe_func)
1200             {
1201               event_remove (event);
1202               break;
1203             }
1204         }
1205
1206       return;
1207     }
1208
1209   header = g_strdup_printf (
1210       _("%s would like permission to see when you are online"),
1211       empathy_contact_get_alias (contact));
1212
1213   if (!EMP_STR_EMPTY (message))
1214     event_msg = g_strdup_printf (_("\nMessage: %s"), message);
1215   else
1216     event_msg = NULL;
1217
1218   event_manager_add (manager, NULL, contact, EMPATHY_EVENT_TYPE_SUBSCRIPTION,
1219       GTK_STOCK_DIALOG_QUESTION, header, event_msg, NULL,
1220       event_pending_subscribe_func, NULL);
1221
1222   g_free (event_msg);
1223   g_free (header);
1224 }
1225
1226 static void
1227 event_manager_presence_changed_cb (EmpathyContact *contact,
1228     TpConnectionPresenceType current,
1229     TpConnectionPresenceType previous,
1230     EmpathyEventManager *manager)
1231 {
1232   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
1233   TpAccount *account;
1234   EmpathyPresenceManager *presence_mgr;
1235   GtkWidget *window = empathy_main_window_dup ();
1236
1237   account = empathy_contact_get_account (contact);
1238   presence_mgr = empathy_presence_manager_dup_singleton ();
1239
1240   if (empathy_presence_manager_account_is_just_connected (presence_mgr, account))
1241     goto out;
1242
1243   if (tp_connection_presence_type_cmp_availability (previous,
1244         TP_CONNECTION_PRESENCE_TYPE_OFFLINE) > 0)
1245     {
1246       /* contact was online */
1247       if (tp_connection_presence_type_cmp_availability (current,
1248           TP_CONNECTION_PRESENCE_TYPE_OFFLINE) <= 0)
1249         {
1250           /* someone is logging off */
1251           empathy_sound_manager_play (priv->sound_mgr, window,
1252               EMPATHY_SOUND_CONTACT_DISCONNECTED);
1253
1254           if (g_settings_get_boolean (priv->gsettings_notif,
1255                 EMPATHY_PREFS_NOTIFICATIONS_CONTACT_SIGNOUT))
1256             {
1257               event_manager_add (manager, NULL, contact,
1258                   EMPATHY_EVENT_TYPE_PRESENCE_OFFLINE,
1259                   EMPATHY_IMAGE_AVATAR_DEFAULT,
1260                   empathy_contact_get_alias (contact), _("Disconnected"),
1261                   NULL, NULL, NULL);
1262             }
1263         }
1264     }
1265   else
1266     {
1267       /* contact was offline */
1268       if (tp_connection_presence_type_cmp_availability (current,
1269             TP_CONNECTION_PRESENCE_TYPE_OFFLINE) > 0)
1270         {
1271           /* someone is logging in */
1272           empathy_sound_manager_play (priv->sound_mgr, window,
1273               EMPATHY_SOUND_CONTACT_CONNECTED);
1274
1275           if (g_settings_get_boolean (priv->gsettings_notif,
1276                 EMPATHY_PREFS_NOTIFICATIONS_CONTACT_SIGNIN))
1277             {
1278               event_manager_add (manager, NULL, contact,
1279                   EMPATHY_EVENT_TYPE_PRESENCE_ONLINE,
1280                   EMPATHY_IMAGE_AVATAR_DEFAULT,
1281                   empathy_contact_get_alias (contact), _("Connected"),
1282                   NULL, NULL, NULL);
1283             }
1284         }
1285     }
1286
1287 out:
1288   g_object_unref (presence_mgr);
1289   g_object_unref (window);
1290 }
1291
1292 static void
1293 event_manager_members_changed_cb (EmpathyContactList  *list,
1294     EmpathyContact *contact,
1295     EmpathyContact *actor,
1296     guint reason,
1297     gchar *message,
1298     gboolean is_member,
1299     EmpathyEventManager *manager)
1300 {
1301   if (is_member)
1302     g_signal_connect (contact, "presence-changed",
1303         G_CALLBACK (event_manager_presence_changed_cb), manager);
1304   else
1305     g_signal_handlers_disconnect_by_func (contact,
1306         event_manager_presence_changed_cb, manager);
1307 }
1308
1309 static GObject *
1310 event_manager_constructor (GType type,
1311                            guint n_props,
1312                            GObjectConstructParam *props)
1313 {
1314         GObject *retval;
1315
1316         if (manager_singleton) {
1317                 retval = g_object_ref (manager_singleton);
1318         } else {
1319                 retval = G_OBJECT_CLASS (empathy_event_manager_parent_class)->constructor
1320                         (type, n_props, props);
1321
1322                 manager_singleton = EMPATHY_EVENT_MANAGER (retval);
1323                 g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
1324         }
1325
1326         return retval;
1327 }
1328
1329 static void
1330 event_manager_finalize (GObject *object)
1331 {
1332   EmpathyEventManagerPriv *priv = GET_PRIV (object);
1333
1334   if (priv->ringing > 0)
1335     empathy_sound_manager_stop (priv->sound_mgr, EMPATHY_SOUND_PHONE_INCOMING);
1336
1337   g_slist_foreach (priv->events, (GFunc) event_free, NULL);
1338   g_slist_free (priv->events);
1339   g_slist_foreach (priv->approvals, (GFunc) event_manager_approval_free, NULL);
1340   g_slist_free (priv->approvals);
1341   g_object_unref (priv->contact_manager);
1342   g_object_unref (priv->approver);
1343   g_object_unref (priv->auth_approver);
1344   g_object_unref (priv->gsettings_notif);
1345   g_object_unref (priv->gsettings_ui);
1346   g_object_unref (priv->sound_mgr);
1347 }
1348
1349 static void
1350 empathy_event_manager_class_init (EmpathyEventManagerClass *klass)
1351 {
1352   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1353
1354   object_class->finalize = event_manager_finalize;
1355   object_class->constructor = event_manager_constructor;
1356
1357   signals[EVENT_ADDED] =
1358     g_signal_new ("event-added",
1359       G_TYPE_FROM_CLASS (klass),
1360       G_SIGNAL_RUN_LAST,
1361       0,
1362       NULL, NULL,
1363       g_cclosure_marshal_VOID__POINTER,
1364       G_TYPE_NONE,
1365       1, G_TYPE_POINTER);
1366
1367   signals[EVENT_REMOVED] =
1368   g_signal_new ("event-removed",
1369       G_TYPE_FROM_CLASS (klass),
1370       G_SIGNAL_RUN_LAST,
1371       0,
1372       NULL, NULL,
1373       g_cclosure_marshal_VOID__POINTER,
1374       G_TYPE_NONE, 1, G_TYPE_POINTER);
1375
1376   signals[EVENT_UPDATED] =
1377   g_signal_new ("event-updated",
1378       G_TYPE_FROM_CLASS (klass),
1379       G_SIGNAL_RUN_LAST,
1380       0,
1381       NULL, NULL,
1382       g_cclosure_marshal_VOID__POINTER,
1383       G_TYPE_NONE, 1, G_TYPE_POINTER);
1384
1385   g_type_class_add_private (object_class, sizeof (EmpathyEventManagerPriv));
1386 }
1387
1388 static void
1389 empathy_event_manager_init (EmpathyEventManager *manager)
1390 {
1391   EmpathyEventManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
1392     EMPATHY_TYPE_EVENT_MANAGER, EmpathyEventManagerPriv);
1393   TpDBusDaemon *dbus;
1394   GError *error = NULL;
1395   EmpathyChannelFactory *factory;
1396
1397   manager->priv = priv;
1398
1399   priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1400   priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
1401
1402   priv->sound_mgr = empathy_sound_manager_dup_singleton ();
1403
1404   priv->contact_manager = empathy_contact_manager_dup_singleton ();
1405   g_signal_connect (priv->contact_manager, "pendings-changed",
1406     G_CALLBACK (event_manager_pendings_changed_cb), manager);
1407
1408   g_signal_connect (priv->contact_manager, "members-changed",
1409     G_CALLBACK (event_manager_members_changed_cb), manager);
1410
1411   dbus = tp_dbus_daemon_dup (&error);
1412   if (dbus == NULL)
1413     {
1414       DEBUG ("Failed to get TpDBusDaemon: %s", error->message);
1415       g_error_free (error);
1416       return;
1417     }
1418
1419   priv->approver = tp_simple_approver_new (dbus, "Empathy.EventManager", FALSE,
1420       approve_channels, manager, NULL);
1421
1422   /* EmpathyTpChat relies on this feature being prepared */
1423   tp_base_client_add_connection_features_varargs (priv->approver,
1424     TP_CONNECTION_FEATURE_CAPABILITIES, 0);
1425
1426   /* Private text channels */
1427   tp_base_client_take_approver_filter (priv->approver,
1428       tp_asv_new (
1429         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT,
1430         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
1431         NULL));
1432
1433   /* Muc text channels */
1434   tp_base_client_take_approver_filter (priv->approver,
1435       tp_asv_new (
1436         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT,
1437         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_ROOM,
1438         NULL));
1439
1440   /* File transfer */
1441   tp_base_client_take_approver_filter (priv->approver,
1442       tp_asv_new (
1443         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
1444           TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER,
1445         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
1446         NULL));
1447
1448   /* Calls */
1449   tp_base_client_take_approver_filter (priv->approver,
1450       tp_asv_new (
1451         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
1452           TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
1453         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
1454         NULL));
1455   tp_base_client_take_approver_filter (priv->approver,
1456       tp_asv_new (
1457         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
1458           TPY_IFACE_CHANNEL_TYPE_CALL,
1459         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
1460         NULL));
1461
1462   /* I don't feel good about doing this, and I'm sorry, but the
1463    * capabilities connection feature is added earlier because it's
1464    * needed for EmpathyTpChat. If the capabilities feature is required
1465    * then preparing an auth channel (which of course appears in the
1466    * CONNECTING state) will never be prepared. So the options are
1467    * either to create another approver like I've done, or to port
1468    * EmpathyTpChat and its users to not depend on the connection being
1469    * prepared with capabilities. I chose the former, obviously. :-) */
1470
1471   priv->auth_approver = tp_simple_approver_new (dbus,
1472       "Empathy.AuthEventManager", FALSE, approve_channels, manager,
1473       NULL);
1474
1475   /* SASL auth channels */
1476   tp_base_client_take_approver_filter (priv->auth_approver,
1477       tp_asv_new (
1478         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
1479           TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION,
1480         TP_PROP_CHANNEL_TYPE_SERVER_AUTHENTICATION_AUTHENTICATION_METHOD,
1481           G_TYPE_STRING,
1482           TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION,
1483         NULL));
1484
1485   factory = empathy_channel_factory_dup ();
1486
1487   tp_base_client_set_channel_factory (priv->approver,
1488       TP_CLIENT_CHANNEL_FACTORY (factory));
1489
1490   if (!tp_base_client_register (priv->approver, &error))
1491     {
1492       DEBUG ("Failed to register Approver: %s", error->message);
1493       g_error_free (error);
1494     }
1495
1496   if (!tp_base_client_register (priv->auth_approver, &error))
1497     {
1498       DEBUG ("Failed to register auth Approver: %s", error->message);
1499       g_error_free (error);
1500     }
1501
1502   g_object_unref (factory);
1503   g_object_unref (dbus);
1504 }
1505
1506 EmpathyEventManager *
1507 empathy_event_manager_dup_singleton (void)
1508 {
1509   return g_object_new (EMPATHY_TYPE_EVENT_MANAGER, NULL);
1510 }
1511
1512 GSList *
1513 empathy_event_manager_get_events (EmpathyEventManager *manager)
1514 {
1515   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
1516
1517   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
1518
1519   return priv->events;
1520 }
1521
1522 EmpathyEvent *
1523 empathy_event_manager_get_top_event (EmpathyEventManager *manager)
1524 {
1525   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
1526
1527   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
1528
1529   return priv->events ? priv->events->data : NULL;
1530 }
1531
1532 void
1533 empathy_event_activate (EmpathyEvent *event_public)
1534 {
1535   EventPriv *event = (EventPriv *) event_public;
1536
1537   g_return_if_fail (event_public != NULL);
1538
1539   if (event->func)
1540     event->func (event);
1541   else
1542     event_remove (event);
1543 }
1544
1545 void
1546 empathy_event_inhibit_updates (EmpathyEvent *event_public)
1547 {
1548   EventPriv *event = (EventPriv *) event_public;
1549
1550   g_return_if_fail (event_public != NULL);
1551
1552   event->inhibit = TRUE;
1553 }
1554
1555 void
1556 empathy_event_approve (EmpathyEvent *event_public)
1557 {
1558   EventPriv *event = (EventPriv *) event_public;
1559
1560   g_return_if_fail (event_public != NULL);
1561
1562   event_manager_approval_approve (event->approval);
1563 }
1564
1565 void
1566 empathy_event_decline (EmpathyEvent *event_public)
1567 {
1568   EventPriv *event = (EventPriv *) event_public;
1569
1570   g_return_if_fail (event_public != NULL);
1571
1572   reject_approval (event->approval);
1573 }