]> git.0d.be Git - empathy.git/blob - libempathy/empathy-utils.c
Merge branch 'sasl'
[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  * 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 <time.h>
33 #include <sys/types.h>
34
35 #include <glib/gi18n-lib.h>
36
37 #include <libxml/uri.h>
38
39 #include <folks/folks.h>
40 #include <folks/folks-telepathy.h>
41
42 #include <telepathy-glib/account-manager.h>
43 #include <telepathy-glib/connection.h>
44 #include <telepathy-glib/channel.h>
45 #include <telepathy-glib/dbus.h>
46 #include <telepathy-glib/util.h>
47
48 #include "empathy-utils.h"
49 #include "empathy-contact-manager.h"
50 #include "empathy-individual-manager.h"
51 #include "empathy-dispatcher.h"
52 #include "empathy-idle.h"
53 #include "empathy-tp-call.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                 return _("Unknown");
258         case TP_CONNECTION_PRESENCE_TYPE_UNSET:
259         case TP_CONNECTION_PRESENCE_TYPE_ERROR:
260         default:
261                 return NULL;
262         }
263
264         return NULL;
265 }
266
267 const gchar *
268 empathy_presence_to_str (TpConnectionPresenceType presence)
269 {
270         int i;
271
272         for (i = 0 ; presence_types[i].name != NULL; i++)
273                 if (presence == presence_types[i].type)
274                         return presence_types[i].name;
275
276         return NULL;
277 }
278
279 TpConnectionPresenceType
280 empathy_presence_from_str (const gchar *str)
281 {
282         int i;
283
284         for (i = 0 ; presence_types[i].name != NULL; i++)
285                 if (!tp_strdiff (str, presence_types[i].name))
286                         return presence_types[i].type;
287
288         return TP_CONNECTION_PRESENCE_TYPE_UNSET;
289 }
290
291 static const gchar *
292 empathy_status_reason_get_default_message (TpConnectionStatusReason reason)
293 {
294         switch (reason) {
295         case TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED:
296                 return _("No reason specified");
297         case TP_CONNECTION_STATUS_REASON_REQUESTED:
298                 return _("Status is set to offline");
299         case TP_CONNECTION_STATUS_REASON_NETWORK_ERROR:
300                 return _("Network error");
301         case TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED:
302                 return _("Authentication failed");
303         case TP_CONNECTION_STATUS_REASON_ENCRYPTION_ERROR:
304                 return _("Encryption error");
305         case TP_CONNECTION_STATUS_REASON_NAME_IN_USE:
306                 return _("Name in use");
307         case TP_CONNECTION_STATUS_REASON_CERT_NOT_PROVIDED:
308                 return _("Certificate not provided");
309         case TP_CONNECTION_STATUS_REASON_CERT_UNTRUSTED:
310                 return _("Certificate untrusted");
311         case TP_CONNECTION_STATUS_REASON_CERT_EXPIRED:
312                 return _("Certificate expired");
313         case TP_CONNECTION_STATUS_REASON_CERT_NOT_ACTIVATED:
314                 return _("Certificate not activated");
315         case TP_CONNECTION_STATUS_REASON_CERT_HOSTNAME_MISMATCH:
316                 return _("Certificate hostname mismatch");
317         case TP_CONNECTION_STATUS_REASON_CERT_FINGERPRINT_MISMATCH:
318                 return _("Certificate fingerprint mismatch");
319         case TP_CONNECTION_STATUS_REASON_CERT_SELF_SIGNED:
320                 return _("Certificate self-signed");
321         case TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR:
322                 return _("Certificate error");
323         default:
324                 return _("Unknown reason");
325         }
326 }
327
328 static GHashTable *
329 create_errors_to_message_hash (void)
330 {
331         GHashTable *errors;
332
333         errors = g_hash_table_new (g_str_hash, g_str_equal);
334         g_hash_table_insert (errors, TP_ERROR_STR_NETWORK_ERROR, _("Network error"));
335         g_hash_table_insert (errors, TP_ERROR_STR_AUTHENTICATION_FAILED,
336                 _("Authentication failed"));
337         g_hash_table_insert (errors, TP_ERROR_STR_ENCRYPTION_ERROR,
338                 _("Encryption error"));
339         g_hash_table_insert (errors, TP_ERROR_STR_CERT_NOT_PROVIDED,
340                 _("Certificate not provided"));
341         g_hash_table_insert (errors, TP_ERROR_STR_CERT_UNTRUSTED,
342                 _("Certificate untrusted"));
343         g_hash_table_insert (errors, TP_ERROR_STR_CERT_EXPIRED,
344                 _("Certificate expired"));
345         g_hash_table_insert (errors, TP_ERROR_STR_CERT_NOT_ACTIVATED,
346                 _("Certificate not activated"));
347         g_hash_table_insert (errors, TP_ERROR_STR_CERT_HOSTNAME_MISMATCH,
348                 _("Certificate hostname mismatch"));
349         g_hash_table_insert (errors, TP_ERROR_STR_CERT_FINGERPRINT_MISMATCH,
350                 _("Certificate fingerprint mismatch"));
351         g_hash_table_insert (errors, TP_ERROR_STR_CERT_SELF_SIGNED,
352                 _("Certificate self-signed"));
353         g_hash_table_insert (errors, TP_ERROR_STR_CANCELLED,
354                 _("Status is set to offline"));
355         g_hash_table_insert (errors, TP_ERROR_STR_ENCRYPTION_NOT_AVAILABLE,
356                 _("Encryption is not available"));
357         g_hash_table_insert (errors, TP_ERROR_STR_CERT_INVALID,
358                 _("Certificate is invalid"));
359         g_hash_table_insert (errors, TP_ERROR_STR_CONNECTION_REFUSED,
360                 _("Connection has been refused"));
361         g_hash_table_insert (errors, TP_ERROR_STR_CONNECTION_FAILED,
362                 _("Connection can't be established"));
363         g_hash_table_insert (errors, TP_ERROR_STR_CONNECTION_LOST,
364                 _("Connection has been lost"));
365         g_hash_table_insert (errors, TP_ERROR_STR_ALREADY_CONNECTED,
366                 _("This resource is already connected to the server"));
367         g_hash_table_insert (errors, TP_ERROR_STR_CONNECTION_REPLACED,
368                 _("Connection has been replaced by a new connection using the "
369                 "same resource"));
370         g_hash_table_insert (errors, TP_ERROR_STR_REGISTRATION_EXISTS,
371                 _("The account already exists on the server"));
372         g_hash_table_insert (errors, TP_ERROR_STR_SERVICE_BUSY,
373                 _("Server is currently too busy to handle the connection"));
374         g_hash_table_insert (errors, TP_ERROR_STR_CERT_REVOKED,
375                 _("Certificate has been revoked"));
376         g_hash_table_insert (errors, TP_ERROR_STR_CERT_INSECURE,
377                 _("Certificate uses an insecure cipher algorithm or is "
378                 "cryptographically weak"));
379         g_hash_table_insert (errors, TP_ERROR_STR_CERT_LIMIT_EXCEEDED,
380                 _("The length of the server certificate, or the depth of the "
381                 "server certificate chain, exceed the limits imposed by the "
382                 "cryptography library"));
383
384         return errors;
385 }
386
387 static const gchar *
388 empathy_dbus_error_name_get_default_message  (const gchar *error)
389 {
390         static GHashTable *errors_to_message = NULL;
391
392         if (error == NULL)
393                 return NULL;
394
395         if (G_UNLIKELY (errors_to_message == NULL)) {
396                 errors_to_message = create_errors_to_message_hash ();
397         }
398
399         return g_hash_table_lookup (errors_to_message, error);
400 }
401
402 const gchar *
403 empathy_account_get_error_message (TpAccount *account,
404     gboolean *user_requested)
405 {
406         const gchar *dbus_error;
407         const gchar *message;
408         const GHashTable *details = NULL;
409         TpConnectionStatusReason reason;
410
411         dbus_error = tp_account_get_detailed_error (account, &details);
412
413         if (user_requested != NULL)
414           {
415             if (tp_asv_get_boolean (details, "user-requested", NULL))
416               *user_requested = TRUE;
417             else
418               *user_requested = FALSE;
419           }
420
421         message = empathy_dbus_error_name_get_default_message (dbus_error);
422         if (message != NULL)
423                 return message;
424
425         DEBUG ("Don't understand error '%s'; fallback to the status reason (%u)",
426                 dbus_error, reason);
427
428         tp_account_get_connection_status (account, &reason);
429
430         return empathy_status_reason_get_default_message (reason);
431 }
432
433 gchar *
434 empathy_file_lookup (const gchar *filename, const gchar *subdir)
435 {
436         gchar *path;
437
438         if (!subdir) {
439                 subdir = ".";
440         }
441
442         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), subdir, filename, NULL);
443         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
444                 g_free (path);
445                 path = g_build_filename (DATADIR, "empathy", filename, NULL);
446         }
447
448         return path;
449 }
450
451 guint
452 empathy_proxy_hash (gconstpointer key)
453 {
454         TpProxy      *proxy = TP_PROXY (key);
455         TpProxyClass *proxy_class = TP_PROXY_GET_CLASS (key);
456
457         g_return_val_if_fail (TP_IS_PROXY (proxy), 0);
458         g_return_val_if_fail (proxy_class->must_have_unique_name, 0);
459
460         return g_str_hash (proxy->object_path) ^ g_str_hash (proxy->bus_name);
461 }
462
463 gboolean
464 empathy_proxy_equal (gconstpointer a,
465                      gconstpointer b)
466 {
467         TpProxy *proxy_a = TP_PROXY (a);
468         TpProxy *proxy_b = TP_PROXY (b);
469         TpProxyClass *proxy_a_class = TP_PROXY_GET_CLASS (a);
470         TpProxyClass *proxy_b_class = TP_PROXY_GET_CLASS (b);
471
472         g_return_val_if_fail (TP_IS_PROXY (proxy_a), FALSE);
473         g_return_val_if_fail (TP_IS_PROXY (proxy_b), FALSE);
474         g_return_val_if_fail (proxy_a_class->must_have_unique_name, 0);
475         g_return_val_if_fail (proxy_b_class->must_have_unique_name, 0);
476
477         return g_str_equal (proxy_a->object_path, proxy_b->object_path) &&
478                g_str_equal (proxy_a->bus_name, proxy_b->bus_name);
479 }
480
481 gboolean
482 empathy_check_available_state (void)
483 {
484         TpConnectionPresenceType presence;
485         EmpathyIdle *idle;
486
487         idle = empathy_idle_dup_singleton ();
488         presence = empathy_idle_get_state (idle);
489         g_object_unref (idle);
490
491         if (presence != TP_CONNECTION_PRESENCE_TYPE_AVAILABLE &&
492                 presence != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
493                 return FALSE;
494         }
495
496         return TRUE;
497 }
498
499 gint
500 empathy_uint_compare (gconstpointer a,
501                       gconstpointer b)
502 {
503         return *(guint *) a - *(guint *) b;
504 }
505
506 gchar *
507 empathy_protocol_icon_name (const gchar *protocol)
508 {
509   if (!tp_strdiff (protocol, "yahoojp"))
510     /* Yahoo Japan uses the same icon as Yahoo */
511     protocol = "yahoo";
512   else if (!tp_strdiff (protocol, "simple"))
513     /* SIMPLE uses the same icon as SIP */
514     protocol = "sip";
515   else if (!tp_strdiff (protocol, "sms"))
516     return g_strdup ("phone");
517
518   return g_strdup_printf ("im-%s", protocol);
519 }
520
521 GType
522 empathy_type_dbus_ao (void)
523 {
524   static GType t = 0;
525
526   if (G_UNLIKELY (t == 0))
527      t = dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH);
528
529   return t;
530 }
531
532 const char *
533 empathy_protocol_name_to_display_name (const gchar *proto_name)
534 {
535   int i;
536   static struct {
537     const gchar *proto;
538     const gchar *display;
539     gboolean translated;
540   } names[] = {
541     { "jabber", "Jabber", FALSE },
542     { "msn", "MSN", FALSE, },
543     { "local-xmpp", N_("People Nearby"), TRUE },
544     { "irc", "IRC", FALSE },
545     { "icq", "ICQ", FALSE },
546     { "aim", "AIM", FALSE },
547     { "yahoo", "Yahoo!", FALSE },
548     { "yahoojp", N_("Yahoo! Japan"), TRUE },
549     { "groupwise", "GroupWise", FALSE },
550     { "sip", "SIP", FALSE },
551     { NULL, NULL }
552   };
553
554   for (i = 0; names[i].proto != NULL; i++)
555     {
556       if (!tp_strdiff (proto_name, names[i].proto))
557         {
558           if (names[i].translated)
559             return gettext (names[i].display);
560           else
561             return names[i].display;
562         }
563     }
564
565   return proto_name;
566 }
567
568 const char *
569 empathy_service_name_to_display_name (const gchar *service_name)
570 {
571   int i;
572   static struct {
573     const gchar *service;
574     const gchar *display;
575     gboolean translated;
576   } names[] = {
577     { "google-talk", N_("Google Talk"), FALSE },
578     { "facebook", N_("Facebook Chat"), TRUE },
579     { NULL, NULL }
580   };
581
582   for (i = 0; names[i].service != NULL; i++)
583     {
584       if (!tp_strdiff (service_name, names[i].service))
585         {
586           if (names[i].translated)
587             return gettext (names[i].display);
588           else
589             return names[i].display;
590         }
591     }
592
593   return service_name;
594 }
595
596 /* Note: this function depends on the account manager having its core feature
597  * prepared. */
598 TpAccount *
599 empathy_get_account_for_connection (TpConnection *connection)
600 {
601   TpAccountManager *manager;
602   TpAccount *account = NULL;
603   GList *accounts, *l;
604
605   manager = tp_account_manager_dup ();
606
607   accounts = tp_account_manager_get_valid_accounts (manager);
608
609   for (l = accounts; l != NULL; l = l->next)
610     {
611       TpAccount *a = l->data;
612
613       if (tp_account_get_connection (a) == connection)
614         {
615           account = a;
616           break;
617         }
618     }
619
620   g_list_free (accounts);
621   g_object_unref (manager);
622
623   return account;
624 }
625
626 gboolean
627 empathy_account_manager_get_accounts_connected (gboolean *connecting)
628 {
629   TpAccountManager *manager;
630   GList *accounts, *l;
631   gboolean out_connecting = FALSE;
632   gboolean out_connected = FALSE;
633
634   manager = tp_account_manager_dup ();
635
636   if (G_UNLIKELY (!tp_account_manager_is_prepared (manager,
637           TP_ACCOUNT_MANAGER_FEATURE_CORE)))
638     g_critical (G_STRLOC ": %s called before AccountManager ready", G_STRFUNC);
639
640   accounts = tp_account_manager_get_valid_accounts (manager);
641
642   for (l = accounts; l != NULL; l = l->next)
643     {
644       TpConnectionStatus s = tp_account_get_connection_status (
645           TP_ACCOUNT (l->data), NULL);
646
647       if (s == TP_CONNECTION_STATUS_CONNECTING)
648         out_connecting = TRUE;
649       else if (s == TP_CONNECTION_STATUS_CONNECTED)
650         out_connected = TRUE;
651
652       if (out_connecting && out_connected)
653         break;
654     }
655
656   g_list_free (accounts);
657   g_object_unref (manager);
658
659   if (connecting != NULL)
660     *connecting = out_connecting;
661
662   return out_connected;
663 }
664
665 /* Change the RequestedPresence of a newly created account to ensure that it
666  * is actually connected. */
667 void
668 empathy_connect_new_account (TpAccount *account,
669     TpAccountManager *account_manager)
670 {
671   TpConnectionPresenceType presence;
672   gchar *status, *message;
673
674   /* only force presence if presence was offline, unknown or unset */
675   presence = tp_account_get_requested_presence (account, NULL, NULL);
676   switch (presence)
677     {
678       case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
679       case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
680       case TP_CONNECTION_PRESENCE_TYPE_UNSET:
681         presence = tp_account_manager_get_most_available_presence (
682             account_manager, &status, &message);
683
684         if (presence == TP_CONNECTION_PRESENCE_TYPE_OFFLINE)
685           /* Global presence is offline; we force it so user doesn't have to
686            * manually change the presence to connect his new account. */
687           presence = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
688
689         tp_account_request_presence_async (account, presence,
690             status, NULL, NULL, NULL);
691
692         g_free (status);
693         g_free (message);
694         break;
695
696        case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
697        case TP_CONNECTION_PRESENCE_TYPE_AWAY:
698        case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
699        case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
700        case TP_CONNECTION_PRESENCE_TYPE_BUSY:
701        case TP_CONNECTION_PRESENCE_TYPE_ERROR:
702        default:
703         /* do nothing if the presence is not offline */
704         break;
705     }
706 }
707
708 /* Translate Folks' general presence type to the Tp presence type */
709 TpConnectionPresenceType
710 empathy_folks_presence_type_to_tp (FolksPresenceType type)
711 {
712   return (TpConnectionPresenceType) type;
713 }
714
715 /* Returns TRUE if the given Individual contains a TpContact */
716 gboolean
717 empathy_folks_individual_contains_contact (FolksIndividual *individual)
718 {
719   GList *personas, *l;
720
721   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), FALSE);
722
723   personas = folks_individual_get_personas (individual);
724   for (l = personas; l != NULL; l = l->next)
725     {
726       if (TPF_IS_PERSONA (l->data))
727         return (tpf_persona_get_contact (TPF_PERSONA (l->data)) != NULL);
728     }
729
730   return FALSE;
731 }
732
733 /* TODO: this needs to be eliminated (and replaced in some cases with user
734  * prompts) when we break the assumption that FolksIndividuals are 1:1 with
735  * TpContacts */
736
737 /* Retrieve the EmpathyContact corresponding to the first TpContact contained
738  * within the given Individual. Note that this is a temporary convenience. See
739  * the TODO above. */
740 EmpathyContact *
741 empathy_contact_dup_from_folks_individual (FolksIndividual *individual)
742 {
743   GList *personas, *l;
744   EmpathyContact *contact = NULL;
745
746   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
747
748   personas = folks_individual_get_personas (individual);
749   for (l = personas; (l != NULL) && (contact == NULL); l = l->next)
750     {
751       TpfPersona *persona = l->data;
752
753       if (TPF_IS_PERSONA (persona))
754         {
755           TpContact *tp_contact;
756
757           tp_contact = tpf_persona_get_contact (persona);
758           contact = empathy_contact_dup_from_tp_contact (tp_contact);
759           empathy_contact_set_persona (contact, FOLKS_PERSONA (persona));
760         }
761     }
762
763   return contact;
764 }
765
766 TpChannelGroupChangeReason
767 tp_channel_group_change_reason_from_folks_groups_change_reason (
768     FolksGroupableChangeReason reason)
769 {
770   return (TpChannelGroupChangeReason) reason;
771 }
772
773 TpfPersonaStore *
774 empathy_get_persona_store_for_connection (TpConnection *connection)
775 {
776   FolksBackendStore *backend_store;
777   FolksBackend *backend;
778   TpfPersonaStore *result = NULL;
779
780   backend_store = folks_backend_store_dup ();
781   backend = folks_backend_store_get_backend_by_name (backend_store,
782       "telepathy");
783   if (backend != NULL)
784     {
785       GHashTable *stores_hash;
786       GList *stores, *l;
787
788       stores_hash = folks_backend_get_persona_stores (backend);
789       stores = g_hash_table_get_values (stores_hash);
790       for (l = stores; l != NULL && result == NULL; l = l->next)
791         {
792           TpfPersonaStore *persona_store = TPF_PERSONA_STORE (l->data);
793           TpAccount *account;
794           TpConnection *conn_cur;
795
796           account = tpf_persona_store_get_account (persona_store);
797           conn_cur = tp_account_get_connection (account);
798           if (conn_cur == connection)
799             result = persona_store;
800         }
801
802       g_list_free (stores);
803     }
804
805   g_object_unref (backend);
806   g_object_unref (backend_store);
807
808   return result;
809 }
810
811 gboolean
812 empathy_connection_can_add_personas (TpConnection *connection)
813 {
814   FolksPersonaStore *persona_store;
815
816   g_return_val_if_fail (TP_IS_CONNECTION (connection), FALSE);
817
818   persona_store = FOLKS_PERSONA_STORE (
819       empathy_get_persona_store_for_connection (connection));
820
821   return (folks_persona_store_get_can_add_personas (persona_store) ==
822       FOLKS_MAYBE_BOOL_TRUE);
823 }
824
825 gboolean
826 empathy_connection_can_alias_personas (TpConnection *connection)
827 {
828   FolksPersonaStore *persona_store;
829
830   g_return_val_if_fail (TP_IS_CONNECTION (connection), FALSE);
831
832   persona_store = FOLKS_PERSONA_STORE (
833       empathy_get_persona_store_for_connection (connection));
834
835   return (folks_persona_store_get_can_alias_personas (persona_store) ==
836       FOLKS_MAYBE_BOOL_TRUE);
837 }
838
839 gboolean
840 empathy_connection_can_group_personas (TpConnection *connection)
841 {
842   FolksPersonaStore *persona_store;
843
844   g_return_val_if_fail (TP_IS_CONNECTION (connection), FALSE);
845
846   persona_store = FOLKS_PERSONA_STORE (
847       empathy_get_persona_store_for_connection (connection));
848
849   return (folks_persona_store_get_can_group_personas (persona_store) ==
850       FOLKS_MAYBE_BOOL_TRUE);
851 }
852
853 gchar *
854 empathy_get_x509_certificate_hostname (gnutls_x509_crt_t cert)
855 {
856   gchar dns_name[256];
857   gsize dns_name_size;
858   gint idx;
859   gint res = 0;
860
861   /* this snippet is taken from GnuTLS.
862    * see gnutls/lib/x509/rfc2818_hostname.c
863    */
864   for (idx = 0; res >= 0; idx++)
865     {
866       dns_name_size = sizeof (dns_name);
867       res = gnutls_x509_crt_get_subject_alt_name (cert, idx,
868           dns_name, &dns_name_size, NULL);
869
870       if (res == GNUTLS_SAN_DNSNAME || res == GNUTLS_SAN_IPADDRESS)
871         return g_strndup (dns_name, dns_name_size);
872     }
873
874   dns_name_size = sizeof (dns_name);
875   res = gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME,
876       0, 0, dns_name, &dns_name_size);
877
878   if (res >= 0)
879     return g_strndup (dns_name, dns_name_size);
880
881   return NULL;
882 }