]> git.0d.be Git - empathy.git/blob - src/empathy-event-manager.c
Remove useless include of empathy-tp-group.h
[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/util.h>
28
29 #include <libempathy/empathy-dispatcher.h>
30 #include <libempathy/empathy-contact-factory.h>
31 #include <libempathy/empathy-contact-manager.h>
32 #include <libempathy/empathy-tp-chat.h>
33 #include <libempathy/empathy-tp-call.h>
34 #include <libempathy/empathy-utils.h>
35
36 #include <extensions/extensions.h>
37
38 #include <libempathy-gtk/empathy-images.h>
39 #include <libempathy-gtk/empathy-contact-dialogs.h>
40
41 #include "empathy-event-manager.h"
42 #include "empathy-tube-dispatch.h"
43
44 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
45 #include <libempathy/empathy-debug.h>
46
47 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyEventManager)
48
49 typedef struct {
50   EmpathyEventManager *manager;
51   EmpathyDispatchOperation *operation;
52   gulong approved_handler;
53   gulong claimed_handler;
54   gulong invalidated_handler;
55   /* Remove contact if applicable */
56   EmpathyContact *contact;
57   /* Tube dispatcher if applicable */
58   EmpathyTubeDispatch *tube_dispatch;
59   /* option signal handler */
60   gulong handler;
61 } EventManagerApproval;
62
63 typedef struct {
64   EmpathyDispatcher *dispatcher;
65   EmpathyContactManager *contact_manager;
66   GSList *events;
67   /* Ongoing approvals */
68   GSList *approvals;
69 } EmpathyEventManagerPriv;
70
71 typedef struct _EventPriv EventPriv;
72 typedef void (*EventFunc) (EventPriv *event);
73
74 struct _EventPriv {
75   EmpathyEvent public;
76   EmpathyEventManager *manager;
77   EventManagerApproval *approval;
78   EventFunc func;
79   gpointer user_data;
80 };
81
82 enum {
83   EVENT_ADDED,
84   EVENT_REMOVED,
85   LAST_SIGNAL
86 };
87
88 static guint signals[LAST_SIGNAL];
89
90 G_DEFINE_TYPE (EmpathyEventManager, empathy_event_manager, G_TYPE_OBJECT);
91
92 static EmpathyEventManager * manager_singleton = NULL;
93
94 static EventManagerApproval *
95 event_manager_approval_new (EmpathyEventManager *manager,
96   EmpathyDispatchOperation *operation)
97 {
98   EventManagerApproval *result = g_slice_new0 (EventManagerApproval);
99   result->operation = g_object_ref (operation);
100   result->manager = manager;
101
102   return result;
103 }
104
105 static void
106 event_manager_approval_free (EventManagerApproval *approval)
107 {
108   g_signal_handler_disconnect (approval->operation,
109     approval->approved_handler);
110   g_signal_handler_disconnect (approval->operation,
111     approval->claimed_handler);
112   g_signal_handler_disconnect (approval->operation,
113     approval->invalidated_handler);
114   g_object_unref (approval->operation);
115
116   if (approval->contact != NULL)
117     g_object_unref (approval->contact);
118
119   if (approval->tube_dispatch != NULL)
120     g_object_unref (approval->tube_dispatch);
121
122   g_slice_free (EventManagerApproval, approval);
123 }
124
125 static void event_remove (EventPriv *event);
126
127 static void
128 event_free (EventPriv *event)
129 {
130   g_free (event->public.icon_name);
131   g_free (event->public.message);
132
133   if (event->public.contact)
134     {
135       g_object_unref (event->public.contact);
136     }
137
138   g_slice_free (EventPriv, event);
139 }
140
141 static void
142 event_remove (EventPriv *event)
143 {
144   EmpathyEventManagerPriv *priv = GET_PRIV (event->manager);
145
146   DEBUG ("Removing event %p", event);
147   priv->events = g_slist_remove (priv->events, event);
148   g_signal_emit (event->manager, signals[EVENT_REMOVED], 0, event);
149   event_free (event);
150 }
151
152 static void
153 event_manager_add (EmpathyEventManager *manager, EmpathyContact *contact,
154   const gchar *icon_name, const gchar *message, EventManagerApproval *approval,
155   EventFunc func, gpointer user_data)
156 {
157   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
158   EventPriv               *event;
159
160   event = g_slice_new0 (EventPriv);
161   event->public.contact = contact ? g_object_ref (contact) : NULL;
162   event->public.icon_name = g_strdup (icon_name);
163   event->public.message = g_strdup (message);
164   event->func = func;
165   event->user_data = user_data;
166   event->manager = manager;
167   event->approval = approval;
168
169   DEBUG ("Adding event %p", event);
170   priv->events = g_slist_prepend (priv->events, event);
171   g_signal_emit (event->manager, signals[EVENT_ADDED], 0, event);
172 }
173
174 static void
175 event_channel_process_func (EventPriv *event)
176 {
177   empathy_dispatch_operation_approve (event->approval->operation);
178 }
179
180 static void
181 event_manager_chat_message_received_cb (EmpathyTpChat *tp_chat,
182   EmpathyMessage *message, EventManagerApproval *approval)
183 {
184   EmpathyContact  *sender;
185   gchar           *msg;
186   TpChannel       *channel;
187
188   g_signal_handlers_disconnect_by_func (tp_chat,
189     event_manager_chat_message_received_cb, approval);
190
191   sender = empathy_message_get_sender (message);
192   msg = g_strdup_printf (_("New message from %s:\n%s"),
193     empathy_contact_get_name (sender), empathy_message_get_body (message));
194
195   channel = empathy_tp_chat_get_channel (tp_chat);
196   event_manager_add (approval->manager, sender, EMPATHY_IMAGE_NEW_MESSAGE, msg,
197     approval, event_channel_process_func, NULL);
198
199   g_free (msg);
200 }
201
202 static void
203 event_manager_approval_done (EventManagerApproval *approval)
204 {
205   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
206   GSList                  *l;
207
208   priv->approvals = g_slist_remove (priv->approvals, approval);
209
210   for (l = priv->events; l; l = l->next)
211     {
212       EventPriv *event = l->data;
213
214       if (event->approval == approval)
215         {
216           event_remove (event);
217           break;
218         }
219     }
220
221   event_manager_approval_free (approval);
222 }
223
224 static void
225 event_manager_operation_approved_cb (EmpathyDispatchOperation *operation,
226   EventManagerApproval *approval)
227 {
228   event_manager_approval_done (approval);
229 }
230
231 static void
232 event_manager_operation_claimed_cb (EmpathyDispatchOperation *operation,
233   EventManagerApproval *approval)
234 {
235   event_manager_approval_done (approval);
236 }
237
238 static void
239 event_manager_operation_invalidated_cb (EmpathyDispatchOperation *operation,
240   guint domain, gint code, gchar *message,
241   EventManagerApproval *approval)
242 {
243   event_manager_approval_done (approval);
244 }
245
246 static void
247 event_manager_media_channel_got_name_cb (EmpathyContact *contact,
248   const GError *error, gpointer user_data, GObject *object)
249 {
250   EventManagerApproval *approval = user_data;
251   gchar *msg;
252
253   if (error != NULL)
254     {
255       /* FIXME just returning assuming the operation will be invalidated as
256        * well */
257       return;
258     }
259
260   msg = g_strdup_printf (_("Incoming call from %s"),
261     empathy_contact_get_name (contact));
262
263   event_manager_add (approval->manager,
264     approval->contact, EMPATHY_IMAGE_VOIP, msg,
265     approval, event_channel_process_func, NULL);
266
267   g_free (msg);
268 }
269
270 static void
271 event_manager_media_channel_got_contact (EventManagerApproval *approval)
272 {
273   empathy_contact_call_when_ready (approval->contact,
274      EMPATHY_CONTACT_READY_NAME, event_manager_media_channel_got_name_cb,
275         approval, NULL, G_OBJECT (approval->manager));
276 }
277
278 static void
279 event_manager_media_channel_contact_changed_cb (EmpathyTpCall *call,
280   GParamSpec *param, EventManagerApproval *approval)
281 {
282   EmpathyContact *contact;
283
284   g_object_get (G_OBJECT (call), "contact", &contact, NULL);
285
286   if (contact == NULL)
287     return;
288
289   approval->contact = contact;
290   event_manager_media_channel_got_contact (approval);
291 }
292
293 static void
294 event_manager_tube_approved_cb (EventPriv *event)
295 {
296   empathy_tube_dispatch_handle (event->approval->tube_dispatch);
297 }
298
299 static void
300 event_manager_add_tube_approval (EventManagerApproval *approval,
301   EmpathyTubeDispatchAbility ability)
302 {
303   const gchar *icon_name;
304   gchar       *msg;
305
306   if (ability == EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE)
307     {
308       icon_name = GTK_STOCK_EXECUTE;
309       msg = g_strdup_printf (_("%s is offering you an invitation."
310         " An external application will be started to handle it."),
311       empathy_contact_get_name (approval->contact));
312     }
313   else
314     {
315       icon_name = GTK_STOCK_DIALOG_ERROR;
316       msg = g_strdup_printf (_("%s is offering you an invitation, but "
317         "you don't have the needed external "
318         "application to handle it."),
319         empathy_contact_get_name (approval->contact));
320     }
321
322   event_manager_add (approval->manager, approval->contact, icon_name, msg,
323     approval, event_manager_tube_approved_cb, approval);
324
325   g_free (msg);
326 }
327
328 static void
329 event_manager_tube_dispatch_ability_cb (GObject *object,
330    GParamSpec *spec, gpointer user_data)
331 {
332   EventManagerApproval *approval = (EventManagerApproval *)user_data;
333   EmpathyTubeDispatchAbility dispatchability;
334
335   dispatchability =
336     empathy_tube_dispatch_is_dispatchable (approval->tube_dispatch);
337
338   if (dispatchability != EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN)
339     {
340       event_manager_add_tube_approval (approval, dispatchability);
341       g_signal_handler_disconnect (object, approval->handler);
342       approval->handler = 0;
343     }
344 }
345
346 static void
347 event_manager_tube_got_contact_name_cb (EmpathyContact *contact,
348   const GError *error, gpointer user_data, GObject *object)
349 {
350   EventManagerApproval *approval = (EventManagerApproval *)user_data;
351   EmpathyTubeDispatchAbility dispatchability;
352
353   if (error != NULL)
354     {
355       /* FIXME?, we assume that the operation gets invalidated as well (if it
356        * didn't already */
357        return;
358     }
359
360   dispatchability = empathy_tube_dispatch_is_dispatchable
361     (approval->tube_dispatch);
362
363
364   switch (dispatchability)
365     {
366       case EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN:
367         approval->handler = g_signal_connect (approval->tube_dispatch,
368           "notify::dispatchability",
369           G_CALLBACK (event_manager_tube_dispatch_ability_cb), approval);
370         break;
371       case EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE:
372         /* fallthrough */
373       case EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE:
374         event_manager_add_tube_approval (approval, dispatchability);
375         break;
376     }
377 }
378
379 static void
380 event_manager_approve_channel_cb (EmpathyDispatcher *dispatcher,
381   EmpathyDispatchOperation  *operation, EmpathyEventManager *manager)
382 {
383   const gchar *channel_type;
384   EventManagerApproval *approval;
385   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
386
387   channel_type = empathy_dispatch_operation_get_channel_type (operation);
388
389   approval = event_manager_approval_new (manager, operation);
390   priv->approvals = g_slist_prepend (priv->approvals, approval);
391
392   approval->approved_handler = g_signal_connect (operation, "approved",
393     G_CALLBACK (event_manager_operation_approved_cb), approval);
394
395   approval->claimed_handler = g_signal_connect (operation, "claimed",
396      G_CALLBACK (event_manager_operation_claimed_cb), approval);
397
398   approval->invalidated_handler = g_signal_connect (operation, "invalidated",
399      G_CALLBACK (event_manager_operation_invalidated_cb), approval);
400
401   if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT))
402     {
403       EmpathyTpChat *tp_chat =
404         EMPATHY_TP_CHAT (
405           empathy_dispatch_operation_get_channel_wrapper (operation));
406
407       g_signal_connect (tp_chat, "message-received",
408         G_CALLBACK (event_manager_chat_message_received_cb), approval);
409
410     }
411   else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA))
412     {
413       EmpathyContact *contact;
414       EmpathyTpCall *call = EMPATHY_TP_CALL (
415           empathy_dispatch_operation_get_channel_wrapper (operation));
416
417       g_object_get (G_OBJECT (call), "contact", &contact, NULL);
418
419       if (contact == NULL)
420         {
421           g_signal_connect (call, "notify::contact",
422             G_CALLBACK (event_manager_media_channel_contact_changed_cb),
423             approval);
424         }
425       else
426         {
427           approval->contact = contact;
428           event_manager_media_channel_got_contact (approval);
429         }
430
431     }
432   else if (!tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_FILE_TRANSFER))
433     {
434       EmpathyContact        *contact;
435       gchar                 *msg;
436       TpHandle               handle;
437       McAccount             *account;
438       EmpathyContactFactory *factory;
439       TpChannel *channel = empathy_dispatch_operation_get_channel (operation);
440
441       factory = empathy_contact_factory_dup_singleton ();
442       handle = tp_channel_get_handle (channel, NULL);
443       account = empathy_channel_get_account (channel);
444
445       contact = empathy_contact_factory_get_from_handle (factory, account,
446         handle);
447
448       empathy_contact_run_until_ready (contact,
449         EMPATHY_CONTACT_READY_NAME, NULL);
450
451       msg = g_strdup_printf (_("Incoming file transfer from %s"),
452         empathy_contact_get_name (contact));
453
454       event_manager_add (manager, contact, EMPATHY_IMAGE_DOCUMENT_SEND,
455         msg, approval, event_channel_process_func, NULL);
456
457       g_object_unref (factory);
458       g_object_unref (account);
459     }
460   else if (!tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_STREAM_TUBE) ||
461       !tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_DBUS_TUBE))
462     {
463       EmpathyContact        *contact;
464       TpHandle               handle;
465       TpHandleType           handle_type;
466       McAccount             *account;
467       EmpathyContactFactory *factory;
468       EmpathyTubeDispatch *tube_dispatch;
469       TpChannel *channel;
470
471       channel = empathy_dispatch_operation_get_channel (operation);
472
473       handle = tp_channel_get_handle (channel, &handle_type);
474
475       /* Only understand p2p tubes */
476       if (handle_type != TP_HANDLE_TYPE_CONTACT)
477         return;
478
479       factory = empathy_contact_factory_dup_singleton ();
480       account = empathy_channel_get_account (channel);
481
482       contact = empathy_contact_factory_get_from_handle (factory, account,
483         handle);
484
485       tube_dispatch = empathy_tube_dispatch_new (operation);
486
487       approval->contact = contact;
488       approval->tube_dispatch = tube_dispatch;
489
490       empathy_contact_call_when_ready (contact,
491         EMPATHY_CONTACT_READY_NAME, event_manager_tube_got_contact_name_cb,
492         approval, NULL, G_OBJECT (manager));
493
494       g_object_unref (factory);
495       g_object_unref (account);
496     }
497   else
498     {
499       DEBUG ("Unknown channel type, ignoring..");
500     }
501 }
502
503 static void
504 event_pending_subscribe_func (EventPriv *event)
505 {
506   empathy_subscription_dialog_show (event->public.contact, NULL);
507   event_remove (event);
508 }
509
510 static void
511 event_manager_pendings_changed_cb (EmpathyContactList  *list,
512   EmpathyContact *contact, EmpathyContact *actor,
513   guint reason, gchar *message, gboolean is_pending,
514   EmpathyEventManager *manager)
515 {
516   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
517   GString                 *str;
518
519   if (!is_pending)
520     {
521       GSList *l;
522
523       for (l = priv->events; l; l = l->next)
524         {
525           EventPriv *event = l->data;
526
527       if (event->public.contact == contact &&
528           event->func == event_pending_subscribe_func)
529         {
530           event_remove (event);
531           break;
532         }
533       }
534
535       return;
536     }
537
538   empathy_contact_run_until_ready (contact, EMPATHY_CONTACT_READY_NAME, NULL);
539
540   str = g_string_new (NULL);
541   g_string_printf (str, _("Subscription requested by %s"),
542     empathy_contact_get_name (contact));
543
544   if (!G_STR_EMPTY (message))
545     g_string_append_printf (str, _("\nMessage: %s"), message);
546
547   event_manager_add (manager, contact, GTK_STOCK_DIALOG_QUESTION, str->str,
548     NULL, event_pending_subscribe_func, NULL);
549
550   g_string_free (str, TRUE);
551 }
552
553 static GObject *
554 event_manager_constructor (GType type,
555                            guint n_props,
556                            GObjectConstructParam *props)
557 {
558         GObject *retval;
559
560         if (manager_singleton) {
561                 retval = g_object_ref (manager_singleton);
562         } else {
563                 retval = G_OBJECT_CLASS (empathy_event_manager_parent_class)->constructor
564                         (type, n_props, props);
565
566                 manager_singleton = EMPATHY_EVENT_MANAGER (retval);
567                 g_object_add_weak_pointer (retval, (gpointer *) &manager_singleton);
568         }
569
570         return retval;
571 }
572
573 static void
574 event_manager_finalize (GObject *object)
575 {
576   EmpathyEventManagerPriv *priv = GET_PRIV (object);
577
578   g_slist_foreach (priv->events, (GFunc) event_free, NULL);
579   g_slist_free (priv->events);
580   g_slist_foreach (priv->approvals, (GFunc) event_manager_approval_free, NULL);
581   g_slist_free (priv->approvals);
582   g_object_unref (priv->contact_manager);
583   g_object_unref (priv->dispatcher);
584 }
585
586 static void
587 empathy_event_manager_class_init (EmpathyEventManagerClass *klass)
588 {
589   GObjectClass *object_class = G_OBJECT_CLASS (klass);
590
591   object_class->finalize = event_manager_finalize;
592   object_class->constructor = event_manager_constructor;
593
594   signals[EVENT_ADDED] =
595     g_signal_new ("event-added",
596       G_TYPE_FROM_CLASS (klass),
597       G_SIGNAL_RUN_LAST,
598       0,
599       NULL, NULL,
600       g_cclosure_marshal_VOID__POINTER,
601       G_TYPE_NONE,
602       1, G_TYPE_POINTER);
603
604   signals[EVENT_REMOVED] =
605   g_signal_new ("event-removed",
606       G_TYPE_FROM_CLASS (klass),
607       G_SIGNAL_RUN_LAST,
608       0,
609       NULL, NULL,
610       g_cclosure_marshal_VOID__POINTER,
611       G_TYPE_NONE, 1, G_TYPE_POINTER);
612
613   g_type_class_add_private (object_class, sizeof (EmpathyEventManagerPriv));
614 }
615
616 static void
617 empathy_event_manager_init (EmpathyEventManager *manager)
618 {
619   EmpathyEventManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
620     EMPATHY_TYPE_EVENT_MANAGER, EmpathyEventManagerPriv);
621
622   manager->priv = priv;
623
624   priv->dispatcher = empathy_dispatcher_dup_singleton ();
625   priv->contact_manager = empathy_contact_manager_dup_singleton ();
626   g_signal_connect (priv->dispatcher, "approve",
627     G_CALLBACK (event_manager_approve_channel_cb), manager);
628   g_signal_connect (priv->contact_manager, "pendings-changed",
629     G_CALLBACK (event_manager_pendings_changed_cb), manager);
630 }
631
632 EmpathyEventManager *
633 empathy_event_manager_dup_singleton (void)
634 {
635   return g_object_new (EMPATHY_TYPE_EVENT_MANAGER, NULL);
636 }
637
638 GSList *
639 empathy_event_manager_get_events (EmpathyEventManager *manager)
640 {
641   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
642
643   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
644
645   return priv->events;
646 }
647
648 EmpathyEvent *
649 empathy_event_manager_get_top_event (EmpathyEventManager *manager)
650 {
651   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
652
653   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
654
655   return priv->events ? priv->events->data : NULL;
656 }
657
658 void
659 empathy_event_activate (EmpathyEvent *event_public)
660 {
661   EventPriv *event = (EventPriv*) event_public;
662
663   g_return_if_fail (event_public != NULL);
664
665   if (event->func)
666     event->func (event);
667   else
668     event_remove (event);
669 }