]> git.0d.be Git - empathy.git/blob - libempathy/empathy-utils.c
Base the contact list around libfolks metacontacts. Not yet to feature-parity
[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 <folks/folks.h>
36 #include <folks/folks-telepathy.h>
37
38 #include <telepathy-glib/account-manager.h>
39 #include <telepathy-glib/connection.h>
40 #include <telepathy-glib/channel.h>
41 #include <telepathy-glib/dbus.h>
42 #include <telepathy-glib/util.h>
43
44 #include "empathy-utils.h"
45 #include "empathy-contact-manager.h"
46 #include "empathy-dispatcher.h"
47 #include "empathy-dispatch-operation.h"
48 #include "empathy-idle.h"
49 #include "empathy-tp-call.h"
50 #include "empathy-tp-contact-factory.h"
51
52 #include <extensions/extensions.h>
53
54 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
55 #include "empathy-debug.h"
56
57 /* Translation between presence types and string */
58 static struct {
59         gchar *name;
60         TpConnectionPresenceType type;
61 } presence_types[] = {
62         { "available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE },
63         { "busy",      TP_CONNECTION_PRESENCE_TYPE_BUSY },
64         { "away",      TP_CONNECTION_PRESENCE_TYPE_AWAY },
65         { "ext_away",  TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY },
66         { "hidden",    TP_CONNECTION_PRESENCE_TYPE_HIDDEN },
67         { "offline",   TP_CONNECTION_PRESENCE_TYPE_OFFLINE },
68         { "unset",     TP_CONNECTION_PRESENCE_TYPE_UNSET },
69         { "unknown",   TP_CONNECTION_PRESENCE_TYPE_UNKNOWN },
70         { "error",     TP_CONNECTION_PRESENCE_TYPE_ERROR },
71         /* alternative names */
72         { "dnd",      TP_CONNECTION_PRESENCE_TYPE_BUSY },
73         { "brb",      TP_CONNECTION_PRESENCE_TYPE_AWAY },
74         { "xa",       TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY },
75         { NULL, },
76 };
77
78
79
80 void
81 empathy_init (void)
82 {
83         static gboolean initialized = FALSE;
84
85         if (initialized)
86                 return;
87
88         g_type_init ();
89
90         /* Setup gettext */
91         bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
92         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
93
94         /* Setup debug output for empathy and telepathy-glib */
95         if (g_getenv ("EMPATHY_TIMING") != NULL) {
96                 g_log_set_default_handler (tp_debug_timestamped_log_handler, NULL);
97         }
98         empathy_debug_set_flags (g_getenv ("EMPATHY_DEBUG"));
99         tp_debug_divert_messages (g_getenv ("EMPATHY_LOGFILE"));
100
101         emp_cli_init ();
102
103         initialized = TRUE;
104 }
105
106 gchar *
107 empathy_substring (const gchar *str,
108                   gint         start,
109                   gint         end)
110 {
111         return g_strndup (str + start, end - start);
112 }
113
114 gint
115 empathy_strcasecmp (const gchar *s1,
116                    const gchar *s2)
117 {
118         return empathy_strncasecmp (s1, s2, -1);
119 }
120
121 gint
122 empathy_strncasecmp (const gchar *s1,
123                     const gchar *s2,
124                     gsize        n)
125 {
126         gchar *u1, *u2;
127         gint   ret_val;
128
129         u1 = g_utf8_casefold (s1, n);
130         u2 = g_utf8_casefold (s2, n);
131
132         ret_val = g_utf8_collate (u1, u2);
133         g_free (u1);
134         g_free (u2);
135
136         return ret_val;
137 }
138
139 gboolean
140 empathy_xml_validate (xmlDoc      *doc,
141                      const gchar *dtd_filename)
142 {
143         gchar        *path;
144         xmlChar      *escaped;
145         xmlValidCtxt  cvp;
146         xmlDtd       *dtd;
147         gboolean      ret;
148
149         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "libempathy",
150                                  dtd_filename, NULL);
151         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
152                 g_free (path);
153                 path = g_build_filename (DATADIR, "empathy", dtd_filename, NULL);
154         }
155         DEBUG ("Loading dtd file %s", path);
156
157         /* The list of valid chars is taken from libxml. */
158         escaped = xmlURIEscapeStr ((const xmlChar *) path,
159                 (const xmlChar *)":@&=+$,/?;");
160         g_free (path);
161
162         memset (&cvp, 0, sizeof (cvp));
163         dtd = xmlParseDTD (NULL, escaped);
164         ret = xmlValidateDtd (&cvp, doc, dtd);
165
166         xmlFree (escaped);
167         xmlFreeDtd (dtd);
168
169         return ret;
170 }
171
172 xmlNodePtr
173 empathy_xml_node_get_child (xmlNodePtr   node,
174                            const gchar *child_name)
175 {
176         xmlNodePtr l;
177
178         g_return_val_if_fail (node != NULL, NULL);
179         g_return_val_if_fail (child_name != NULL, NULL);
180
181         for (l = node->children; l; l = l->next) {
182                 if (l->name && strcmp ((const gchar *) l->name, child_name) == 0) {
183                         return l;
184                 }
185         }
186
187         return NULL;
188 }
189
190 xmlChar *
191 empathy_xml_node_get_child_content (xmlNodePtr   node,
192                                    const gchar *child_name)
193 {
194         xmlNodePtr l;
195
196         g_return_val_if_fail (node != NULL, NULL);
197         g_return_val_if_fail (child_name != NULL, NULL);
198
199         l = empathy_xml_node_get_child (node, child_name);
200         if (l) {
201                 return xmlNodeGetContent (l);
202         }
203
204         return NULL;
205 }
206
207 xmlNodePtr
208 empathy_xml_node_find_child_prop_value (xmlNodePtr   node,
209                                        const gchar *prop_name,
210                                        const gchar *prop_value)
211 {
212         xmlNodePtr l;
213         xmlNodePtr found = NULL;
214
215         g_return_val_if_fail (node != NULL, NULL);
216         g_return_val_if_fail (prop_name != NULL, NULL);
217         g_return_val_if_fail (prop_value != NULL, NULL);
218
219         for (l = node->children; l && !found; l = l->next) {
220                 xmlChar *prop;
221
222                 if (!xmlHasProp (l, (const xmlChar *) prop_name)) {
223                         continue;
224                 }
225
226                 prop = xmlGetProp (l, (const xmlChar *) prop_name);
227                 if (prop && strcmp ((const gchar *) prop, prop_value) == 0) {
228                         found = l;
229                 }
230
231                 xmlFree (prop);
232         }
233
234         return found;
235 }
236
237 const gchar *
238 empathy_presence_get_default_message (TpConnectionPresenceType presence)
239 {
240         switch (presence) {
241         case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
242                 return _("Available");
243         case TP_CONNECTION_PRESENCE_TYPE_BUSY:
244                 return _("Busy");
245         case TP_CONNECTION_PRESENCE_TYPE_AWAY:
246         case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
247                 return _("Away");
248         case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
249                 return _("Invisible");
250         case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
251                 return _("Offline");
252         case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
253                 return _("Unknown");
254         case TP_CONNECTION_PRESENCE_TYPE_UNSET:
255         case TP_CONNECTION_PRESENCE_TYPE_ERROR:
256                 return NULL;
257         }
258
259         return NULL;
260 }
261
262 const gchar *
263 empathy_presence_to_str (TpConnectionPresenceType presence)
264 {
265         int i;
266
267         for (i = 0 ; presence_types[i].name != NULL; i++)
268                 if (presence == presence_types[i].type)
269                         return presence_types[i].name;
270
271         return NULL;
272 }
273
274 TpConnectionPresenceType
275 empathy_presence_from_str (const gchar *str)
276 {
277         int i;
278
279         for (i = 0 ; presence_types[i].name != NULL; i++)
280                 if (!tp_strdiff (str, presence_types[i].name))
281                         return presence_types[i].type;
282
283         return TP_CONNECTION_PRESENCE_TYPE_UNSET;
284 }
285
286 const gchar *
287 empathy_status_reason_get_default_message (TpConnectionStatusReason reason)
288 {
289         switch (reason) {
290         case TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED:
291                 return _("No reason specified");
292         case TP_CONNECTION_STATUS_REASON_REQUESTED:
293                 return _("Status is set to offline");
294         case TP_CONNECTION_STATUS_REASON_NETWORK_ERROR:
295                 return _("Network error");
296         case TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED:
297                 return _("Authentication failed");
298         case TP_CONNECTION_STATUS_REASON_ENCRYPTION_ERROR:
299                 return _("Encryption error");
300         case TP_CONNECTION_STATUS_REASON_NAME_IN_USE:
301                 return _("Name in use");
302         case TP_CONNECTION_STATUS_REASON_CERT_NOT_PROVIDED:
303                 return _("Certificate not provided");
304         case TP_CONNECTION_STATUS_REASON_CERT_UNTRUSTED:
305                 return _("Certificate untrusted");
306         case TP_CONNECTION_STATUS_REASON_CERT_EXPIRED:
307                 return _("Certificate expired");
308         case TP_CONNECTION_STATUS_REASON_CERT_NOT_ACTIVATED:
309                 return _("Certificate not activated");
310         case TP_CONNECTION_STATUS_REASON_CERT_HOSTNAME_MISMATCH:
311                 return _("Certificate hostname mismatch");
312         case TP_CONNECTION_STATUS_REASON_CERT_FINGERPRINT_MISMATCH:
313                 return _("Certificate fingerprint mismatch");
314         case TP_CONNECTION_STATUS_REASON_CERT_SELF_SIGNED:
315                 return _("Certificate self-signed");
316         case TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR:
317                 return _("Certificate error");
318         default:
319                 return _("Unknown reason");
320         }
321 }
322
323 gchar *
324 empathy_file_lookup (const gchar *filename, const gchar *subdir)
325 {
326         gchar *path;
327
328         if (!subdir) {
329                 subdir = ".";
330         }
331
332         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), subdir, filename, NULL);
333         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
334                 g_free (path);
335                 path = g_build_filename (DATADIR, "empathy", filename, NULL);
336         }
337
338         return path;
339 }
340
341 guint
342 empathy_proxy_hash (gconstpointer key)
343 {
344         TpProxy      *proxy = TP_PROXY (key);
345         TpProxyClass *proxy_class = TP_PROXY_GET_CLASS (key);
346
347         g_return_val_if_fail (TP_IS_PROXY (proxy), 0);
348         g_return_val_if_fail (proxy_class->must_have_unique_name, 0);
349
350         return g_str_hash (proxy->object_path) ^ g_str_hash (proxy->bus_name);
351 }
352
353 gboolean
354 empathy_proxy_equal (gconstpointer a,
355                      gconstpointer b)
356 {
357         TpProxy *proxy_a = TP_PROXY (a);
358         TpProxy *proxy_b = TP_PROXY (b);
359         TpProxyClass *proxy_a_class = TP_PROXY_GET_CLASS (a);
360         TpProxyClass *proxy_b_class = TP_PROXY_GET_CLASS (b);
361
362         g_return_val_if_fail (TP_IS_PROXY (proxy_a), FALSE);
363         g_return_val_if_fail (TP_IS_PROXY (proxy_b), FALSE);
364         g_return_val_if_fail (proxy_a_class->must_have_unique_name, 0);
365         g_return_val_if_fail (proxy_b_class->must_have_unique_name, 0);
366
367         return g_str_equal (proxy_a->object_path, proxy_b->object_path) &&
368                g_str_equal (proxy_a->bus_name, proxy_b->bus_name);
369 }
370
371 gboolean
372 empathy_check_available_state (void)
373 {
374         TpConnectionPresenceType presence;
375         EmpathyIdle *idle;
376
377         idle = empathy_idle_dup_singleton ();
378         presence = empathy_idle_get_state (idle);
379         g_object_unref (idle);
380
381         if (presence != TP_CONNECTION_PRESENCE_TYPE_AVAILABLE &&
382                 presence != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
383                 return FALSE;
384         }
385
386         return TRUE;
387 }
388
389 gint
390 empathy_uint_compare (gconstpointer a,
391                       gconstpointer b)
392 {
393         return *(guint *) a - *(guint *) b;
394 }
395
396 gchar *
397 empathy_protocol_icon_name (const gchar *protocol)
398 {
399   if (!tp_strdiff (protocol, "yahoojp"))
400     /* Yahoo Japan uses the same icon as Yahoo */
401     protocol = "yahoo";
402   else if (!tp_strdiff (protocol, "simple"))
403     /* SIMPLE uses the same icon as SIP */
404     protocol = "sip";
405   else if (!tp_strdiff (protocol, "sms"))
406     return g_strdup ("phone");
407
408   return g_strdup_printf ("im-%s", protocol);
409 }
410
411 GType
412 empathy_type_dbus_ao (void)
413 {
414   static GType t = 0;
415
416   if (G_UNLIKELY (t == 0))
417      t = dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH);
418
419   return t;
420 }
421
422 const char *
423 empathy_protocol_name_to_display_name (const gchar *proto_name)
424 {
425   int i;
426   static struct {
427     const gchar *proto;
428     const gchar *display;
429     gboolean translated;
430   } names[] = {
431     { "jabber", "Jabber", FALSE },
432     { "gtalk", "Google Talk", FALSE },
433     { "msn", "MSN", FALSE, },
434     { "local-xmpp", N_("People Nearby"), TRUE },
435     { "irc", "IRC", FALSE },
436     { "icq", "ICQ", FALSE },
437     { "aim", "AIM", FALSE },
438     { "yahoo", "Yahoo!", FALSE },
439     { "yahoojp", N_("Yahoo! Japan"), TRUE },
440     { "facebook", N_("Facebook Chat"), TRUE },
441     { "groupwise", "GroupWise", FALSE },
442     { "sip", "SIP", FALSE },
443     { NULL, NULL }
444   };
445
446   for (i = 0; names[i].proto != NULL; i++)
447     {
448       if (!tp_strdiff (proto_name, names[i].proto))
449         {
450           if (names[i].translated)
451             return _(names[i].display);
452           else
453             return names[i].display;
454         }
455     }
456
457   return NULL;
458 }
459
460 /* Note: this function depends on the account manager having its core feature
461  * prepared. */
462 TpAccount *
463 empathy_get_account_for_connection (TpConnection *connection)
464 {
465   TpAccountManager *manager;
466   TpAccount *account = NULL;
467   GList *accounts, *l;
468
469   manager = tp_account_manager_dup ();
470
471   accounts = tp_account_manager_get_valid_accounts (manager);
472
473   for (l = accounts; l != NULL; l = l->next)
474     {
475       TpAccount *a = l->data;
476
477       if (tp_account_get_connection (a) == connection)
478         {
479           account = a;
480           break;
481         }
482     }
483
484   g_list_free (accounts);
485   g_object_unref (manager);
486
487   return account;
488 }
489
490 gboolean
491 empathy_account_manager_get_accounts_connected (gboolean *connecting)
492 {
493   TpAccountManager *manager;
494   GList *accounts, *l;
495   gboolean out_connecting = FALSE;
496   gboolean out_connected = FALSE;
497
498   manager = tp_account_manager_dup ();
499
500   if (G_UNLIKELY (!tp_account_manager_is_prepared (manager,
501           TP_ACCOUNT_MANAGER_FEATURE_CORE)))
502     g_critical (G_STRLOC ": %s called before AccountManager ready", G_STRFUNC);
503
504   accounts = tp_account_manager_get_valid_accounts (manager);
505
506   for (l = accounts; l != NULL; l = l->next)
507     {
508       TpConnectionStatus s = tp_account_get_connection_status (
509           TP_ACCOUNT (l->data), NULL);
510
511       if (s == TP_CONNECTION_STATUS_CONNECTING)
512         out_connecting = TRUE;
513       else if (s == TP_CONNECTION_STATUS_CONNECTED)
514         out_connected = TRUE;
515
516       if (out_connecting && out_connected)
517         break;
518     }
519
520   g_list_free (accounts);
521   g_object_unref (manager);
522
523   if (connecting != NULL)
524     *connecting = out_connecting;
525
526   return out_connected;
527 }
528
529 /* Change the RequestedPresence of a newly created account to ensure that it
530  * is actually connected. */
531 void
532 empathy_connect_new_account (TpAccount *account,
533     TpAccountManager *account_manager)
534 {
535   TpConnectionPresenceType presence;
536   gchar *status, *message;
537
538   /* only force presence if presence was offline, unknown or unset */
539   presence = tp_account_get_requested_presence (account, NULL, NULL);
540   switch (presence)
541     {
542       case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
543       case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
544       case TP_CONNECTION_PRESENCE_TYPE_UNSET:
545         presence = tp_account_manager_get_most_available_presence (
546             account_manager, &status, &message);
547
548         if (presence == TP_CONNECTION_PRESENCE_TYPE_OFFLINE)
549           /* Global presence is offline; we force it so user doesn't have to
550            * manually change the presence to connect his new account. */
551           presence = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
552
553         tp_account_request_presence_async (account, presence,
554             status, NULL, NULL, NULL);
555
556         g_free (status);
557         g_free (message);
558         break;
559
560        default:
561         /* do nothing if the presence is not offline */
562         break;
563     }
564 }
565
566 TpConnectionPresenceType
567 empathy_folks_presence_type_to_tp (FolksPresenceType type)
568 {
569   return (TpConnectionPresenceType) type;
570 }
571
572 EmpathyContact *
573 empathy_contact_from_folks_individual (FolksIndividual *individual)
574 {
575   GList *personas, *l;
576   EmpathyContact *contact = NULL;
577
578   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
579
580   personas = folks_individual_get_personas (individual);
581   for (l = personas; (l != NULL) && (contact == NULL); l = l->next)
582     {
583       TpfPersona *persona = l->data;
584
585       if (TPF_IS_PERSONA (persona))
586         {
587           TpContact *tp_contact;
588
589           tp_contact = tpf_persona_get_contact (persona);
590           contact = empathy_contact_dup_from_tp_contact (tp_contact);
591         }
592     }
593
594   return contact;
595 }