]> git.0d.be Git - empathy.git/blob - libempathy/empathy-utils.c
add myself to AUTHORS
[empathy.git] / libempathy / empathy-utils.c
1 /*
2  * Copyright (C) 2003-2007 Imendio AB
3  * Copyright (C) 2007-2008 Collabora Ltd.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program 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  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301  USA
19  *
20  * Authors: Richard Hult <richard@imendio.com>
21  *          Martyn Russell <martyn@imendio.com>
22  *          Xavier Claessens <xclaesse@gmail.com>
23  */
24
25 #include "config.h"
26
27 #include <string.h>
28 #include <time.h>
29 #include <sys/types.h>
30
31 #include <glib/gi18n-lib.h>
32
33 #include <libxml/uri.h>
34
35 #include <telepathy-glib/account-manager.h>
36 #include <telepathy-glib/connection.h>
37 #include <telepathy-glib/channel.h>
38 #include <telepathy-glib/dbus.h>
39 #include <telepathy-glib/util.h>
40
41 #include "empathy-utils.h"
42 #include "empathy-contact-manager.h"
43 #include "empathy-dispatcher.h"
44 #include "empathy-dispatch-operation.h"
45 #include "empathy-idle.h"
46 #include "empathy-tp-call.h"
47
48 #include <extensions/extensions.h>
49
50 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
51 #include "empathy-debug.h"
52
53 /* Translation between presence types and string */
54 static struct {
55         gchar *name;
56         TpConnectionPresenceType type;
57 } presence_types[] = {
58         { "available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE },
59         { "busy",      TP_CONNECTION_PRESENCE_TYPE_BUSY },
60         { "away",      TP_CONNECTION_PRESENCE_TYPE_AWAY },
61         { "ext_away",  TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY },
62         { "hidden",    TP_CONNECTION_PRESENCE_TYPE_HIDDEN },
63         { "offline",   TP_CONNECTION_PRESENCE_TYPE_OFFLINE },
64         { "unset",     TP_CONNECTION_PRESENCE_TYPE_UNSET },
65         { "unknown",   TP_CONNECTION_PRESENCE_TYPE_UNKNOWN },
66         { "error",     TP_CONNECTION_PRESENCE_TYPE_ERROR },
67         /* alternative names */
68         { "dnd",      TP_CONNECTION_PRESENCE_TYPE_BUSY },
69         { "brb",      TP_CONNECTION_PRESENCE_TYPE_AWAY },
70         { "xa",       TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY },
71         { NULL, },
72 };
73
74
75
76 void
77 empathy_init (void)
78 {
79         static gboolean initialized = FALSE;
80
81         if (initialized)
82                 return;
83
84         g_type_init ();
85
86         /* Setup gettext */
87         bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
88         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
89
90         /* Setup debug output for empathy and telepathy-glib */
91         if (g_getenv ("EMPATHY_TIMING") != NULL) {
92                 g_log_set_default_handler (tp_debug_timestamped_log_handler, NULL);
93         }
94         empathy_debug_set_flags (g_getenv ("EMPATHY_DEBUG"));
95         tp_debug_divert_messages (g_getenv ("EMPATHY_LOGFILE"));
96
97         emp_cli_init ();
98
99         initialized = TRUE;
100 }
101
102 gchar *
103 empathy_substring (const gchar *str,
104                   gint         start,
105                   gint         end)
106 {
107         return g_strndup (str + start, end - start);
108 }
109
110 gint
111 empathy_strcasecmp (const gchar *s1,
112                    const gchar *s2)
113 {
114         return empathy_strncasecmp (s1, s2, -1);
115 }
116
117 gint
118 empathy_strncasecmp (const gchar *s1,
119                     const gchar *s2,
120                     gsize        n)
121 {
122         gchar *u1, *u2;
123         gint   ret_val;
124
125         u1 = g_utf8_casefold (s1, n);
126         u2 = g_utf8_casefold (s2, n);
127
128         ret_val = g_utf8_collate (u1, u2);
129         g_free (u1);
130         g_free (u2);
131
132         return ret_val;
133 }
134
135 gboolean
136 empathy_xml_validate (xmlDoc      *doc,
137                      const gchar *dtd_filename)
138 {
139         gchar        *path;
140         xmlChar      *escaped;
141         xmlValidCtxt  cvp;
142         xmlDtd       *dtd;
143         gboolean      ret;
144
145         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "libempathy",
146                                  dtd_filename, NULL);
147         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
148                 g_free (path);
149                 path = g_build_filename (DATADIR, "empathy", dtd_filename, NULL);
150         }
151         DEBUG ("Loading dtd file %s", path);
152
153         /* The list of valid chars is taken from libxml. */
154         escaped = xmlURIEscapeStr ((const xmlChar *) path,
155                 (const xmlChar *)":@&=+$,/?;");
156         g_free (path);
157
158         memset (&cvp, 0, sizeof (cvp));
159         dtd = xmlParseDTD (NULL, escaped);
160         ret = xmlValidateDtd (&cvp, doc, dtd);
161
162         xmlFree (escaped);
163         xmlFreeDtd (dtd);
164
165         return ret;
166 }
167
168 xmlNodePtr
169 empathy_xml_node_get_child (xmlNodePtr   node,
170                            const gchar *child_name)
171 {
172         xmlNodePtr l;
173
174         g_return_val_if_fail (node != NULL, NULL);
175         g_return_val_if_fail (child_name != NULL, NULL);
176
177         for (l = node->children; l; l = l->next) {
178                 if (l->name && strcmp ((const gchar *) l->name, child_name) == 0) {
179                         return l;
180                 }
181         }
182
183         return NULL;
184 }
185
186 xmlChar *
187 empathy_xml_node_get_child_content (xmlNodePtr   node,
188                                    const gchar *child_name)
189 {
190         xmlNodePtr l;
191
192         g_return_val_if_fail (node != NULL, NULL);
193         g_return_val_if_fail (child_name != NULL, NULL);
194
195         l = empathy_xml_node_get_child (node, child_name);
196         if (l) {
197                 return xmlNodeGetContent (l);
198         }
199
200         return NULL;
201 }
202
203 xmlNodePtr
204 empathy_xml_node_find_child_prop_value (xmlNodePtr   node,
205                                        const gchar *prop_name,
206                                        const gchar *prop_value)
207 {
208         xmlNodePtr l;
209         xmlNodePtr found = NULL;
210
211         g_return_val_if_fail (node != NULL, NULL);
212         g_return_val_if_fail (prop_name != NULL, NULL);
213         g_return_val_if_fail (prop_value != NULL, NULL);
214
215         for (l = node->children; l && !found; l = l->next) {
216                 xmlChar *prop;
217
218                 if (!xmlHasProp (l, (const xmlChar *) prop_name)) {
219                         continue;
220                 }
221
222                 prop = xmlGetProp (l, (const xmlChar *) prop_name);
223                 if (prop && strcmp ((const gchar *) prop, prop_value) == 0) {
224                         found = l;
225                 }
226
227                 xmlFree (prop);
228         }
229
230         return found;
231 }
232
233 const gchar *
234 empathy_presence_get_default_message (TpConnectionPresenceType presence)
235 {
236         switch (presence) {
237         case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
238                 return _("Available");
239         case TP_CONNECTION_PRESENCE_TYPE_BUSY:
240                 return _("Busy");
241         case TP_CONNECTION_PRESENCE_TYPE_AWAY:
242         case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
243                 return _("Away");
244         case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
245                 return _("Hidden");
246         case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
247                 return _("Offline");
248         case TP_CONNECTION_PRESENCE_TYPE_UNSET:
249         case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
250         case TP_CONNECTION_PRESENCE_TYPE_ERROR:
251                 return NULL;
252         }
253
254         return NULL;
255 }
256
257 const gchar *
258 empathy_presence_to_str (TpConnectionPresenceType presence)
259 {
260         int i;
261
262         for (i = 0 ; presence_types[i].name != NULL; i++)
263                 if (presence == presence_types[i].type)
264                         return presence_types[i].name;
265
266         return NULL;
267 }
268
269 TpConnectionPresenceType
270 empathy_presence_from_str (const gchar *str)
271 {
272         int i;
273
274         for (i = 0 ; presence_types[i].name != NULL; i++)
275                 if (!tp_strdiff (str, presence_types[i].name))
276                         return presence_types[i].type;
277
278         return TP_CONNECTION_PRESENCE_TYPE_UNSET;
279 }
280
281 gchar *
282 empathy_file_lookup (const gchar *filename, const gchar *subdir)
283 {
284         gchar *path;
285
286         if (!subdir) {
287                 subdir = ".";
288         }
289
290         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), subdir, filename, NULL);
291         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
292                 g_free (path);
293                 path = g_build_filename (DATADIR, "empathy", filename, NULL);
294         }
295
296         return path;
297 }
298
299 guint
300 empathy_proxy_hash (gconstpointer key)
301 {
302         TpProxy      *proxy = TP_PROXY (key);
303         TpProxyClass *proxy_class = TP_PROXY_GET_CLASS (key);
304
305         g_return_val_if_fail (TP_IS_PROXY (proxy), 0);
306         g_return_val_if_fail (proxy_class->must_have_unique_name, 0);
307
308         return g_str_hash (proxy->object_path) ^ g_str_hash (proxy->bus_name);
309 }
310
311 gboolean
312 empathy_proxy_equal (gconstpointer a,
313                      gconstpointer b)
314 {
315         TpProxy *proxy_a = TP_PROXY (a);
316         TpProxy *proxy_b = TP_PROXY (b);
317         TpProxyClass *proxy_a_class = TP_PROXY_GET_CLASS (a);
318         TpProxyClass *proxy_b_class = TP_PROXY_GET_CLASS (b);
319
320         g_return_val_if_fail (TP_IS_PROXY (proxy_a), FALSE);
321         g_return_val_if_fail (TP_IS_PROXY (proxy_b), FALSE);
322         g_return_val_if_fail (proxy_a_class->must_have_unique_name, 0);
323         g_return_val_if_fail (proxy_b_class->must_have_unique_name, 0);
324
325         return g_str_equal (proxy_a->object_path, proxy_b->object_path) &&
326                g_str_equal (proxy_a->bus_name, proxy_b->bus_name);
327 }
328
329 gboolean
330 empathy_check_available_state (void)
331 {
332         TpConnectionPresenceType presence;
333         EmpathyIdle *idle;
334
335         idle = empathy_idle_dup_singleton ();
336         presence = empathy_idle_get_state (idle);
337         g_object_unref (idle);
338
339         if (presence != TP_CONNECTION_PRESENCE_TYPE_AVAILABLE &&
340                 presence != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
341                 return FALSE;
342         }
343
344         return TRUE;
345 }
346
347 gint
348 empathy_uint_compare (gconstpointer a,
349                       gconstpointer b)
350 {
351         return *(guint *) a - *(guint *) b;
352 }
353
354 gchar *
355 empathy_protocol_icon_name (const gchar *protocol)
356 {
357   return g_strdup_printf ("im-%s", protocol);
358 }
359
360 GType
361 empathy_type_dbus_ao (void)
362 {
363   static GType t = 0;
364
365   if (G_UNLIKELY (t == 0))
366      t = dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH);
367
368   return t;
369 }
370
371 const char *
372 empathy_protocol_name_to_display_name (const gchar *proto_name)
373 {
374   int i;
375   static struct {
376     const gchar *proto;
377     const gchar *display;
378     gboolean translated;
379   } names[] = {
380     { "jabber", "Jabber", FALSE },
381     { "gtalk", "Google Talk", FALSE },
382     { "msn", "MSN", FALSE, },
383     { "local-xmpp", N_("People Nearby"), TRUE },
384     { "irc", "IRC", FALSE },
385     { "icq", "ICQ", FALSE },
386     { "aim", "AIM", FALSE },
387     { "yahoo", "Yahoo!", FALSE },
388     { "yahoojp", N_("Yahoo! Japan"), TRUE },
389     { "facebook", N_("Facebook Chat"), TRUE },
390     { "groupwise", "GroupWise", FALSE },
391     { "sip", "SIP", FALSE },
392     { NULL, NULL }
393   };
394
395   for (i = 0; names[i].proto != NULL; i++)
396     {
397       if (!tp_strdiff (proto_name, names[i].proto))
398         {
399           if (names[i].translated)
400             return _(names[i].display);
401           else
402             return names[i].display;
403         }
404     }
405
406   return NULL;
407 }
408
409 typedef struct {
410     GObject *instance;
411     GObject *user_data;
412     gulong handler_id;
413 } WeakHandlerCtx;
414
415 static WeakHandlerCtx *
416 whc_new (GObject *instance,
417          GObject *user_data)
418 {
419   WeakHandlerCtx *ctx = g_slice_new0 (WeakHandlerCtx);
420
421   ctx->instance = instance;
422   ctx->user_data = user_data;
423
424   return ctx;
425 }
426
427 static void
428 whc_free (WeakHandlerCtx *ctx)
429 {
430   g_slice_free (WeakHandlerCtx, ctx);
431 }
432
433 static void user_data_destroyed_cb (gpointer, GObject *);
434
435 static void
436 instance_destroyed_cb (gpointer ctx_,
437                        GObject *where_the_instance_was)
438 {
439   WeakHandlerCtx *ctx = ctx_;
440
441   DEBUG ("instance for %p destroyed; cleaning up", ctx);
442
443   /* No need to disconnect the signal here, the instance has gone away. */
444   g_object_weak_unref (ctx->user_data, user_data_destroyed_cb, ctx);
445   whc_free (ctx);
446 }
447
448 static void
449 user_data_destroyed_cb (gpointer ctx_,
450                         GObject *where_the_user_data_was)
451 {
452   WeakHandlerCtx *ctx = ctx_;
453
454   DEBUG ("user_data for %p destroyed; disconnecting", ctx);
455
456   g_signal_handler_disconnect (ctx->instance, ctx->handler_id);
457   g_object_weak_unref (ctx->instance, instance_destroyed_cb, ctx);
458   whc_free (ctx);
459 }
460
461 /* This function is copied from telepathy-gabble: util.c */
462 /**
463  * empathy_signal_connect_weak:
464  * @instance: the instance to connect to.
465  * @detailed_signal: a string of the form "signal-name::detail".
466  * @c_handler: the GCallback to connect.
467  * @user_data: an object to pass as data to c_handler calls.
468  *
469  * Connects a #GCallback function to a signal for a particular object, as if
470  * with g_signal_connect(). Additionally, arranges for the signal handler to be
471  * disconnected if @user_data is destroyed.
472  *
473  * This is intended to be a convenient way for objects to use themselves as
474  * user_data for callbacks without having to explicitly disconnect all the
475  * handlers in their finalizers.
476  */
477 void
478 empathy_signal_connect_weak (gpointer instance,
479     const gchar *detailed_signal,
480     GCallback c_handler,
481     GObject *user_data)
482 {
483   GObject *instance_obj = G_OBJECT (instance);
484   WeakHandlerCtx *ctx = whc_new (instance_obj, user_data);
485
486   DEBUG ("connecting to %p:%s with context %p", instance, detailed_signal, ctx);
487
488   ctx->handler_id = g_signal_connect (instance, detailed_signal, c_handler,
489       user_data);
490
491   g_object_weak_ref (instance_obj, instance_destroyed_cb, ctx);
492   g_object_weak_ref (user_data, user_data_destroyed_cb, ctx);
493 }
494
495 /* Note: this function depends on the account manager having its core feature
496  * prepared. */
497 TpAccount *
498 empathy_get_account_for_connection (TpConnection *connection)
499 {
500   TpAccountManager *manager;
501   TpAccount *account = NULL;
502   GList *accounts, *l;
503
504   manager = tp_account_manager_dup ();
505
506   accounts = tp_account_manager_get_valid_accounts (manager);
507
508   for (l = accounts; l != NULL; l = l->next)
509     {
510       TpAccount *a = l->data;
511
512       if (tp_account_get_connection (a) == connection)
513         {
514           account = a;
515           break;
516         }
517     }
518
519   g_list_free (accounts);
520   g_object_unref (manager);
521
522   return account;
523 }
524
525 gboolean
526 empathy_account_manager_get_accounts_connected (gboolean *connecting)
527 {
528   TpAccountManager *manager;
529   GList *accounts, *l;
530   gboolean out_connecting = FALSE;
531   gboolean out_connected = FALSE;
532
533   manager = tp_account_manager_dup ();
534
535   if (G_UNLIKELY (!tp_account_manager_is_prepared (manager,
536           TP_ACCOUNT_MANAGER_FEATURE_CORE)))
537     g_critical (G_STRLOC ": %s called before AccountManager ready", G_STRFUNC);
538
539   accounts = tp_account_manager_get_valid_accounts (manager);
540
541   for (l = accounts; l != NULL; l = l->next)
542     {
543       TpConnectionStatus s = tp_account_get_connection_status (
544           TP_ACCOUNT (l->data), NULL);
545
546       if (s == TP_CONNECTION_STATUS_CONNECTING)
547         out_connecting = TRUE;
548       else if (s == TP_CONNECTION_STATUS_CONNECTED)
549         out_connected = TRUE;
550
551       if (out_connecting && out_connected)
552         break;
553     }
554
555   g_list_free (accounts);
556   g_object_unref (manager);
557
558   if (connecting != NULL)
559     *connecting = out_connecting;
560
561   return out_connected;
562 }