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