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