]> git.0d.be Git - empathy.git/blobdiff - libempathy/empathy-utils.c
Merge branch 'gnome-3-8'
[empathy.git] / libempathy / empathy-utils.c
index e334d6ca7f88d808d6aabc31534571cceb003899..0efe3a1807ebd959cbea8353fa7e38402a098ad7 100644 (file)
  */
 
 #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 <dbus/dbus-protocol.h>
+#include <math.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 "empathy-utils.h"
-#include "empathy-contact-manager.h"
-#include "empathy-individual-manager.h"
+#include "empathy-client-factory.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"
@@ -79,10 +63,22 @@ static struct {
   { 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)
 {
   static gboolean initialized = FALSE;
+  TpAccountManager *am;
+  EmpathyClientFactory *factory;
 
   if (initialized)
     return;
@@ -103,72 +99,46 @@ empathy_init (void)
   emp_cli_init ();
 
   initialized = TRUE;
-}
 
-gchar *
-empathy_substring (const gchar *str,
-    gint start,
-    gint end)
-{
-  return g_strndup (str + start, end - start);
-}
+  factory = empathy_client_factory_dup ();
+  am = tp_account_manager_new_with_factory (TP_SIMPLE_CLIENT_FACTORY (factory));
+  tp_account_manager_set_default (am);
 
-gint
-empathy_strcasecmp (const gchar *s1,
-    const gchar *s2)
-{
-  return empathy_strncasecmp (s1, s2, -1);
-}
-
-gint
-empathy_strncasecmp (const gchar *s1,
-    const gchar *s2,
-    gsize n)
-{
-  gchar *u1, *u2;
-  gint ret_val;
-
-  u1 = g_utf8_casefold (s1, n);
-  u2 = g_utf8_casefold (s2, n);
-
-  ret_val = g_utf8_collate (u1, u2);
-  g_free (u1);
-  g_free (u2);
-
-  return ret_val;
+  g_object_unref (factory);
+  g_object_unref (am);
 }
 
 gboolean
-empathy_xml_validate (xmlDoc      *doc,
-    const gchar *dtd_filename)
+empathy_xml_validate_from_resource (xmlDoc *doc,
+    const gchar *dtd_resourcename)
 {
-  gchar *path;
-  xmlChar *escaped;
+  GBytes *resourcecontents;
+  gconstpointer resourcedata;
+  gsize resourcesize;
+  xmlParserInputBufferPtr buffer;
   xmlValidCtxt  cvp;
   xmlDtd *dtd;
+  GError *error = NULL;
   gboolean ret;
 
-  path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "libempathy",
-         dtd_filename, NULL);
-  if (!g_file_test (path, G_FILE_TEST_EXISTS))
+  DEBUG ("Loading dtd resource %s", dtd_resourcename);
+
+  resourcecontents = g_resources_lookup_data (dtd_resourcename, G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
+  if (error != NULL)
     {
-      g_free (path);
-      path = g_build_filename (DATADIR, "empathy", dtd_filename, NULL);
+      g_warning ("Unable to load dtd resource '%s': %s", dtd_resourcename, error->message);
+      g_error_free (error);
+      return FALSE;
     }
-
-  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);
+  resourcedata = g_bytes_get_data (resourcecontents, &resourcesize);
+  buffer = xmlParserInputBufferCreateStatic (resourcedata, resourcesize, XML_CHAR_ENCODING_UTF8);
 
   memset (&cvp, 0, sizeof (cvp));
-  dtd = xmlParseDTD (NULL, escaped);
+  dtd = xmlIOParseDTD (NULL, buffer, XML_CHAR_ENCODING_UTF8);
   ret = xmlValidateDtd (&cvp, doc, dtd);
 
-  xmlFree (escaped);
   xmlFreeDtd (dtd);
+  g_bytes_unref (resourcecontents);
 
   return ret;
 }
@@ -364,7 +334,7 @@ create_errors_to_message_hash (void)
   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"));
@@ -381,6 +351,10 @@ create_errors_to_message_hash (void)
     _("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;
 }
@@ -405,7 +379,7 @@ empathy_account_get_error_message (TpAccount *account,
 {
   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);
@@ -422,11 +396,11 @@ empathy_account_get_error_message (TpAccount *account,
   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);
 }
 
@@ -448,36 +422,6 @@ empathy_file_lookup (const gchar *filename, const gchar *subdir)
   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)
 {
@@ -547,6 +491,13 @@ empathy_protocol_name_to_display_name (const gchar *proto_name)
     { "yahoojp", N_("Yahoo! Japan"), TRUE },
     { "groupwise", "GroupWise", FALSE },
     { "sip", "SIP", FALSE },
+    { "gadugadu", "Gadu-Gadu", FALSE },
+    { "mxit", "Mxit", FALSE },
+    { "myspace", "Myspace", FALSE },
+    { "sametime", "Sametime", FALSE },
+    { "skype-dbus", "Skype (D-BUS)", FALSE },
+    { "skype-x11", "Skype (X11)", FALSE },
+    { "zephyr", "Zephyr", FALSE },
     { NULL, NULL }
   };
 
