Commit 2a88b99a authored by Philip Withnall's avatar Philip Withnall Committed by Travis Reitter
Browse files

telepathy: Add support for caching vCard parameters for contacts

Add birthday, full name, e-mail address, phone number and URL support to the
Telepathy object cache so that contacts’ vCards are correctly stored when
the cache is updated.

Closes: bgo#661475
parent 5691fe6d
......@@ -6,6 +6,7 @@ Bugs fixed:
* Bug 657602 — Telepathy backend fails to set Personas' phone numbers from
ContactInfo
* Bug 660937 — [regression] build failure: No package 'gee-1.0' found
* Bug 661475 — string_strip: assertion `self != NULL' failed
API changes:
* Implement PhoneDetails on Tpf.Persona
......
......@@ -37,6 +37,9 @@ using Folks;
* # Alias (`s`)
* # In contact list? (`b`)
* # Avatar file URI (`s`)
* # Birthday date as a Unix timestamp (`s`)
* # Set of e-mail addresses and parameters (`a(sa(ss))`)
* # Full name (`s`)
*
* @since 0.6.0
*/
......@@ -48,7 +51,7 @@ internal class Tpf.PersonaStoreCache : Folks.ObjectCache<Tpf.Persona>
* get_serialised_object_type(). This must be modified whenever that variant
* type or its semantics are changed, since that would necessitate a cache
* refresh. */
private static const uint8 _FILE_FORMAT_VERSION = 1;
private static const uint8 _FILE_FORMAT_VERSION = 2;
internal PersonaStoreCache (PersonaStore store)
{
......@@ -81,6 +84,44 @@ internal class Tpf.PersonaStoreCache : Folks.ObjectCache<Tpf.Persona>
new VariantType.maybe (VariantType.STRING) // Avatar
});
}
else if (object_version == 2 || object_version == uint8.MAX)
{
return new VariantType.tuple ({
VariantType.STRING, // UID
VariantType.STRING, // IID
VariantType.STRING, // ID
VariantType.STRING, // Protocol
new VariantType.array (VariantType.STRING), // Groups
VariantType.BOOLEAN, // Favourite?
VariantType.STRING, // Alias
VariantType.BOOLEAN, // In contact list?
VariantType.BOOLEAN, // Is user?
new VariantType.maybe (VariantType.STRING), // Avatar
new VariantType.maybe (VariantType.INT64), // Birthday
VariantType.STRING, // Full name
new VariantType.array (new VariantType.tuple ({
VariantType.STRING, // E-mail address
new VariantType.array (new VariantType.tuple ({
VariantType.STRING, // Key
VariantType.STRING // Value
})) // Parameters
})), // E-mail addresses
new VariantType.array (new VariantType.tuple ({
VariantType.STRING, // Phone number
new VariantType.array (new VariantType.tuple ({
VariantType.STRING, // Key
VariantType.STRING // Value
})) // Parameters
})), // Phone numbers
new VariantType.array (new VariantType.tuple ({
VariantType.STRING, // URL
new VariantType.array (new VariantType.tuple ({
VariantType.STRING, // Key
VariantType.STRING // Value
})) // Parameters
})) // URLs
});
}
// Unsupported version
return null;
......@@ -91,6 +132,40 @@ internal class Tpf.PersonaStoreCache : Folks.ObjectCache<Tpf.Persona>
return this._FILE_FORMAT_VERSION;
}
private Variant[] serialise_abstract_field_details (
Set<AbstractFieldDetails<string>> field_details_set)
{
Variant[] output_variants = new Variant[field_details_set.size];
uint i = 0;
foreach (var afd in field_details_set)
{
Variant[] parameters = new Variant[afd.parameters.size];
uint f = 0;
foreach (var key in afd.parameters.get_keys ())
{
foreach (var val in afd.parameters.get (key))
{
parameters[f] = new Variant.tuple ({
new Variant.string (key), // Key
new Variant.string (val) // Value
});
}
}
output_variants[i] = new Variant.tuple ({
afd.value, // Variant value (e.g. e-mail address)
new Variant.array (new VariantType.tuple ({
VariantType.STRING, // Key
VariantType.STRING // Value
}), parameters)
});
}
return output_variants;
}
protected override Variant serialise_object (Tpf.Persona persona)
{
// Sort out the groups
......@@ -117,6 +192,17 @@ internal class Tpf.PersonaStoreCache : Folks.ObjectCache<Tpf.Persona>
var avatar_variant = (avatar_file != null) ?
new Variant.string (avatar_file.get_uri ()) : null;
// Birthday
var birthday_variant = (persona.birthday != null) ?
new Variant.int64 (persona.birthday.to_unix ()) : null;
// Sort out the e-mail addresses, phone numbers and URLs
var email_addresses =
this.serialise_abstract_field_details (persona.email_addresses);
var phone_numbers =
this.serialise_abstract_field_details (persona.phone_numbers);
var urls = this.serialise_abstract_field_details (persona.urls);
// Serialise the persona
return new Variant.tuple ({
new Variant.string (persona.uid),
......@@ -128,10 +214,61 @@ internal class Tpf.PersonaStoreCache : Folks.ObjectCache<Tpf.Persona>
new Variant.string (persona.alias),
new Variant.boolean (persona.is_in_contact_list),
new Variant.boolean (persona.is_user),
new Variant.maybe (VariantType.STRING, avatar_variant)
new Variant.maybe (VariantType.STRING, avatar_variant),
new Variant.maybe (VariantType.INT64, birthday_variant),
new Variant.string (persona.full_name),
new Variant.array (new VariantType.tuple ({
VariantType.STRING, // E-mail address
new VariantType.array (new VariantType.tuple ({
VariantType.STRING, // Key
VariantType.STRING // Value
})) // Parameters
}), email_addresses),
new Variant.array (new VariantType.tuple ({
VariantType.STRING, // Phone number
new VariantType.array (new VariantType.tuple ({
VariantType.STRING, // Key
VariantType.STRING // Value
})) // Parameters
}), phone_numbers),
new Variant.array (new VariantType.tuple ({
VariantType.STRING, // URL
new VariantType.array (new VariantType.tuple ({
VariantType.STRING, // Key
VariantType.STRING // Value
})) // Parameters
}), urls)
});
}
private delegate void AfdDeserialisationCallback (string val,
HashMultiMap<string, string> parameters);
private void deserialise_abstract_field_details (Variant input_variants,
AfdDeserialisationCallback cb)
{
for (uint i = 0; i < input_variants.n_children (); i++)
{
var input_variant = input_variants.get_child_value (i);
var val = input_variant.get_child_value (0).get_string ();
var parameters = new HashMultiMap<string, string> ();
var params_variants = input_variant.get_child_value (1);
for (uint f = 0; f < params_variants.n_children (); f++)
{
var params_variant = params_variants.get_child_value (f);
parameters.set (
params_variant.get_child_value (0).get_string (),
params_variant.get_child_value (1).get_string ());
}
// Output
cb (val, parameters);
}
}
protected override Tpf.Persona deserialise_object (Variant variant,
uint8 object_version)
{
......@@ -159,9 +296,57 @@ internal class Tpf.PersonaStoreCache : Folks.ObjectCache<Tpf.Persona>
new FileIcon (File.new_for_uri (avatar_variant.get_string ())) :
null;
// Deserialise the birthday
DateTime? birthday = null;
if (object_version == 2)
{
var birthday_variant = variant.get_child_value (10).get_maybe ();
if (birthday_variant != null)
{
birthday =
new DateTime.from_unix_utc (birthday_variant.get_int64 ());
}
}
var full_name = "";
if (object_version == 2)
{
full_name = variant.get_child_value (11).get_string();
}
var email_address_set = new HashSet<EmailFieldDetails> (
(GLib.HashFunc) EmailFieldDetails.hash,
(GLib.EqualFunc) EmailFieldDetails.equal);
var phone_number_set = new HashSet<PhoneFieldDetails> (
(GLib.HashFunc) PhoneFieldDetails.hash,
(GLib.EqualFunc) PhoneFieldDetails.equal);
var url_set = new HashSet<UrlFieldDetails> (
(GLib.HashFunc) UrlFieldDetails.hash,
(GLib.EqualFunc) UrlFieldDetails.equal);
if (object_version == 2)
{
this.deserialise_abstract_field_details (variant.get_child_value (12),
(v, p) =>
{
email_address_set.add (new EmailFieldDetails (v, p));
});
this.deserialise_abstract_field_details (variant.get_child_value (13),
(v, p) =>
{
phone_number_set.add (new PhoneFieldDetails (v, p));
});
this.deserialise_abstract_field_details (variant.get_child_value (14),
(v, p) =>
{
url_set.add (new UrlFieldDetails (v, p));
});
}
return new Tpf.Persona.from_cache (this._store, uid, iid, display_id,
im_protocol, group_set, is_favourite, alias, is_in_contact_list,
is_user, avatar);
is_user, avatar, birthday, full_name, email_address_set,
phone_number_set, url_set);
}
}
......
......@@ -871,6 +871,16 @@ public class Tpf.Persona : Folks.Persona,
* @param is_user Whether the persona is the user.
* @param avatar The icon for the persona's cached avatar, or `null` if they
* have no avatar.
* @param birthday The date/time of birth of the persona, or `null` if it's
* unknown.
* @param full_name The persona's full name, or the empty string if it's
* unknown.
* @param email_addresses A set of the persona's e-mail addresses, which may
* be empty (but may not be `null`).
* @param phone_numbers A set of the persona's phone numbers, which may be
* empty (but may not be `null`).
* @param urls A set of the persona's URLs, which may be empty (but may not be
* `null`).
* @return A new {@link Tpf.Persona} representing the cached persona.
*
* @since 0.6.0
......@@ -878,7 +888,9 @@ public class Tpf.Persona : Folks.Persona,
internal Persona.from_cache (PersonaStore store, string uid, string iid,
string im_address, string protocol, HashSet<string> groups,
bool is_favourite, string alias, bool is_in_contact_list, bool is_user,
LoadableIcon? avatar)
LoadableIcon? avatar, DateTime? birthday, string full_name,
HashSet<EmailFieldDetails> email_addresses,
HashSet<PhoneFieldDetails> phone_numbers, HashSet<UrlFieldDetails> urls)
{
Object (contact: null,
display_id: im_address,
......@@ -901,6 +913,18 @@ public class Tpf.Persona : Folks.Persona,
this._groups = groups;
this._groups_ro = this._groups.read_only_view;
// E-mail addresses
this._email_addresses = email_addresses;
this._email_addresses_ro = this._email_addresses.read_only_view;
// Phone numbers
this._phone_numbers = phone_numbers;
this._phone_numbers_ro = this._phone_numbers.read_only_view;
// URLs
this._urls = urls;
this._urls_ro = this._urls.read_only_view;
// Other properties
if (alias == null)
{
......@@ -908,10 +932,18 @@ public class Tpf.Persona : Folks.Persona,
alias = "";
}
if (full_name == null)
{
/* Deal with badly-behaved callers */
full_name = "";
}
this._alias = alias;
this._is_favourite = is_favourite;
this.is_in_contact_list = is_in_contact_list;
this._avatar = avatar;
this._birthday = birthday;
this._full_name = full_name;
// Make the persona appear offline
this.presence_type = PresenceType.OFFLINE;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment