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