@@ -592,36 +543,6 @@ empathy_service_name_to_display_name (const gchar *service_name)
   return service_name;
 }
 
-/* Note: this function depends on the account manager having its core feature
- * prepared. */
-TpAccount *
-empathy_get_account_for_connection (TpConnection *connection)
-{
-  TpAccountManager *manager;
-  TpAccount *account = NULL;
-  GList *accounts, *l;
-
-  manager = tp_account_manager_dup ();
-
-  accounts = tp_account_manager_get_valid_accounts (manager);
-
-  for (l = accounts; l != NULL; l = l->next)
-    {
-      TpAccount *a = l->data;
-
-      if (tp_account_get_connection (a) == connection)
-        {
-          account = a;
-          break;
-        }
-    }
-
-  g_list_free (accounts);
-  g_object_unref (manager);
-
-  return account;
-}
-
 gboolean
 empathy_account_manager_get_accounts_connected (gboolean *connecting)
 {
@@ -636,7 +557,7 @@ empathy_account_manager_get_accounts_connected (gboolean *connecting)
           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)
     {
@@ -652,7 +573,7 @@ empathy_account_manager_get_accounts_connected (gboolean *connecting)
         break;
     }
 
-  g_list_free (accounts);
+  g_list_free_full (accounts, g_object_unref);
   g_object_unref (manager);
 
   if (connecting != NULL)
@@ -768,13 +689,22 @@ empathy_contact_dup_from_folks_individual (FolksIndividual *individual)
           TpContact *tp_contact;
 
           tp_contact = tpf_persona_get_contact (persona);
-          contact = empathy_contact_dup_from_tp_contact (tp_contact);
-          empathy_contact_set_persona (contact, FOLKS_PERSONA (persona));
+          if (tp_contact != NULL)
+            {
+              contact = empathy_contact_dup_from_tp_contact (tp_contact);
+              empathy_contact_set_persona (contact, FOLKS_PERSONA (persona));
+            }
         }
       g_clear_object (&persona);
     }
   g_clear_object (&iter);
 
+  if (contact == NULL)
+    {
+      DEBUG ("Can't create an EmpathyContact for Individual %s",
+          folks_individual_get_id (individual));
+    }
+
   return contact;
 }
 
@@ -830,6 +760,10 @@ empathy_connection_can_add_personas (TpConnection *connection)
 
   g_return_val_if_fail (TP_IS_CONNECTION (connection), FALSE);
 
+  if (tp_connection_get_status (connection, NULL) !=
+          TP_CONNECTION_STATUS_CONNECTED)
+      return FALSE;
+
   persona_store = FOLKS_PERSONA_STORE (
       empathy_dup_persona_store_for_connection (connection));
 
@@ -842,39 +776,35 @@ empathy_connection_can_add_personas (TpConnection *connection)
 }
 
 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);
 
-  persona_store = FOLKS_PERSONA_STORE (
-      empathy_dup_persona_store_for_connection (connection));
+  if (tp_connection_get_status (connection, NULL) !=
+          TP_CONNECTION_STATUS_CONNECTED)
+      return FALSE;
 
-  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);
 
-  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);
+  if (tp_connection_get_status (connection, NULL) !=
+          TP_CONNECTION_STATUS_CONNECTED)
+      return FALSE;
 
-  g_clear_object (&persona_store);
+  retval = check_writeable_property (connection, individual, "groups");
 
   return retval;
 }
@@ -1005,3 +935,329 @@ empathy_format_currency (gint amount,
 
   return money;
 }
+
+gboolean
+empathy_account_has_uri_scheme_tel (TpAccount *account)
+{
+  const gchar * const * uri_schemes;
+  guint i;
+
+  uri_schemes = tp_account_get_uri_schemes (account);
+  if (uri_schemes == NULL)
+    return FALSE;
+
+  for (i = 0; uri_schemes[i] != NULL; i++)
+    {
+      if (!tp_strdiff (uri_schemes[i], "tel"))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+/* Return the TpContact on @conn associated with @individual, if any */
+TpContact *
+empathy_get_tp_contact_for_individual (FolksIndividual *individual,
+    TpConnection *conn)
+{
+  TpContact *contact = NULL;
+  GeeSet *personas;
+  GeeIterator *iter;
+
+  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;
+
+      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 (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;
+}
+
+FolksIndividual *
+empathy_create_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 = create_individual_from_persona (FOLKS_PERSONA (persona));
+
+  g_object_unref (persona);
+  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);
+}