]> git.0d.be Git - empathy.git/blob - libempathy/empathy-utils.c
add m4/as-compiler-flag.m4
[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., 51 Franklin St, Fifth Floor,
19  * Boston, MA  02110-1301  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-lib.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 #include <telepathy-glib/util.h>
39
40 #include "empathy-utils.h"
41 #include "empathy-contact-manager.h"
42 #include "empathy-dispatcher.h"
43 #include "empathy-dispatch-operation.h"
44 #include "empathy-idle.h"
45 #include "empathy-tp-call.h"
46
47 #include <extensions/extensions.h>
48
49 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
50 #include "empathy-debug.h"
51
52 /* Translation between presence types and string */
53 static struct {
54         gchar *name;
55         TpConnectionPresenceType type;
56 } presence_types[] = {
57         { "available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE },
58         { "busy",      TP_CONNECTION_PRESENCE_TYPE_BUSY },
59         { "away",      TP_CONNECTION_PRESENCE_TYPE_AWAY },
60         { "ext_away",  TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY },
61         { "hidden",    TP_CONNECTION_PRESENCE_TYPE_HIDDEN },
62         { "offline",   TP_CONNECTION_PRESENCE_TYPE_OFFLINE },
63         { "unset",     TP_CONNECTION_PRESENCE_TYPE_UNSET },
64         { "unknown",   TP_CONNECTION_PRESENCE_TYPE_UNKNOWN },
65         { "error",     TP_CONNECTION_PRESENCE_TYPE_ERROR },
66         /* alternative names */
67         { "dnd",      TP_CONNECTION_PRESENCE_TYPE_BUSY },
68         { "brb",      TP_CONNECTION_PRESENCE_TYPE_AWAY },
69         { "xa",       TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY },
70         { NULL, },
71 };
72
73
74
75 void
76 empathy_init (void)
77 {
78         static gboolean initialized = FALSE;
79
80         if (initialized)
81                 return;
82
83         g_type_init ();
84
85         /* Setup gettext */
86         bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
87         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
88
89         /* Setup debug output for empathy and telepathy-glib */
90         if (g_getenv ("EMPATHY_TIMING") != NULL) {
91                 g_log_set_default_handler (tp_debug_timestamped_log_handler, NULL);
92         }
93         empathy_debug_set_flags (g_getenv ("EMPATHY_DEBUG"));
94         tp_debug_divert_messages (g_getenv ("EMPATHY_LOGFILE"));
95
96         emp_cli_init ();
97
98         initialized = TRUE;
99 }
100
101 gchar *
102 empathy_substring (const gchar *str,
103                   gint         start,
104                   gint         end)
105 {
106         return g_strndup (str + start, end - start);
107 }
108
109 gint
110 empathy_strcasecmp (const gchar *s1,
111                    const gchar *s2)
112 {
113         return empathy_strncasecmp (s1, s2, -1);
114 }
115
116 gint
117 empathy_strncasecmp (const gchar *s1,
118                     const gchar *s2,
119                     gsize        n)
120 {
121         gchar *u1, *u2;
122         gint   ret_val;
123
124         u1 = g_utf8_casefold (s1, n);
125         u2 = g_utf8_casefold (s2, n);
126
127         ret_val = g_utf8_collate (u1, u2);
128         g_free (u1);
129         g_free (u2);
130
131         return ret_val;
132 }
133
134 gboolean
135 empathy_xml_validate (xmlDoc      *doc,
136                      const gchar *dtd_filename)
137 {
138         gchar        *path;
139         xmlChar      *escaped;
140         xmlValidCtxt  cvp;
141         xmlDtd       *dtd;
142         gboolean      ret;
143
144         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "libempathy",
145                                  dtd_filename, NULL);
146         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
147                 g_free (path);
148                 path = g_build_filename (DATADIR, "empathy", dtd_filename, NULL);
149         }
150         DEBUG ("Loading dtd file %s", path);
151
152         /* The list of valid chars is taken from libxml. */
153         escaped = xmlURIEscapeStr ((const xmlChar *) path,
154                 (const xmlChar *)":@&=+$,/?;");
155         g_free (path);
156
157         memset (&cvp, 0, sizeof (cvp));
158         dtd = xmlParseDTD (NULL, escaped);
159         ret = xmlValidateDtd (&cvp, doc, dtd);
160
161         xmlFree (escaped);
162         xmlFreeDtd (dtd);
163
164         return ret;
165 }
166
167 xmlNodePtr
168 empathy_xml_node_get_child (xmlNodePtr   node,
169                            const gchar *child_name)
170 {
171         xmlNodePtr l;
172
173         g_return_val_if_fail (node != NULL, NULL);
174         g_return_val_if_fail (child_name != NULL, NULL);
175
176         for (l = node->children; l; l = l->next) {
177                 if (l->name && strcmp ((const gchar *) l->name, child_name) == 0) {
178                         return l;
179                 }
180         }
181
182         return NULL;
183 }
184
185 xmlChar *
186 empathy_xml_node_get_child_content (xmlNodePtr   node,
187                                    const gchar *child_name)
188 {
189         xmlNodePtr l;
190
191         g_return_val_if_fail (node != NULL, NULL);
192         g_return_val_if_fail (child_name != NULL, NULL);
193
194         l = empathy_xml_node_get_child (node, child_name);
195         if (l) {
196                 return xmlNodeGetContent (l);
197         }
198
199         return NULL;
200 }
201
202 xmlNodePtr
203 empathy_xml_node_find_child_prop_value (xmlNodePtr   node,
204                                        const gchar *prop_name,
205                                        const gchar *prop_value)
206 {
207         xmlNodePtr l;
208         xmlNodePtr found = NULL;
209
210         g_return_val_if_fail (node != NULL, NULL);
211         g_return_val_if_fail (prop_name != NULL, NULL);
212         g_return_val_if_fail (prop_value != NULL, NULL);
213
214         for (l = node->children; l && !found; l = l->next) {
215                 xmlChar *prop;
216
217                 if (!xmlHasProp (l, (const xmlChar *) prop_name)) {
218                         continue;
219                 }
220
221                 prop = xmlGetProp (l, (const xmlChar *) prop_name);
222                 if (prop && strcmp ((const gchar *) prop, prop_value) == 0) {
223                         found = l;
224                 }
225
226                 xmlFree (prop);
227         }
228
229         return found;
230 }
231
232 const gchar *
233 empathy_presence_get_default_message (TpConnectionPresenceType presence)
234 {
235         switch (presence) {
236         case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
237                 return _("Available");
238         case TP_CONNECTION_PRESENCE_TYPE_BUSY:
239                 return _("Busy");
240         case TP_CONNECTION_PRESENCE_TYPE_AWAY:
241         case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
242                 return _("Away");
243         case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
244                 return _("Hidden");
245         case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
246                 return _("Offline");
247         case TP_CONNECTION_PRESENCE_TYPE_UNSET:
248         case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
249         case TP_CONNECTION_PRESENCE_TYPE_ERROR:
250                 return NULL;
251         }
252
253         return NULL;
254 }
255
256 const gchar *
257 empathy_presence_to_str (TpConnectionPresenceType presence)
258 {
259         int i;
260
261         for (i = 0 ; presence_types[i].name != NULL; i++)
262                 if (presence == presence_types[i].type)
263                         return presence_types[i].name;
264
265         return NULL;
266 }
267
268 TpConnectionPresenceType
269 empathy_presence_from_str (const gchar *str)
270 {
271         int i;
272
273         for (i = 0 ; presence_types[i].name != NULL; i++)
274                 if (!tp_strdiff (str, presence_types[i].name))
275                         return presence_types[i].type;
276
277         return TP_CONNECTION_PRESENCE_TYPE_UNSET;
278 }
279
280 gchar *
281 empathy_file_lookup (const gchar *filename, const gchar *subdir)
282 {
283         gchar *path;
284
285         if (!subdir) {
286                 subdir = ".";
287         }
288
289         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), subdir, filename, NULL);
290         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
291                 g_free (path);
292                 path = g_build_filename (DATADIR, "empathy", filename, NULL);
293         }
294
295         return path;
296 }
297
298 guint
299 empathy_proxy_hash (gconstpointer key)
300 {
301         TpProxy      *proxy = TP_PROXY (key);
302         TpProxyClass *proxy_class = TP_PROXY_GET_CLASS (key);
303
304         g_return_val_if_fail (TP_IS_PROXY (proxy), 0);
305         g_return_val_if_fail (proxy_class->must_have_unique_name, 0);
306
307         return g_str_hash (proxy->object_path) ^ g_str_hash (proxy->bus_name);
308 }
309
310 gboolean
311 empathy_proxy_equal (gconstpointer a,
312                      gconstpointer b)
313 {
314         TpProxy *proxy_a = TP_PROXY (a);
315         TpProxy *proxy_b = TP_PROXY (b);
316         TpProxyClass *proxy_a_class = TP_PROXY_GET_CLASS (a);
317         TpProxyClass *proxy_b_class = TP_PROXY_GET_CLASS (b);
318
319         g_return_val_if_fail (TP_IS_PROXY (proxy_a), FALSE);
320         g_return_val_if_fail (TP_IS_PROXY (proxy_b), FALSE);
321         g_return_val_if_fail (proxy_a_class->must_have_unique_name, 0);
322         g_return_val_if_fail (proxy_b_class->must_have_unique_name, 0);
323
324         return g_str_equal (proxy_a->object_path, proxy_b->object_path) &&
325                g_str_equal (proxy_a->bus_name, proxy_b->bus_name);
326 }
327
328 gboolean
329 empathy_check_available_state (void)
330 {
331         TpConnectionPresenceType presence;
332         EmpathyIdle *idle;
333
334         idle = empathy_idle_dup_singleton ();
335         presence = empathy_idle_get_state (idle);
336         g_object_unref (idle);
337
338         if (presence != TP_CONNECTION_PRESENCE_TYPE_AVAILABLE &&
339                 presence != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
340                 return FALSE;
341         }
342
343         return TRUE;
344 }
345
346 gint
347 empathy_uint_compare (gconstpointer a,
348                       gconstpointer b)
349 {
350         return *(guint *) a - *(guint *) b;
351 }
352
353 gchar *
354 empathy_protocol_icon_name (const gchar *protocol)
355 {
356   return g_strdup_printf ("im-%s", protocol);
357 }
358
359 GType
360 empathy_type_dbus_ao (void)
361 {
362   static GType t = 0;
363
364   if (G_UNLIKELY (t == 0))
365      t = dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH);
366
367   return t;
368 }
369
370 const char *
371 empathy_protocol_name_to_display_name (const gchar *proto_name)
372 {
373   int i;
374   static struct {
375     const gchar *proto;
376     const gchar *display;
377     gboolean translated;
378   } names[] = {
379     { "jabber", "Jabber", FALSE },
380     { "gtalk", "Google Talk", FALSE },
381     { "msn", "MSN", FALSE, },
382     { "local-xmpp", N_("People Nearby"), TRUE },
383     { "irc", "IRC", FALSE },
384     { "icq", "ICQ", FALSE },
385     { "aim", "AIM", FALSE },
386     { "yahoo", "Yahoo!", FALSE },
387     { "yahoojp", N_("Yahoo! Japan"), TRUE },
388     { "facebook", N_("Facebook Chat"), TRUE },
389     { "groupwise", "GroupWise", FALSE },
390     { "sip", "SIP", FALSE },
391     { NULL, NULL }
392   };
393
394   for (i = 0; names[i].proto != NULL; i++)
395     {
396       if (!tp_strdiff (proto_name, names[i].proto))
397         {
398           if (names[i].translated)
399             return _(names[i].display);
400           else
401             return names[i].display;
402         }
403     }
404
405   return NULL;
406 }
407
408 typedef struct {
409     GObject *instance;
410     GObject *user_data;
411     gulong handler_id;
412 } WeakHandlerCtx;
413
414 static WeakHandlerCtx *
415 whc_new (GObject *instance,
416          GObject *user_data)
417 {
418   WeakHandlerCtx *ctx = g_slice_new0 (WeakHandlerCtx);
419
420   ctx->instance = instance;
421   ctx->user_data = user_data;
422
423   return ctx;
424 }
425
426 static void
427 whc_free (WeakHandlerCtx *ctx)
428 {
429   g_slice_free (WeakHandlerCtx, ctx);
430 }
431
432 static void user_data_destroyed_cb (gpointer, GObject *);
433
434 static void
435 instance_destroyed_cb (gpointer ctx_,
436                        GObject *where_the_instance_was)
437 {
438   WeakHandlerCtx *ctx = ctx_;
439
440   DEBUG ("instance for %p destroyed; cleaning up", ctx);
441
442   /* No need to disconnect the signal here, the instance has gone away. */
443   g_object_weak_unref (ctx->user_data, user_data_destroyed_cb, ctx);
444   whc_free (ctx);
445 }
446
447 static void
448 user_data_destroyed_cb (gpointer ctx_,
449                         GObject *where_the_user_data_was)
450 {
451   WeakHandlerCtx *ctx = ctx_;
452
453   DEBUG ("user_data for %p destroyed; disconnecting", ctx);
454
455   g_signal_handler_disconnect (ctx->instance, ctx->handler_id);
456   g_object_weak_unref (ctx->instance, instance_destroyed_cb, ctx);
457   whc_free (ctx);
458 }
459
460 /* This function is copied from telepathy-gabble: util.c */
461 /**
462  * empathy_signal_connect_weak:
463  * @instance: the instance to connect to.
464  * @detailed_signal: a string of the form "signal-name::detail".
465  * @c_handler: the GCallback to connect.
466  * @user_data: an object to pass as data to c_handler calls.
467  *
468  * Connects a #GCallback function to a signal for a particular object, as if
469  * with g_signal_connect(). Additionally, arranges for the signal handler to be
470  * disconnected if @user_data is destroyed.
471  *
472  * This is intended to be a convenient way for objects to use themselves as
473  * user_data for callbacks without having to explicitly disconnect all the
474  * handlers in their finalizers.
475  */
476 void
477 empathy_signal_connect_weak (gpointer instance,
478     const gchar *detailed_signal,
479     GCallback c_handler,
480     GObject *user_data)
481 {
482   GObject *instance_obj = G_OBJECT (instance);
483   WeakHandlerCtx *ctx = whc_new (instance_obj, user_data);
484
485   DEBUG ("connecting to %p:%s with context %p", instance, detailed_signal, ctx);
486
487   ctx->handler_id = g_signal_connect (instance, detailed_signal, c_handler,
488       user_data);
489
490   g_object_weak_ref (instance_obj, instance_destroyed_cb, ctx);
491   g_object_weak_ref (user_data, user_data_destroyed_cb, ctx);
492 }