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