]> git.0d.be Git - empathy.git/blob - libempathy/empathy-dispatcher.c
Move non-gtk parts of EmpathyFilter to EmpathyDispatcher in libempathy, gtk parts...
[empathy.git] / libempathy / empathy-dispatcher.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007-2008 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  * 
19  * Authors: Xavier Claessens <xclaesse@gmail.com>
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25
26 #include <glib/gi18n.h>
27
28 #include <telepathy-glib/enums.h>
29 #include <telepathy-glib/connection.h>
30 #include <telepathy-glib/util.h>
31 #include <telepathy-glib/dbus.h>
32 #include <telepathy-glib/proxy-subclass.h>
33
34 #include <libmissioncontrol/mission-control.h>
35 #include <libmissioncontrol/mc-account.h>
36
37 #include <extensions/extensions.h>
38
39 #include "empathy-dispatcher.h"
40 #include "empathy-utils.h"
41 #include "empathy-tube-handler.h"
42 #include "empathy-contact-factory.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, EmpathyDispatcher)
48 typedef struct {
49         GHashTable     *connections;
50         gpointer        token;
51         MissionControl *mc;
52         GHashTable     *tubes;
53 } EmpathyDispatcherPriv;
54
55 G_DEFINE_TYPE (EmpathyDispatcher, empathy_dispatcher, G_TYPE_OBJECT);
56
57 enum {
58         DISPATCH_CHANNEL,
59         FILTER_CHANNEL,
60         FILTER_TUBE,
61         LAST_SIGNAL
62 };
63
64 static guint signals[LAST_SIGNAL];
65 static EmpathyDispatcher *dispatcher = NULL;
66
67 void
68 empathy_dispatcher_channel_process (EmpathyDispatcher *dispatcher,
69                                     TpChannel         *channel)
70 {
71         g_signal_emit (dispatcher, signals[DISPATCH_CHANNEL], 0, channel);
72 }
73
74 typedef struct {
75         EmpathyDispatcherTube  public;
76         EmpathyContactFactory *factory;
77         gchar                 *bus_name;
78         gchar                 *object_path;
79         guint                  ref_count;
80         gboolean               handled;
81 } DispatcherTube;
82
83 GType
84 empathy_dispatcher_tube_get_type (void)
85 {
86         static GType type_id = 0;
87
88         if (!type_id) {
89                 type_id = g_boxed_type_register_static ("EmpathyDispatcherTube",
90                                                         (GBoxedCopyFunc) empathy_dispatcher_tube_ref,
91                                                         (GBoxedFreeFunc) empathy_dispatcher_tube_unref);
92         }
93
94         return type_id;
95 }
96
97 EmpathyDispatcherTube *
98 empathy_dispatcher_tube_ref (EmpathyDispatcherTube *data)
99 {
100         DispatcherTube *tube = (DispatcherTube*) data;
101
102         g_return_val_if_fail (tube != NULL, NULL);
103
104         tube->ref_count++;
105
106         return data;
107 }
108
109 void
110 empathy_dispatcher_tube_unref (EmpathyDispatcherTube *data)
111 {
112         DispatcherTube *tube = (DispatcherTube*) data;
113
114         g_return_if_fail (tube != NULL);
115
116         if (--tube->ref_count == 0) {
117                 if (!tube->handled) {
118                         DEBUG ("Tube can't be handled, closing");
119                         tp_cli_channel_type_tubes_call_close_tube (tube->public.channel, -1,
120                                                                    tube->public.id,
121                                                                    NULL, NULL, NULL,
122                                                                    NULL);
123                 }
124
125                 g_free (tube->bus_name);
126                 g_free (tube->object_path);
127                 g_object_unref (tube->factory);
128                 g_object_unref (tube->public.channel);
129                 g_object_unref (tube->public.initiator);
130                 g_slice_free (DispatcherTube, tube);
131         }
132 }
133
134 static void
135 dispatcher_tubes_handle_tube_cb (TpProxy      *channel,
136                                  const GError *error,
137                                  gpointer      user_data,
138                                  GObject      *dispatcher)
139 {
140         DispatcherTube *tube = user_data;
141
142         if (error) {
143                 DEBUG ("Error: %s", error->message);
144         } else {
145                 tube->handled = TRUE;
146         }
147 }
148
149 void
150 empathy_dispatcher_tube_process (EmpathyDispatcher     *dispatcher,
151                                  EmpathyDispatcherTube *user_data)
152 {
153         DispatcherTube *tube = (DispatcherTube*) user_data;
154
155         if (tube->public.activatable) {
156                 TpProxy *connection;
157                 TpProxy *thandler;
158                 gchar   *object_path;
159                 guint    handle_type;
160                 guint    handle;
161
162                 /* Create the proxy for the tube handler */
163                 thandler = g_object_new (TP_TYPE_PROXY,
164                                          "dbus-connection", tp_get_bus (),
165                                          "bus-name", tube->bus_name,
166                                          "object-path", tube->object_path,
167                                          NULL);
168                 tp_proxy_add_interface_by_id (thandler, EMP_IFACE_QUARK_TUBE_HANDLER);
169
170                 /* Give the tube to the handler */
171                 g_object_get (tube->public.channel,
172                               "connection", &connection,
173                               "object-path", &object_path,
174                               "handle_type", &handle_type,
175                               "handle", &handle,
176                               NULL);
177
178                 DEBUG ("Dispatching tube");
179                 emp_cli_tube_handler_call_handle_tube (thandler, -1,
180                                                        connection->bus_name,
181                                                        connection->object_path,
182                                                        object_path, handle_type,
183                                                        handle, tube->public.id,
184                                                        dispatcher_tubes_handle_tube_cb,
185                                                        empathy_dispatcher_tube_ref (user_data),
186                                                        (GDestroyNotify) empathy_dispatcher_tube_unref,
187                                                        G_OBJECT (dispatcher));
188
189                 g_object_unref (thandler);
190                 g_object_unref (connection);
191                 g_free (object_path);
192         }
193 }
194
195 static void
196 dispatcher_tubes_new_tube_cb (TpChannel   *channel,
197                               guint        id,
198                               guint        initiator,
199                               guint        type,
200                               const gchar *service,
201                               GHashTable  *parameters,
202                               guint        state,
203                               gpointer     user_data,
204                               GObject     *dispatcher)
205 {
206         EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
207         static TpDBusDaemon   *daemon = NULL;
208         DispatcherTube        *tube;
209         McAccount             *account;
210         guint                  number;
211         gchar                **names;
212         gboolean               running = FALSE;
213         GError                *error = NULL;
214
215         /* Increase tube count */
216         number = GPOINTER_TO_UINT (g_hash_table_lookup (priv->tubes, channel));
217         g_hash_table_replace (priv->tubes, g_object_ref (channel),
218                               GUINT_TO_POINTER (++number));
219         DEBUG ("Increased tube count for channel %p: %d", channel, number);
220
221         /* We dispatch only local pending tubes */
222         if (state != TP_TUBE_STATE_LOCAL_PENDING) {
223                 return;
224         }
225
226         if (!daemon) {
227                 daemon = tp_dbus_daemon_new (tp_get_bus ());
228         }
229
230         account = empathy_channel_get_account (channel);
231         tube = g_slice_new (DispatcherTube);
232         tube->ref_count = 1;
233         tube->handled = FALSE;
234         tube->factory = empathy_contact_factory_new ();
235         tube->bus_name = empathy_tube_handler_build_bus_name (type, service);
236         tube->object_path = empathy_tube_handler_build_object_path (type, service);
237         tube->public.activatable = FALSE;
238         tube->public.id = id;
239         tube->public.channel = g_object_ref (channel);
240         tube->public.initiator = empathy_contact_factory_get_from_handle (tube->factory,
241                                                                           account,
242                                                                           initiator);
243         g_object_unref (account);
244
245         /* Check if that bus-name has an owner, if it has one that means the
246          * app is already running and we can directly give the channel. */
247         tp_cli_dbus_daemon_run_name_has_owner (daemon, -1, tube->bus_name,
248                                                &running, NULL, NULL);
249         if (running) {
250                 DEBUG ("Tube handler running");
251                 tube->public.activatable = TRUE;
252                 empathy_dispatcher_tube_process (EMPATHY_DISPATCHER (dispatcher),
253                                                  (EmpathyDispatcherTube*) tube);
254                 empathy_dispatcher_tube_unref ((EmpathyDispatcherTube*) tube);
255                 return;
256         }
257
258         /* Check if that bus-name is activatable, if not that means the
259          * application needed to handle this tube isn't installed. */
260         if (!tp_cli_dbus_daemon_run_list_activatable_names (daemon, -1,
261                                                             &names, &error,
262                                                             NULL)) {
263                 DEBUG ("Error listing activatable names: %s", error->message);
264                 g_clear_error (&error);
265         } else {
266                 gchar **name;
267
268                 for (name = names; *name; name++) {
269                         if (!tp_strdiff (*name, tube->bus_name)) {
270                                 tube->public.activatable = TRUE;
271                                 break;
272                         }
273                 }
274                 g_strfreev (names);
275         }
276
277         g_signal_emit (dispatcher, signals[FILTER_TUBE], 0, tube);
278         empathy_dispatcher_tube_unref ((EmpathyDispatcherTube*) tube);
279 }
280
281 static void
282 dispatcher_tubes_list_tubes_cb (TpChannel       *channel,
283                                 const GPtrArray *tubes,
284                                 const GError    *error,
285                                 gpointer         user_data,
286                                 GObject         *dispatcher)
287 {
288         guint i;
289
290         if (error) {
291                 DEBUG ("Error: %s", error->message);
292                 return;
293         }
294
295         for (i = 0; i < tubes->len; i++) {
296                 GValueArray *values;
297
298                 values = g_ptr_array_index (tubes, i);
299                 dispatcher_tubes_new_tube_cb (channel,
300                                               g_value_get_uint (g_value_array_get_nth (values, 0)),
301                                               g_value_get_uint (g_value_array_get_nth (values, 1)),
302                                               g_value_get_uint (g_value_array_get_nth (values, 2)),
303                                               g_value_get_string (g_value_array_get_nth (values, 3)),
304                                               g_value_get_boxed (g_value_array_get_nth (values, 4)),
305                                               g_value_get_uint (g_value_array_get_nth (values, 5)),
306                                               user_data, dispatcher);
307         }
308 }
309
310 static void
311 dispatcher_tubes_channel_invalidated_cb (TpProxy       *proxy,
312                                          guint          domain,
313                                          gint           code,
314                                          gchar         *message,
315                                          EmpathyDispatcher *dispatcher)
316 {
317         EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
318
319         DEBUG ("Error: %s", message);
320
321         g_hash_table_remove (priv->tubes, proxy);
322 }
323
324 static void
325 dispatcher_tubes_tube_closed_cb (TpChannel *channel,
326                                  guint      id,
327                                  gpointer   user_data,
328                                  GObject   *dispatcher)
329 {
330         EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
331         guint                  number;
332
333         number = GPOINTER_TO_UINT (g_hash_table_lookup (priv->tubes, channel));
334         if (number == 1) {
335                 DEBUG ("No more tube, closing channel");
336                 tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL);
337         }
338         else if (number > 1) {
339                 DEBUG ("Decrease tube count: %d", number);
340                 g_hash_table_replace (priv->tubes, g_object_ref (channel),
341                                       GUINT_TO_POINTER (--number));
342         }
343 }
344
345 static void
346 dispatcher_tubes_handle_channel (EmpathyDispatcher *dispatcher,
347                                  TpChannel         *channel)
348 {
349         EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
350
351         if (g_hash_table_lookup (priv->tubes, channel)) {
352                 return;
353         }
354
355         DEBUG ("Handling new channel");
356
357         g_hash_table_insert (priv->tubes, g_object_ref (channel),
358                              GUINT_TO_POINTER (0));
359
360         g_signal_connect (channel, "invalidated",
361                           G_CALLBACK (dispatcher_tubes_channel_invalidated_cb),
362                           dispatcher);
363
364         tp_cli_channel_type_tubes_connect_to_tube_closed (channel,
365                                                           dispatcher_tubes_tube_closed_cb,
366                                                           NULL, NULL,
367                                                           G_OBJECT (dispatcher), NULL);
368         tp_cli_channel_type_tubes_connect_to_new_tube (channel,
369                                                        dispatcher_tubes_new_tube_cb,
370                                                        NULL, NULL,
371                                                        G_OBJECT (dispatcher), NULL);
372         tp_cli_channel_type_tubes_call_list_tubes (channel, -1,
373                                                    dispatcher_tubes_list_tubes_cb,
374                                                    NULL, NULL,
375                                                    G_OBJECT (dispatcher));
376 }
377
378 static void
379 dispatcher_connection_invalidated_cb (TpConnection  *connection,
380                                       guint          domain,
381                                       gint           code,
382                                       gchar         *message,
383                                       EmpathyDispatcher *dispatcher)
384 {
385         EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
386         GHashTableIter         iter;
387         gpointer               key, value;
388
389         DEBUG ("Error: %s", message);
390
391         g_hash_table_iter_init (&iter, priv->connections);
392         while (g_hash_table_iter_next (&iter, &key, &value)) {
393                 if (value == connection) {
394                         g_hash_table_remove (priv->connections, key);
395                         break;
396                 }
397         }
398 }
399
400 static void
401 dispatcher_connection_new_channel_cb (TpConnection *connection,
402                                       const gchar  *object_path,
403                                       const gchar  *channel_type,
404                                       guint         handle_type,
405                                       guint         handle,
406                                       gboolean      suppress_handler,
407                                       gpointer      user_data,
408                                       GObject      *object)
409 {
410         EmpathyDispatcher *dispatcher = EMPATHY_DISPATCHER (object);
411         TpChannel         *channel;
412         gpointer           had_channels;
413
414         had_channels = g_object_get_data (G_OBJECT (connection), "had-channels");
415         if (had_channels == NULL) {
416                 /* ListChannels didn't return yet, return to avoid duplicate
417                  * dispatching */
418                 return;
419         }
420
421         channel = tp_channel_new (connection, object_path, channel_type,
422                                   handle_type, handle, NULL);
423         tp_channel_run_until_ready (channel, NULL, NULL);
424
425         if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES)) {
426                 dispatcher_tubes_handle_channel (dispatcher, channel);
427         }
428
429         if (suppress_handler) {
430                 g_signal_emit (dispatcher, signals[DISPATCH_CHANNEL], 0, channel);
431         } else {
432                 g_signal_emit (dispatcher, signals[FILTER_CHANNEL], 0, channel);
433         }
434
435         g_object_unref (channel);
436 }
437
438 static void
439 dispatcher_connection_list_channels_cb (TpConnection    *connection,
440                                         const GPtrArray *channels,
441                                         const GError    *error,
442                                         gpointer         user_data,
443                                         GObject         *dispatcher)
444 {
445         guint i;
446
447         if (error) {
448                 DEBUG ("Error: %s", error->message);
449                 return;
450         }
451
452         g_object_set_data (G_OBJECT (connection), "had-channels",
453                            GUINT_TO_POINTER (1));
454
455         for (i = 0; i < channels->len; i++) {
456                 GValueArray *values;
457
458                 values = g_ptr_array_index (channels, i);
459                 dispatcher_connection_new_channel_cb (connection,
460                         g_value_get_boxed (g_value_array_get_nth (values, 0)),
461                         g_value_get_string (g_value_array_get_nth (values, 1)),
462                         g_value_get_uint (g_value_array_get_nth (values, 2)),
463                         g_value_get_uint (g_value_array_get_nth (values, 3)),
464                         FALSE, user_data, dispatcher);
465         }
466 }
467
468 static void
469 dispatcher_connection_advertise_capabilities_cb (TpConnection    *connection,
470                                                  const GPtrArray *capabilities,
471                                                  const GError    *error,
472                                                  gpointer         user_data,
473                                                  GObject         *dispatcher)
474 {
475         if (error) {
476                 DEBUG ("Error: %s", error->message);
477         }
478 }
479
480 static void
481 dispatcher_connection_ready_cb (TpConnection  *connection,
482                                 const GError  *error,
483                                 gpointer       dispatcher)
484 {
485         GPtrArray   *capabilities;
486         GType        cap_type;
487         GValue       cap = {0, };
488         const gchar *remove = NULL;
489
490         if (error) {
491                 dispatcher_connection_invalidated_cb (connection,
492                                                       error->domain,
493                                                       error->code,
494                                                       error->message,
495                                                       dispatcher);
496                 return;
497         }
498
499         g_signal_connect (connection, "invalidated",
500                           G_CALLBACK (dispatcher_connection_invalidated_cb),
501                           dispatcher);
502         tp_cli_connection_connect_to_new_channel (connection,
503                                                   dispatcher_connection_new_channel_cb,
504                                                   NULL, NULL,
505                                                   G_OBJECT (dispatcher), NULL);
506         tp_cli_connection_call_list_channels (connection, -1,
507                                               dispatcher_connection_list_channels_cb,
508                                               NULL, NULL,
509                                               G_OBJECT (dispatcher));
510
511         /* Advertise VoIP capabilities */
512         capabilities = g_ptr_array_sized_new (1);
513         cap_type = dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING,
514                                            G_TYPE_UINT, G_TYPE_INVALID);
515         g_value_init (&cap, cap_type);
516         g_value_take_boxed (&cap, dbus_g_type_specialized_construct (cap_type));
517         dbus_g_type_struct_set (&cap,
518                                 0, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
519                                 1, TP_CHANNEL_MEDIA_CAPABILITY_AUDIO |
520                                    TP_CHANNEL_MEDIA_CAPABILITY_VIDEO |
521                                    TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_STUN  |
522                                    TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_GTALK_P2P,
523                                 G_MAXUINT);
524         g_ptr_array_add (capabilities, g_value_get_boxed (&cap));
525
526         tp_cli_connection_interface_capabilities_call_advertise_capabilities (
527                 connection, -1,
528                 capabilities, &remove,
529                 dispatcher_connection_advertise_capabilities_cb,
530                 NULL, NULL, G_OBJECT (dispatcher));
531         /* FIXME: Is that leaked? */
532 }
533
534 static void
535 dispatcher_update_account (EmpathyDispatcher *dispatcher,
536                            McAccount         *account)
537 {
538         EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
539         TpConnection          *connection;
540
541         connection = g_hash_table_lookup (priv->connections, account);
542         if (connection) {
543                 return;
544         }
545
546         connection = mission_control_get_tpconnection (priv->mc, account, NULL);
547         if (!connection) {
548                 return;
549         }
550
551         g_hash_table_insert (priv->connections, g_object_ref (account), connection);
552         tp_connection_call_when_ready (connection,
553                                        dispatcher_connection_ready_cb,
554                                        dispatcher);
555 }
556
557 static void
558 dispatcher_status_changed_cb (MissionControl           *mc,
559                               TpConnectionStatus        status,
560                               McPresence                presence,
561                               TpConnectionStatusReason  reason,
562                               const gchar              *unique_name,
563                               EmpathyDispatcher            *dispatcher)
564 {
565         McAccount *account;
566
567         account = mc_account_lookup (unique_name);
568         dispatcher_update_account (dispatcher, account);
569         g_object_unref (account);
570 }
571
572 static guint
573 dispatcher_channel_hash (gconstpointer key)
574 {
575         TpProxy *channel = TP_PROXY (key);
576
577         return g_str_hash (channel->object_path);
578 }
579
580 static gboolean
581 dispatcher_channel_equal (gconstpointer a,
582                       gconstpointer b)
583 {
584         TpProxy *channel_a = TP_PROXY (a);
585         TpProxy *channel_b = TP_PROXY (b);
586
587         return g_str_equal (channel_a->object_path, channel_b->object_path);
588 }
589
590 static void
591 dispatcher_finalize (GObject *object)
592 {
593         EmpathyDispatcherPriv *priv = GET_PRIV (object);
594
595         empathy_disconnect_account_status_changed (priv->token);
596         g_object_unref (priv->mc);
597
598         g_hash_table_destroy (priv->connections);
599         g_hash_table_destroy (priv->tubes);
600 }
601
602 static void
603 empathy_dispatcher_class_init (EmpathyDispatcherClass *klass)
604 {
605         GObjectClass *object_class = G_OBJECT_CLASS (klass);
606
607         object_class->finalize = dispatcher_finalize;
608
609         signals[DISPATCH_CHANNEL] =
610                 g_signal_new ("dispatch-channel",
611                               G_TYPE_FROM_CLASS (klass),
612                               G_SIGNAL_RUN_LAST,
613                               0,
614                               NULL, NULL,
615                               g_cclosure_marshal_VOID__OBJECT,
616                               G_TYPE_NONE,
617                               1, TP_TYPE_CHANNEL);
618         signals[FILTER_CHANNEL] =
619                 g_signal_new ("filter-channel",
620                               G_TYPE_FROM_CLASS (klass),
621                               G_SIGNAL_RUN_LAST,
622                               0,
623                               NULL, NULL,
624                               g_cclosure_marshal_VOID__OBJECT,
625                               G_TYPE_NONE,
626                               1, TP_TYPE_CHANNEL);
627         signals[FILTER_TUBE] =
628                 g_signal_new ("filter-tube",
629                               G_TYPE_FROM_CLASS (klass),
630                               G_SIGNAL_RUN_LAST,
631                               0,
632                               NULL, NULL,
633                               g_cclosure_marshal_VOID__BOXED,
634                               G_TYPE_NONE,
635                               1, EMPATHY_TYPE_DISPATCHER_TUBE);
636
637         g_type_class_add_private (object_class, sizeof (EmpathyDispatcherPriv));
638 }
639
640 static void
641 empathy_dispatcher_init (EmpathyDispatcher *dispatcher)
642 {
643         GList                 *accounts, *l;
644         EmpathyDispatcherPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (dispatcher,
645                 EMPATHY_TYPE_DISPATCHER, EmpathyDispatcherPriv);
646
647         dispatcher->priv = priv;
648         priv->tubes = g_hash_table_new_full (dispatcher_channel_hash,
649                                              dispatcher_channel_equal,
650                                              g_object_unref, NULL);
651
652         priv->mc = empathy_mission_control_new ();
653         priv->token = empathy_connect_to_account_status_changed (priv->mc,
654                 G_CALLBACK (dispatcher_status_changed_cb),
655                 dispatcher, NULL);
656
657         priv->connections = g_hash_table_new_full (empathy_account_hash,
658                                                    empathy_account_equal,
659                                                    g_object_unref,
660                                                    g_object_unref);
661         accounts = mc_accounts_list_by_enabled (TRUE);
662         for (l = accounts; l; l = l->next) {
663                 dispatcher_update_account (dispatcher, l->data);
664                 g_object_unref (l->data);
665         }
666         g_list_free (accounts);
667 }
668
669 EmpathyDispatcher *
670 empathy_dispatcher_new (void)
671 {
672         if (!dispatcher) {
673                 dispatcher = g_object_new (EMPATHY_TYPE_DISPATCHER, NULL);
674                 g_object_add_weak_pointer (G_OBJECT (dispatcher), (gpointer) &dispatcher);
675         } else {
676                 g_object_ref (dispatcher);
677         }
678
679         return dispatcher;
680 }
681