[folks] Tidy up UIDs and IIDs in the backends



commit 6e9544b47635d2128549241709c2cc169943a278
Author: Philip Withnall <philip withnall collabora co uk>
Date:   Tue Jul 20 17:51:46 2010 +0100

    Tidy up UIDs and IIDs in the backends
    
    IIDs are now defined as unique within a backend, and formatted to make them
    most useful for linking. UIDs are now defined as unique across all backends,
    and formatted as Backend.name:PersonaStore.id:Persona ID.

 backends/key-file/kf-persona.vala   |   11 ++--
 backends/telepathy/tpf-persona.vala |   17 +++---
 folks/individual-aggregator.vala    |   12 ++--
 folks/persona.vala                  |  102 +++++++++++++++++++++++++++++++++-
 4 files changed, 119 insertions(+), 23 deletions(-)
---
diff --git a/backends/key-file/kf-persona.vala b/backends/key-file/kf-persona.vala
index a40dd0a..8f755f1 100644
--- a/backends/key-file/kf-persona.vala
+++ b/backends/key-file/kf-persona.vala
@@ -80,13 +80,12 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
    * Create a new persona for the { link PersonaStore} `store`, representing
    * the Persona given by the group `uid` in the key file `key_file`.
    */
-  public Persona (KeyFile key_file, string uid, Folks.PersonaStore store)
+  public Persona (KeyFile key_file, string id, Folks.PersonaStore store)
     {
-      string iid = "key-file:" + uid;
       string[] linkable_properties = { "im-addresses" };
 
-      Object (iid: iid,
-              uid: uid,
+      Object (iid: id,
+              uid: this.build_uid ("key-file", store.id, id),
               store: store,
               linkable_properties: linkable_properties);
 
@@ -97,10 +96,10 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
       /* Load the IM addresses from the key file */
       try
         {
-          string[] keys = this.key_file.get_keys (uid);
+          string[] keys = this.key_file.get_keys (id);
           foreach (string protocol in keys)
             {
-              string[] im_addresses = this.key_file.get_string_list (uid,
+              string[] im_addresses = this.key_file.get_string_list (id,
                   protocol);
 
               /* FIXME: We have to convert our nice efficient string[] to a
diff --git a/backends/telepathy/tpf-persona.vala b/backends/telepathy/tpf-persona.vala
index 14d7d63..f6fdf79 100644
--- a/backends/telepathy/tpf-persona.vala
+++ b/backends/telepathy/tpf-persona.vala
@@ -173,16 +173,13 @@ public class Tpf.Persona : Folks.Persona,
       /* FIXME: There is the possibility of a crash in the error condition below
        * due to bgo#604299, where the C self variable isn't initialised until we
        * chain up to the Object constructor, below. */
-      var uid = contact.get_identifier ();
-      if (uid == null || uid == "")
+      unowned string id = contact.get_identifier ();
+      if (id == null || id == "")
         throw new Tpf.PersonaError.INVALID_ARGUMENT ("contact has an " +
-            "invalid UID");
+            "invalid ID");
 
       var account = account_for_connection (contact.get_connection ());
-      var account_id = ((Proxy) account).object_path;
-      /* this isn't meant to convey any real information, so no need to escape
-       * existing delimiters */
-      var iid = "telepathy:" + account_id + ":" + uid;
+      string uid = this.build_uid ("telepathy", account.get_protocol (), id);
 
       var alias = contact.get_alias ();
       if (alias == null || alias.strip () == "")
@@ -190,7 +187,11 @@ public class Tpf.Persona : Folks.Persona,
 
       Object (alias: alias,
               contact: contact,
-              iid: iid,
+              /* FIXME: This IID format should be moved out to the IMable
+               * interface along with the code in
+               * Kf.Persona.linkable_property_to_links(), but that depends on
+               * bgo#624842 being fixed. */
+              iid: account.get_protocol () + ":" + id,
               uid: uid,
               store: store);
 
diff --git a/folks/individual-aggregator.vala b/folks/individual-aggregator.vala
index 9f900e0..4f79a79 100644
--- a/folks/individual-aggregator.vala
+++ b/folks/individual-aggregator.vala
@@ -213,11 +213,11 @@ public class Folks.IndividualAggregator : Object
           /* If we don't trust the PersonaStore at all, we can't link the
            * Persona to any existing Individual */
           if (trust_level != PersonaStoreTrust.NONE)
-            candidate_ind = this.link_map.lookup (persona.uid);
+            candidate_ind = this.link_map.lookup (persona.iid);
 
           if (candidate_ind != null)
             {
-              /* The Persona's UID matches a linkable field which is already in
+              /* The Persona's IID matches a linkable field which is already in
                * the link map, so we add the new Persona to that Individual. */
               GLib.List<unowned Persona> personas =
                   candidate_ind.personas.copy ();
@@ -237,12 +237,12 @@ public class Folks.IndividualAggregator : Object
               new_individuals.prepend (candidate_ind);
               this.individuals.insert (candidate_ind.id, candidate_ind);
 
-              /* Only add the Persona to the link map if we trust its UID. */
+              /* Only add the Persona to the link map if we trust its IID. */
               if (trust_level != PersonaStoreTrust.NONE)
-                this.link_map.insert (persona.uid, candidate_ind);
+                this.link_map.insert (persona.iid, candidate_ind);
             }
 
-          /* Only allow linking on non-UID properties of the Persona if we fully
+          /* Only allow linking on non-IID properties of the Persona if we fully
            * trust the PersonaStore it came from. */
           if (persona.store.trust_level == PersonaStoreTrust.FULL)
             {
@@ -272,7 +272,7 @@ public class Folks.IndividualAggregator : Object
           PersonaStoreTrust trust_level = persona.store.trust_level;
 
           if (trust_level != PersonaStoreTrust.NONE)
-            this.link_map.remove (persona.uid);
+            this.link_map.remove (persona.iid);
 
           if (trust_level == PersonaStoreTrust.FULL)
             {
diff --git a/folks/persona.vala b/folks/persona.vala
index f79b1dd..4f43e70 100644
--- a/folks/persona.vala
+++ b/folks/persona.vala
@@ -30,20 +30,39 @@ using Folks;
 public abstract class Folks.Persona : Object
 {
   /**
-   * The internal ID used to represent the Persona within its { link Backend}.
+   * The internal ID used to represent the Persona for linking.
    *
-   * This should not be used by client code.
+   * This is opaque, and shouldn't be parsed or considered meaningful by
+   * clients.
+   *
+   * The internal ID should be unique within a backend, but may not be unique
+   * across backends, so that links can be made between Personas with similar
+   * internal IDs.
    */
+  /* For example: jabber:foo xmpp example org or joe example org */
   public string iid { get; construct; }
 
   /**
    * The universal ID used to represent the Persona outside its { link Backend}.
    *
-   * For example: `foo@@xmpp.example.org`.
+   * This is opaque, and should only be parsed by clients using
+   * { link Persona.split_uid}.
    *
    * This is the canonical way to refer to any Persona. It is guaranteed to be
    * unique within the Persona's { link PersonaStore}.
+   *
+   * @see Persona.build_uid
+   * @see Persona.split_uid
    */
+  /* For example: telepathy:jabber:foo xmpp example org or
+   * key-file:relationships.ini:joe example org
+   *
+   * It comprises three components, separated by colons:
+   * # { link Backend.name}
+   * # { link PersonaStore.id}
+   * # Persona identifier
+   * Each component is escaped by replacing all colons with double underscores
+   * before building the UID.*/
   public string uid { get; construct; }
 
   /**
@@ -101,4 +120,81 @@ public abstract class Folks.Persona : Object
        * any linkable properties */
       assert_not_reached ();
     }
+
+  private static string escape_uid_component (string component)
+    {
+      /* Escape colons with backslashes */
+      string escaped = component.replace ("\\", "\\\\");
+      return escaped.replace (":", "\\:");
+    }
+
+  private static string unescape_uid_component (string component)
+    {
+      /* Unescape colons and backslashes */
+      string unescaped = component.replace ("\\:", ":");
+      return unescaped.replace ("\\", "\\\\");
+    }
+
+  /**
+   * Build a UID from the given components.
+   *
+   * Each component is escaped before the UID is built.
+   *
+   * @param backend_name the { link Backend.name}
+   * @param persona_store_id the { link PersonaStore.id}
+   * @param persona_id the Persona identifier (backend-specific)
+   * @return a valid UID
+   * @see Persona.split_uid
+   */
+  public static string build_uid (string backend_name,
+      string persona_store_id, string persona_id)
+    {
+      return "%s:%s:%s".printf (Persona.escape_uid_component (backend_name),
+                                Persona.escape_uid_component (persona_store_id),
+                                Persona.escape_uid_component (persona_id));
+    }
+
+  /**
+   * Split a UID into its component parts.
+   *
+   * Each component is unescaped before being returned. The UID //must// be
+   * correctly formed.
+   *
+   * @param uid a valid UID
+   * @param backend_name the { link Backend.name}
+   * @param persona_store_id the { link PersonaStore.id}
+   * @param persona_id the Persona identifier (backend-specific)
+   * @see Persona.build_uid
+   */
+  public static void split_uid (string uid, out string backend_name,
+      out string persona_store_id, out string persona_id)
+    {
+      assert (uid.validate ());
+
+      size_t backend_name_length = 0, persona_store_id_length = 0;
+      bool escaped = false;
+      for (unowned string i = uid; i.get_char () != '\0'; i = i.next_char ())
+        {
+          if (i.get_char () == '\\')
+            escaped = !escaped;
+          else if (escaped == false && i.get_char () == ':')
+            {
+              if (backend_name_length == 0)
+                backend_name_length = ((char*) i) - ((char*) uid);
+              else
+                persona_store_id_length = ((char*) i) - ((char*) uid);
+            }
+        }
+
+      assert (backend_name_length != 0 && persona_store_id_length != 0);
+
+      backend_name = Persona.unescape_uid_component (
+          uid.ndup (backend_name_length));
+      persona_store_id = Persona.unescape_uid_component (
+          ((string) ((char*) uid + backend_name_length + 1)).ndup (
+              persona_store_id_length));
+      persona_id = Persona.unescape_uid_component (
+          ((string) ((char*) uid + backend_name_length +
+              persona_store_id_length + 2)));
+    }
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]