]> git.0d.be Git - empathy.git/blob - libempathy/empathy-utils.c
Add empathy_init(), empathy_gtk_init() and empathy_gtk_init_with_args(). They have...
[empathy.git] / libempathy / empathy-utils.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2003-2007 Imendio AB
4  * Copyright (C) 2007-2008 Collabora Ltd.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *
21  * Authors: Richard Hult <richard@imendio.com>
22  *          Martyn Russell <martyn@imendio.com>
23  *          Xavier Claessens <xclaesse@gmail.com>
24  */
25
26 #include "config.h"
27
28 #include <string.h>
29 #include <time.h>
30 #include <sys/types.h>
31
32 #include <glib/gi18n.h>
33
34 #include <libxml/uri.h>
35 #include <telepathy-glib/connection.h>
36 #include <telepathy-glib/channel.h>
37 #include <telepathy-glib/dbus.h>
38
39 #include "empathy-utils.h"
40 #include "empathy-contact-factory.h"
41 #include "empathy-contact-manager.h"
42
43 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
44 #include "empathy-debug.h"
45
46 void
47 empathy_init (void)
48 {
49         static gboolean initialized = FALSE;
50
51         if (initialized)
52                 return;
53
54         /* Setup glib. Threads are required for async GIO. */
55         g_thread_init (NULL);
56         g_type_init ();
57
58         /* Setup gettext */
59         bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
60         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
61         textdomain (GETTEXT_PACKAGE);
62
63         /* Setup debug output for empathy and telepathy-glib */
64         if (g_getenv ("EMPATHY_TIMING") != NULL) {
65                 g_log_set_default_handler (tp_debug_timestamped_log_handler, NULL);
66         }
67         empathy_debug_set_flags (g_getenv ("EMPATHY_DEBUG"));
68         tp_debug_divert_messages (g_getenv ("EMPATHY_LOGFILE"));
69
70         initialized = TRUE;
71 }
72
73 gchar *
74 empathy_substring (const gchar *str,
75                   gint         start,
76                   gint         end)
77 {
78         return g_strndup (str + start, end - start);
79 }
80
81 gint
82 empathy_strcasecmp (const gchar *s1,
83                    const gchar *s2)
84 {
85         return empathy_strncasecmp (s1, s2, -1);
86 }
87
88 gint
89 empathy_strncasecmp (const gchar *s1,
90                     const gchar *s2,
91                     gsize        n)
92 {
93         gchar *u1, *u2;
94         gint   ret_val;
95
96         u1 = g_utf8_casefold (s1, n);
97         u2 = g_utf8_casefold (s2, n);
98
99         ret_val = g_utf8_collate (u1, u2);
100         g_free (u1);
101         g_free (u2);
102
103         return ret_val;
104 }
105
106 gboolean
107 empathy_xml_validate (xmlDoc      *doc,
108                      const gchar *dtd_filename)
109 {
110         gchar        *path, *escaped;
111         xmlValidCtxt  cvp;
112         xmlDtd       *dtd;
113         gboolean      ret;
114
115         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "libempathy",
116                                  dtd_filename, NULL);
117         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
118                 g_free (path);
119                 path = g_build_filename (DATADIR, "empathy", dtd_filename, NULL);
120         }
121         DEBUG ("Loading dtd file %s", path);
122
123         /* The list of valid chars is taken from libxml. */
124         escaped = xmlURIEscapeStr (path, ":@&=+$,/?;");
125         g_free (path);
126
127         memset (&cvp, 0, sizeof (cvp));
128         dtd = xmlParseDTD (NULL, escaped);
129         ret = xmlValidateDtd (&cvp, doc, dtd);
130
131         xmlFree (escaped);
132         xmlFreeDtd (dtd);
133
134         return ret;
135 }
136
137 xmlNodePtr
138 empathy_xml_node_get_child (xmlNodePtr   node, 
139                            const gchar *child_name)
140 {
141         xmlNodePtr l;
142
143         g_return_val_if_fail (node != NULL, NULL);
144         g_return_val_if_fail (child_name != NULL, NULL);
145
146         for (l = node->children; l; l = l->next) {
147                 if (l->name && strcmp (l->name, child_name) == 0) {
148                         return l;
149                 }
150         }
151
152         return NULL;
153 }
154
155 xmlChar *
156 empathy_xml_node_get_child_content (xmlNodePtr   node, 
157                                    const gchar *child_name)
158 {
159         xmlNodePtr l;
160
161         g_return_val_if_fail (node != NULL, NULL);
162         g_return_val_if_fail (child_name != NULL, NULL);
163
164         l = empathy_xml_node_get_child (node, child_name);
165         if (l) {
166                 return xmlNodeGetContent (l);
167         }
168                 
169         return NULL;
170 }
171
172 xmlNodePtr
173 empathy_xml_node_find_child_prop_value (xmlNodePtr   node, 
174                                        const gchar *prop_name,
175                                        const gchar *prop_value)
176 {
177         xmlNodePtr l;
178         xmlNodePtr found = NULL;
179
180         g_return_val_if_fail (node != NULL, NULL);
181         g_return_val_if_fail (prop_name != NULL, NULL);
182         g_return_val_if_fail (prop_value != NULL, NULL);
183
184         for (l = node->children; l && !found; l = l->next) {
185                 xmlChar *prop;
186
187                 if (!xmlHasProp (l, prop_name)) {
188                         continue;
189                 }
190
191                 prop = xmlGetProp (l, prop_name);
192                 if (prop && strcmp (prop, prop_value) == 0) {
193                         found = l;
194                 }
195                 
196                 xmlFree (prop);
197         }
198                 
199         return found;
200 }
201
202 guint
203 empathy_account_hash (gconstpointer key)
204 {
205         g_return_val_if_fail (MC_IS_ACCOUNT (key), 0);
206
207         return g_str_hash (mc_account_get_unique_name (MC_ACCOUNT (key)));
208 }
209
210 gboolean
211 empathy_account_equal (gconstpointer a,
212                        gconstpointer b)
213 {
214         const gchar *name_a;
215         const gchar *name_b;
216
217         g_return_val_if_fail (MC_IS_ACCOUNT (a), FALSE);
218         g_return_val_if_fail (MC_IS_ACCOUNT (b), FALSE);
219
220         name_a = mc_account_get_unique_name (MC_ACCOUNT (a));
221         name_b = mc_account_get_unique_name (MC_ACCOUNT (b));
222
223         return g_str_equal (name_a, name_b);
224 }
225
226 MissionControl *
227 empathy_mission_control_new (void)
228 {
229         static MissionControl *mc = NULL;
230
231         if (!mc) {
232                 mc = mission_control_new (tp_get_bus ());
233                 g_object_add_weak_pointer (G_OBJECT (mc), (gpointer) &mc);
234         } else {
235                 g_object_ref (mc);
236         }
237
238         return mc;
239 }
240
241 const gchar *
242 empathy_presence_get_default_message (McPresence presence)
243 {
244         switch (presence) {
245         case MC_PRESENCE_AVAILABLE:
246                 return _("Available");
247         case MC_PRESENCE_DO_NOT_DISTURB:
248                 return _("Busy");
249         case MC_PRESENCE_AWAY:
250         case MC_PRESENCE_EXTENDED_AWAY:
251                 return _("Away");
252         case MC_PRESENCE_HIDDEN:
253                 return _("Hidden");
254         case MC_PRESENCE_OFFLINE:
255         case MC_PRESENCE_UNSET:
256                 return _("Offline");
257         default:
258                 g_assert_not_reached ();
259         }
260
261         return NULL;
262 }
263
264 const gchar *
265 empathy_presence_to_str (McPresence presence)
266 {
267         switch (presence) {
268         case MC_PRESENCE_AVAILABLE:
269                 return "available";
270         case MC_PRESENCE_DO_NOT_DISTURB:
271                 return "busy";
272         case MC_PRESENCE_AWAY:
273                 return "away";
274         case MC_PRESENCE_EXTENDED_AWAY:
275                 return "ext_away";
276         case MC_PRESENCE_HIDDEN:
277                 return "hidden";
278         case MC_PRESENCE_OFFLINE:
279                 return "offline";
280         case MC_PRESENCE_UNSET:
281                 return "unset";
282         default:
283                 g_assert_not_reached ();
284         }
285
286         return NULL;
287 }
288
289 McPresence
290 empathy_presence_from_str (const gchar *str)
291 {
292         if (strcmp (str, "available") == 0) {
293                 return MC_PRESENCE_AVAILABLE;
294         } else if ((strcmp (str, "dnd") == 0) || (strcmp (str, "busy") == 0)) {
295                 return MC_PRESENCE_DO_NOT_DISTURB;
296         } else if ((strcmp (str, "away") == 0) || (strcmp (str, "brb") == 0)) {
297                 return MC_PRESENCE_AWAY;
298         } else if ((strcmp (str, "xa") == 0) || (strcmp (str, "ext_away") == 0)) {
299                 return MC_PRESENCE_EXTENDED_AWAY;
300         } else if (strcmp (str, "hidden") == 0) {
301                 return MC_PRESENCE_HIDDEN;
302         } else if (strcmp (str, "offline") == 0) {
303                 return MC_PRESENCE_OFFLINE;
304         } else if (strcmp (str, "unset") == 0) {
305                 return MC_PRESENCE_UNSET;
306         }
307
308         return MC_PRESENCE_UNSET;
309 }
310
311 gchar *
312 empathy_file_lookup (const gchar *filename, const gchar *subdir)
313 {
314         gchar *path;
315
316         if (!subdir) {
317                 subdir = ".";
318         }
319
320         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), subdir, filename, NULL);
321         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
322                 g_free (path);
323                 path = g_build_filename (DATADIR, "empathy", filename, NULL);
324         }
325
326         return path;
327 }
328
329 typedef struct {
330         EmpathyRunUntilReadyFunc  func;
331         gpointer                  user_data;
332         GObject                  *object;
333         GMainLoop                *loop;
334 } RunUntilReadyData;
335
336 static void
337 run_until_ready_cb (RunUntilReadyData *data)
338 {
339         if (!data->func || data->func (data->object, data->user_data)) {
340                 DEBUG ("Object %p is ready", data->object);
341                 g_main_loop_quit (data->loop);
342         }
343 }
344
345 static gboolean
346 object_is_ready (GObject *object,
347                  gpointer user_data)
348 {
349         gboolean ready;
350
351         g_object_get (object, "ready", &ready, NULL);
352
353         return ready;
354 }
355
356 void
357 empathy_run_until_ready_full (gpointer                  object,
358                               const gchar              *signal,
359                               EmpathyRunUntilReadyFunc  func,
360                               gpointer                  user_data,
361                               GMainLoop               **loop)
362 {
363         RunUntilReadyData  data;
364         gulong             signal_id;
365
366         g_return_if_fail (G_IS_OBJECT (object));
367         g_return_if_fail (signal != NULL);
368
369         if (func && func (object, user_data)) {
370                 return;
371         }
372
373         DEBUG ("Starting run until ready for object %p", object);
374
375         data.func = func;
376         data.user_data = user_data;
377         data.object = object;
378         data.loop = g_main_loop_new (NULL, FALSE);
379
380         signal_id = g_signal_connect_swapped (object, signal,
381                                               G_CALLBACK (run_until_ready_cb),
382                                               &data);
383         if (loop != NULL) {
384                 *loop = data.loop;
385         }
386
387         g_main_loop_run (data.loop);
388
389         if (loop != NULL) {
390                 *loop = NULL;
391         }
392
393         g_signal_handler_disconnect (object, signal_id);
394         g_main_loop_unref (data.loop);
395 }
396
397 void
398 empathy_run_until_ready (gpointer object)
399 {
400         empathy_run_until_ready_full (object, "notify::ready", object_is_ready,
401                                       NULL, NULL);
402 }
403
404 McAccount *
405 empathy_channel_get_account (TpChannel *channel)
406 {
407         TpConnection   *connection;
408         McAccount      *account;
409         MissionControl *mc;
410
411         g_object_get (channel, "connection", &connection, NULL);
412         mc = empathy_mission_control_new ();
413         account = mission_control_get_account_for_tpconnection (mc, connection, NULL);
414         g_object_unref (connection);
415         g_object_unref (mc);
416
417         return account;
418 }
419
420 typedef void (*AccountStatusChangedFunc) (MissionControl           *mc,
421                                           TpConnectionStatus        status,
422                                           McPresence                presence,
423                                           TpConnectionStatusReason  reason,
424                                           const gchar              *unique_name,
425                                           gpointer                  user_data);
426
427 typedef struct {
428         AccountStatusChangedFunc handler;
429         gpointer                 user_data;
430         GClosureNotify           free_func;
431         MissionControl          *mc;
432 } AccountStatusChangedData;
433
434 typedef struct {
435         TpConnectionStatus        status;
436         McPresence                presence;
437         TpConnectionStatusReason  reason;
438         gchar                    *unique_name;
439         AccountStatusChangedData *data;
440 } InvocationData;
441
442 static void
443 account_status_changed_data_free (gpointer ptr,
444                                   GClosure *closure)
445 {
446         AccountStatusChangedData *data = ptr;
447
448         if (data->free_func) {
449                 data->free_func (data->user_data, closure);
450         }
451         g_object_unref (data->mc);
452         g_slice_free (AccountStatusChangedData, data);
453 }
454
455 static gboolean
456 account_status_changed_invoke_callback (gpointer data)
457 {
458         InvocationData *invocation_data = data;
459
460         invocation_data->data->handler (invocation_data->data->mc,
461                                         invocation_data->status,
462                                         invocation_data->presence,
463                                         invocation_data->reason,
464                                         invocation_data->unique_name,
465                                         invocation_data->data->user_data);
466
467         g_free (invocation_data->unique_name);
468         g_slice_free (InvocationData, invocation_data);
469
470         return FALSE;
471 }
472
473 static void
474 account_status_changed_cb (MissionControl           *mc,
475                            TpConnectionStatus        status,
476                            McPresence                presence,
477                            TpConnectionStatusReason  reason,
478                            const gchar              *unique_name,
479                            AccountStatusChangedData *data)
480 {
481         InvocationData *invocation_data;
482
483         invocation_data = g_slice_new (InvocationData);
484         invocation_data->status = status;
485         invocation_data->presence = presence;
486         invocation_data->reason = reason;
487         invocation_data->unique_name = g_strdup (unique_name);
488         invocation_data->data = data;
489
490         g_idle_add_full (G_PRIORITY_HIGH,
491                          account_status_changed_invoke_callback,
492                          invocation_data, NULL);
493 }
494
495 gpointer
496 empathy_connect_to_account_status_changed (MissionControl *mc,
497                                            GCallback       handler,
498                                            gpointer        user_data,
499                                            GClosureNotify  free_func)
500 {
501         AccountStatusChangedData *data;
502
503         g_return_val_if_fail (IS_MISSIONCONTROL (mc), NULL);
504         g_return_val_if_fail (handler != NULL, NULL);
505         
506         data = g_slice_new (AccountStatusChangedData);
507         data->handler = (AccountStatusChangedFunc) handler;
508         data->user_data = user_data;
509         data->free_func = free_func;
510         data->mc = g_object_ref (mc);
511
512         dbus_g_proxy_connect_signal (DBUS_G_PROXY (mc), "AccountStatusChanged",
513                                      G_CALLBACK (account_status_changed_cb),
514                                      data, account_status_changed_data_free);
515
516         return data;
517 }
518
519 void
520 empathy_disconnect_account_status_changed (gpointer token)
521 {
522         AccountStatusChangedData *data = token;
523
524         dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (data->mc),
525                                         "AccountStatusChanged",
526                                         G_CALLBACK (account_status_changed_cb),
527                                         data);
528 }
529
530 guint
531 empathy_proxy_hash (gconstpointer key)
532 {
533         TpProxy      *proxy = TP_PROXY (key);
534         TpProxyClass *proxy_class = TP_PROXY_GET_CLASS (key);
535
536         g_return_val_if_fail (TP_IS_PROXY (proxy), 0);
537         g_return_val_if_fail (proxy_class->must_have_unique_name, 0);
538
539         return g_str_hash (proxy->object_path) ^ g_str_hash (proxy->bus_name);
540 }
541
542 gboolean
543 empathy_proxy_equal (gconstpointer a,
544                      gconstpointer b)
545 {
546         TpProxy *proxy_a = TP_PROXY (a);
547         TpProxy *proxy_b = TP_PROXY (b);
548         TpProxyClass *proxy_a_class = TP_PROXY_GET_CLASS (a);
549         TpProxyClass *proxy_b_class = TP_PROXY_GET_CLASS (b);
550
551         g_return_val_if_fail (TP_IS_PROXY (proxy_a), FALSE);
552         g_return_val_if_fail (TP_IS_PROXY (proxy_b), FALSE);
553         g_return_val_if_fail (proxy_a_class->must_have_unique_name, 0);
554         g_return_val_if_fail (proxy_b_class->must_have_unique_name, 0);
555
556         return g_str_equal (proxy_a->object_path, proxy_b->object_path) &&
557                g_str_equal (proxy_a->bus_name, proxy_b->bus_name);
558 }
559
560 typedef struct {
561         gint timeout_ms;
562         gchar *channel_type;
563         guint handle_type;
564         empathy_connection_callback_for_request_channel callback;
565         gpointer user_data;
566         GDestroyNotify destroy;
567         TpHandle handle;
568         gboolean suppress_handler;
569         guint ref_count;
570 } ConnectionRequestChannelData;
571
572 static void
573 connection_request_channel_data_unref (gpointer user_data)
574 {
575         ConnectionRequestChannelData *data = (ConnectionRequestChannelData*) user_data;
576
577         if (--data->ref_count == 0) {
578                 g_free (data->channel_type);
579                 if (data->destroy) {
580                         data->destroy (data->user_data);
581                 }
582                 g_slice_free (ConnectionRequestChannelData, data);
583         }
584 }
585
586 static void
587 connection_request_channel_cb (TpConnection *connection,
588                                const gchar *object_path,
589                                const GError *error,
590                                gpointer user_data,
591                                GObject *weak_object)
592 {
593         ConnectionRequestChannelData *data = (ConnectionRequestChannelData*) user_data;
594         TpChannel *channel;
595
596         if (!data->callback) {
597                 return;
598         }
599
600         if (error) {
601                 data->callback (connection, NULL, error, data->user_data, weak_object);
602                 return;
603         }
604
605         channel = tp_channel_new (connection, object_path,
606                                   data->channel_type,
607                                   data->handle_type,
608                                   data->handle, NULL);
609
610         data->callback (connection, channel, NULL, data->user_data, weak_object);
611         g_object_unref (channel);
612 }
613
614 static void
615 connection_request_handles_cb (TpConnection *connection,
616                                const GArray *handles,
617                                const GError *error,
618                                gpointer user_data,
619                                GObject *weak_object)
620 {
621         ConnectionRequestChannelData *data = (ConnectionRequestChannelData*) user_data;
622
623         if (error) {
624                 if (data->callback) {
625                         data->callback (connection, NULL, error, data->user_data, weak_object);
626                 }
627                 return;
628         }
629
630         data->handle = g_array_index (handles, guint, 0);
631         data->ref_count++;
632         tp_cli_connection_call_request_channel (connection, data->timeout_ms,
633                                                 data->channel_type,
634                                                 data->handle_type,
635                                                 data->handle,
636                                                 data->suppress_handler,
637                                                 connection_request_channel_cb,
638                                                 data,
639                                                 (GDestroyNotify) connection_request_channel_data_unref,
640                                                 weak_object);
641 }
642
643 void
644 empathy_connection_request_channel (TpConnection *connection,
645                                     gint timeout_ms,
646                                     const gchar *channel_type,
647                                     guint handle_type,
648                                     const gchar *name,
649                                     gboolean suppress_handler,
650                                     empathy_connection_callback_for_request_channel callback,
651                                     gpointer user_data,
652                                     GDestroyNotify destroy,
653                                     GObject *weak_object)
654 {
655         const gchar *names[] = {name, NULL};
656         ConnectionRequestChannelData *data;
657
658         data = g_slice_new (ConnectionRequestChannelData);
659         data->timeout_ms = timeout_ms;
660         data->channel_type = g_strdup (channel_type);
661         data->handle_type = handle_type;
662         data->callback = callback;
663         data->user_data = user_data;
664         data->destroy = destroy;
665         data->handle = 0;
666         data->suppress_handler = suppress_handler;
667         data->ref_count = 1;
668         tp_cli_connection_call_request_handles (connection,
669                                                 timeout_ms,
670                                                 handle_type,
671                                                 names,
672                                                 connection_request_handles_cb,
673                                                 data,
674                                                 (GDestroyNotify) connection_request_channel_data_unref,
675                                                 weak_object);
676 }
677