2 * Copyright (C) 2003-2007 Imendio AB
3 * Copyright (C) 2007-2008 Collabora Ltd.
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.
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.
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
20 * Authors: Richard Hult <richard@imendio.com>
21 * Martyn Russell <martyn@imendio.com>
22 * Xavier Claessens <xclaesse@gmail.com>
29 #include <sys/types.h>
31 #include <glib/gi18n-lib.h>
33 #include <libxml/uri.h>
35 #include <folks/folks.h>
36 #include <folks/folks-telepathy.h>
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>
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"
53 #include <extensions/extensions.h>
55 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
56 #include "empathy-debug.h"
58 /* Translation between presence types and string */
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 },
84 static gboolean initialized = FALSE;
92 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
93 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
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);
99 empathy_debug_set_flags (g_getenv ("EMPATHY_DEBUG"));
100 tp_debug_divert_messages (g_getenv ("EMPATHY_LOGFILE"));
108 empathy_substring (const gchar *str,
112 return g_strndup (str + start, end - start);
116 empathy_strcasecmp (const gchar *s1,
119 return empathy_strncasecmp (s1, s2, -1);
123 empathy_strncasecmp (const gchar *s1,
130 u1 = g_utf8_casefold (s1, n);
131 u2 = g_utf8_casefold (s2, n);
133 ret_val = g_utf8_collate (u1, u2);
141 empathy_xml_validate (xmlDoc *doc,
142 const gchar *dtd_filename)
150 path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "libempathy",
152 if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
154 path = g_build_filename (DATADIR, "empathy", dtd_filename, NULL);
156 DEBUG ("Loading dtd file %s", path);
158 /* The list of valid chars is taken from libxml. */
159 escaped = xmlURIEscapeStr ((const xmlChar *) path,
160 (const xmlChar *)":@&=+$,/?;");
163 memset (&cvp, 0, sizeof (cvp));
164 dtd = xmlParseDTD (NULL, escaped);
165 ret = xmlValidateDtd (&cvp, doc, dtd);
174 empathy_xml_node_get_child (xmlNodePtr node,
175 const gchar *child_name)
179 g_return_val_if_fail (node != NULL, NULL);
180 g_return_val_if_fail (child_name != NULL, NULL);
182 for (l = node->children; l; l = l->next) {
183 if (l->name && strcmp ((const gchar *) l->name, child_name) == 0) {
192 empathy_xml_node_get_child_content (xmlNodePtr node,
193 const gchar *child_name)
197 g_return_val_if_fail (node != NULL, NULL);
198 g_return_val_if_fail (child_name != NULL, NULL);
200 l = empathy_xml_node_get_child (node, child_name);
202 return xmlNodeGetContent (l);
209 empathy_xml_node_find_child_prop_value (xmlNodePtr node,
210 const gchar *prop_name,
211 const gchar *prop_value)
214 xmlNodePtr found = NULL;
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);
220 for (l = node->children; l && !found; l = l->next) {
223 if (!xmlHasProp (l, (const xmlChar *) prop_name)) {
227 prop = xmlGetProp (l, (const xmlChar *) prop_name);
228 if (prop && strcmp ((const gchar *) prop, prop_value) == 0) {
239 empathy_presence_get_default_message (TpConnectionPresenceType presence)
242 case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
243 return _("Available");
244 case TP_CONNECTION_PRESENCE_TYPE_BUSY:
246 case TP_CONNECTION_PRESENCE_TYPE_AWAY:
247 case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
249 case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
250 return _("Invisible");
251 case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
253 case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
255 case TP_CONNECTION_PRESENCE_TYPE_UNSET:
256 case TP_CONNECTION_PRESENCE_TYPE_ERROR:
264 empathy_presence_to_str (TpConnectionPresenceType presence)
268 for (i = 0 ; presence_types[i].name != NULL; i++)
269 if (presence == presence_types[i].type)
270 return presence_types[i].name;
275 TpConnectionPresenceType
276 empathy_presence_from_str (const gchar *str)
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;
284 return TP_CONNECTION_PRESENCE_TYPE_UNSET;
288 empathy_status_reason_get_default_message (TpConnectionStatusReason 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");
320 return _("Unknown reason");
325 empathy_file_lookup (const gchar *filename, const gchar *subdir)
333 path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), subdir, filename, NULL);
334 if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
336 path = g_build_filename (DATADIR, "empathy", filename, NULL);
343 empathy_proxy_hash (gconstpointer key)
345 TpProxy *proxy = TP_PROXY (key);
346 TpProxyClass *proxy_class = TP_PROXY_GET_CLASS (key);
348 g_return_val_if_fail (TP_IS_PROXY (proxy), 0);
349 g_return_val_if_fail (proxy_class->must_have_unique_name, 0);
351 return g_str_hash (proxy->object_path) ^ g_str_hash (proxy->bus_name);
355 empathy_proxy_equal (gconstpointer a,
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);
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);
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);
373 empathy_check_available_state (void)
375 TpConnectionPresenceType presence;
378 idle = empathy_idle_dup_singleton ();
379 presence = empathy_idle_get_state (idle);
380 g_object_unref (idle);
382 if (presence != TP_CONNECTION_PRESENCE_TYPE_AVAILABLE &&
383 presence != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
391 empathy_uint_compare (gconstpointer a,
394 return *(guint *) a - *(guint *) b;
398 empathy_protocol_icon_name (const gchar *protocol)
400 if (!tp_strdiff (protocol, "yahoojp"))
401 /* Yahoo Japan uses the same icon as Yahoo */
403 else if (!tp_strdiff (protocol, "simple"))
404 /* SIMPLE uses the same icon as SIP */
406 else if (!tp_strdiff (protocol, "sms"))
407 return g_strdup ("phone");
409 return g_strdup_printf ("im-%s", protocol);
413 empathy_type_dbus_ao (void)
417 if (G_UNLIKELY (t == 0))
418 t = dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH);
424 empathy_protocol_name_to_display_name (const gchar *proto_name)
429 const gchar *display;
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 },
447 for (i = 0; names[i].proto != NULL; i++)
449 if (!tp_strdiff (proto_name, names[i].proto))
451 if (names[i].translated)
452 return _(names[i].display);
454 return names[i].display;
461 /* Note: this function depends on the account manager having its core feature
464 empathy_get_account_for_connection (TpConnection *connection)
466 TpAccountManager *manager;
467 TpAccount *account = NULL;
470 manager = tp_account_manager_dup ();
472 accounts = tp_account_manager_get_valid_accounts (manager);
474 for (l = accounts; l != NULL; l = l->next)
476 TpAccount *a = l->data;
478 if (tp_account_get_connection (a) == connection)
485 g_list_free (accounts);
486 g_object_unref (manager);
492 empathy_account_manager_get_accounts_connected (gboolean *connecting)
494 TpAccountManager *manager;
496 gboolean out_connecting = FALSE;
497 gboolean out_connected = FALSE;
499 manager = tp_account_manager_dup ();
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);
505 accounts = tp_account_manager_get_valid_accounts (manager);
507 for (l = accounts; l != NULL; l = l->next)
509 TpConnectionStatus s = tp_account_get_connection_status (
510 TP_ACCOUNT (l->data), NULL);
512 if (s == TP_CONNECTION_STATUS_CONNECTING)
513 out_connecting = TRUE;
514 else if (s == TP_CONNECTION_STATUS_CONNECTED)
515 out_connected = TRUE;
517 if (out_connecting && out_connected)
521 g_list_free (accounts);
522 g_object_unref (manager);
524 if (connecting != NULL)
525 *connecting = out_connecting;
527 return out_connected;
530 /* Change the RequestedPresence of a newly created account to ensure that it
531 * is actually connected. */
533 empathy_connect_new_account (TpAccount *account,
534 TpAccountManager *account_manager)
536 TpConnectionPresenceType presence;
537 gchar *status, *message;
539 /* only force presence if presence was offline, unknown or unset */
540 presence = tp_account_get_requested_presence (account, NULL, NULL);
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);
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;
554 tp_account_request_presence_async (account, presence,
555 status, NULL, NULL, NULL);
562 /* do nothing if the presence is not offline */
567 TpConnectionPresenceType
568 empathy_folks_presence_type_to_tp (FolksPresenceType type)
570 return (TpConnectionPresenceType) type;
574 empathy_folks_individual_contains_contact (FolksIndividual *individual)
578 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), FALSE);
580 personas = folks_individual_get_personas (individual);
581 for (l = personas; l != NULL; l = l->next)
583 TpfPersona *persona = l->data;
585 if (TPF_IS_PERSONA (persona))
587 TpContact *contact = tpf_persona_get_contact (persona);
589 if (TP_IS_CONTACT (contact))
597 /* TODO: this needs to be eliminated (and replaced in some cases with user
598 * prompts) when we break the assumption that FolksIndividuals are 1:1 with
601 empathy_contact_from_folks_individual (FolksIndividual *individual)
604 EmpathyContact *contact = NULL;
606 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
608 personas = folks_individual_get_personas (individual);
609 for (l = personas; (l != NULL) && (contact == NULL); l = l->next)
611 TpfPersona *persona = l->data;
613 if (TPF_IS_PERSONA (persona))
615 TpContact *tp_contact;
617 tp_contact = tpf_persona_get_contact (persona);
618 contact = empathy_contact_dup_from_tp_contact (tp_contact);
625 /* TODO: This also needs to be eliminated, and is horrifically slow. */
627 folks_individual_from_empathy_contact (EmpathyContact *contact)
629 EmpathyIndividualManager *manager;
630 FolksIndividual *individual = NULL;
631 GList *individuals, *l;
633 manager = empathy_individual_manager_dup_singleton ();
634 individuals = empathy_individual_manager_get_members (manager);
636 for (l = individuals; (l != NULL) && (individual == NULL); l = l->next)
638 FolksIndividual *i = FOLKS_INDIVIDUAL (l->data);
639 EmpathyContact *c = empathy_contact_from_folks_individual (i);
642 individual = g_object_ref (i);
647 g_list_free (individuals);
648 g_object_unref (manager);