2 * Copyright (C) 2012 Intel Corporation
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 * The D-Bus IPC binding for folks.h. Maps FolksIndividual to and
22 * from the D-Bus dict described in pim-manager-api.txt.
25 #include "individual-traits.h"
26 #include "persona-details.h"
29 SE_GLIB_TYPE(GDateTime, g_date_time)
30 SE_GOBJECT_TYPE(GTimeZone)
31 SE_GOBJECT_TYPE(GObject)
35 typedef GValueDynTypedCXX<GDateTime *, g_date_time_get_type> GValueDateTimeCXX;
37 static const char * const CONTACT_HASH_FULL_NAME = "full-name";
38 static const char * const CONTACT_HASH_NICKNAME = "nickname";
39 static const char * const CONTACT_HASH_STRUCTURED_NAME = "structured-name";
40 static const char * const CONTACT_HASH_STRUCTURED_NAME_FAMILY = "family";
41 static const char * const CONTACT_HASH_STRUCTURED_NAME_GIVEN = "given";
42 static const char * const CONTACT_HASH_STRUCTURED_NAME_ADDITIONAL = "additional";
43 static const char * const CONTACT_HASH_STRUCTURED_NAME_PREFIXES = "prefixes";
44 static const char * const CONTACT_HASH_STRUCTURED_NAME_SUFFIXES = "suffixes";
45 static const char * const CONTACT_HASH_ALIAS = "alias";
46 static const char * const CONTACT_HASH_PHOTO = "photo";
47 static const char * const CONTACT_HASH_BIRTHDAY = "birthday";
48 static const char * const CONTACT_HASH_LOCATION = "location";
49 static const char * const CONTACT_HASH_EMAILS = "emails";
50 static const char * const CONTACT_HASH_PHONES = "phones";
51 static const char * const CONTACT_HASH_URLS = "urls";
52 static const char * const CONTACT_HASH_NOTES = "notes";
53 static const char * const CONTACT_HASH_ADDRESSES = "addresses";
54 static const char * const CONTACT_HASH_ADDRESSES_PO_BOX = "po-box";
55 static const char * const CONTACT_HASH_ADDRESSES_EXTENSION = "extension";
56 static const char * const CONTACT_HASH_ADDRESSES_STREET = "street";
57 static const char * const CONTACT_HASH_ADDRESSES_LOCALITY = "locality";
58 static const char * const CONTACT_HASH_ADDRESSES_REGION = "region";
59 static const char * const CONTACT_HASH_ADDRESSES_POSTAL_CODE = "postal-code";
60 static const char * const CONTACT_HASH_ADDRESSES_COUNTRY = "country";
61 static const char * const CONTACT_HASH_ROLES = "roles";
62 static const char * const CONTACT_HASH_ROLES_ORGANISATION = "organisation";
63 static const char * const CONTACT_HASH_ROLES_TITLE = "title";
64 static const char * const CONTACT_HASH_ROLES_ROLE = "role";
65 static const char * const CONTACT_HASH_GROUPS = "groups";
66 static const char * const CONTACT_HASH_SOURCE = "source";
67 static const char * const CONTACT_HASH_ID = "id";
69 static const char * const INDIVIDUAL_DICT = "a{sv}";
70 static const char * const INDIVIDUAL_DICT_ENTRY = "{sv}";
73 * Checks whether a certain value is the default value and thus
74 * can be skipped when converting to D-Bus.
76 * class B provides additional type information, which is necessary
77 * to check a GeeSet containing FolksRoleFieldDetails differently
80 template <class B> struct IsNonDefault
82 template<class V> static bool check(V value)
84 // Default version uses normal C/C++ rules, for example pointer non-NULL.
85 // For bool and integer, the value will only be sent if true or non-zero.
89 static bool check(const gchar *value)
91 // Don't send empty strings.
92 return value && value[0];
95 static bool check(GeeSet *value)
97 // Don't send empty sets.
98 return value && gee_collection_get_size(GEE_COLLECTION(value));
102 template <> struct IsNonDefault< GeeCollCXX<FolksRoleFieldDetails *> >
104 static bool check(GeeSet *value)
106 // Don't send empty set and set which contains only empty roles.
108 BOOST_FOREACH (FolksRoleFieldDetails *value, GeeCollCXX<FolksRoleFieldDetails *>(value)) {
109 FolksRole *role = static_cast<FolksRole *>(const_cast<gpointer>((folks_abstract_field_details_get_value(FOLKS_ABSTRACT_FIELD_DETAILS(value)))));
110 if (IsNonDefault<const gchar *>::check(folks_role_get_organisation_name(role)) ||
111 IsNonDefault<const gchar *>::check(folks_role_get_title(role)) ||
112 IsNonDefault<const gchar *>::check(folks_role_get_role(role))) {
122 * Adds a dict entry to the builder, with 'key' as string key and the
123 * result of 'get()' as value.
125 template <class O, class V> void SerializeFolks(GDBusCXX::builder_type &builder,
131 SE_THROW("casting to base class failed");
135 if (IsNonDefault<V>::check(value)) {
136 g_variant_builder_open(&builder, G_VARIANT_TYPE(INDIVIDUAL_DICT_ENTRY)); // dict entry
137 GDBusCXX::dbus_traits<std::string>::append(builder, key);
138 g_variant_builder_open(&builder, G_VARIANT_TYPE("v")); // variant
139 GDBusCXX::dbus_traits<V>::append(builder, value);
140 g_variant_builder_close(&builder); // variant
141 g_variant_builder_close(&builder); // dict entry
145 template <class O, class V, class B> void SerializeFolks(GDBusCXX::builder_type &builder,
148 B *, // dummy parameter, determines base type
152 SE_THROW("casting to base class failed");
156 if (IsNonDefault<B>::check(value)) {
157 g_variant_builder_open(&builder, G_VARIANT_TYPE(INDIVIDUAL_DICT_ENTRY)); // dict entry
158 GDBusCXX::dbus_traits<std::string>::append(builder, key);
159 g_variant_builder_open(&builder, G_VARIANT_TYPE("v")); // variant
160 GDBusCXX::dbus_traits<B>::append(builder, value);
161 g_variant_builder_close(&builder); // variant
162 g_variant_builder_close(&builder); // dict entry
170 template <> struct dbus_traits<FolksStructuredName *> {
171 static void append(builder_type &builder, FolksStructuredName *value) {
172 g_variant_builder_open(&builder, G_VARIANT_TYPE(INDIVIDUAL_DICT)); // dict
173 SyncEvo::SerializeFolks(builder, value, folks_structured_name_get_family_name, CONTACT_HASH_STRUCTURED_NAME_FAMILY);
174 SyncEvo::SerializeFolks(builder, value, folks_structured_name_get_given_name, CONTACT_HASH_STRUCTURED_NAME_GIVEN);
175 SyncEvo::SerializeFolks(builder, value, folks_structured_name_get_additional_names, CONTACT_HASH_STRUCTURED_NAME_ADDITIONAL);
176 SyncEvo::SerializeFolks(builder, value, folks_structured_name_get_prefixes, CONTACT_HASH_STRUCTURED_NAME_PREFIXES);
177 SyncEvo::SerializeFolks(builder, value, folks_structured_name_get_suffixes, CONTACT_HASH_STRUCTURED_NAME_SUFFIXES);
178 g_variant_builder_close(&builder); // dict
182 /** Path to icon (the expected case) or uninitialized string (not set or not a file). */
183 static InitStateString ExtractFilePath(GLoadableIcon *value)
186 G_IS_FILE_ICON(value)) {
187 GFileIcon *fileIcon = G_FILE_ICON(value);
188 GFile *file = g_file_icon_get_file(fileIcon);
190 PlainGStr uri(g_file_get_uri(file));
192 return InitStateString(uri.get(), true);
196 return InitStateString();
199 template <> struct dbus_traits<GLoadableIcon *> {
200 static void append(builder_type &builder, GLoadableIcon *value) {
201 InitStateString path = ExtractFilePath(value);
202 // EDS is expected to only work with URIs for the PHOTO
203 // property, therefore we shouldn't get here without a valid
204 // path. Either way, we need to store something.
205 GDBusCXX::dbus_traits<const char *>::append(builder, path.c_str());
209 template <> struct dbus_traits<FolksPostalAddress *> {
210 static void append(builder_type &builder, FolksPostalAddress *value) {
211 g_variant_builder_open(&builder, G_VARIANT_TYPE(INDIVIDUAL_DICT)); // dict
212 SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_po_box, CONTACT_HASH_ADDRESSES_PO_BOX);
213 SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_extension, CONTACT_HASH_ADDRESSES_EXTENSION);
214 SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_street, CONTACT_HASH_ADDRESSES_STREET);
215 SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_locality, CONTACT_HASH_ADDRESSES_LOCALITY);
216 SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_region, CONTACT_HASH_ADDRESSES_REGION);
217 SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_postal_code, CONTACT_HASH_ADDRESSES_POSTAL_CODE);
218 SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_country, CONTACT_HASH_ADDRESSES_COUNTRY);
220 // Not used by EDS. The tracker backend in folks was able to provide such a uid.
221 // SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_address_format, CONTACT_HASH_ADDRESSES_FORMAT);
222 // SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_uid, "uid");
223 g_variant_builder_close(&builder); // dict
227 template <> struct dbus_traits<FolksRole *> {
228 static void append(builder_type &builder, FolksRole *value) {
229 g_variant_builder_open(&builder, G_VARIANT_TYPE(INDIVIDUAL_DICT)); // dict
230 // Other parts of ORG are not currently supported by libfolks.
231 SyncEvo::SerializeFolks(builder, value, folks_role_get_organisation_name, CONTACT_HASH_ROLES_ORGANISATION);
232 SyncEvo::SerializeFolks(builder, value, folks_role_get_title, CONTACT_HASH_ROLES_TITLE);
233 SyncEvo::SerializeFolks(builder, value, folks_role_get_role, CONTACT_HASH_ROLES_ROLE);
235 // SyncEvo::SerializeFolks(builder, value, folks_role_get_uid, "uid");
236 g_variant_builder_close(&builder); // dict
240 // TODO: put into global header file
241 static const char * const MANAGER_PREFIX = "pim-manager-";
243 template <> struct dbus_traits<FolksPersona *> {
244 static void append(builder_type &builder, FolksPersona *value) {
245 g_variant_builder_open(&builder, G_VARIANT_TYPE("(ss)")); // pair of peer ID and local ID
246 const gchar *uid = folks_persona_get_uid(value);
248 gchar *backend, *storeID, *personaID;
249 folks_persona_split_uid(uid, &backend, &storeID, &personaID);
250 PlainGStr tmp1(backend), tmp2(storeID), tmp3(personaID);
251 if (boost::starts_with(storeID, MANAGER_PREFIX)) {
252 dbus_traits<const char *>::append(builder, storeID + strlen(MANAGER_PREFIX));
254 // Must be the system address book.
255 dbus_traits<const char *>::append(builder, "");
257 dbus_traits<const char *>::append(builder, personaID);
259 dbus_traits<const char *>::append(builder, "");
260 dbus_traits<const char *>::append(builder, "");
262 g_variant_builder_close(&builder); // pair
266 // Only use this with FolksAbstractFieldDetails instances where
267 // the value is a string.
268 template <> struct dbus_traits<FolksAbstractFieldDetails *> {
269 static void append(builder_type &builder, FolksAbstractFieldDetails *value) {
270 g_variant_builder_open(&builder, G_VARIANT_TYPE("(sas)")); // pair of string and string list
271 gconstpointer v = folks_abstract_field_details_get_value(value);
272 dbus_traits<const char *>::append(builder, v ? (const char *)v : "");
273 g_variant_builder_open(&builder, G_VARIANT_TYPE("as"));
274 GeeMultiMap *map = folks_abstract_field_details_get_parameters(value);
276 BOOST_FOREACH (const char *type, GeeCollCXX<const char *>(gee_multi_map_get(map, FOLKS_ABSTRACT_FIELD_DETAILS_PARAM_TYPE))) {
277 dbus_traits<const char *>::append(builder, type);
280 g_variant_builder_close(&builder); // string list
281 g_variant_builder_close(&builder); // pair
285 template <> struct dbus_traits<FolksPostalAddressFieldDetails *> {
286 static void append(builder_type &builder, FolksPostalAddressFieldDetails *value) {
287 g_variant_builder_open(&builder, G_VARIANT_TYPE(StringPrintf("(%sas)", INDIVIDUAL_DICT).c_str())); // pair of dict and string list
288 FolksAbstractFieldDetails *fieldDetails = FOLKS_ABSTRACT_FIELD_DETAILS(value);
289 gconstpointer v = folks_abstract_field_details_get_value(FOLKS_ABSTRACT_FIELD_DETAILS(fieldDetails));
290 dbus_traits<FolksPostalAddress *>::append(builder, static_cast<FolksPostalAddress *>(const_cast<gpointer>(v)));
291 g_variant_builder_open(&builder, G_VARIANT_TYPE("as"));
292 GeeMultiMap *map = folks_abstract_field_details_get_parameters(fieldDetails);
294 BOOST_FOREACH (const char *type, GeeCollCXX<const char *>(gee_multi_map_get(map, "type"))) {
295 dbus_traits<const char *>::append(builder, type);
298 g_variant_builder_close(&builder); // string list
299 g_variant_builder_close(&builder); // pair
303 template <> struct dbus_traits<FolksNoteFieldDetails *> {
304 static void append(builder_type &builder, FolksNoteFieldDetails *value) {
305 FolksAbstractFieldDetails *fieldDetails = FOLKS_ABSTRACT_FIELD_DETAILS(value);
306 gconstpointer v = folks_abstract_field_details_get_value(FOLKS_ABSTRACT_FIELD_DETAILS(fieldDetails));
307 dbus_traits<const char *>::append(builder, static_cast<const char *>(v)); // plain string
308 // Ignore parameters. LANGUAGE is hardly ever set.
312 template <> struct dbus_traits<FolksRoleFieldDetails *> {
313 static void append(builder_type &builder, FolksRoleFieldDetails *value) {
314 FolksAbstractFieldDetails *fieldDetails = FOLKS_ABSTRACT_FIELD_DETAILS(value);
315 gconstpointer v = folks_abstract_field_details_get_value(FOLKS_ABSTRACT_FIELD_DETAILS(fieldDetails));
316 dbus_traits<FolksRole *>::append(builder, static_cast<FolksRole *>(const_cast<gpointer>((v))));
317 // Ignore parameters. LANGUAGE is hardly ever set.
321 // Used for types like V = FolksEmailFieldDetails *
322 template <class V> struct dbus_traits< GeeCollCXX<V> > {
323 static void append(builder_type &builder, const GeeCollCXX<V> &collection) {
324 g_variant_builder_open(&builder, G_VARIANT_TYPE("av")); // array of variants
325 BOOST_FOREACH (V value, collection) {
326 g_variant_builder_open(&builder, G_VARIANT_TYPE("v")); // variant
327 dbus_traits<V>::append(builder, value);
328 g_variant_builder_close(&builder); // variant
330 g_variant_builder_close(&builder); // array of variants
334 template <> struct dbus_traits<GDateTime *> {
335 static void append(builder_type &builder, GDateTime *value) {
336 // Extract local date from UTC date + time + UTC offset.
338 // The libfolks EDS backend does date + 00:00 in local time,
339 // then converts to UTC. We need to hard-code the stripping
340 // of the time. Folks should make it easier to extract the date, see
341 // https://bugzilla.gnome.org/show_bug.cgi?id=684905
343 // TODO: if folks doesn't get fixed, then we should cache the
344 // GTimeZone local = g_time_zone_new_local()
345 // and use that throughout the runtime of the process, like
347 GDateTimeCXX local(g_date_time_to_local(value), false);
348 gint year, month, day;
349 g_date_time_get_ymd(local.get(), &year, &month, &day);
350 g_variant_builder_open(&builder, G_VARIANT_TYPE("(iii)")); // tuple with year, month, day
351 GDBusCXX::dbus_traits<int>::append(builder, year);
352 GDBusCXX::dbus_traits<int>::append(builder, month);
353 GDBusCXX::dbus_traits<int>::append(builder, day);
354 g_variant_builder_close(&builder); // tuple
358 template <> struct dbus_traits<FolksLocation *> {
359 static void append(builder_type &builder, FolksLocation *value) {
360 g_variant_builder_open(&builder, G_VARIANT_TYPE("(dd)")); // tuple with latitude + longitude
361 GDBusCXX::dbus_traits<double>::append(builder, value->latitude);
362 GDBusCXX::dbus_traits<double>::append(builder, value->longitude);
363 g_variant_builder_close(&builder); // tuple
367 } // namespace GDBusCXX
371 static guint FolksAbstractFieldDetailsHash(gconstpointer v, void *d)
373 return folks_abstract_field_details_hash((FolksAbstractFieldDetails *)v);
375 static gboolean FolksAbstractFieldDetailsEqual(gconstpointer a, gconstpointer b, void *d)
377 return folks_abstract_field_details_equal((FolksAbstractFieldDetails *)a,
378 (FolksAbstractFieldDetails *)b);
382 * Copy from D-Bus into a type derived from FolksAbstractFieldDetails,
383 * including type flags.
385 static void DBus2AbstractField(GDBusCXX::ExtractArgs &context,
386 GVariantIter &valueIter,
387 PersonaDetails &details,
389 FolksPersonaDetail detail,
390 FolksAbstractFieldDetails *(*fieldNew)(const gchar *, GeeMultiMap *))
392 // type of emails, urls, etc.
393 typedef std::vector< std::pair< std::string, std::vector<std::string> > > Details_t;
396 GDBusCXX::dbus_traits<typeof(value)>::get(context, valueIter, value);
397 GeeHashSetCXX set(gee_hash_set_new(detailType,
400 FolksAbstractFieldDetailsHash, NULL, NULL,
401 FolksAbstractFieldDetailsEqual, NULL, NULL),
403 BOOST_FOREACH (const Details_t::value_type &entry, value) {
404 const Details_t::value_type::first_type &val = entry.first;
405 const Details_t::value_type::second_type &flags = entry.second;
406 FolksAbstractFieldDetailsCXX field(fieldNew(val.c_str(), NULL), false);
407 BOOST_FOREACH (const std::string &type, flags) {
408 folks_abstract_field_details_add_parameter(field.get(),
409 FOLKS_ABSTRACT_FIELD_DETAILS_PARAM_TYPE,
412 gee_collection_add(GEE_COLLECTION(set.get()),
415 g_hash_table_insert(details.get(),
416 const_cast<gchar *>(folks_persona_store_detail_key(detail)),
417 new GValueObjectCXX(set.get()));
421 * Copy from D-Bus into a type derived from FolksAbstractFieldDetails,
422 * excluding type flags.
424 static void DBus2SimpleAbstractField(GDBusCXX::ExtractArgs &context,
425 GVariantIter &valueIter,
426 PersonaDetails &details,
428 FolksPersonaDetail detail,
429 FolksAbstractFieldDetails *(*fieldNew)(const gchar *, GeeMultiMap *))
432 typedef std::vector<std::string> Details_t;
435 GDBusCXX::dbus_traits<typeof(value)>::get(context, valueIter, value);
436 GeeHashSetCXX set(gee_hash_set_new(detailType,
439 FolksAbstractFieldDetailsHash, NULL, NULL,
440 FolksAbstractFieldDetailsEqual, NULL, NULL),
442 BOOST_FOREACH (const std::string &val, value) {
443 FolksAbstractFieldDetailsCXX field(fieldNew(val.c_str(), NULL), false);
444 gee_collection_add(GEE_COLLECTION(set.get()),
447 g_hash_table_insert(details.get(),
448 const_cast<gchar *>(folks_persona_store_detail_key(detail)),
449 new GValueObjectCXX(set.get()));
453 * Copy from D-Bus into FolksRoleFieldDetails.
455 static void DBus2Role(GDBusCXX::ExtractArgs &context,
456 GVariantIter &valueIter,
457 PersonaDetails &details)
460 typedef std::vector<StringMap> Details_t;
463 GDBusCXX::dbus_traits<typeof(value)>::get(context, valueIter, value);
464 GeeHashSetCXX set(gee_hash_set_new(FOLKS_TYPE_ROLE_FIELD_DETAILS,
467 FolksAbstractFieldDetailsHash, NULL, NULL,
468 FolksAbstractFieldDetailsEqual, NULL, NULL),
470 BOOST_FOREACH (const StringMap &entry, value) {
471 FolksRoleCXX role(folks_role_new(NULL, NULL, NULL),
473 BOOST_FOREACH (const StringPair &aspect, entry) {
474 const std::string &k = aspect.first;
475 const std::string &v = aspect.second;
476 if (k == CONTACT_HASH_ROLES_ORGANISATION) {
477 folks_role_set_organisation_name(role, v.c_str());
478 } else if (k == CONTACT_HASH_ROLES_TITLE) {
479 folks_role_set_title(role, v.c_str());
480 } else if (k == CONTACT_HASH_ROLES_ROLE) {
481 folks_role_set_role(role, v.c_str());
484 FolksRoleFieldDetailsCXX field(folks_role_field_details_new(role.get(), NULL),
486 gee_collection_add(GEE_COLLECTION(set.get()),
489 g_hash_table_insert(details.get(),
490 const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_ROLES)),
491 new GValueObjectCXX(set.get()));
495 * Copy from D-Bus into a set of strings.
497 static void DBus2Groups(GDBusCXX::ExtractArgs &context,
498 GVariantIter &valueIter,
499 PersonaDetails &details)
501 std::list<std::string> value;
502 GDBusCXX::dbus_traits<typeof(value)>::get(context, valueIter, value);
503 GeeHashSetCXX set(gee_hash_set_new(G_TYPE_STRING, (GBoxedCopyFunc)g_strdup, g_free, NULL, NULL, NULL, NULL, NULL, NULL), false);
504 BOOST_FOREACH(const std::string &entry, value) {
505 gee_collection_add(GEE_COLLECTION(set.get()),
508 g_hash_table_insert(details.get(),
509 const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_GROUPS)),
510 new GValueObjectCXX(set.get()));
514 * Copy from D-Bus into a FolksAddressFieldDetails.
516 static void DBus2Addr(GDBusCXX::ExtractArgs &context,
517 GVariantIter &valueIter,
518 PersonaDetails &details)
520 // type of CONTACT_HASH_ADDRESSES
521 typedef std::vector< std::pair< StringMap, std::vector<std::string> > > Details_t;
524 GDBusCXX::dbus_traits<typeof(value)>::get(context, valueIter, value);
525 GeeHashSetCXX set(gee_hash_set_new(FOLKS_TYPE_POSTAL_ADDRESS_FIELD_DETAILS,
528 FolksAbstractFieldDetailsHash, NULL, NULL,
529 FolksAbstractFieldDetailsEqual, NULL, NULL),
531 BOOST_FOREACH (const Details_t::value_type &entry, value) {
532 const StringMap &fields = entry.first;
533 const std::vector<std::string> &flags = entry.second;
534 FolksPostalAddressCXX address(folks_postal_address_new(GetWithDef(fields, CONTACT_HASH_ADDRESSES_PO_BOX).c_str(),
535 GetWithDef(fields, CONTACT_HASH_ADDRESSES_EXTENSION).c_str(),
536 GetWithDef(fields, CONTACT_HASH_ADDRESSES_STREET).c_str(),
537 GetWithDef(fields, CONTACT_HASH_ADDRESSES_LOCALITY).c_str(),
538 GetWithDef(fields, CONTACT_HASH_ADDRESSES_REGION).c_str(),
539 GetWithDef(fields, CONTACT_HASH_ADDRESSES_POSTAL_CODE).c_str(),
540 GetWithDef(fields, CONTACT_HASH_ADDRESSES_COUNTRY).c_str(),
541 NULL /* address format */,
544 FolksAbstractFieldDetailsCXX field(FOLKS_ABSTRACT_FIELD_DETAILS(folks_postal_address_field_details_new(address.get(), NULL)),
546 BOOST_FOREACH (const std::string &type, flags) {
547 folks_abstract_field_details_add_parameter(field.get(),
548 FOLKS_ABSTRACT_FIELD_DETAILS_PARAM_TYPE,
551 gee_collection_add(GEE_COLLECTION(set.get()),
554 g_hash_table_insert(details.get(),
555 const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_POSTAL_ADDRESSES)),
556 new GValueObjectCXX(set.get()));
559 void DBus2PersonaDetails(GDBusCXX::ExtractArgs &context,
560 GDBusCXX::reader_type &iter,
561 PersonaDetails &details)
563 GDBusCXX::GVariantCXX var(g_variant_iter_next_value(&iter));
564 if (var == NULL || !g_variant_type_is_subtype_of(g_variant_get_type(var), G_VARIANT_TYPE_ARRAY)) {
565 SE_THROW("D-Bus contact hash: unexpected content");
568 GVariantIter contIter;
569 GDBusCXX::GVariantCXX child;
570 g_variant_iter_init(&contIter, var); // array
571 while((child = g_variant_iter_next_value(&contIter)) != NULL) {
572 GVariantIter childIter;
573 g_variant_iter_init(&childIter, child); // dict entry
575 GDBusCXX::dbus_traits<std::string>::get(context, childIter, key);
576 GDBusCXX::GVariantCXX valueVarient(g_variant_iter_next_value(&childIter));
577 if (valueVarient == NULL || !g_variant_type_equal(g_variant_get_type(valueVarient), G_VARIANT_TYPE_VARIANT)) {
578 SE_THROW("D-Bus contact hash entry: value must be variant");
581 GVariantIter valueIter;
582 g_variant_iter_init(&valueIter, valueVarient);
583 if (key == CONTACT_HASH_FULL_NAME) {
585 GDBusCXX::dbus_traits<std::string>::get(context, valueIter, value);
586 g_hash_table_insert(details.get(),
587 const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_FULL_NAME)),
588 new GValueStringCXX(value.c_str()));
589 } else if (key == CONTACT_HASH_STRUCTURED_NAME) {
591 GDBusCXX::dbus_traits<StringMap>::get(context, valueIter, value);
592 g_hash_table_insert(details.get(),
593 const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_STRUCTURED_NAME)),
594 new GValueObjectCXX(folks_structured_name_new(GetWithDef(value, CONTACT_HASH_STRUCTURED_NAME_FAMILY).c_str(),
595 GetWithDef(value, CONTACT_HASH_STRUCTURED_NAME_GIVEN).c_str(),
596 GetWithDef(value, CONTACT_HASH_STRUCTURED_NAME_ADDITIONAL).c_str(),
597 GetWithDef(value, CONTACT_HASH_STRUCTURED_NAME_PREFIXES).c_str(),
598 GetWithDef(value, CONTACT_HASH_STRUCTURED_NAME_SUFFIXES).c_str()),
600 } else if (key == CONTACT_HASH_PHOTO) {
602 GDBusCXX::dbus_traits<std::string>::get(context, valueIter, value);
603 GFileCXX file(g_file_new_for_uri(value.c_str()),
605 g_hash_table_insert(details.get(),
606 const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_AVATAR)),
607 new GValueObjectCXX(g_file_icon_new(file.get()), false));
608 } else if (key == CONTACT_HASH_BIRTHDAY) {
609 boost::tuple<int, int, int> value;
610 GDBusCXX::dbus_traits<typeof(value)>::get(context, valueIter, value);
611 GDateTimeCXX local(g_date_time_new_local(value.get<0>(),
616 g_hash_table_insert(details.get(),
617 const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_BIRTHDAY)),
618 new GValueDateTimeCXX(g_date_time_to_utc(local.get()), false));
619 } else if (key == CONTACT_HASH_LOCATION) {
620 boost::tuple<double, double> value;
621 GDBusCXX::dbus_traits<typeof(value)>::get(context, valueIter, value);
622 FolksLocationCXX location(folks_location_new(value.get<0>(),
625 g_hash_table_insert(details.get(),
626 const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_LOCATION)),
627 new GValueObjectCXX(location.get()));
628 } else if (key == CONTACT_HASH_EMAILS) {
629 DBus2AbstractField(context, valueIter, details,
630 FOLKS_TYPE_EMAIL_FIELD_DETAILS,
631 FOLKS_PERSONA_DETAIL_EMAIL_ADDRESSES,
632 reinterpret_cast<FolksAbstractFieldDetails *(*)(const gchar *, GeeMultiMap *)>(folks_email_field_details_new));
633 } else if (key == CONTACT_HASH_PHONES) {
634 DBus2AbstractField(context, valueIter, details,
635 FOLKS_TYPE_PHONE_FIELD_DETAILS,
636 FOLKS_PERSONA_DETAIL_PHONE_NUMBERS,
637 reinterpret_cast<FolksAbstractFieldDetails *(*)(const gchar *, GeeMultiMap *)>(folks_phone_field_details_new));
638 } else if (key == CONTACT_HASH_URLS) {
639 DBus2AbstractField(context, valueIter, details,
640 FOLKS_TYPE_URL_FIELD_DETAILS,
641 FOLKS_PERSONA_DETAIL_URLS,
642 reinterpret_cast<FolksAbstractFieldDetails *(*)(const gchar *, GeeMultiMap *)>(folks_url_field_details_new));
643 } else if (key == CONTACT_HASH_NOTES) {
644 DBus2SimpleAbstractField(context, valueIter, details,
645 FOLKS_TYPE_NOTE_FIELD_DETAILS,
646 FOLKS_PERSONA_DETAIL_NOTES,
647 reinterpret_cast<FolksAbstractFieldDetails *(*)(const gchar *, GeeMultiMap *)>(folks_note_field_details_new));
648 } else if (key == CONTACT_HASH_ROLES) {
649 DBus2Role(context, valueIter, details);
650 } else if (key == CONTACT_HASH_GROUPS) {
651 DBus2Groups(context, valueIter, details);
652 } else if (key == CONTACT_HASH_ADDRESSES) {
653 DBus2Addr(context, valueIter, details);
660 Pending(const Result<void ()> &result,
661 FolksPersona *persona) :
667 Result<void ()> m_result;
668 FolksPersonaCXX m_persona;
669 typedef boost::function<void (const GError *)> AsyncDone;
670 typedef boost::function<void (AsyncDone *)> Prepare;
671 typedef boost::tuple<Prepare,
673 boost::shared_ptr<void> > Change;
674 typedef std::vector<Change> Changes;
679 static bool GeeCollectionEqual(GeeCollection *a, GeeCollection *b)
681 return gee_collection_get_size(a) == gee_collection_get_size(b) &&
682 gee_collection_contains_all(a, b);
686 * Gets called by event loop. All errors must be reported back to the caller.
688 static void Details2PersonaStep(const GError *gerror, const boost::shared_ptr<Pending> &pending) throw ()
692 GErrorCXX::throwError("modifying property", gerror);
694 size_t current = pending->m_current++;
695 if (current < pending->m_changes.size()) {
696 // send next change, as determined earlier
697 Pending::Change &change = pending->m_changes[current];
698 SE_LOG_DEBUG(NULL, NULL, "modification step %d/%d: %s",
700 (int)pending->m_changes.size(),
701 boost::get<1>(change));
702 boost::get<0>(change)(new Pending::AsyncDone(boost::bind(Details2PersonaStep, _1, pending)));
704 pending->m_result.done();
707 // Tell caller about specific error.
708 pending->m_result.failed();
712 void Details2Persona(const Result<void ()> &result, const PersonaDetails &details, FolksPersona *persona)
714 boost::shared_ptr<Pending> pending(new Pending(result, persona));
716 #define PUSH_CHANGE(_prepare) \
717 SE_LOG_DEBUG(NULL, NULL, "queueing new change: %s", #_prepare); \
718 pending->m_changes.push_back(Pending::Change(boost::bind(_prepare, \
721 SYNCEVO_GLIB_CALL_ASYNC_CXX(_prepare)::handleGLibResult, \
726 const GValue *gvalue;
727 boost::shared_ptr<void> tracker;
728 gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(),
729 folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_FULL_NAME)));
733 value = g_value_get_string(gvalue);
734 tracker = boost::shared_ptr<void>(g_strdup(value), g_free);
738 FolksNameDetails *details = FOLKS_NAME_DETAILS(persona);
739 if (g_strcmp0(value, folks_name_details_get_full_name(details))) {
740 PUSH_CHANGE(folks_name_details_change_full_name);
745 gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_STRUCTURED_NAME)));
747 FolksStructuredName *value;
749 value = FOLKS_STRUCTURED_NAME(g_value_get_object(gvalue));
750 tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
752 tracker = boost::shared_ptr<void>(folks_structured_name_new("", "", "", "", ""), g_object_unref);
753 value = static_cast<FolksStructuredName *>(tracker.get());
755 FolksNameDetails *details = FOLKS_NAME_DETAILS(persona);
756 if (!folks_structured_name_equal(value, folks_name_details_get_structured_name(details))) {
757 PUSH_CHANGE(folks_name_details_change_structured_name);
761 gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_AVATAR)));
763 FolksAvatarDetails *details = FOLKS_AVATAR_DETAILS(persona);
764 GLoadableIcon *value;
766 value = G_LOADABLE_ICON(g_value_get_object(gvalue));
767 tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
769 tracker = boost::shared_ptr<void>();
772 InitStateString newPath = GDBusCXX::ExtractFilePath(value);
773 InitStateString oldPath = GDBusCXX::ExtractFilePath(folks_avatar_details_get_avatar(details));
774 if (newPath.wasSet() != oldPath.wasSet() ||
775 newPath != oldPath) {
776 PUSH_CHANGE(folks_avatar_details_change_avatar);
780 gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_BIRTHDAY)));
784 value = static_cast<GDateTime *>(g_value_get_boxed(gvalue));
785 tracker = boost::shared_ptr<void>(g_date_time_ref(value), g_date_time_unref);
787 tracker = boost::shared_ptr<void>();
790 FolksBirthdayDetails *details = FOLKS_BIRTHDAY_DETAILS(persona);
791 GDateTime *old = folks_birthday_details_get_birthday(details);
792 if ((value == NULL && old != NULL) ||
793 (value != NULL && old == NULL) ||
794 !g_date_time_equal(value, old)) {
795 PUSH_CHANGE(folks_birthday_details_change_birthday);
799 gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_LOCATION)));
801 FolksLocation *value;
803 value = static_cast<FolksLocation *>(g_value_get_object(gvalue));
804 tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
806 tracker = boost::shared_ptr<void>();
809 FolksLocationDetails *details = FOLKS_LOCATION_DETAILS(persona);
810 FolksLocation *old = folks_location_details_get_location(details);
811 if ((value == NULL && old != NULL) ||
812 (value != NULL && old == NULL) ||
814 (value->latitude != old->latitude ||
815 value->longitude != old->longitude))) {
816 PUSH_CHANGE(folks_location_details_change_location);
820 gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_EMAIL_ADDRESSES)));
824 value = GEE_SET(g_value_get_object(gvalue));
825 tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
827 tracker = boost::shared_ptr<void>(gee_hash_set_new(FOLKS_TYPE_EMAIL_FIELD_DETAILS,
830 FolksAbstractFieldDetailsHash, NULL, NULL,
831 FolksAbstractFieldDetailsEqual, NULL, NULL),
833 value = static_cast<GeeSet *>(tracker.get());
835 FolksEmailDetails *details = FOLKS_EMAIL_DETAILS(persona);
836 if (!GeeCollectionEqual(GEE_COLLECTION(value),
837 GEE_COLLECTION(folks_email_details_get_email_addresses(details)))) {
838 PUSH_CHANGE(folks_email_details_change_email_addresses);
842 gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_PHONE_NUMBERS)));
846 value = GEE_SET(g_value_get_object(gvalue));
847 tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
849 tracker = boost::shared_ptr<void>(gee_hash_set_new(FOLKS_TYPE_PHONE_FIELD_DETAILS,
852 FolksAbstractFieldDetailsHash, NULL, NULL,
853 FolksAbstractFieldDetailsEqual, NULL, NULL),
855 value = static_cast<GeeSet *>(tracker.get());
857 FolksPhoneDetails *details = FOLKS_PHONE_DETAILS(persona);
858 if (!GeeCollectionEqual(GEE_COLLECTION(value),
859 GEE_COLLECTION(folks_phone_details_get_phone_numbers(details)))) {
860 PUSH_CHANGE(folks_phone_details_change_phone_numbers);
864 gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_URLS)));
868 value = GEE_SET(g_value_get_object(gvalue));
869 tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
871 tracker = boost::shared_ptr<void>(gee_hash_set_new(FOLKS_TYPE_URL_FIELD_DETAILS,
874 FolksAbstractFieldDetailsHash, NULL, NULL,
875 FolksAbstractFieldDetailsEqual, NULL, NULL),
877 value = static_cast<GeeSet *>(tracker.get());
879 FolksUrlDetails *details = FOLKS_URL_DETAILS(persona);
880 if (!GeeCollectionEqual(GEE_COLLECTION(value),
881 GEE_COLLECTION(folks_url_details_get_urls(details)))) {
882 PUSH_CHANGE(folks_url_details_change_urls);
886 gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_NOTES)));
888 // At the moment the comparison fails and tiggers a write on each update?!
891 value = GEE_SET(g_value_get_object(gvalue));
892 tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
894 tracker = boost::shared_ptr<void>(gee_hash_set_new(FOLKS_TYPE_NOTE_FIELD_DETAILS,
897 FolksAbstractFieldDetailsHash, NULL, NULL,
898 FolksAbstractFieldDetailsEqual, NULL, NULL),
900 value = static_cast<GeeSet *>(tracker.get());
902 FolksNoteDetails *details = FOLKS_NOTE_DETAILS(persona);
903 if (!GeeCollectionEqual(GEE_COLLECTION(value),
904 GEE_COLLECTION(folks_note_details_get_notes(details)))) {
905 PUSH_CHANGE(folks_note_details_change_notes);
909 gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_ROLES)));
913 value = GEE_SET(g_value_get_object(gvalue));
914 tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
916 tracker = boost::shared_ptr<void>(gee_hash_set_new(FOLKS_TYPE_ROLE_FIELD_DETAILS,
919 FolksAbstractFieldDetailsHash, NULL, NULL,
920 FolksAbstractFieldDetailsEqual, NULL, NULL),
922 value = static_cast<GeeSet *>(tracker.get());
924 FolksRoleDetails *details = FOLKS_ROLE_DETAILS(persona);
925 if (!GeeCollectionEqual(GEE_COLLECTION(value),
926 GEE_COLLECTION(folks_role_details_get_roles(details)))) {
927 PUSH_CHANGE(folks_role_details_change_roles);
931 gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_GROUPS)));
935 value = GEE_SET(g_value_get_object(gvalue));
936 tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
938 tracker = boost::shared_ptr<void>(gee_hash_set_new(G_TYPE_STRING,
939 (GBoxedCopyFunc)g_strdup,
948 value = static_cast<GeeSet *>(tracker.get());
950 FolksGroupDetails *details = FOLKS_GROUP_DETAILS(persona);
951 if (!GeeCollectionEqual(GEE_COLLECTION(value),
952 GEE_COLLECTION(folks_group_details_get_groups(details)))) {
953 PUSH_CHANGE(folks_group_details_change_groups);
957 gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_POSTAL_ADDRESSES)));
961 value = GEE_SET(g_value_get_object(gvalue));
962 tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
964 tracker = boost::shared_ptr<void>(gee_hash_set_new(FOLKS_TYPE_POSTAL_ADDRESS_FIELD_DETAILS,
967 FolksAbstractFieldDetailsHash, NULL, NULL,
968 FolksAbstractFieldDetailsEqual, NULL, NULL),
970 value = static_cast<GeeSet *>(tracker.get());
972 FolksPostalAddressDetails *details = FOLKS_POSTAL_ADDRESS_DETAILS(persona);
973 if (!GeeCollectionEqual(GEE_COLLECTION(value),
974 GEE_COLLECTION(folks_postal_address_details_get_postal_addresses(details)))) {
975 PUSH_CHANGE(folks_postal_address_details_change_postal_addresses);
979 Details2PersonaStep(NULL, pending);
982 void FolksIndividual2DBus(const FolksIndividualCXX &individual, GDBusCXX::builder_type &builder)
984 g_variant_builder_open(&builder, G_VARIANT_TYPE(INDIVIDUAL_DICT)); // dict
986 FolksNameDetails *name = FOLKS_NAME_DETAILS(individual.get());
987 SerializeFolks(builder, name, folks_name_details_get_full_name,
988 CONTACT_HASH_FULL_NAME);
989 SerializeFolks(builder, name, folks_name_details_get_nickname,
990 CONTACT_HASH_NICKNAME);
991 SerializeFolks(builder, name, folks_name_details_get_structured_name,
992 CONTACT_HASH_STRUCTURED_NAME);
994 // gconstpointer folks_abstract_field_details_get_value (FolksAbstractFieldDetails* self);
996 FolksAliasDetails *alias = FOLKS_ALIAS_DETAILS(individual.get());
997 SerializeFolks(builder, alias, folks_alias_details_get_alias,
1000 FolksAvatarDetails *avatar = FOLKS_AVATAR_DETAILS(individual.get());
1001 SerializeFolks(builder, avatar, folks_avatar_details_get_avatar,
1002 CONTACT_HASH_PHOTO);
1004 FolksBirthdayDetails *birthday = FOLKS_BIRTHDAY_DETAILS(individual.get());
1005 SerializeFolks(builder, birthday, folks_birthday_details_get_birthday,
1006 CONTACT_HASH_BIRTHDAY);
1007 // const gchar* folks_birthday_details_get_calendar_event_id (FolksBirthdayDetails* self);
1009 FolksLocationDetails *location = FOLKS_LOCATION_DETAILS(individual.get());
1010 SerializeFolks(builder, location, folks_location_details_get_location,
1011 CONTACT_HASH_LOCATION);
1013 FolksEmailDetails *emails = FOLKS_EMAIL_DETAILS(individual.get());
1014 SerializeFolks(builder, emails, folks_email_details_get_email_addresses,
1015 (GeeCollCXX<FolksAbstractFieldDetails *>*)NULL,
1016 CONTACT_HASH_EMAILS);
1018 FolksPhoneDetails *phones = FOLKS_PHONE_DETAILS(individual.get());
1019 SerializeFolks(builder, phones, folks_phone_details_get_phone_numbers,
1020 (GeeCollCXX<FolksAbstractFieldDetails *>*)NULL,
1021 CONTACT_HASH_PHONES);
1023 FolksUrlDetails *urls = FOLKS_URL_DETAILS(individual.get());
1024 SerializeFolks(builder, urls, folks_url_details_get_urls,
1025 (GeeCollCXX<FolksAbstractFieldDetails *>*)NULL,
1028 // Doesn't work like this, folks_im_details_get_im_addresses returns
1029 // a GeeMultiMap, not a GeeList. Not required anyway.
1030 // FolksImDetails *im = FOLKS_IM_DETAILS(individual.get());
1031 // SerializeFolks(builder, im, folks_im_details_get_im_addresses,
1032 // (GeeCollCXX<FolksAbstractFieldDetails *>*)NULL, "im");
1034 FolksNoteDetails *notes = FOLKS_NOTE_DETAILS(individual.get());
1035 SerializeFolks(builder, notes, folks_note_details_get_notes,
1036 (GeeCollCXX<FolksNoteFieldDetails *>*)NULL,
1037 CONTACT_HASH_NOTES);
1039 FolksPostalAddressDetails *postal = FOLKS_POSTAL_ADDRESS_DETAILS(individual.get());
1040 SerializeFolks(builder, postal, folks_postal_address_details_get_postal_addresses,
1041 (GeeCollCXX<FolksPostalAddressFieldDetails *>*)NULL,
1042 CONTACT_HASH_ADDRESSES);
1044 FolksRoleDetails *roles = FOLKS_ROLE_DETAILS(individual.get());
1045 SerializeFolks(builder, roles, folks_role_details_get_roles,
1046 (GeeCollCXX<FolksRoleFieldDetails *>*)NULL,
1047 CONTACT_HASH_ROLES);
1049 FolksGroupDetails *groups = FOLKS_GROUP_DETAILS(individual.get());
1050 SerializeFolks(builder, groups, folks_group_details_get_groups,
1051 (GeeStringCollection*)NULL,
1052 CONTACT_HASH_GROUPS);
1054 SerializeFolks(builder, individual.get(), folks_individual_get_personas,
1055 (GeeCollCXX<FolksPersona *>*)NULL,
1056 CONTACT_HASH_SOURCE);
1058 SerializeFolks(builder, individual.get(), folks_individual_get_id,
1062 // Not exposed via D-Bus.
1063 FolksGender folks_gender_details_get_gender (FolksGenderDetails* self);
1064 GeeMultiMap* folks_web_service_details_get_web_service_addresses (FolksWebServiceDetails* self);
1065 guint folks_interaction_details_get_im_interaction_count (FolksInteractionDetails* self);
1066 GDateTime* folks_interaction_details_get_last_im_interaction_datetime (FolksInteractionDetails* self);
1067 guint folks_interaction_details_get_call_interaction_count (FolksInteractionDetails* self);
1068 GDateTime* folks_interaction_details_get_last_call_interaction_datetime (FolksInteractionDetails* self);
1069 GeeSet* folks_local_id_details_get_local_ids (FolksLocalIdDetails* self);
1070 const gchar* folks_presence_details_get_default_message_from_type (FolksPresenceType type);
1071 FolksPresenceType folks_presence_details_get_presence_type (FolksPresenceDetails* self);
1072 const gchar* folks_presence_details_get_presence_message (FolksPresenceDetails* self);
1073 const gchar* folks_presence_details_get_presence_status (FolksPresenceDetails* self);
1076 g_variant_builder_close(&builder); // dict