*/
#include "config.h"
-
-#include <string.h>
-#include <math.h>
-#include <time.h>
-#include <sys/types.h>
+#include "empathy-utils.h"
#include <glib/gi18n-lib.h>
-
-#include <libxml/uri.h>
-
-#include <folks/folks.h>
-#include <folks/folks-telepathy.h>
-
-#include <telepathy-glib/account-manager.h>
-#include <telepathy-glib/connection.h>
-#include <telepathy-glib/channel.h>
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/util.h>
+#include <dbus/dbus-protocol.h>
+#include <math.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
#include "empathy-client-factory.h"
-#include "empathy-utils.h"
-#include "empathy-contact-manager.h"
-#include "empathy-individual-manager.h"
-#include "empathy-presence-manager.h"
-#include "empathy-request-util.h"
-#include "empathy-tp-contact-factory.h"
+#include "extensions.h"
-#include <extensions/extensions.h>
+#include <math.h>
#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
#include "empathy-debug.h"
{ NULL, },
};
+static gboolean
+properties_contains (gchar **list,
+ gint length,
+ const gchar *property);
+
+static gboolean
+check_writeable_property (TpConnection *connection,
+ FolksIndividual *individual,
+ gchar *property);
+
void
empathy_init (void)
{
g_object_unref (am);
}
-gboolean
-empathy_xml_validate (xmlDoc *doc,
- const gchar *dtd_filename)
-{
- gchar *path;
- xmlChar *escaped;
- xmlValidCtxt cvp;
- xmlDtd *dtd;
- gboolean ret;
-
- path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "libempathy",
- dtd_filename, NULL);
- if (!g_file_test (path, G_FILE_TEST_EXISTS))
- {
- g_free (path);
- path = g_build_filename (DATADIR, "empathy", dtd_filename, NULL);
- }
-
- DEBUG ("Loading dtd file %s", path);
-
- /* The list of valid chars is taken from libxml. */
- escaped = xmlURIEscapeStr ((const xmlChar *) path,
- (const xmlChar *)":@&=+$,/?;");
- g_free (path);
-
- memset (&cvp, 0, sizeof (cvp));
- dtd = xmlParseDTD (NULL, escaped);
- ret = xmlValidateDtd (&cvp, doc, dtd);
-
- xmlFree (escaped);
- xmlFreeDtd (dtd);
-
- return ret;
-}
-
xmlNodePtr
empathy_xml_node_get_child (xmlNodePtr node,
const gchar *child_name)
g_hash_table_insert (errors, TP_ERROR_STR_CONNECTION_LOST,
_("Connection has been lost"));
g_hash_table_insert (errors, TP_ERROR_STR_ALREADY_CONNECTED,
- _("This resource is already connected to the server"));
+ _("This account is already connected to the server"));
g_hash_table_insert (errors, TP_ERROR_STR_CONNECTION_REPLACED,
_("Connection has been replaced by a new connection using the "
"same resource"));
_("The length of the server certificate, or the depth of the "
"server certificate chain, exceed the limits imposed by the "
"cryptography library"));
+ g_hash_table_insert (errors, TP_ERROR_STR_SOFTWARE_UPGRADE_REQUIRED,
+ _("Your software is too old"));
+ g_hash_table_insert (errors, DBUS_ERROR_NO_REPLY,
+ _("Internal error"));
return errors;
}
{
const gchar *dbus_error;
const gchar *message;
- const GHashTable *details = NULL;
+ const GHashTable *details = NULL;
TpConnectionStatusReason reason;
dbus_error = tp_account_get_detailed_error (account, &details);
if (message != NULL)
return message;
+ tp_account_get_connection_status (account, &reason);
+
DEBUG ("Don't understand error '%s'; fallback to the status reason (%u)",
dbus_error, reason);
- tp_account_get_connection_status (account, &reason);
-
return empathy_status_reason_get_default_message (reason);
}
return path;
}
-guint
-empathy_proxy_hash (gconstpointer key)
-{
- TpProxy *proxy = TP_PROXY (key);
- TpProxyClass *proxy_class = TP_PROXY_GET_CLASS (key);
-
- g_return_val_if_fail (TP_IS_PROXY (proxy), 0);
- g_return_val_if_fail (proxy_class->must_have_unique_name, 0);
-
- return g_str_hash (proxy->object_path) ^ g_str_hash (proxy->bus_name);
-}
-
-gboolean
-empathy_proxy_equal (gconstpointer a,
- gconstpointer b)
-{
- TpProxy *proxy_a = TP_PROXY (a);
- TpProxy *proxy_b = TP_PROXY (b);
- TpProxyClass *proxy_a_class = TP_PROXY_GET_CLASS (a);
- TpProxyClass *proxy_b_class = TP_PROXY_GET_CLASS (b);
-
- g_return_val_if_fail (TP_IS_PROXY (proxy_a), FALSE);
- g_return_val_if_fail (TP_IS_PROXY (proxy_b), FALSE);
- g_return_val_if_fail (proxy_a_class->must_have_unique_name, 0);
- g_return_val_if_fail (proxy_b_class->must_have_unique_name, 0);
-
- return g_str_equal (proxy_a->object_path, proxy_b->object_path) &&
- g_str_equal (proxy_a->bus_name, proxy_b->bus_name);
-}
-
-gboolean
-empathy_check_available_state (void)
-{
- TpConnectionPresenceType presence;
- EmpathyPresenceManager *presence_mgr;
-
- presence_mgr = empathy_presence_manager_dup_singleton ();
- presence = empathy_presence_manager_get_state (presence_mgr);
- g_object_unref (presence_mgr);
-
- if (presence != TP_CONNECTION_PRESENCE_TYPE_AVAILABLE &&
- presence != TP_CONNECTION_PRESENCE_TYPE_UNSET)
- return FALSE;
-
- return TRUE;
-}
-
gint
empathy_uint_compare (gconstpointer a,
gconstpointer b)
return *(guint *) a - *(guint *) b;
}
-gchar *
-empathy_protocol_icon_name (const gchar *protocol)
-{
- if (!tp_strdiff (protocol, "yahoojp"))
- /* Yahoo Japan uses the same icon as Yahoo */
- protocol = "yahoo";
- else if (!tp_strdiff (protocol, "simple"))
- /* SIMPLE uses the same icon as SIP */
- protocol = "sip";
- else if (!tp_strdiff (protocol, "sms"))
- return g_strdup ("phone");
-
- return g_strdup_printf ("im-%s", protocol);
-}
-
GType
empathy_type_dbus_ao (void)
{
return t;
}
-const char *
-empathy_protocol_name_to_display_name (const gchar *proto_name)
-{
- int i;
- static struct {
- const gchar *proto;
- const gchar *display;
- gboolean translated;
- } names[] = {
- { "jabber", "Jabber", FALSE },
- { "msn", "Windows Live (MSN)", FALSE, },
- { "local-xmpp", N_("People Nearby"), TRUE },
- { "irc", "IRC", FALSE },
- { "icq", "ICQ", FALSE },
- { "aim", "AIM", FALSE },
- { "yahoo", "Yahoo!", FALSE },
- { "yahoojp", N_("Yahoo! Japan"), TRUE },
- { "groupwise", "GroupWise", FALSE },
- { "sip", "SIP", FALSE },
- { NULL, NULL }
- };
-
- for (i = 0; names[i].proto != NULL; i++)
- {
- if (!tp_strdiff (proto_name, names[i].proto))
- {
- if (names[i].translated)
- return gettext (names[i].display);
- else
- return names[i].display;
- }
- }
-
- return proto_name;
-}
-
-const char *
-empathy_service_name_to_display_name (const gchar *service_name)
-{
- int i;
- static struct {
- const gchar *service;
- const gchar *display;
- gboolean translated;
- } names[] = {
- { "google-talk", N_("Google Talk"), FALSE },
- { "facebook", N_("Facebook Chat"), TRUE },
- { NULL, NULL }
- };
-
- for (i = 0; names[i].service != NULL; i++)
- {
- if (!tp_strdiff (service_name, names[i].service))
- {
- if (names[i].translated)
- return gettext (names[i].display);
- else
- return names[i].display;
- }
- }
-
- return service_name;
-}
-
gboolean
empathy_account_manager_get_accounts_connected (gboolean *connecting)
{
manager = tp_account_manager_dup ();
- if (G_UNLIKELY (!tp_account_manager_is_prepared (manager,
+ if (G_UNLIKELY (!tp_proxy_is_prepared (manager,
TP_ACCOUNT_MANAGER_FEATURE_CORE)))
g_critical (G_STRLOC ": %s called before AccountManager ready", G_STRFUNC);
- accounts = tp_account_manager_get_valid_accounts (manager);
+ accounts = tp_account_manager_dup_valid_accounts (manager);
for (l = accounts; l != NULL; l = l->next)
{
break;
}
- g_list_free (accounts);
+ g_list_free_full (accounts, g_object_unref);
g_object_unref (manager);
if (connecting != NULL)
return out_connected;
}
-/* Change the RequestedPresence of a newly created account to ensure that it
- * is actually connected. */
-void
-empathy_connect_new_account (TpAccount *account,
- TpAccountManager *account_manager)
-{
- TpConnectionPresenceType presence;
- gchar *status, *message;
-
- /* only force presence if presence was offline, unknown or unset */
- presence = tp_account_get_requested_presence (account, NULL, NULL);
- switch (presence)
- {
- case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
- case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
- case TP_CONNECTION_PRESENCE_TYPE_UNSET:
- presence = tp_account_manager_get_most_available_presence (
- account_manager, &status, &message);
-
- if (presence == TP_CONNECTION_PRESENCE_TYPE_OFFLINE)
- /* Global presence is offline; we force it so user doesn't have to
- * manually change the presence to connect his new account. */
- presence = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
-
- tp_account_request_presence_async (account, presence,
- status, NULL, NULL, NULL);
-
- g_free (status);
- g_free (message);
- break;
-
- case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
- case TP_CONNECTION_PRESENCE_TYPE_AWAY:
- case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
- case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
- case TP_CONNECTION_PRESENCE_TYPE_BUSY:
- case TP_CONNECTION_PRESENCE_TYPE_ERROR:
- default:
- /* do nothing if the presence is not offline */
- break;
- }
-}
-
/* Translate Folks' general presence type to the Tp presence type */
TpConnectionPresenceType
empathy_folks_presence_type_to_tp (FolksPresenceType type)
account = tpf_persona_store_get_account (persona_store);
conn_cur = tp_account_get_connection (account);
if (conn_cur == connection)
- result = persona_store;
+ result = g_object_ref (persona_store);
+
+ g_clear_object (&persona_store);
}
g_clear_object (&iter);
}
}
gboolean
-empathy_connection_can_alias_personas (TpConnection *connection)
+empathy_connection_can_alias_personas (TpConnection *connection,
+ FolksIndividual *individual)
{
gboolean retval;
- FolksPersonaStore *persona_store;
g_return_val_if_fail (TP_IS_CONNECTION (connection), FALSE);
TP_CONNECTION_STATUS_CONNECTED)
return FALSE;
- persona_store = FOLKS_PERSONA_STORE (
- empathy_dup_persona_store_for_connection (connection));
-
- retval = (folks_persona_store_get_can_alias_personas (persona_store) ==
- FOLKS_MAYBE_BOOL_TRUE);
-
- g_clear_object (&persona_store);
+ retval = check_writeable_property (connection, individual, "alias");
return retval;
}
gboolean
-empathy_connection_can_group_personas (TpConnection *connection)
+empathy_connection_can_group_personas (TpConnection *connection,
+ FolksIndividual *individual)
{
gboolean retval;
- FolksPersonaStore *persona_store;
g_return_val_if_fail (TP_IS_CONNECTION (connection), FALSE);
TP_CONNECTION_STATUS_CONNECTED)
return FALSE;
- persona_store = FOLKS_PERSONA_STORE (
- empathy_dup_persona_store_for_connection (connection));
-
- retval = (folks_persona_store_get_can_group_personas (persona_store) ==
- FOLKS_MAYBE_BOOL_TRUE);
-
- g_clear_object (&persona_store);
+ retval = check_writeable_property (connection, individual, "groups");
return retval;
}
return money;
}
-gboolean
-empathy_account_has_uri_scheme_tel (TpAccount *account)
+/* Return the TpContact on @conn associated with @individual, if any */
+TpContact *
+empathy_get_tp_contact_for_individual (FolksIndividual *individual,
+ TpConnection *conn)
{
- const gchar * const * uri_schemes;
- guint i;
+ TpContact *contact = NULL;
+ GeeSet *personas;
+ GeeIterator *iter;
- uri_schemes = tp_account_get_uri_schemes (account);
- if (uri_schemes == NULL)
- return FALSE;
+ personas = folks_individual_get_personas (individual);
+ iter = gee_iterable_iterator (GEE_ITERABLE (personas));
+ while (contact == NULL && gee_iterator_next (iter))
+ {
+ TpfPersona *persona = gee_iterator_get (iter);
+ TpConnection *contact_conn;
+ TpContact *contact_cur = NULL;
- for (i = 0; uri_schemes[i] != NULL; i++)
+ if (TPF_IS_PERSONA (persona))
+ {
+ contact_cur = tpf_persona_get_contact (persona);
+ if (contact_cur != NULL)
+ {
+ contact_conn = tp_contact_get_connection (contact_cur);
+
+ if (!tp_strdiff (tp_proxy_get_object_path (contact_conn),
+ tp_proxy_get_object_path (conn)))
+ contact = contact_cur;
+ }
+ }
+
+ g_clear_object (&persona);
+ }
+ g_clear_object (&iter);
+
+ return contact;
+}
+
+static gboolean
+properties_contains (gchar **list,
+ gint length,
+ const gchar *property)
+{
+ gint i;
+
+ for (i = 0; i < length; i++)
{
- if (!tp_strdiff (uri_schemes[i], "tel"))
- return TRUE;
+ if (!tp_strdiff (list[i], property))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+check_writeable_property (TpConnection *connection,
+ FolksIndividual *individual,
+ gchar *property)
+{
+ gchar **properties;
+ gint prop_len;
+ gboolean retval = FALSE;
+ GeeSet *personas;
+ GeeIterator *iter;
+ FolksPersonaStore *persona_store;
+
+ persona_store = FOLKS_PERSONA_STORE (
+ empathy_dup_persona_store_for_connection (connection));
+
+ properties =
+ folks_persona_store_get_always_writeable_properties (persona_store,
+ &prop_len);
+ retval = properties_contains (properties, prop_len, property);
+ if (retval == TRUE)
+ goto out;
+
+ /* Lets see if the Individual contains a Persona with the given property */
+ personas = folks_individual_get_personas (individual);
+ iter = gee_iterable_iterator (GEE_ITERABLE (personas));
+ while (!retval && gee_iterator_next (iter))
+ {
+ FolksPersona *persona = gee_iterator_get (iter);
+
+ properties =
+ folks_persona_get_writeable_properties (persona, &prop_len);
+ retval = properties_contains (properties, prop_len, property);
+
+ g_clear_object (&persona);
+
+ if (retval == TRUE)
+ break;
}
+ g_clear_object (&iter);
+
+out:
+ g_clear_object (&persona_store);
+ return retval;
+}
+
+/* Calculate whether the Individual can do audio or video calls.
+ * FIXME: We can remove this once libfolks has grown capabilities support
+ * again: bgo#626179. */
+void
+empathy_individual_can_audio_video_call (FolksIndividual *individual,
+ gboolean *can_audio_call,
+ gboolean *can_video_call,
+ EmpathyContact **out_contact)
+{
+ GeeSet *personas;
+ GeeIterator *iter;
+ gboolean can_audio = FALSE, can_video = FALSE;
+
+ personas = folks_individual_get_personas (individual);
+ iter = gee_iterable_iterator (GEE_ITERABLE (personas));
+ while (gee_iterator_next (iter))
+ {
+ FolksPersona *persona = gee_iterator_get (iter);
+ TpContact *tp_contact;
+
+ if (!empathy_folks_persona_is_interesting (persona))
+ goto while_finish;
+
+ tp_contact = tpf_persona_get_contact (TPF_PERSONA (persona));
+ if (tp_contact != NULL)
+ {
+ EmpathyContact *contact;
+
+ contact = empathy_contact_dup_from_tp_contact (tp_contact);
+ empathy_contact_set_persona (contact, persona);
+
+ can_audio = can_audio || empathy_contact_get_capabilities (contact) &
+ EMPATHY_CAPABILITIES_AUDIO;
+ can_video = can_video || empathy_contact_get_capabilities (contact) &
+ EMPATHY_CAPABILITIES_VIDEO;
+
+ if (out_contact != NULL)
+ *out_contact = g_object_ref (contact);
+
+ g_object_unref (contact);
+ }
+
+while_finish:
+ g_clear_object (&persona);
+
+ if (can_audio && can_video)
+ break;
+ }
+
+ g_clear_object (&iter);
+
+ if (can_audio_call != NULL)
+ *can_audio_call = can_audio;
+
+ if (can_video_call != NULL)
+ *can_video_call = can_video;
+}
+
+gboolean
+empathy_client_types_contains_mobile_device (const GStrv types) {
+ int i;
+
+ if (types == NULL)
+ return FALSE;
+
+ for (i = 0; types[i] != NULL; i++)
+ if (!tp_strdiff (types[i], "phone") || !tp_strdiff (types[i], "handheld"))
+ return TRUE;
return FALSE;
}
+
+static FolksIndividual *
+create_individual_from_persona (FolksPersona *persona)
+{
+ GeeSet *personas;
+ FolksIndividual *individual;
+
+ personas = GEE_SET (
+ gee_hash_set_new (FOLKS_TYPE_PERSONA, g_object_ref, g_object_unref,
+ NULL, NULL, NULL, NULL, NULL, NULL));
+
+ gee_collection_add (GEE_COLLECTION (personas), persona);
+
+ individual = folks_individual_new (personas);
+
+ g_clear_object (&personas);
+
+ return individual;
+}
+
+/* Look for a FolksIndividual containing @contact as one of his persona
+ * and create one if needed */
+FolksIndividual *
+empathy_ensure_individual_from_tp_contact (TpContact *contact)
+{
+ TpfPersona *persona;
+ FolksIndividual *individual;
+
+ persona = tpf_persona_dup_for_contact (contact);
+ if (persona == NULL)
+ {
+ DEBUG ("Failed to get a persona for %s",
+ tp_contact_get_identifier (contact));
+ return NULL;
+ }
+
+ individual = folks_persona_get_individual (FOLKS_PERSONA (persona));
+
+ if (individual != NULL)
+ g_object_ref (individual);
+ else
+ individual = create_individual_from_persona (FOLKS_PERSONA (persona));
+
+ g_object_unref (persona);
+ return individual;
+}
+
+const gchar * const *
+empathy_individual_get_client_types (FolksIndividual *individual)
+{
+ GeeSet *personas;
+ GeeIterator *iter;
+ const gchar * const *types = NULL;
+ FolksPresenceType presence_type = FOLKS_PRESENCE_TYPE_UNSET;
+
+ personas = folks_individual_get_personas (individual);
+ iter = gee_iterable_iterator (GEE_ITERABLE (personas));
+ while (gee_iterator_next (iter))
+ {
+ FolksPresenceDetails *presence;
+ FolksPersona *persona = gee_iterator_get (iter);
+
+ /* We only want personas which have presence and a TpContact */
+ if (!empathy_folks_persona_is_interesting (persona))
+ goto while_finish;
+
+ presence = FOLKS_PRESENCE_DETAILS (persona);
+
+ if (folks_presence_details_typecmp (
+ folks_presence_details_get_presence_type (presence),
+ presence_type) > 0)
+ {
+ TpContact *tp_contact;
+
+ presence_type = folks_presence_details_get_presence_type (presence);
+
+ tp_contact = tpf_persona_get_contact (TPF_PERSONA (persona));
+ if (tp_contact != NULL)
+ types = tp_contact_get_client_types (tp_contact);
+ }
+
+while_finish:
+ g_clear_object (&persona);
+ }
+ g_clear_object (&iter);
+
+ return types;
+}
+
+GVariant *
+empathy_asv_to_vardict (const GHashTable *asv)
+{
+ return empathy_boxed_to_variant (TP_HASH_TYPE_STRING_VARIANT_MAP, "a{sv}",
+ (gpointer) asv);
+}
+
+GVariant *
+empathy_boxed_to_variant (GType gtype,
+ const gchar *variant_type,
+ gpointer boxed)
+{
+ GValue v = G_VALUE_INIT;
+ GVariant *ret;
+
+ g_return_val_if_fail (boxed != NULL, NULL);
+
+ g_value_init (&v, gtype);
+ g_value_set_boxed (&v, boxed);
+
+ ret = dbus_g_value_build_g_variant (&v);
+ g_return_val_if_fail (!tp_strdiff (g_variant_get_type_string (ret),
+ variant_type), NULL);
+
+ g_value_unset (&v);
+
+ return g_variant_ref_sink (ret);
+}