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