2 * Copyright (C) 2007-2008 Collabora Ltd.
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.
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.
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
18 * Authors: Xavier Claessens <xclaesse@gmail.com>
19 * Sjoerd Simons <sjoerd.simons@collabora.co.uk>
23 #include "empathy-event-manager.h"
25 #include <glib/gi18n.h>
26 #include <tp-account-widgets/tpaw-images.h>
27 #include <tp-account-widgets/tpaw-utils.h>
28 #include <telepathy-glib/telepathy-glib-dbus.h>
30 #include "empathy-call-utils.h"
31 #include "empathy-connection-aggregator.h"
32 #include "empathy-gsettings.h"
33 #include "empathy-images.h"
34 #include "empathy-presence-manager.h"
35 #include "empathy-sasl-mechanisms.h"
36 #include "empathy-sound-manager.h"
37 #include "empathy-subscription-dialog.h"
38 #include "empathy-tp-chat.h"
39 #include "empathy-ui-utils.h"
40 #include "empathy-utils.h"
42 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
43 #include "empathy-debug.h"
45 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyEventManager)
47 #define NOTIFICATION_TIMEOUT 2 /* seconds */
49 #define ACCEPT_WITHOUT_VIDEO 1
51 /* The time interval in milliseconds between 2 incoming rings */
52 #define MS_BETWEEN_RING 500
55 EmpathyEventManager
*manager
;
56 TpChannelDispatchOperation
*operation
;
57 gulong invalidated_handler
;
58 /* Remove contact if applicable */
59 EmpathyContact
*contact
;
60 /* option signal handler and it's instance */
62 GObject
*handler_instance
;
63 /* optional accept widget */
65 /* Channel of the CDO that will be used during the approval */
66 TpChannel
*main_channel
;
67 gboolean auto_approved
;
68 } EventManagerApproval
;
71 TpBaseClient
*approver
;
72 TpBaseClient
*auth_approver
;
73 EmpathyConnectionAggregator
*conn_aggregator
;
75 /* Ongoing approvals */
80 GSettings
*gsettings_notif
;
81 GSettings
*gsettings_ui
;
83 EmpathySoundManager
*sound_mgr
;
85 /* TpContact -> EmpathyContact */
87 } EmpathyEventManagerPriv
;
89 typedef struct _EventPriv EventPriv
;
90 typedef void (*EventFunc
) (EventPriv
*event
);
94 EmpathyEventManager
*manager
;
95 EventManagerApproval
*approval
;
99 guint autoremove_timeout_id
;
109 static guint signals
[LAST_SIGNAL
];
111 G_DEFINE_TYPE (EmpathyEventManager
, empathy_event_manager
, G_TYPE_OBJECT
);
113 static EmpathyEventManager
* manager_singleton
= NULL
;
115 static EventManagerApproval
*
116 event_manager_approval_new (EmpathyEventManager
*manager
,
117 TpChannelDispatchOperation
*operation
,
118 TpChannel
*main_channel
)
120 EventManagerApproval
*result
= g_slice_new0 (EventManagerApproval
);
121 result
->operation
= g_object_ref (operation
);
122 result
->manager
= manager
;
123 result
->main_channel
= g_object_ref (main_channel
);
129 event_manager_approval_free (EventManagerApproval
*approval
)
131 g_signal_handler_disconnect (approval
->operation
,
132 approval
->invalidated_handler
);
133 g_object_unref (approval
->operation
);
135 g_object_unref (approval
->main_channel
);
137 if (approval
->handler
!= 0)
138 g_signal_handler_disconnect (approval
->handler_instance
,
141 if (approval
->handler_instance
!= NULL
)
142 g_object_unref (approval
->handler_instance
);
144 if (approval
->contact
!= NULL
)
145 g_object_unref (approval
->contact
);
147 if (approval
->dialog
!= NULL
)
149 gtk_widget_destroy (approval
->dialog
);
152 g_slice_free (EventManagerApproval
, approval
);
156 event_free (EventPriv
*event
)
158 g_free (event
->public.icon_name
);
159 g_free (event
->public.header
);
160 g_free (event
->public.message
);
162 if (event
->autoremove_timeout_id
!= 0)
163 g_source_remove (event
->autoremove_timeout_id
);
165 tp_clear_object (&(event
->public.contact
));
166 tp_clear_object (&(event
->public.account
));
168 g_slice_free (EventPriv
, event
);
172 event_remove (EventPriv
*event
)
174 EmpathyEventManagerPriv
*priv
= GET_PRIV (event
->manager
);
176 DEBUG ("Removing event %p", event
);
178 priv
->events
= g_slist_remove (priv
->events
, event
);
179 g_signal_emit (event
->manager
, signals
[EVENT_REMOVED
], 0, event
);
184 empathy_event_remove (EmpathyEvent
*event_public
)
186 EventPriv
*event
= (EventPriv
*) event_public
;
188 event_remove (event
);
192 autoremove_event_timeout_cb (EventPriv
*event
)
194 event
->autoremove_timeout_id
= 0;
195 event_remove (event
);
200 display_notify_area (EmpathyEventManager
*self
)
202 EmpathyEventManagerPriv
*priv
= GET_PRIV (self
);
204 return g_settings_get_boolean (priv
->gsettings_ui
,
205 EMPATHY_PREFS_UI_EVENTS_NOTIFY_AREA
);
209 event_manager_add (EmpathyEventManager
*manager
,
211 EmpathyContact
*contact
,
212 EmpathyEventType type
,
213 const gchar
*icon_name
,
215 const gchar
*message
,
216 EventManagerApproval
*approval
,
220 EmpathyEventManagerPriv
*priv
= GET_PRIV (manager
);
223 event
= g_slice_new0 (EventPriv
);
224 event
->public.account
= account
!= NULL
? g_object_ref (account
) : NULL
;
225 event
->public.contact
= contact
? g_object_ref (contact
) : NULL
;
226 event
->public.type
= type
;
227 event
->public.icon_name
= g_strdup (icon_name
);
228 event
->public.header
= g_strdup (header
);
229 event
->public.message
= g_strdup (message
);
230 event
->public.must_ack
= (func
!= NULL
);
231 if (approval
!= NULL
)
232 event
->public.handler_instance
= approval
->handler_instance
;
233 event
->inhibit
= FALSE
;
235 event
->user_data
= user_data
;
236 event
->manager
= manager
;
237 event
->approval
= approval
;
239 DEBUG ("Adding event %p", event
);
240 priv
->events
= g_slist_prepend (priv
->events
, event
);
242 if (!display_notify_area (manager
))
244 /* Don't fire the 'event-added' signal as we activate the event now */
245 if (approval
!= NULL
)
246 approval
->auto_approved
= TRUE
;
248 empathy_event_activate (&event
->public);
252 g_signal_emit (event
->manager
, signals
[EVENT_ADDED
], 0, event
);
254 if (!event
->public.must_ack
)
256 event
->autoremove_timeout_id
= g_timeout_add_seconds (
257 NOTIFICATION_TIMEOUT
, (GSourceFunc
) autoremove_event_timeout_cb
,
263 handle_with_time_cb (GObject
*source
,
264 GAsyncResult
*result
,
267 TpChannelDispatchOperation
*cdo
= TP_CHANNEL_DISPATCH_OPERATION (source
);
268 GError
*error
= NULL
;
270 if (!tp_channel_dispatch_operation_handle_with_time_finish (cdo
, result
,
273 DEBUG ("HandleWithTime failed: %s\n", error
->message
);
274 g_error_free (error
);
279 event_manager_approval_approve (EventManagerApproval
*approval
)
283 if (approval
->auto_approved
)
285 timestamp
= TP_USER_ACTION_TIME_NOT_USER_ACTION
;
289 timestamp
= empathy_get_current_action_time ();
292 g_assert (approval
->operation
!= NULL
);
294 tp_channel_dispatch_operation_handle_with_time_async (approval
->operation
,
295 NULL
, timestamp
, handle_with_time_cb
, approval
);
299 event_channel_process_func (EventPriv
*event
)
301 event_manager_approval_approve (event
->approval
);
305 event_text_channel_process_func (EventPriv
*event
)
307 EmpathyTpChat
*tp_chat
;
309 if (event
->approval
->handler
!= 0)
311 tp_chat
= EMPATHY_TP_CHAT (event
->approval
->handler_instance
);
313 g_signal_handler_disconnect (tp_chat
, event
->approval
->handler
);
314 event
->approval
->handler
= 0;
317 event_manager_approval_approve (event
->approval
);
321 event_lookup_by_approval (EmpathyEventManager
*manager
,
322 EventManagerApproval
*approval
)
324 EmpathyEventManagerPriv
*priv
= GET_PRIV (manager
);
326 EventPriv
*retval
= NULL
;
328 for (l
= priv
->events
; l
; l
= l
->next
)
330 EventPriv
*event
= l
->data
;
332 if (event
->approval
== approval
)
343 event_update (EmpathyEventManager
*manager
, EventPriv
*event
,
344 const char *icon_name
, const char *header
, const char *msg
)
346 g_free (event
->public.icon_name
);
347 g_free (event
->public.header
);
348 g_free (event
->public.message
);
350 event
->public.icon_name
= g_strdup (icon_name
);
351 event
->public.header
= g_strdup (header
);
352 event
->public.message
= g_strdup (msg
);
354 g_signal_emit (manager
, signals
[EVENT_UPDATED
], 0, event
);
358 reject_channel_claim_cb (GObject
*source
,
359 GAsyncResult
*result
,
362 TpChannelDispatchOperation
*cdo
= TP_CHANNEL_DISPATCH_OPERATION (source
);
363 GError
*error
= NULL
;
365 if (!tp_channel_dispatch_operation_claim_with_finish (cdo
, result
, &error
))
367 DEBUG ("Failed to claim channel: %s", error
->message
);
369 g_error_free (error
);
373 if (TP_IS_CALL_CHANNEL (user_data
))
375 tp_call_channel_hangup_async (user_data
,
376 TP_CALL_STATE_CHANGE_REASON_USER_REQUESTED
,
378 tp_channel_close_async (user_data
, NULL
, NULL
);
380 else if (EMPATHY_IS_TP_CHAT (user_data
))
382 empathy_tp_chat_leave (user_data
, "");
384 else if (TP_IS_FILE_TRANSFER_CHANNEL (user_data
))
386 tp_channel_close_async (user_data
, NULL
, NULL
);
390 g_object_unref (user_data
);
394 reject_approval (EventManagerApproval
*approval
)
396 EmpathyEventManagerPriv
*priv
= GET_PRIV (approval
->manager
);
398 /* We have to claim the channel before closing it */
400 /* Unfortunately, we need to special case the auth channels for the
401 * time being as they don't have a wrapper object handler in
402 * approval->handler_instance as they're not actually handled by
403 * this process, so we can just use a noddy callback to call Close()
405 if (approval
->handler_instance
!= NULL
)
407 tp_channel_dispatch_operation_claim_with_async (approval
->operation
,
408 priv
->approver
, reject_channel_claim_cb
,
409 g_object_ref (approval
->handler_instance
));
411 else if (tp_channel_get_channel_type_id (approval
->main_channel
)
412 == TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
)
414 tp_channel_dispatch_operation_close_channels_async (approval
->operation
,
420 event_manager_call_window_confirmation_dialog_response_cb (GtkDialog
*dialog
,
421 gint response
, gpointer user_data
)
423 EventManagerApproval
*approval
= user_data
;
425 gtk_widget_destroy (approval
->dialog
);
426 approval
->dialog
= NULL
;
428 if (response
== GTK_RESPONSE_ACCEPT
)
430 event_manager_approval_approve (approval
);
432 else if (response
== ACCEPT_WITHOUT_VIDEO
)
434 empathy_call_channel_send_video (TP_CALL_CHANNEL (approval
->main_channel
),
436 event_manager_approval_approve (approval
);
440 reject_approval (approval
);
445 event_channel_process_voip_func (EventPriv
*event
)
452 EmpathyEventType etype
= event
->public.type
;
454 if (event
->approval
->dialog
!= NULL
)
456 gtk_window_present (GTK_WINDOW (event
->approval
->dialog
));
460 if (etype
== EMPATHY_EVENT_TYPE_CALL
)
463 call
= TP_CALL_CHANNEL (event
->approval
->handler_instance
);
464 video
= tp_call_channel_has_initial_video (call
, NULL
);
468 g_warning ("Unknown event type: %d", event
->public.type
);
472 dialog
= gtk_message_dialog_new (NULL
, 0,
473 GTK_MESSAGE_QUESTION
, GTK_BUTTONS_NONE
,
474 video
? _("Incoming video call"): _("Incoming call"));
476 gtk_message_dialog_format_secondary_text (
477 GTK_MESSAGE_DIALOG (dialog
), video
?
478 _("%s is video calling you. Do you want to answer?"):
479 _("%s is calling you. Do you want to answer?"),
480 empathy_contact_get_alias (event
->approval
->contact
));
482 title
= g_strdup_printf (_("Incoming call from %s"),
483 empathy_contact_get_alias (event
->approval
->contact
));
485 gtk_window_set_title (GTK_WINDOW (dialog
), title
);
488 /* Set image of the dialog */
491 image
= gtk_image_new_from_icon_name (EMPATHY_IMAGE_VIDEO_CALL
,
492 GTK_ICON_SIZE_DIALOG
);
496 image
= gtk_image_new_from_icon_name (EMPATHY_IMAGE_VOIP
,
497 GTK_ICON_SIZE_DIALOG
);
500 gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog
), image
);
501 gtk_widget_show (image
);
503 gtk_dialog_set_default_response (GTK_DIALOG (dialog
),
506 button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
507 _("_Reject"), GTK_RESPONSE_REJECT
);
508 image
= gtk_image_new_from_icon_name ("call-stop",
509 GTK_ICON_SIZE_BUTTON
);
510 gtk_button_set_image (GTK_BUTTON (button
), image
);
512 if (video
&& etype
== EMPATHY_EVENT_TYPE_CALL
)
514 button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
515 _("_Answer"), ACCEPT_WITHOUT_VIDEO
);
517 image
= gtk_image_new_from_icon_name ("call-start",
518 GTK_ICON_SIZE_BUTTON
);
519 gtk_button_set_image (GTK_BUTTON (button
), image
);
522 button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
523 video
? _("_Answer with video") : _("_Answer"), GTK_RESPONSE_ACCEPT
);
525 image
= gtk_image_new_from_icon_name ("call-start",
526 GTK_ICON_SIZE_BUTTON
);
527 gtk_button_set_image (GTK_BUTTON (button
), image
);
529 g_signal_connect (dialog
, "response",
530 G_CALLBACK (event_manager_call_window_confirmation_dialog_response_cb
),
533 gtk_widget_show (dialog
);
535 event
->approval
->dialog
= dialog
;
539 event_manager_chat_message_received_cb (EmpathyTpChat
*tp_chat
,
540 EmpathyMessage
*message
,
541 EventManagerApproval
*approval
)
543 EmpathyContact
*sender
;
547 EmpathyEventManagerPriv
*priv
= GET_PRIV (approval
->manager
);
549 /* try to update the event if it's referring to a chat which is already in the
551 event
= event_lookup_by_approval (approval
->manager
, approval
);
553 sender
= empathy_message_get_sender (message
);
555 /* We only want to show incoming messages */
556 if (empathy_contact_is_user (sender
))
559 header
= empathy_contact_get_alias (sender
);
560 msg
= empathy_message_get_body (message
);
563 event_update (approval
->manager
, event
, EMPATHY_IMAGE_NEW_MESSAGE
, header
,
566 event_manager_add (approval
->manager
, NULL
, sender
,
567 EMPATHY_EVENT_TYPE_CHAT
, EMPATHY_IMAGE_NEW_MESSAGE
, header
, msg
,
568 approval
, event_text_channel_process_func
, NULL
);
570 empathy_sound_manager_play (priv
->sound_mgr
, NULL
,
571 EMPATHY_SOUND_CONVERSATION_NEW
);
575 event_manager_approval_done (EventManagerApproval
*approval
)
577 EmpathyEventManagerPriv
*priv
= GET_PRIV (approval
->manager
);
580 if (approval
->operation
!= NULL
)
584 channel_type
= tp_channel_get_channel_type_id (approval
->main_channel
);
586 if (channel_type
== TP_IFACE_QUARK_CHANNEL_TYPE_CALL
)
589 if (priv
->ringing
== 0)
590 empathy_sound_manager_stop (priv
->sound_mgr
,
591 EMPATHY_SOUND_PHONE_INCOMING
);
595 priv
->approvals
= g_slist_remove (priv
->approvals
, approval
);
597 for (l
= priv
->events
; l
; l
= l
->next
)
599 EventPriv
*event
= l
->data
;
601 if (event
->approval
== approval
)
603 event_remove (event
);
608 event_manager_approval_free (approval
);
612 cdo_invalidated_cb (TpProxy
*cdo
,
616 EventManagerApproval
*approval
)
618 DEBUG ("ChannelDispatchOperation has been invalidated: %s", message
);
620 event_manager_approval_done (approval
);
624 event_manager_call_state_changed_cb (TpCallChannel
*call
,
627 TpCallStateReason
*reason
,
629 EventManagerApproval
*approval
)
631 if (state
== TP_CALL_STATE_ENDED
)
633 DEBUG ("Call ended, seems we missed it :/");
634 reject_approval (approval
);
639 invite_dialog_response_cb (GtkDialog
*dialog
,
641 EventManagerApproval
*approval
)
643 gtk_widget_destroy (GTK_WIDGET (approval
->dialog
));
644 approval
->dialog
= NULL
;
646 if (response
!= GTK_RESPONSE_OK
)
649 DEBUG ("Muc invitation rejected");
651 reject_approval (approval
);
656 DEBUG ("Muc invitation accepted");
658 /* We'll join the room when handling the channel */
659 event_manager_approval_approve (approval
);
663 event_room_channel_process_func (EventPriv
*event
)
665 GtkWidget
*dialog
, *button
, *image
;
666 TpChannel
*channel
= event
->approval
->main_channel
;
669 if (event
->approval
->dialog
!= NULL
)
671 gtk_window_present (GTK_WINDOW (event
->approval
->dialog
));
676 dialog
= gtk_message_dialog_new (NULL
, 0,
677 GTK_MESSAGE_QUESTION
, GTK_BUTTONS_NONE
, _("Room invitation"));
679 title
= g_strdup_printf (_("Invitation to join %s"),
680 tp_channel_get_identifier (channel
));
682 gtk_window_set_title (GTK_WINDOW (dialog
), title
);
685 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog
),
686 _("%s is inviting you to join %s"),
687 empathy_contact_get_alias (event
->approval
->contact
),
688 tp_channel_get_identifier (channel
));
690 gtk_dialog_set_default_response (GTK_DIALOG (dialog
),
693 button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
694 _("_Decline"), GTK_RESPONSE_CANCEL
);
695 image
= gtk_image_new_from_icon_name (GTK_STOCK_CANCEL
, GTK_ICON_SIZE_BUTTON
);
696 gtk_button_set_image (GTK_BUTTON (button
), image
);
698 button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
699 _("_Join"), GTK_RESPONSE_OK
);
700 image
= gtk_image_new_from_icon_name (GTK_STOCK_APPLY
, GTK_ICON_SIZE_BUTTON
);
701 gtk_button_set_image (GTK_BUTTON (button
), image
);
703 g_signal_connect (dialog
, "response",
704 G_CALLBACK (invite_dialog_response_cb
), event
->approval
);
706 gtk_widget_show (dialog
);
708 event
->approval
->dialog
= dialog
;
712 display_invite_room_dialog (EventManagerApproval
*approval
)
714 const gchar
*invite_msg
;
716 TpContact
*self_contact
;
717 EmpathyEventManagerPriv
*priv
= GET_PRIV (approval
->manager
);
719 self_contact
= tp_channel_group_get_self_contact (approval
->main_channel
);
720 tp_channel_group_get_local_pending_contact_info (approval
->main_channel
,
721 self_contact
, NULL
, NULL
, &invite_msg
);
723 if (approval
->contact
!= NULL
)
725 msg
= g_strdup_printf (_("%s invited you to join %s"),
726 empathy_contact_get_alias (approval
->contact
),
727 tp_channel_get_identifier (approval
->main_channel
));
731 msg
= g_strdup_printf (_("You have been invited to join %s"),
732 tp_channel_get_identifier (approval
->main_channel
));
735 event_manager_add (approval
->manager
, NULL
,
736 approval
->contact
, EMPATHY_EVENT_TYPE_INVITATION
,
737 EMPATHY_IMAGE_GROUP_MESSAGE
, msg
, invite_msg
, approval
,
738 event_room_channel_process_func
, NULL
);
740 empathy_sound_manager_play (priv
->sound_mgr
, NULL
,
741 EMPATHY_SOUND_CONVERSATION_NEW
);
747 event_manager_auth_process_func (EventPriv
*event
)
749 empathy_event_approve ((EmpathyEvent
*) event
);
752 /* If there is a file-transfer, media, or auth channel consider it as
755 find_main_channel (GList
*channels
)
758 TpChannel
*text
= NULL
;
760 for (l
= channels
; l
!= NULL
; l
= g_list_next (l
))
762 TpChannel
*channel
= l
->data
;
765 if (tp_proxy_get_invalidated (channel
) != NULL
)
768 channel_type
= tp_channel_get_channel_type_id (channel
);
770 if (channel_type
== TP_IFACE_QUARK_CHANNEL_TYPE_CALL
||
771 channel_type
== TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER
||
772 channel_type
== TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
)
775 else if (channel_type
== TP_IFACE_QUARK_CHANNEL_TYPE_TEXT
)
783 approve_text_channel (EmpathyEventManager
*self
,
784 EventManagerApproval
*approval
,
785 TpAddDispatchOperationContext
*context
,
786 EmpathyTpChat
*tp_chat
)
790 approval
->handler_instance
= g_object_ref (tp_chat
);
792 if (tp_proxy_has_interface (tp_chat
, TP_IFACE_CHANNEL_INTERFACE_GROUP
))
794 /* Are we in local-pending ? */
797 if (empathy_tp_chat_is_invited (tp_chat
, &inviter
))
799 /* We are invited to a room */
800 DEBUG ("Have been invited to %s. Ask user if he wants to accept",
801 tp_channel_get_identifier (TP_CHANNEL (tp_chat
)));
805 approval
->contact
= empathy_contact_dup_from_tp_contact (
809 display_invite_room_dialog (approval
);
810 tp_add_dispatch_operation_context_accept (context
);
815 /* We are not invited, approve the channel right now */
816 tp_add_dispatch_operation_context_accept (context
);
818 approval
->auto_approved
= TRUE
;
819 event_manager_approval_approve (approval
);
823 /* 1-1 text channel, wait for the first message */
824 approval
->handler
= g_signal_connect (tp_chat
, "message-received-empathy",
825 G_CALLBACK (event_manager_chat_message_received_cb
), approval
);
827 messages
= (GList
*) empathy_tp_chat_get_pending_messages (tp_chat
);
828 for (l
= messages
; l
!= NULL
; l
= g_list_next (l
))
830 EmpathyMessage
*msg
= l
->data
;
832 event_manager_chat_message_received_cb (tp_chat
, msg
, approval
);
835 tp_add_dispatch_operation_context_accept (context
);
839 approval_set_target_contact (EventManagerApproval
*approval
,
844 contact
= tp_channel_get_target_contact (channel
);
845 approval
->contact
= empathy_contact_dup_from_tp_contact (contact
);
849 approve_call_channel (EmpathyEventManager
*self
,
850 EventManagerApproval
*approval
,
851 TpAddDispatchOperationContext
*context
,
854 TpChannel
*channel
= TP_CHANNEL (call
);
855 EmpathyEventManagerPriv
*priv
= GET_PRIV (approval
->manager
);
859 approval
->handler_instance
= g_object_ref (call
);
860 approval_set_target_contact (approval
, channel
);
862 tp_add_dispatch_operation_context_accept (context
);
864 if (tp_call_channel_get_state (call
, NULL
, NULL
, NULL
) == TP_CALL_STATE_ENDED
)
866 DEBUG ("Call already ended, seems we missed it :/");
867 reject_approval (approval
);
871 approval
->handler
= g_signal_connect (call
, "state-changed",
872 G_CALLBACK (event_manager_call_state_changed_cb
), approval
);
874 g_object_get (G_OBJECT (call
), "initial-video", &video
, NULL
);
876 header
= g_strdup_printf (
877 video
? _("Incoming video call from %s") :_("Incoming call from %s"),
878 empathy_contact_get_alias (approval
->contact
));
880 event_manager_add (approval
->manager
, NULL
,
881 approval
->contact
, EMPATHY_EVENT_TYPE_CALL
,
882 video
? EMPATHY_IMAGE_VIDEO_CALL
: EMPATHY_IMAGE_VOIP
,
883 header
, NULL
, approval
,
884 event_channel_process_voip_func
, NULL
);
889 if (priv
->ringing
== 1)
891 TpAccountManager
*am
= tp_account_manager_dup ();
892 TpConnectionPresenceType presence
;
894 presence
= tp_account_manager_get_most_available_presence (am
,
897 if (presence
!= TP_CONNECTION_PRESENCE_TYPE_BUSY
)
898 empathy_sound_manager_start_playing (priv
->sound_mgr
, NULL
,
899 EMPATHY_SOUND_PHONE_INCOMING
, MS_BETWEEN_RING
);
906 approve_ft_channel (EmpathyEventManager
*self
,
907 EventManagerApproval
*approval
,
908 TpAddDispatchOperationContext
*context
,
909 TpFileTransferChannel
*ft
)
911 TpChannel
*channel
= TP_CHANNEL (ft
);
912 EmpathyEventManagerPriv
*priv
= GET_PRIV (approval
->manager
);
915 approval
->handler_instance
= g_object_ref (ft
);
916 approval_set_target_contact (approval
, channel
);
918 tp_add_dispatch_operation_context_accept (context
);
920 header
= g_strdup_printf (_("Incoming file transfer from %s"),
921 empathy_contact_get_alias (approval
->contact
));
923 event_manager_add (approval
->manager
, NULL
,
924 approval
->contact
, EMPATHY_EVENT_TYPE_TRANSFER
,
925 EMPATHY_IMAGE_DOCUMENT_SEND
, header
, NULL
,
926 approval
, event_channel_process_func
, NULL
);
928 /* FIXME better sound for incoming file transfers ?*/
929 empathy_sound_manager_play (priv
->sound_mgr
, NULL
,
930 EMPATHY_SOUND_CONVERSATION_NEW
);
936 approve_sasl_channel (EmpathyEventManager
*self
,
938 EventManagerApproval
*approval
,
939 TpAddDispatchOperationContext
*context
,
942 if (empathy_sasl_channel_supports_mechanism (channel
, "X-TELEPATHY-PASSWORD"))
944 event_manager_add (approval
->manager
, account
, NULL
,
945 EMPATHY_EVENT_TYPE_AUTH
,
946 GTK_STOCK_DIALOG_AUTHENTICATION
,
947 tp_account_get_display_name (account
),
948 _("Password required"), approval
,
949 event_manager_auth_process_func
, NULL
);
953 GError error
= { TP_ERROR
, TP_ERROR_NOT_IMPLEMENTED
,
954 "Support only X-TELEPATHY-PASSWORD auth method" };
956 tp_add_dispatch_operation_context_fail (context
, &error
);
960 tp_add_dispatch_operation_context_accept (context
);
964 approve_channels (TpSimpleApprover
*approver
,
966 TpConnection
*connection
,
968 TpChannelDispatchOperation
*dispatch_operation
,
969 TpAddDispatchOperationContext
*context
,
972 EmpathyEventManager
*self
= user_data
;
973 EmpathyEventManagerPriv
*priv
= GET_PRIV (self
);
975 EventManagerApproval
*approval
;
978 channel
= find_main_channel (channels
);
981 GError error
= { TP_ERROR
, TP_ERROR_INVALID_ARGUMENT
,
982 "Unknown channel type" };
984 DEBUG ("Failed to find the main channel; ignoring");
986 tp_add_dispatch_operation_context_fail (context
, &error
);
990 approval
= event_manager_approval_new (self
, dispatch_operation
, channel
);
991 priv
->approvals
= g_slist_prepend (priv
->approvals
, approval
);
993 approval
->invalidated_handler
= g_signal_connect (dispatch_operation
,
994 "invalidated", G_CALLBACK (cdo_invalidated_cb
), approval
);
996 channel_type
= tp_channel_get_channel_type_id (channel
);
998 if (EMPATHY_IS_TP_CHAT (channel
))
1000 approve_text_channel (self
, approval
, context
, EMPATHY_TP_CHAT (channel
));
1002 else if (TP_IS_CALL_CHANNEL (channel
))
1004 approve_call_channel (self
, approval
, context
, TP_CALL_CHANNEL (channel
));
1006 else if (TP_IS_FILE_TRANSFER_CHANNEL (channel
))
1008 approve_ft_channel (self
, approval
, context
,
1009 TP_FILE_TRANSFER_CHANNEL (channel
));
1011 else if (channel_type
== TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
)
1013 approve_sasl_channel (self
, account
, approval
, context
, channel
);
1017 GError error
= { TP_ERROR
, TP_ERROR_INVALID_ARGUMENT
,
1018 "Invalid channel type" };
1020 DEBUG ("Unknown channel type (%s), ignoring..",
1021 g_quark_to_string (channel_type
));
1023 tp_add_dispatch_operation_context_fail (context
, &error
);
1029 event_pending_subscribe_func (EventPriv
*event
)
1032 FolksIndividual
*individual
;
1034 individual
= empathy_ensure_individual_from_tp_contact (
1035 empathy_contact_get_tp_contact (event
->public.contact
));
1037 dialog
= empathy_subscription_dialog_new (individual
, event
->public.message
);
1038 gtk_window_present (GTK_WINDOW (dialog
));
1040 event_remove (event
);
1042 g_object_unref (individual
);
1046 check_publish_state (EmpathyEventManager
*self
,
1047 TpContact
*tp_contact
)
1049 EmpathyEventManagerPriv
*priv
= GET_PRIV (self
);
1050 gchar
*header
, *event_msg
;
1051 TpSubscriptionState state
;
1052 EmpathyContact
*contact
;
1053 const gchar
*message
;
1055 state
= tp_contact_get_publish_state (tp_contact
);
1057 contact
= empathy_contact_dup_from_tp_contact (tp_contact
);
1059 if (state
!= TP_SUBSCRIPTION_STATE_ASK
)
1063 for (l
= priv
->events
; l
; l
= l
->next
)
1065 EventPriv
*event
= l
->data
;
1067 if (event
->public.contact
== contact
&&
1068 event
->func
== event_pending_subscribe_func
)
1070 event_remove (event
);
1078 header
= g_strdup_printf (
1079 _("%s would like permission to see when you are online"),
1080 empathy_contact_get_alias (contact
));
1082 message
= tp_contact_get_publish_request (tp_contact
);
1084 if (!TPAW_STR_EMPTY (message
))
1085 event_msg
= g_strdup_printf (_("\nMessage: %s"), message
);
1089 event_manager_add (self
, NULL
, contact
, EMPATHY_EVENT_TYPE_SUBSCRIPTION
,
1090 GTK_STOCK_DIALOG_QUESTION
, header
, event_msg
, NULL
,
1091 event_pending_subscribe_func
, NULL
);
1097 g_object_unref (contact
);
1101 event_manager_publish_state_changed_cb (TpContact
*contact
,
1103 EmpathyEventManager
*self
)
1105 check_publish_state (self
, contact
);
1109 check_presence (EmpathyEventManager
*manager
,
1110 EmpathyContact
*contact
,
1111 TpConnectionPresenceType current
,
1112 TpConnectionPresenceType previous
)
1114 EmpathyEventManagerPriv
*priv
= GET_PRIV (manager
);
1116 EmpathyPresenceManager
*presence_mgr
;
1118 account
= empathy_contact_get_account (contact
);
1119 presence_mgr
= empathy_presence_manager_dup_singleton ();
1121 if (empathy_presence_manager_account_is_just_connected (presence_mgr
, account
))
1124 if (tp_connection_presence_type_cmp_availability (previous
,
1125 TP_CONNECTION_PRESENCE_TYPE_OFFLINE
) > 0)
1127 /* contact was online */
1128 if (tp_connection_presence_type_cmp_availability (current
,
1129 TP_CONNECTION_PRESENCE_TYPE_OFFLINE
) <= 0)
1131 /* someone is logging off */
1132 empathy_sound_manager_play (priv
->sound_mgr
, NULL
,
1133 EMPATHY_SOUND_CONTACT_DISCONNECTED
);
1135 if (g_settings_get_boolean (priv
->gsettings_notif
,
1136 EMPATHY_PREFS_NOTIFICATIONS_CONTACT_SIGNOUT
))
1138 event_manager_add (manager
, NULL
, contact
,
1139 EMPATHY_EVENT_TYPE_PRESENCE_OFFLINE
,
1140 TPAW_IMAGE_AVATAR_DEFAULT
,
1141 empathy_contact_get_alias (contact
), _("Disconnected"),
1148 /* contact was offline */
1149 if (tp_connection_presence_type_cmp_availability (current
,
1150 TP_CONNECTION_PRESENCE_TYPE_OFFLINE
) > 0)
1152 /* someone is logging in */
1153 empathy_sound_manager_play (priv
->sound_mgr
, NULL
,
1154 EMPATHY_SOUND_CONTACT_CONNECTED
);
1156 if (g_settings_get_boolean (priv
->gsettings_notif
,
1157 EMPATHY_PREFS_NOTIFICATIONS_CONTACT_SIGNIN
))
1159 event_manager_add (manager
, NULL
, contact
,
1160 EMPATHY_EVENT_TYPE_PRESENCE_ONLINE
,
1161 TPAW_IMAGE_AVATAR_DEFAULT
,
1162 empathy_contact_get_alias (contact
), _("Connected"),
1169 g_object_unref (presence_mgr
);
1173 event_manager_presence_changed_cb (EmpathyContact
*contact
,
1174 TpConnectionPresenceType current
,
1175 TpConnectionPresenceType previous
,
1176 EmpathyEventManager
*manager
)
1178 check_presence (manager
, contact
, current
, previous
);
1182 event_manager_constructor (GType type
,
1184 GObjectConstructParam
*props
)
1188 if (manager_singleton
) {
1189 retval
= g_object_ref (manager_singleton
);
1191 retval
= G_OBJECT_CLASS (empathy_event_manager_parent_class
)->constructor
1192 (type
, n_props
, props
);
1194 manager_singleton
= EMPATHY_EVENT_MANAGER (retval
);
1195 g_object_add_weak_pointer (retval
, (gpointer
) &manager_singleton
);
1202 event_manager_finalize (GObject
*object
)
1204 EmpathyEventManagerPriv
*priv
= GET_PRIV (object
);
1206 if (priv
->ringing
> 0)
1207 empathy_sound_manager_stop (priv
->sound_mgr
, EMPATHY_SOUND_PHONE_INCOMING
);
1209 g_slist_foreach (priv
->events
, (GFunc
) event_free
, NULL
);
1210 g_slist_free (priv
->events
);
1211 g_slist_foreach (priv
->approvals
, (GFunc
) event_manager_approval_free
, NULL
);
1212 g_slist_free (priv
->approvals
);
1213 g_object_unref (priv
->conn_aggregator
);
1214 g_object_unref (priv
->approver
);
1215 g_object_unref (priv
->auth_approver
);
1216 g_object_unref (priv
->gsettings_notif
);
1217 g_object_unref (priv
->gsettings_ui
);
1218 g_object_unref (priv
->sound_mgr
);
1219 g_hash_table_unref (priv
->contacts
);
1223 empathy_event_manager_class_init (EmpathyEventManagerClass
*klass
)
1225 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
1227 object_class
->finalize
= event_manager_finalize
;
1228 object_class
->constructor
= event_manager_constructor
;
1230 signals
[EVENT_ADDED
] =
1231 g_signal_new ("event-added",
1232 G_TYPE_FROM_CLASS (klass
),
1236 g_cclosure_marshal_generic
,
1240 signals
[EVENT_REMOVED
] =
1241 g_signal_new ("event-removed",
1242 G_TYPE_FROM_CLASS (klass
),
1246 g_cclosure_marshal_generic
,
1247 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
1249 signals
[EVENT_UPDATED
] =
1250 g_signal_new ("event-updated",
1251 G_TYPE_FROM_CLASS (klass
),
1255 g_cclosure_marshal_generic
,
1256 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
1258 g_type_class_add_private (object_class
, sizeof (EmpathyEventManagerPriv
));
1262 contact_list_changed_cb (EmpathyConnectionAggregator
*aggregator
,
1265 EmpathyEventManager
*self
)
1267 EmpathyEventManagerPriv
*priv
= GET_PRIV (self
);
1270 for (i
= 0; i
< added
->len
; i
++)
1272 TpContact
*tp_contact
= g_ptr_array_index (added
, i
);
1273 EmpathyContact
*contact
;
1275 if (g_hash_table_lookup (priv
->contacts
, tp_contact
) != NULL
)
1278 contact
= empathy_contact_dup_from_tp_contact (tp_contact
);
1280 tp_g_signal_connect_object (contact
, "presence-changed",
1281 G_CALLBACK (event_manager_presence_changed_cb
), self
, 0);
1283 check_presence (self
, contact
,
1284 empathy_contact_get_presence (contact
),
1285 TP_CONNECTION_PRESENCE_TYPE_OFFLINE
);
1287 tp_g_signal_connect_object (tp_contact
, "notify::publish-state",
1288 G_CALLBACK (event_manager_publish_state_changed_cb
), self
, 0);
1290 check_publish_state (self
, tp_contact
);
1292 /* Pass ownership to the hash table */
1293 g_hash_table_insert (priv
->contacts
, g_object_ref (tp_contact
), contact
);
1296 for (i
= 0; i
< removed
->len
; i
++)
1298 TpContact
*tp_contact
= g_ptr_array_index (removed
, i
);
1299 EmpathyContact
*contact
;
1301 contact
= g_hash_table_lookup (priv
->contacts
, tp_contact
);
1302 if (contact
== NULL
)
1305 g_signal_handlers_disconnect_by_func (contact
,
1306 event_manager_presence_changed_cb
, self
);
1308 g_signal_handlers_disconnect_by_func (tp_contact
,
1309 event_manager_publish_state_changed_cb
, self
);
1311 g_hash_table_remove (priv
->contacts
, tp_contact
);
1316 empathy_event_manager_init (EmpathyEventManager
*manager
)
1318 EmpathyEventManagerPriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (manager
,
1319 EMPATHY_TYPE_EVENT_MANAGER
, EmpathyEventManagerPriv
);
1320 GError
*error
= NULL
;
1321 TpAccountManager
*am
;
1322 GPtrArray
*contacts
, *empty
;
1324 manager
->priv
= priv
;
1326 priv
->gsettings_notif
= g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA
);
1327 priv
->gsettings_ui
= g_settings_new (EMPATHY_PREFS_UI_SCHEMA
);
1329 priv
->sound_mgr
= empathy_sound_manager_dup_singleton ();
1331 priv
->contacts
= g_hash_table_new_full (NULL
, NULL
, g_object_unref
,
1334 priv
->conn_aggregator
= empathy_connection_aggregator_dup_singleton ();
1336 tp_g_signal_connect_object (priv
->conn_aggregator
, "contact-list-changed",
1337 G_CALLBACK (contact_list_changed_cb
), manager
, 0);
1339 contacts
= empathy_connection_aggregator_dup_all_contacts (
1340 priv
->conn_aggregator
);
1342 empty
= g_ptr_array_new ();
1344 contact_list_changed_cb (priv
->conn_aggregator
, contacts
, empty
, manager
);
1346 g_ptr_array_unref (contacts
);
1347 g_ptr_array_unref (empty
);
1349 am
= tp_account_manager_dup ();
1351 priv
->approver
= tp_simple_approver_new_with_am (am
, "Empathy.EventManager",
1352 FALSE
, approve_channels
, manager
, NULL
);
1354 /* Private text channels */
1355 tp_base_client_take_approver_filter (priv
->approver
,
1357 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
, TP_IFACE_CHANNEL_TYPE_TEXT
,
1358 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, G_TYPE_UINT
, TP_HANDLE_TYPE_CONTACT
,
1361 /* Muc text channels */
1362 tp_base_client_take_approver_filter (priv
->approver
,
1364 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
, TP_IFACE_CHANNEL_TYPE_TEXT
,
1365 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, G_TYPE_UINT
, TP_HANDLE_TYPE_ROOM
,
1369 tp_base_client_take_approver_filter (priv
->approver
,
1371 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
,
1372 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
,
1373 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, G_TYPE_UINT
, TP_HANDLE_TYPE_CONTACT
,
1377 tp_base_client_take_approver_filter (priv
->approver
,
1379 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
,
1380 TP_IFACE_CHANNEL_TYPE_CALL
,
1381 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, G_TYPE_UINT
, TP_HANDLE_TYPE_CONTACT
,
1384 /* I don't feel good about doing this, and I'm sorry, but the
1385 * capabilities connection feature is added earlier because it's
1386 * needed for EmpathyTpChat. If the capabilities feature is required
1387 * then preparing an auth channel (which of course appears in the
1388 * CONNECTING state) will never be prepared. So the options are
1389 * either to create another approver like I've done, or to port
1390 * EmpathyTpChat and its users to not depend on the connection being
1391 * prepared with capabilities. I chose the former, obviously. :-) */
1393 priv
->auth_approver
= tp_simple_approver_new_with_am (am
,
1394 "Empathy.AuthEventManager", FALSE
, approve_channels
, manager
,
1397 /* SASL auth channels */
1398 tp_base_client_take_approver_filter (priv
->auth_approver
,
1400 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
,
1401 TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION
,
1402 TP_PROP_CHANNEL_TYPE_SERVER_AUTHENTICATION_AUTHENTICATION_METHOD
,
1404 TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION
,
1407 if (!tp_base_client_register (priv
->approver
, &error
))
1409 DEBUG ("Failed to register Approver: %s", error
->message
);
1410 g_error_free (error
);
1413 if (!tp_base_client_register (priv
->auth_approver
, &error
))
1415 DEBUG ("Failed to register auth Approver: %s", error
->message
);
1416 g_error_free (error
);
1419 g_object_unref (am
);
1422 EmpathyEventManager
*
1423 empathy_event_manager_dup_singleton (void)
1425 return g_object_new (EMPATHY_TYPE_EVENT_MANAGER
, NULL
);
1429 empathy_event_manager_get_events (EmpathyEventManager
*manager
)
1431 EmpathyEventManagerPriv
*priv
= GET_PRIV (manager
);
1433 g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager
), NULL
);
1435 return priv
->events
;
1439 empathy_event_manager_get_top_event (EmpathyEventManager
*manager
)
1441 EmpathyEventManagerPriv
*priv
= GET_PRIV (manager
);
1443 g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager
), NULL
);
1445 return priv
->events
? priv
->events
->data
: NULL
;
1449 empathy_event_activate (EmpathyEvent
*event_public
)
1451 EventPriv
*event
= (EventPriv
*) event_public
;
1453 g_return_if_fail (event_public
!= NULL
);
1456 event
->func (event
);
1458 event_remove (event
);
1462 empathy_event_inhibit_updates (EmpathyEvent
*event_public
)
1464 EventPriv
*event
= (EventPriv
*) event_public
;
1466 g_return_if_fail (event_public
!= NULL
);
1468 event
->inhibit
= TRUE
;
1472 empathy_event_approve (EmpathyEvent
*event_public
)
1474 EventPriv
*event
= (EventPriv
*) event_public
;
1476 g_return_if_fail (event_public
!= NULL
);
1478 event_manager_approval_approve (event
->approval
);
1482 empathy_event_decline (EmpathyEvent
*event_public
)
1484 EventPriv
*event
= (EventPriv
*) event_public
;
1486 g_return_if_fail (event_public
!= NULL
);
1488 reject_approval (event
->approval
);