]> git.0d.be Git - empathy.git/blob - libempathy/empathy-utils.c
Move empathy_call_* util functions to libempathy-gtk
[empathy.git] / libempathy / empathy-utils.c
1 /*
2  * Copyright (C) 2003-2007 Imendio AB
3  * Copyright (C) 2007-2011 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  * Some snippets are taken from GnuTLS 2.8.6, which is distributed under the
25  * same GNU Lesser General Public License 2.1 (or later) version. See
26  * empathy_get_x509_certified_hostname ().
27  */
28
29 #include "config.h"
30
31 #include <string.h>
32 #include <math.h>
33 #include <time.h>
34 #include <sys/types.h>
35
36 #include <glib/gi18n-lib.h>
37
38 #include <libxml/uri.h>
39
40 #include <folks/folks.h>
41 #include <folks/folks-telepathy.h>
42
43 #include <telepathy-glib/account-manager.h>
44 #include <telepathy-glib/connection.h>
45 #include <telepathy-glib/channel.h>
46 #include <telepathy-glib/dbus.h>
47 #include <telepathy-glib/util.h>
48
49 #include "empathy-utils.h"
50 #include "empathy-contact-manager.h"
51 #include "empathy-individual-manager.h"
52 #include "empathy-presence-manager.h"
53 #include "empathy-request-util.h"
54 #include "empathy-tp-contact-factory.h"
55
56 #include <extensions/extensions.h>
57
58 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
59 #include "empathy-debug.h"
60
61 /* Translation between presence types and string */
62 static struct {
63         const gchar *name;
64         TpConnectionPresenceType type;
65 } presence_types[] = {
66         { "available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE },
67         { "busy",      TP_CONNECTION_PRESENCE_TYPE_BUSY },
68         { "away",      TP_CONNECTION_PRESENCE_TYPE_AWAY },
69         { "ext_away",  TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY },
70         { "hidden",    TP_CONNECTION_PRESENCE_TYPE_HIDDEN },
71         { "offline",   TP_CONNECTION_PRESENCE_TYPE_OFFLINE },
72         { "unset",     TP_CONNECTION_PRESENCE_TYPE_UNSET },
73         { "unknown",   TP_CONNECTION_PRESENCE_TYPE_UNKNOWN },
74         { "error",     TP_CONNECTION_PRESENCE_TYPE_ERROR },
75         /* alternative names */
76         { "dnd",      TP_CONNECTION_PRESENCE_TYPE_BUSY },
77         { "brb",      TP_CONNECTION_PRESENCE_TYPE_AWAY },
78         { "xa",       TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY },
79         { NULL, },
80 };
81
82
83
84 void
85 empathy_init (void)
86 {
87         static gboolean initialized = FALSE;
88
89         if (initialized)
90                 return;
91
92         g_type_init ();
93
94         /* Setup gettext */
95         bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
96         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
97
98         /* Setup debug output for empathy and telepathy-glib */
99         if (g_getenv ("EMPATHY_TIMING") != NULL) {
100                 g_log_set_default_handler (tp_debug_timestamped_log_handler, NULL);
101         }
102         empathy_debug_set_flags (g_getenv ("EMPATHY_DEBUG"));
103         tp_debug_divert_messages (g_getenv ("EMPATHY_LOGFILE"));
104
105         emp_cli_init ();
106
107         initialized = TRUE;
108 }
109
110 gchar *
111 empathy_substring (const gchar *str,
112                   gint         start,
113                   gint         end)
114 {
115         return g_strndup (str + start, end - start);
116 }
117
118 gint
119 empathy_strcasecmp (const gchar *s1,
120                    const gchar *s2)
121 {
122         return empathy_strncasecmp (s1, s2, -1);
123 }
124
125 gint
126 empathy_strncasecmp (const gchar *s1,
127                     const gchar *s2,
128                     gsize        n)
129 {
130         gchar *u1, *u2;
131         gint   ret_val;
132
133         u1 = g_utf8_casefold (s1, n);
134         u2 = g_utf8_casefold (s2, n);
135
136         ret_val = g_utf8_collate (u1, u2);
137         g_free (u1);
138         g_free (u2);
139
140         return ret_val;
141 }
142
143 gboolean
144 empathy_xml_validate (xmlDoc      *doc,
145                      const gchar *dtd_filename)
146 {
147         gchar        *path;
148         xmlChar      *escaped;
149         xmlValidCtxt  cvp;
150         xmlDtd       *dtd;
151         gboolean      ret;
152
153         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "libempathy",
154                                  dtd_filename, NULL);
155         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
156                 g_free (path);
157                 path = g_build_filename (DATADIR, "empathy", dtd_filename, NULL);
158         }
159         DEBUG ("Loading dtd file %s", path);
160
161         /* The list of valid chars is taken from libxml. */
162         escaped = xmlURIEscapeStr ((const xmlChar *) path,
163                 (const xmlChar *)":@&=+$,/?;");
164         g_free (path);
165
166         memset (&cvp, 0, sizeof (cvp));
167         dtd = xmlParseDTD (NULL, escaped);
168         ret = xmlValidateDtd (&cvp, doc, dtd);
169
170         xmlFree (escaped);
171         xmlFreeDtd (dtd);
172
173         return ret;
174 }
175
176 xmlNodePtr
177 empathy_xml_node_get_child (xmlNodePtr   node,
178                            const gchar *child_name)
179 {
180         xmlNodePtr l;
181
182         g_return_val_if_fail (node != NULL, NULL);
183         g_return_val_if_fail (child_name != NULL, NULL);
184
185         for (l = node->children; l; l = l->next) {
186                 if (l->name && strcmp ((const gchar *) l->name, child_name) == 0) {
187                         return l;
188                 }
189         }
190
191         return NULL;
192 }
193
194 xmlChar *
195 empathy_xml_node_get_child_content (xmlNodePtr   node,
196                                    const gchar *child_name)
197 {
198         xmlNodePtr l;
199
200         g_return_val_if_fail (node != NULL, NULL);
201         g_return_val_if_fail (child_name != NULL, NULL);
202
203         l = empathy_xml_node_get_child (node, child_name);
204         if (l) {
205                 return xmlNodeGetContent (l);
206         }
207
208         return NULL;
209 }
210
211 xmlNodePtr
212 empathy_xml_node_find_child_prop_value (xmlNodePtr   node,
213                                        const gchar *prop_name,
214                                        const gchar *prop_value)
215 {
216         xmlNodePtr l;
217         xmlNodePtr found = NULL;
218
219         g_return_val_if_fail (node != NULL, NULL);
220         g_return_val_if_fail (prop_name != NULL, NULL);
221         g_return_val_if_fail (prop_value != NULL, NULL);
222
223         for (l = node->children; l && !found; l = l->next) {
224                 xmlChar *prop;
225
226                 if (!xmlHasProp (l, (const xmlChar *) prop_name)) {
227                         continue;
228                 }
229
230                 prop = xmlGetProp (l, (const xmlChar *) prop_name);
231                 if (prop && strcmp ((const gchar *) prop, prop_value) == 0) {
232                         found = l;
233                 }
234
235                 xmlFree (prop);
236         }
237
238         return found;
239 }
240
241 const gchar *
242 empathy_presence_get_default_message (TpConnectionPresenceType presence)
243 {
244         switch (presence) {
245         case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
246                 return _("Available");
247         case TP_CONNECTION_PRESENCE_TYPE_BUSY:
248                 return _("Busy");
249         case TP_CONNECTION_PRESENCE_TYPE_AWAY:
250         case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
251                 return _("Away");
252         case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
253                 return _("Invisible");
254         case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
255                 return _("Offline");
256         case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
257                 /* translators: presence type is unknown */
258                 return C_("presence", "Unknown");
259         case TP_CONNECTION_PRESENCE_TYPE_UNSET:
260         case TP_CONNECTION_PRESENCE_TYPE_ERROR:
261         default:
262                 return NULL;
263         }
264
265         return NULL;
266 }
267
268 const gchar *
269 empathy_presence_to_str (TpConnectionPresenceType presence)
270 {
271         int i;
272
273         for (i = 0 ; presence_types[i].name != NULL; i++)
274                 if (presence == presence_types[i].type)
275                         return presence_types[i].name;
276
277         return NULL;
278 }
279
280 TpConnectionPresenceType
281 empathy_presence_from_str (const gchar *str)
282 {
283         int i;
284
285         for (i = 0 ; presence_types[i].name != NULL; i++)
286                 if (!tp_strdiff (str, presence_types[i].name))
287                         return presence_types[i].type;
288
289         return TP_CONNECTION_PRESENCE_TYPE_UNSET;
290 }
291
292 static const gchar *
293 empathy_status_reason_get_default_message (TpConnectionStatusReason reason)
294 {
295         switch (reason) {
296         case TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED:
297                 return _("No reason specified");
298         case TP_CONNECTION_STATUS_REASON_REQUESTED:
299                 return _("Status is set to offline");
300         case TP_CONNECTION_STATUS_REASON_NETWORK_ERROR:
301                 return _("Network error");
302         case TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED:
303                 return _("Authentication failed");
304         case TP_CONNECTION_STATUS_REASON_ENCRYPTION_ERROR:
305                 return _("Encryption error");
306         case TP_CONNECTION_STATUS_REASON_NAME_IN_USE:
307                 return _("Name in use");
308         case TP_CONNECTION_STATUS_REASON_CERT_NOT_PROVIDED:
309                 return _("Certificate not provided");
310         case TP_CONNECTION_STATUS_REASON_CERT_UNTRUSTED:
311                 return _("Certificate untrusted");
312         case TP_CONNECTION_STATUS_REASON_CERT_EXPIRED:
313                 return _("Certificate expired");
314         case TP_CONNECTION_STATUS_REASON_CERT_NOT_ACTIVATED:
315                 return _("Certificate not activated");
316         case TP_CONNECTION_STATUS_REASON_CERT_HOSTNAME_MISMATCH:
317                 return _("Certificate hostname mismatch");
318         case TP_CONNECTION_STATUS_REASON_CERT_FINGERPRINT_MISMATCH:
319                 return _("Certificate fingerprint mismatch");
320         case TP_CONNECTION_STATUS_REASON_CERT_SELF_SIGNED:
321                 return _("Certificate self-signed");
322         case TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR:
323                 return _("Certificate error");
324         default:
325                 return _("Unknown reason");
326         }
327 }
328
329 static GHashTable *
330 create_errors_to_message_hash (void)
331 {
332         GHashTable *errors;
333
334         errors = g_hash_table_new (g_str_hash, g_str_equal);
335         g_hash_table_insert (errors, TP_ERROR_STR_NETWORK_ERROR, _("Network error"));
336         g_hash_table_insert (errors, TP_ERROR_STR_AUTHENTICATION_FAILED,
337                 _("Authentication failed"));
338         g_hash_table_insert (errors, TP_ERROR_STR_ENCRYPTION_ERROR,
339                 _("Encryption error"));
340         g_hash_table_insert (errors, TP_ERROR_STR_CERT_NOT_PROVIDED,
341                 _("Certificate not provided"));
342         g_hash_table_insert (errors, TP_ERROR_STR_CERT_UNTRUSTED,
343                 _("Certificate untrusted"));
344         g_hash_table_insert (errors, TP_ERROR_STR_CERT_EXPIRED,
345                 _("Certificate expired"));
346         g_hash_table_insert (errors, TP_ERROR_STR_CERT_NOT_ACTIVATED,
347                 _("Certificate not activated"));
348         g_hash_table_insert (errors, TP_ERROR_STR_CERT_HOSTNAME_MISMATCH,
349                 _("Certificate hostname mismatch"));
350         g_hash_table_insert (errors, TP_ERROR_STR_CERT_FINGERPRINT_MISMATCH,
351                 _("Certificate fingerprint mismatch"));
352         g_hash_table_insert (errors, TP_ERROR_STR_CERT_SELF_SIGNED,
353                 _("Certificate self-signed"));
354         g_hash_table_insert (errors, TP_ERROR_STR_CANCELLED,
355                 _("Status is set to offline"));
356         g_hash_table_insert (errors, TP_ERROR_STR_ENCRYPTION_NOT_AVAILABLE,
357                 _("Encryption is not available"));
358         g_hash_table_insert (errors, TP_ERROR_STR_CERT_INVALID,
359                 _("Certificate is invalid"));
360         g_hash_table_insert (errors, TP_ERROR_STR_CONNECTION_REFUSED,
361                 _("Connection has been refused"));
362         g_hash_table_insert (errors, TP_ERROR_STR_CONNECTION_FAILED,
363                 _("Connection can't be established"));
364         g_hash_table_insert (errors, TP_ERROR_STR_CONNECTION_LOST,
365                 _("Connection has been lost"));
366         g_hash_table_insert (errors, TP_ERROR_STR_ALREADY_CONNECTED,
367                 _("This resource is already connected to the server"));
368         g_hash_table_insert (errors, TP_ERROR_STR_CONNECTION_REPLACED,
369                 _("Connection has been replaced by a new connection using the "
370                 "same resource"));
371         g_hash_table_insert (errors, TP_ERROR_STR_REGISTRATION_EXISTS,
372                 _("The account already exists on the server"));
373         g_hash_table_insert (errors, TP_ERROR_STR_SERVICE_BUSY,
374                 _("Server is currently too busy to handle the connection"));
375         g_hash_table_insert (errors, TP_ERROR_STR_CERT_REVOKED,
376                 _("Certificate has been revoked"));
377         g_hash_table_insert (errors, TP_ERROR_STR_CERT_INSECURE,
378                 _("Certificate uses an insecure cipher algorithm or is "
379                 "cryptographically weak"));
380         g_hash_table_insert (errors, TP_ERROR_STR_CERT_LIMIT_EXCEEDED,
381                 _("The length of the server certificate, or the depth of the "
382                 "server certificate chain, exceed the limits imposed by the "
383                 "cryptography library"));
384
385         return errors;
386 }
387
388 static const gchar *
389 empathy_dbus_error_name_get_default_message  (const gchar *error)
390 {
391         static GHashTable *errors_to_message = NULL;
392
393         if (error == NULL)
394                 return NULL;
395
396         if (G_UNLIKELY (errors_to_message == NULL)) {
397                 errors_to_message = create_errors_to_message_hash ();
398         }
399
400         return g_hash_table_lookup (errors_to_message, error);
401 }
402
403 const gchar *
404 empathy_account_get_error_message (TpAccount *account,
405     gboolean *user_requested)
406 {
407         const gchar *dbus_error;
408         const gchar *message;
409         const GHashTable *details = NULL;
410         TpConnectionStatusReason reason;
411
412         dbus_error = tp_account_get_detailed_error (account, &details);
413
414         if (user_requested != NULL)
415           {
416             if (tp_asv_get_boolean (details, "user-requested", NULL))
417               *user_requested = TRUE;
418             else
419               *user_requested = FALSE;
420           }
421
422         message = empathy_dbus_error_name_get_default_message (dbus_error);
423         if (message != NULL)
424                 return message;
425
426         DEBUG ("Don't understand error '%s'; fallback to the status reason (%u)",
427                 dbus_error, reason);
428
429         tp_account_get_connection_status (account, &reason);
430
431         return empathy_status_reason_get_default_message (reason);
432 }
433
434 gchar *
435 empathy_file_lookup (const gchar *filename, const gchar *subdir)
436 {
437         gchar *path;
438
439         if (!subdir) {
440                 subdir = ".";
441         }
442
443         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), subdir, filename, NULL);
444         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
445                 g_free (path);
446                 path = g_build_filename (DATADIR, "empathy", filename, NULL);
447         }
448
449         return path;
450 }
451
452 guint
453 empathy_proxy_hash (gconstpointer key)
454 {
455         TpProxy      *proxy = TP_PROXY (key);
456         TpProxyClass *proxy_class = TP_PROXY_GET_CLASS (key);
457
458         g_return_val_if_fail (TP_IS_PROXY (proxy), 0);
459         g_return_val_if_fail (proxy_class->must_have_unique_name, 0);
460
461         return g_str_hash (proxy->object_path) ^ g_str_hash (proxy->bus_name);
462 }
463
464 gboolean
465 empathy_proxy_equal (gconstpointer a,
466                      gconstpointer b)
467 {
468         TpProxy *proxy_a = TP_PROXY (a);
469         TpProxy *proxy_b = TP_PROXY (b);
470         TpProxyClass *proxy_a_class = TP_PROXY_GET_CLASS (a);
471         TpProxyClass *proxy_b_class = TP_PROXY_GET_CLASS (b);
472
473         g_return_val_if_fail (TP_IS_PROXY (proxy_a), FALSE);
474         g_return_val_if_fail (TP_IS_PROXY (proxy_b), FALSE);
475         g_return_val_if_fail (proxy_a_class->must_have_unique_name, 0);
476         g_return_val_if_fail (proxy_b_class->must_have_unique_name, 0);
477
478         return g_str_equal (proxy_a->object_path, proxy_b->object_path) &&
479                g_str_equal (proxy_a->bus_name, proxy_b->bus_name);
480 }
481
482 gboolean
483 empathy_check_available_state (void)
484 {
485         TpConnectionPresenceType presence;
486         EmpathyPresenceManager *presence_mgr;
487
488         presence_mgr = empathy_presence_manager_dup_singleton ();
489         presence = empathy_presence_manager_get_state (presence_mgr);
490         g_object_unref (presence_mgr);
491
492         if (presence != TP_CONNECTION_PRESENCE_TYPE_AVAILABLE &&
493                 presence != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
494                 return FALSE;
495         }
496
497         return TRUE;
498 }
499
500 gint
501 empathy_uint_compare (gconstpointer a,
502                       gconstpointer b)
503 {
504         return *(guint *) a - *(guint *) b;
505 }
506
507 gchar *
508 empathy_protocol_icon_name (const gchar *protocol)
509 {
510   if (!tp_strdiff (protocol, "yahoojp"))
511     /* Yahoo Japan uses the same icon as Yahoo */
512     protocol = "yahoo";
513   else if (!tp_strdiff (protocol, "simple"))
514     /* SIMPLE uses the same icon as SIP */
515     protocol = "sip";
516   else if (!tp_strdiff (protocol, "sms"))
517     return g_strdup ("phone");
518
519   return g_strdup_printf ("im-%s", protocol);
520 }
521
522 GType
523 empathy_type_dbus_ao (void)
524 {
525   static GType t = 0;
526
527   if (G_UNLIKELY (t == 0))
528      t = dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH);
529
530   return t;
531 }
532
533 const char *
534 empathy_protocol_name_to_display_name (const gchar *proto_name)
535 {
536   int i;
537   static struct {
538     const gchar *proto;
539     const gchar *display;
540     gboolean translated;
541   } names[] = {
542     { "jabber", "Jabber", FALSE },
543     { "msn", "Windows Live (MSN)", FALSE, },
544     { "local-xmpp", N_("People Nearby"), TRUE },
545     { "irc", "IRC", FALSE },
546     { "icq", "ICQ", FALSE },
547     { "aim", "AIM", FALSE },
548     { "yahoo", "Yahoo!", FALSE },
549     { "yahoojp", N_("Yahoo! Japan"), TRUE },
550     { "groupwise", "GroupWise", FALSE },
551     { "sip", "SIP", FALSE },
552     { NULL, NULL }
553   };
554
555   for (i = 0; names[i].proto != NULL; i++)
556     {
557       if (!tp_strdiff (proto_name, names[i].proto))
558         {
559           if (names[i].translated)
560             return gettext (names[i].display);
561           else
562             return names[i].display;
563         }
564     }
565
566   return proto_name;
567 }
568
569 const char *
570 empathy_service_name_to_display_name (const gchar *service_name)
571 {
572   int i;
573   static struct {
574     const gchar *service;
575     const gchar *display;
576     gboolean translated;
577   } names[] = {
578     { "google-talk", N_("Google Talk"), FALSE },
579     { "facebook", N_("Facebook Chat"), TRUE },
580     { NULL, NULL }
581   };
582
583   for (i = 0; names[i].service != NULL; i++)
584     {
585       if (!tp_strdiff (service_name, names[i].service))
586         {
587           if (names[i].translated)
588             return gettext (names[i].display);
589           else
590             return names[i].display;
591         }
592     }
593
594   return service_name;
595 }
596
597 /* Note: this function depends on the account manager having its core feature
598  * prepared. */
599 TpAccount *
600 empathy_get_account_for_connection (TpConnection *connection)
601 {
602   TpAccountManager *manager;
603   TpAccount *account = NULL;
604   GList *accounts, *l;
605
606   manager = tp_account_manager_dup ();
607
608   accounts = tp_account_manager_get_valid_accounts (manager);
609
610   for (l = accounts; l != NULL; l = l->next)
611     {
612       TpAccount *a = l->data;
613
614       if (tp_account_get_connection (a) == connection)
615         {
616           account = a;
617           break;
618         }
619     }
620
621   g_list_free (accounts);
622   g_object_unref (manager);
623
624   return account;
625 }
626
627 gboolean
628 empathy_account_manager_get_accounts_connected (gboolean *connecting)
629 {
630   TpAccountManager *manager;
631   GList *accounts, *l;
632   gboolean out_connecting = FALSE;
633   gboolean out_connected = FALSE;
634
635   manager = tp_account_manager_dup ();
636
637   if (G_UNLIKELY (!tp_account_manager_is_prepared (manager,
638           TP_ACCOUNT_MANAGER_FEATURE_CORE)))
639     g_critical (G_STRLOC ": %s called before AccountManager ready", G_STRFUNC);
640
641   accounts = tp_account_manager_get_valid_accounts (manager);
642
643   for (l = accounts; l != NULL; l = l->next)
644     {
645       TpConnectionStatus s = tp_account_get_connection_status (
646           TP_ACCOUNT (l->data), NULL);
647
648       if (s == TP_CONNECTION_STATUS_CONNECTING)
649         out_connecting = TRUE;
650       else if (s == TP_CONNECTION_STATUS_CONNECTED)
651         out_connected = TRUE;
652
653       if (out_connecting && out_connected)
654         break;
655     }
656
657   g_list_free (accounts);
658   g_object_unref (manager);
659
660   if (connecting != NULL)
661     *connecting = out_connecting;
662
663   return out_connected;
664 }
665
666 /* Change the RequestedPresence of a newly created account to ensure that it
667  * is actually connected. */
668 void
669 empathy_connect_new_account (TpAccount *account,
670     TpAccountManager *account_manager)
671 {
672   TpConnectionPresenceType presence;
673   gchar *status, *message;
674
675   /* only force presence if presence was offline, unknown or unset */
676   presence = tp_account_get_requested_presence (account, NULL, NULL);
677   switch (presence)
678     {
679       case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
680       case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
681       case TP_CONNECTION_PRESENCE_TYPE_UNSET:
682         presence = tp_account_manager_get_most_available_presence (
683             account_manager, &status, &message);
684
685         if (presence == TP_CONNECTION_PRESENCE_TYPE_OFFLINE)
686           /* Global presence is offline; we force it so user doesn't have to
687            * manually change the presence to connect his new account. */
688           presence = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
689
690         tp_account_request_presence_async (account, presence,
691             status, NULL, NULL, NULL);
692
693         g_free (status);
694         g_free (message);
695         break;
696
697        case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
698        case TP_CONNECTION_PRESENCE_TYPE_AWAY:
699        case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
700        case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
701        case TP_CONNECTION_PRESENCE_TYPE_BUSY:
702        case TP_CONNECTION_PRESENCE_TYPE_ERROR:
703        default:
704         /* do nothing if the presence is not offline */
705         break;
706     }
707 }
708
709 /* Translate Folks' general presence type to the Tp presence type */
710 TpConnectionPresenceType
711 empathy_folks_presence_type_to_tp (FolksPresenceType type)
712 {
713   return (TpConnectionPresenceType) type;
714 }
715
716 /* Returns TRUE if the given Individual contains a TpContact */
717 gboolean
718 empathy_folks_individual_contains_contact (FolksIndividual *individual)
719 {
720   GeeSet *personas;
721   GeeIterator *iter;
722   gboolean retval = FALSE;
723
724   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), FALSE);
725
726   personas = folks_individual_get_personas (individual);
727   iter = gee_iterable_iterator (GEE_ITERABLE (personas));
728   while (!retval && gee_iterator_next (iter))
729     {
730       FolksPersona *persona = gee_iterator_get (iter);
731       TpContact *contact = NULL;
732
733       if (empathy_folks_persona_is_interesting (persona))
734         contact = tpf_persona_get_contact (TPF_PERSONA (persona));
735
736       g_clear_object (&persona);
737
738       if (contact != NULL)
739         retval = TRUE;
740     }
741   g_clear_object (&iter);
742
743   return retval;
744 }
745
746 /* TODO: this needs to be eliminated (and replaced in some cases with user
747  * prompts) when we break the assumption that FolksIndividuals are 1:1 with
748  * TpContacts */
749
750 /* Retrieve the EmpathyContact corresponding to the first TpContact contained
751  * within the given Individual. Note that this is a temporary convenience. See
752  * the TODO above. */
753 EmpathyContact *
754 empathy_contact_dup_from_folks_individual (FolksIndividual *individual)
755 {
756   GeeSet *personas;
757   GeeIterator *iter;
758   EmpathyContact *contact = NULL;
759
760   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
761
762   personas = folks_individual_get_personas (individual);
763   iter = gee_iterable_iterator (GEE_ITERABLE (personas));
764   while (gee_iterator_next (iter) && (contact == NULL))
765     {
766       TpfPersona *persona = gee_iterator_get (iter);
767
768       if (empathy_folks_persona_is_interesting (FOLKS_PERSONA (persona)))
769         {
770           TpContact *tp_contact;
771
772           tp_contact = tpf_persona_get_contact (persona);
773           contact = empathy_contact_dup_from_tp_contact (tp_contact);
774           empathy_contact_set_persona (contact, FOLKS_PERSONA (persona));
775         }
776       g_clear_object (&persona);
777     }
778   g_clear_object (&iter);
779
780   return contact;
781 }
782
783 TpChannelGroupChangeReason
784 tp_channel_group_change_reason_from_folks_groups_change_reason (
785     FolksGroupDetailsChangeReason reason)
786 {
787   return (TpChannelGroupChangeReason) reason;
788 }
789
790 TpfPersonaStore *
791 empathy_dup_persona_store_for_connection (TpConnection *connection)
792 {
793   FolksBackendStore *backend_store;
794   FolksBackend *backend;
795   TpfPersonaStore *result = NULL;
796
797   backend_store = folks_backend_store_dup ();
798   backend = folks_backend_store_dup_backend_by_name (backend_store,
799       "telepathy");
800   if (backend != NULL)
801     {
802       GeeMap *stores_map;
803       GeeMapIterator *iter;
804
805       stores_map = folks_backend_get_persona_stores (backend);
806       iter = gee_map_map_iterator (stores_map);
807       while (gee_map_iterator_next (iter))
808         {
809           TpfPersonaStore *persona_store = gee_map_iterator_get_value (iter);
810           TpAccount *account;
811           TpConnection *conn_cur;
812
813           account = tpf_persona_store_get_account (persona_store);
814           conn_cur = tp_account_get_connection (account);
815           if (conn_cur == connection)
816             result = persona_store;
817         }
818       g_clear_object (&iter);
819     }
820
821   g_object_unref (backend);
822   g_object_unref (backend_store);
823
824   return result;
825 }
826
827 gboolean
828 empathy_connection_can_add_personas (TpConnection *connection)
829 {
830   gboolean retval;
831   FolksPersonaStore *persona_store;
832
833   g_return_val_if_fail (TP_IS_CONNECTION (connection), FALSE);
834
835   persona_store = FOLKS_PERSONA_STORE (
836       empathy_dup_persona_store_for_connection (connection));
837
838   retval = (folks_persona_store_get_can_add_personas (persona_store) ==
839       FOLKS_MAYBE_BOOL_TRUE);
840
841   g_clear_object (&persona_store);
842
843   return retval;
844 }
845
846 gboolean
847 empathy_connection_can_alias_personas (TpConnection *connection)
848 {
849   gboolean retval;
850   FolksPersonaStore *persona_store;
851
852   g_return_val_if_fail (TP_IS_CONNECTION (connection), FALSE);
853
854   persona_store = FOLKS_PERSONA_STORE (
855       empathy_dup_persona_store_for_connection (connection));
856
857   retval = (folks_persona_store_get_can_alias_personas (persona_store) ==
858       FOLKS_MAYBE_BOOL_TRUE);
859
860   g_clear_object (&persona_store);
861
862   return retval;
863 }
864
865 gboolean
866 empathy_connection_can_group_personas (TpConnection *connection)
867 {
868   gboolean retval;
869   FolksPersonaStore *persona_store;
870
871   g_return_val_if_fail (TP_IS_CONNECTION (connection), FALSE);
872
873   persona_store = FOLKS_PERSONA_STORE (
874       empathy_dup_persona_store_for_connection (connection));
875
876   retval = (folks_persona_store_get_can_group_personas (persona_store) ==
877       FOLKS_MAYBE_BOOL_TRUE);
878
879   g_clear_object (&persona_store);
880
881   return retval;
882 }
883
884 gboolean
885 empathy_folks_persona_is_interesting (FolksPersona *persona)
886 {
887   /* We're not interested in non-Telepathy personas */
888   if (!TPF_IS_PERSONA (persona))
889     return FALSE;
890
891   /* We're not interested in user personas which haven't been added to the
892    * contact list (see bgo#637151). */
893   if (folks_persona_get_is_user (persona) &&
894       !tpf_persona_get_is_in_contact_list (TPF_PERSONA (persona)))
895     {
896       return FALSE;
897     }
898
899   return TRUE;
900 }
901
902 gchar *
903 empathy_get_x509_certificate_hostname (gnutls_x509_crt_t cert)
904 {
905   gchar dns_name[256];
906   gsize dns_name_size;
907   gint idx;
908   gint res = 0;
909
910   /* this snippet is taken from GnuTLS.
911    * see gnutls/lib/x509/rfc2818_hostname.c
912    */
913   for (idx = 0; res >= 0; idx++)
914     {
915       dns_name_size = sizeof (dns_name);
916       res = gnutls_x509_crt_get_subject_alt_name (cert, idx,
917           dns_name, &dns_name_size, NULL);
918
919       if (res == GNUTLS_SAN_DNSNAME || res == GNUTLS_SAN_IPADDRESS)
920         return g_strndup (dns_name, dns_name_size);
921     }
922
923   dns_name_size = sizeof (dns_name);
924   res = gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME,
925       0, 0, dns_name, &dns_name_size);
926
927   if (res >= 0)
928     return g_strndup (dns_name, dns_name_size);
929
930   return NULL;
931 }
932
933 gchar *
934 empathy_format_currency (gint         amount,
935                          guint        scale,
936                          const gchar *currency)
937 {
938 #define MINUS "\342\210\222"
939 #define EURO "\342\202\254"
940 #define YEN "\302\245"
941 #define POUND "\302\243"
942
943         /* localised representations of currency */
944         /* FIXME: check these, especially negatives and decimals */
945         static const struct {
946                 const char *currency;
947                 const char *positive;
948                 const char *negative;
949                 const char *decimal;
950         } currencies[] = {
951                 /* sym   positive    negative          decimal */
952                 { "EUR", EURO "%s",  MINUS EURO "%s",  "." },
953                 { "USD", "$%s",      MINUS "$%s",      "." },
954                 { "JPY", YEN "%s"    MINUS YEN "%s",   "." },
955                 { "GBP", POUND "%s", MINUS POUND "%s", "." },
956                 { "PLN", "%s zl",    MINUS "%s zl",    "." },
957                 { "BRL", "R$%s",     MINUS "R$%s",     "." },
958                 { "SEK", "%s kr",    MINUS "%s kr",    "." },
959                 { "DKK", "kr %s",    "kr " MINUS "%s", "." },
960                 { "HKD", "$%s",      MINUS "$%s",      "." },
961                 { "CHF", "%s Fr.",   MINUS "%s Fr.",   "." },
962                 { "NOK", "kr %s",    "kr" MINUS "%s",  "," },
963                 { "CAD", "$%s",      MINUS "$%s",      "." },
964                 { "TWD", "$%s",      MINUS "$%s",      "." },
965                 { "AUD", "$%s",      MINUS "$%s",      "." },
966         };
967
968         const char *positive = "%s";
969         const char *negative = MINUS "%s";
970         const char *decimal = ".";
971         char *fmt_amount, *money;
972         guint i;
973
974         /* get the localised currency format */
975         for (i = 0; i < G_N_ELEMENTS (currencies); i++) {
976                 if (!tp_strdiff (currency, currencies[i].currency)) {
977                         positive = currencies[i].positive;
978                         negative = currencies[i].negative;
979                         decimal = currencies[i].decimal;
980                         break;
981                 }
982         }
983
984         /* format the amount using the scale */
985         if (scale == 0) {
986                 /* no decimal point required */
987                 fmt_amount = g_strdup_printf ("%d", amount);
988         } else {
989                 /* don't use floating point arithmatic, it's noisy;
990                  * we take the absolute values, because we want the minus
991                  * sign to appear before the $ */
992                 int divisor = pow (10, scale);
993                 int dollars = abs (amount / divisor);
994                 int cents = abs (amount % divisor);
995
996                 fmt_amount = g_strdup_printf ("%d%s%0*d",
997                         dollars, decimal, scale, cents);
998         }
999
1000         money = g_strdup_printf (amount < 0 ? negative : positive, fmt_amount);
1001         g_free (fmt_amount);
1002
1003         return money;
1004 }