]> git.0d.be Git - empathy.git/blob - libempathy/empathy-utils.c
Update to the FolksIndividualAggregator:individuals-changed signal.
[empathy.git] / libempathy / empathy-utils.c
1 /*
2  * Copyright (C) 2003-2007 Imendio AB
3  * Copyright (C) 2007-2008 Collabora Ltd.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301  USA
19  *
20  * Authors: Richard Hult <richard@imendio.com>
21  *          Martyn Russell <martyn@imendio.com>
22  *          Xavier Claessens <xclaesse@gmail.com>
23  */
24
25 #include "config.h"
26
27 #include <string.h>
28 #include <time.h>
29 #include <sys/types.h>
30
31 #include <glib/gi18n-lib.h>
32
33 #include <libxml/uri.h>
34
35 #include <folks/folks.h>
36 #include <folks/folks-telepathy.h>
37
38 #include <telepathy-glib/account-manager.h>
39 #include <telepathy-glib/connection.h>
40 #include <telepathy-glib/channel.h>
41 #include <telepathy-glib/dbus.h>
42 #include <telepathy-glib/util.h>
43
44 #include "empathy-utils.h"
45 #include "empathy-contact-manager.h"
46 #include "empathy-individual-manager.h"
47 #include "empathy-dispatcher.h"
48 #include "empathy-dispatch-operation.h"
49 #include "empathy-idle.h"
50 #include "empathy-tp-call.h"
51 #include "empathy-tp-contact-factory.h"
52
53 #include <extensions/extensions.h>
54
55 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
56 #include "empathy-debug.h"
57
58 /* Translation between presence types and string */
59 static struct {
60         gchar *name;
61         TpConnectionPresenceType type;
62 } presence_types[] = {
63         { "available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE },
64         { "busy",      TP_CONNECTION_PRESENCE_TYPE_BUSY },
65         { "away",      TP_CONNECTION_PRESENCE_TYPE_AWAY },
66         { "ext_away",  TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY },
67         { "hidden",    TP_CONNECTION_PRESENCE_TYPE_HIDDEN },
68         { "offline",   TP_CONNECTION_PRESENCE_TYPE_OFFLINE },
69         { "unset",     TP_CONNECTION_PRESENCE_TYPE_UNSET },
70         { "unknown",   TP_CONNECTION_PRESENCE_TYPE_UNKNOWN },
71         { "error",     TP_CONNECTION_PRESENCE_TYPE_ERROR },
72         /* alternative names */
73         { "dnd",      TP_CONNECTION_PRESENCE_TYPE_BUSY },
74         { "brb",      TP_CONNECTION_PRESENCE_TYPE_AWAY },
75         { "xa",       TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY },
76         { NULL, },
77 };
78
79
80
81 void
82 empathy_init (void)
83 {
84         static gboolean initialized = FALSE;
85
86         if (initialized)
87                 return;
88
89         g_type_init ();
90
91         /* Setup gettext */
92         bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
93         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
94
95         /* Setup debug output for empathy and telepathy-glib */
96         if (g_getenv ("EMPATHY_TIMING") != NULL) {
97                 g_log_set_default_handler (tp_debug_timestamped_log_handler, NULL);
98         }
99         empathy_debug_set_flags (g_getenv ("EMPATHY_DEBUG"));
100         tp_debug_divert_messages (g_getenv ("EMPATHY_LOGFILE"));
101
102         emp_cli_init ();
103
104         initialized = TRUE;
105 }
106
107 gchar *
108 empathy_substring (const gchar *str,
109                   gint         start,
110                   gint         end)
111 {
112         return g_strndup (str + start, end - start);
113 }
114
115 gint
116 empathy_strcasecmp (const gchar *s1,
117                    const gchar *s2)
118 {
119         return empathy_strncasecmp (s1, s2, -1);
120 }
121
122 gint
123 empathy_strncasecmp (const gchar *s1,
124                     const gchar *s2,
125                     gsize        n)
126 {
127         gchar *u1, *u2;
128         gint   ret_val;
129
130         u1 = g_utf8_casefold (s1, n);
131         u2 = g_utf8_casefold (s2, n);
132
133         ret_val = g_utf8_collate (u1, u2);
134         g_free (u1);
135         g_free (u2);
136
137         return ret_val;
138 }
139
140 gboolean
141 empathy_xml_validate (xmlDoc      *doc,
142                      const gchar *dtd_filename)
143 {
144         gchar        *path;
145         xmlChar      *escaped;
146         xmlValidCtxt  cvp;
147         xmlDtd       *dtd;
148         gboolean      ret;
149
150         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "libempathy",
151                                  dtd_filename, NULL);
152         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
153                 g_free (path);
154                 path = g_build_filename (DATADIR, "empathy", dtd_filename, NULL);
155         }
156         DEBUG ("Loading dtd file %s", path);
157
158         /* The list of valid chars is taken from libxml. */
159         escaped = xmlURIEscapeStr ((const xmlChar *) path,
160                 (const xmlChar *)":@&=+$,/?;");
161         g_free (path);
162
163         memset (&cvp, 0, sizeof (cvp));
164         dtd = xmlParseDTD (NULL, escaped);
165         ret = xmlValidateDtd (&cvp, doc, dtd);
166
167         xmlFree (escaped);
168         xmlFreeDtd (dtd);
169
170         return ret;
171 }
172
173 xmlNodePtr
174 empathy_xml_node_get_child (xmlNodePtr   node,
175                            const gchar *child_name)
176 {
177         xmlNodePtr l;
178
179         g_return_val_if_fail (node != NULL, NULL);
180         g_return_val_if_fail (child_name != NULL, NULL);
181
182         for (l = node->children; l; l = l->next) {
183                 if (l->name && strcmp ((const gchar *) l->name, child_name) == 0) {
184                         return l;
185                 }
186         }
187
188         return NULL;
189 }
190
191 xmlChar *
192 empathy_xml_node_get_child_content (xmlNodePtr   node,
193                                    const gchar *child_name)
194 {
195         xmlNodePtr l;
196
197         g_return_val_if_fail (node != NULL, NULL);
198         g_return_val_if_fail (child_name != NULL, NULL);
199
200         l = empathy_xml_node_get_child (node, child_name);
201         if (l) {
202                 return xmlNodeGetContent (l);
203         }
204
205         return NULL;
206 }
207
208 xmlNodePtr
209 empathy_xml_node_find_child_prop_value (xmlNodePtr   node,
210                                        const gchar *prop_name,
211                                        const gchar *prop_value)
212 {
213         xmlNodePtr l;
214         xmlNodePtr found = NULL;
215
216         g_return_val_if_fail (node != NULL, NULL);
217         g_return_val_if_fail (prop_name != NULL, NULL);
218         g_return_val_if_fail (prop_value != NULL, NULL);
219
220         for (l = node->children; l && !found; l = l->next) {
221                 xmlChar *prop;
222
223                 if (!xmlHasProp (l, (const xmlChar *) prop_name)) {
224                         continue;
225                 }
226
227                 prop = xmlGetProp (l, (const xmlChar *) prop_name);
228                 if (prop && strcmp ((const gchar *) prop, prop_value) == 0) {
229                         found = l;
230                 }
231
232                 xmlFree (prop);
233         }
234
235         return found;
236 }
237
238 const gchar *
239 empathy_presence_get_default_message (TpConnectionPresenceType presence)
240 {
241         switch (presence) {
242         case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
243                 return _("Available");
244         case TP_CONNECTION_PRESENCE_TYPE_BUSY:
245                 return _("Busy");
246         case TP_CONNECTION_PRESENCE_TYPE_AWAY:
247         case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
248                 return _("Away");
249         case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
250                 return _("Invisible");
251         case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
252                 return _("Offline");
253         case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
254                 return _("Unknown");
255         case TP_CONNECTION_PRESENCE_TYPE_UNSET:
256         case TP_CONNECTION_PRESENCE_TYPE_ERROR:
257                 return NULL;
258         }
259
260         return NULL;
261 }
262
263 const gchar *
264 empathy_presence_to_str (TpConnectionPresenceType presence)
265 {
266         int i;
267
268         for (i = 0 ; presence_types[i].name != NULL; i++)
269                 if (presence == presence_types[i].type)
270                         return presence_types[i].name;
271
272         return NULL;
273 }
274
275 TpConnectionPresenceType
276 empathy_presence_from_str (const gchar *str)
277 {
278         int i;
279
280         for (i = 0 ; presence_types[i].name != NULL; i++)
281                 if (!tp_strdiff (str, presence_types[i].name))
282                         return presence_types[i].type;
283
284         return TP_CONNECTION_PRESENCE_TYPE_UNSET;
285 }
286
287 const gchar *
288 empathy_status_reason_get_default_message (TpConnectionStatusReason reason)
289 {
290         switch (reason) {
291         case TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED:
292                 return _("No reason specified");
293         case TP_CONNECTION_STATUS_REASON_REQUESTED:
294                 return _("Status is set to offline");
295         case TP_CONNECTION_STATUS_REASON_NETWORK_ERROR:
296                 return _("Network error");
297         case TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED:
298                 return _("Authentication failed");
299         case TP_CONNECTION_STATUS_REASON_ENCRYPTION_ERROR:
300                 return _("Encryption error");
301         case TP_CONNECTION_STATUS_REASON_NAME_IN_USE:
302                 return _("Name in use");
303         case TP_CONNECTION_STATUS_REASON_CERT_NOT_PROVIDED:
304                 return _("Certificate not provided");
305         case TP_CONNECTION_STATUS_REASON_CERT_UNTRUSTED:
306                 return _("Certificate untrusted");
307         case TP_CONNECTION_STATUS_REASON_CERT_EXPIRED:
308                 return _("Certificate expired");
309         case TP_CONNECTION_STATUS_REASON_CERT_NOT_ACTIVATED:
310                 return _("Certificate not activated");
311         case TP_CONNECTION_STATUS_REASON_CERT_HOSTNAME_MISMATCH:
312                 return _("Certificate hostname mismatch");
313         case TP_CONNECTION_STATUS_REASON_CERT_FINGERPRINT_MISMATCH:
314                 return _("Certificate fingerprint mismatch");
315         case TP_CONNECTION_STATUS_REASON_CERT_SELF_SIGNED:
316                 return _("Certificate self-signed");
317         case TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR:
318                 return _("Certificate error");
319         default:
320                 return _("Unknown reason");
321         }
322 }
323
324 gchar *
325 empathy_file_lookup (const gchar *filename, const gchar *subdir)
326 {
327         gchar *path;
328
329         if (!subdir) {
330                 subdir = ".";
331         }
332
333         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), subdir, filename, NULL);
334         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
335                 g_free (path);
336                 path = g_build_filename (DATADIR, "empathy", filename, NULL);
337         }
338
339         return path;
340 }
341
342 guint
343 empathy_proxy_hash (gconstpointer key)
344 {
345         TpProxy      *proxy = TP_PROXY (key);
346         TpProxyClass *proxy_class = TP_PROXY_GET_CLASS (key);
347
348         g_return_val_if_fail (TP_IS_PROXY (proxy), 0);
349         g_return_val_if_fail (proxy_class->must_have_unique_name, 0);
350
351         return g_str_hash (proxy->object_path) ^ g_str_hash (proxy->bus_name);
352 }
353
354 gboolean
355 empathy_proxy_equal (gconstpointer a,
356                      gconstpointer b)
357 {
358         TpProxy *proxy_a = TP_PROXY (a);
359         TpProxy *proxy_b = TP_PROXY (b);
360         TpProxyClass *proxy_a_class = TP_PROXY_GET_CLASS (a);
361         TpProxyClass *proxy_b_class = TP_PROXY_GET_CLASS (b);
362
363         g_return_val_if_fail (TP_IS_PROXY (proxy_a), FALSE);
364         g_return_val_if_fail (TP_IS_PROXY (proxy_b), FALSE);
365         g_return_val_if_fail (proxy_a_class->must_have_unique_name, 0);
366         g_return_val_if_fail (proxy_b_class->must_have_unique_name, 0);
367
368         return g_str_equal (proxy_a->object_path, proxy_b->object_path) &&
369                g_str_equal (proxy_a->bus_name, proxy_b->bus_name);
370 }
371
372 gboolean
373 empathy_check_available_state (void)
374 {
375         TpConnectionPresenceType presence;
376         EmpathyIdle *idle;
377
378         idle = empathy_idle_dup_singleton ();
379         presence = empathy_idle_get_state (idle);
380         g_object_unref (idle);
381
382         if (presence != TP_CONNECTION_PRESENCE_TYPE_AVAILABLE &&
383                 presence != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
384                 return FALSE;
385         }
386
387         return TRUE;
388 }
389
390 gint
391 empathy_uint_compare (gconstpointer a,
392                       gconstpointer b)
393 {
394         return *(guint *) a - *(guint *) b;
395 }
396
397 gchar *
398 empathy_protocol_icon_name (const gchar *protocol)
399 {
400   if (!tp_strdiff (protocol, "yahoojp"))
401     /* Yahoo Japan uses the same icon as Yahoo */
402     protocol = "yahoo";
403   else if (!tp_strdiff (protocol, "simple"))
404     /* SIMPLE uses the same icon as SIP */
405     protocol = "sip";
406   else if (!tp_strdiff (protocol, "sms"))
407     return g_strdup ("phone");
408
409   return g_strdup_printf ("im-%s", protocol);
410 }
411
412 GType
413 empathy_type_dbus_ao (void)
414 {
415   static GType t = 0;
416
417   if (G_UNLIKELY (t == 0))
418      t = dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH);
419
420   return t;
421 }
422
423 const char *
424 empathy_protocol_name_to_display_name (const gchar *proto_name)
425 {
426   int i;
427   static struct {
428     const gchar *proto;
429     const gchar *display;
430     gboolean translated;
431   } names[] = {
432     { "jabber", "Jabber", FALSE },
433     { "gtalk", "Google Talk", FALSE },
434     { "msn", "MSN", FALSE, },
435     { "local-xmpp", N_("People Nearby"), TRUE },
436     { "irc", "IRC", FALSE },
437     { "icq", "ICQ", FALSE },
438     { "aim", "AIM", FALSE },
439     { "yahoo", "Yahoo!", FALSE },
440     { "yahoojp", N_("Yahoo! Japan"), TRUE },
441     { "facebook", N_("Facebook Chat"), TRUE },
442     { "groupwise", "GroupWise", FALSE },
443     { "sip", "SIP", FALSE },
444     { NULL, NULL }
445   };
446
447   for (i = 0; names[i].proto != NULL; i++)
448     {
449       if (!tp_strdiff (proto_name, names[i].proto))
450         {
451           if (names[i].translated)
452             return _(names[i].display);
453           else
454             return names[i].display;
455         }
456     }
457
458   return NULL;
459 }
460
461 /* Note: this function depends on the account manager having its core feature
462  * prepared. */
463 TpAccount *
464 empathy_get_account_for_connection (TpConnection *connection)
465 {
466   TpAccountManager *manager;
467   TpAccount *account = NULL;
468   GList *accounts, *l;
469
470   manager = tp_account_manager_dup ();
471
472   accounts = tp_account_manager_get_valid_accounts (manager);
473
474   for (l = accounts; l != NULL; l = l->next)
475     {
476       TpAccount *a = l->data;
477
478       if (tp_account_get_connection (a) == connection)
479         {
480           account = a;
481           break;
482         }
483     }
484
485   g_list_free (accounts);
486   g_object_unref (manager);
487
488   return account;
489 }
490
491 gboolean
492 empathy_account_manager_get_accounts_connected (gboolean *connecting)
493 {
494   TpAccountManager *manager;
495   GList *accounts, *l;
496   gboolean out_connecting = FALSE;
497   gboolean out_connected = FALSE;
498
499   manager = tp_account_manager_dup ();
500
501   if (G_UNLIKELY (!tp_account_manager_is_prepared (manager,
502           TP_ACCOUNT_MANAGER_FEATURE_CORE)))
503     g_critical (G_STRLOC ": %s called before AccountManager ready", G_STRFUNC);
504
505   accounts = tp_account_manager_get_valid_accounts (manager);
506
507   for (l = accounts; l != NULL; l = l->next)
508     {
509       TpConnectionStatus s = tp_account_get_connection_status (
510           TP_ACCOUNT (l->data), NULL);
511
512       if (s == TP_CONNECTION_STATUS_CONNECTING)
513         out_connecting = TRUE;
514       else if (s == TP_CONNECTION_STATUS_CONNECTED)
515         out_connected = TRUE;
516
517       if (out_connecting && out_connected)
518         break;
519     }
520
521   g_list_free (accounts);
522   g_object_unref (manager);
523
524   if (connecting != NULL)
525     *connecting = out_connecting;
526
527   return out_connected;
528 }
529
530 /* Change the RequestedPresence of a newly created account to ensure that it
531  * is actually connected. */
532 void
533 empathy_connect_new_account (TpAccount *account,
534     TpAccountManager *account_manager)
535 {
536   TpConnectionPresenceType presence;
537   gchar *status, *message;
538
539   /* only force presence if presence was offline, unknown or unset */
540   presence = tp_account_get_requested_presence (account, NULL, NULL);
541   switch (presence)
542     {
543       case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
544       case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
545       case TP_CONNECTION_PRESENCE_TYPE_UNSET:
546         presence = tp_account_manager_get_most_available_presence (
547             account_manager, &status, &message);
548
549         if (presence == TP_CONNECTION_PRESENCE_TYPE_OFFLINE)
550           /* Global presence is offline; we force it so user doesn't have to
551            * manually change the presence to connect his new account. */
552           presence = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
553
554         tp_account_request_presence_async (account, presence,
555             status, NULL, NULL, NULL);
556
557         g_free (status);
558         g_free (message);
559         break;
560
561        default:
562         /* do nothing if the presence is not offline */
563         break;
564     }
565 }
566
567 /* Translate Folks' general presence type to the Tp presence type */
568 TpConnectionPresenceType
569 empathy_folks_presence_type_to_tp (FolksPresenceType type)
570 {
571   return (TpConnectionPresenceType) type;
572 }
573
574 /* Returns TRUE if the given Individual contains a TpContact */
575 gboolean
576 empathy_folks_individual_contains_contact (FolksIndividual *individual)
577 {
578   GList *personas, *l;
579
580   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), FALSE);
581
582   personas = folks_individual_get_personas (individual);
583   for (l = personas; l != NULL; l = l->next)
584     {
585       if (TPF_IS_PERSONA (l->data))
586         return (tpf_persona_get_contact (TPF_PERSONA (l->data)) != NULL);
587     }
588
589   return FALSE;
590 }
591
592 /* TODO: this needs to be eliminated (and replaced in some cases with user
593  * prompts) when we break the assumption that FolksIndividuals are 1:1 with
594  * TpContacts */
595
596 /* Retrieve the EmpathyContact corresponding to the first TpContact contained
597  * within the given Individual. Note that this is a temporary convenience. See
598  * the TODO above. */
599 EmpathyContact *
600 empathy_contact_dup_from_folks_individual (FolksIndividual *individual)
601 {
602   GList *personas, *l;
603   EmpathyContact *contact = NULL;
604
605   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
606
607   personas = folks_individual_get_personas (individual);
608   for (l = personas; (l != NULL) && (contact == NULL); l = l->next)
609     {
610       TpfPersona *persona = l->data;
611
612       if (TPF_IS_PERSONA (persona))
613         {
614           TpContact *tp_contact;
615
616           tp_contact = tpf_persona_get_contact (persona);
617           contact = empathy_contact_dup_from_tp_contact (tp_contact);
618         }
619     }
620
621   return contact;
622 }
623
624 /* TODO: This also needs to be eliminated, and is horrifically slow. */
625
626 /* Retrieve the first Individual containing a TpContact that corresponds to the
627  * given EmpathyContact. Note that this is a temporary convenience. See
628  * the TODO above. */
629 FolksIndividual *
630 folks_individual_dup_from_empathy_contact (EmpathyContact *contact)
631 {
632   EmpathyIndividualManager *manager;
633   FolksIndividual *individual = NULL;
634   GList *individuals, *l;
635
636   manager = empathy_individual_manager_dup_singleton ();
637   individuals = empathy_individual_manager_get_members (manager);
638
639   for (l = individuals; (l != NULL) && (individual == NULL); l = l->next)
640     {
641       FolksIndividual *i = FOLKS_INDIVIDUAL (l->data);
642       EmpathyContact *c = empathy_contact_dup_from_folks_individual (i);
643
644       if (c == contact)
645         individual = g_object_ref (i);
646
647       g_object_unref (c);
648     }
649
650   g_list_free (individuals);
651   g_object_unref (manager);
652
653   return individual;
654 }
655
656 TpChannelGroupChangeReason
657 tp_chanel_group_change_reason_from_folks_groups_change_reason (
658     FolksGroupsChangeReason reason)
659 {
660   return (TpChannelGroupChangeReason) reason;
661 }