Diff between 075df1e1bf8a52de39fd0946c7df276916f14b7a and 581b305d17b53397ea7abc0d614b64811f2d27cf

Changed Files

File Additions Deletions Status
obexd/plugins/filesystem.c +12 -7 modified
obexd/plugins/filesystem.h +1 -1 modified
obexd/plugins/pbap.c +173 -97 modified
obexd/plugins/phonebook-dummy.c +4 -4 modified
obexd/plugins/phonebook-ebook.c +104 -96 modified
obexd/plugins/phonebook.h +38 -3 modified
obexd/plugins/syncevolution.c +4 -2 modified

Full Patch

diff --git a/obexd/plugins/filesystem.c b/obexd/plugins/filesystem.c
index f94dd73..168058b 100644
--- a/obexd/plugins/filesystem.c
+++ b/obexd/plugins/filesystem.c
@@ -468,13 +468,11 @@ int string_free(gpointer object)
 	return 0;
 }
 
-ssize_t string_read(gpointer object, void *buf, size_t count, guint8 *hi)
+ssize_t string_read(gpointer object, void *buf, size_t count)
 {
 	GString *string = object;
 	ssize_t len;
 
-	*hi = OBEX_HDR_BODY;
-
 	if (string->len == 0)
 		return 0;
 
@@ -485,18 +483,25 @@ ssize_t string_read(gpointer object, void *buf, size_t count, guint8 *hi)
 	return len;
 }
 
+static ssize_t folder_read(gpointer object, void *buf, size_t count, guint8 *hi)
+{
+	*hi = OBEX_HDR_BODY;
+	return string_read(object, buf, count);
+}
+
 static ssize_t capability_read(gpointer object, void *buf, size_t count,
 								guint8 *hi)
 {
 	struct capability_object *obj = object;
 
+	*hi = OBEX_HDR_BODY;
+
 	if (obj->buffer)
-		return string_read(obj->buffer, buf, count, hi);
+		return string_read(obj->buffer, buf, count);
 
 	if (obj->pid >= 0)
 		return -EAGAIN;
 
-	*hi = OBEX_HDR_BODY;
 	return read(obj->output, buf, count);
 }
 
@@ -539,7 +544,7 @@ static struct obex_mime_type_driver folder = {
 	.mimetype = "x-obex/folder-listing",
 	.open = folder_open,
 	.close = string_free,
-	.read = string_read,
+	.read = folder_read,
 };
 
 static struct obex_mime_type_driver pcsuite = {
@@ -549,7 +554,7 @@ static struct obex_mime_type_driver pcsuite = {
 	.mimetype = "x-obex/folder-listing",
 	.open = pcsuite_open,
 	.close = string_free,
-	.read = string_read,
+	.read = folder_read,
 };
 
 static int filesystem_init(void)
diff --git a/obexd/plugins/filesystem.h b/obexd/plugins/filesystem.h
index 1e1b352..636ddb2 100644
--- a/obexd/plugins/filesystem.h
+++ b/obexd/plugins/filesystem.h
@@ -22,4 +22,4 @@
  */
 
 int string_free(gpointer object);
-ssize_t string_read(gpointer object, void *buf, size_t count, guint8 *hi);
+ssize_t string_read(gpointer object, void *buf, size_t count);
diff --git a/obexd/plugins/pbap.c b/obexd/plugins/pbap.c
index f7b7b9b..af6ae39 100644
--- a/obexd/plugins/pbap.c
+++ b/obexd/plugins/pbap.c
@@ -26,6 +26,7 @@
 #include <config.h>
 #endif
 
+#include <stdio.h>
 #include <string.h>
 #include <errno.h>
 #include <glib.h>
@@ -75,10 +76,6 @@
 #define MCH		"telecom/mch.vcf"
 #define SIM1_MCH	"SIM1/telecom/mch.vcf"
 
-#define DEFAULT_COUNT 65535
-
-#define APPARAM_HDR_SIZE 2
-
 #define PBAP_CHANNEL	15
 
 #define PBAP_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>	\
@@ -130,12 +127,15 @@ struct aparam_header {
 
 struct cache {
 	gboolean ready;
+	gboolean valid;
+	guint32 index;
+	gchar *folder;
 	GSList *entries;
 };
 
 struct cache_entry {
-	gchar *handle;
-	gpointer id;
+	guint32 handle;
+	gchar *id;
 	gchar *name;
 	gchar *sound;
 	gchar *tel;
@@ -145,7 +145,6 @@ struct pbap_session {
 	struct apparam_field *params;
 	gchar *folder;
 	GString *buffer;
-	guint16 phonebooksize;
 	struct cache cache;
 };
 
@@ -156,24 +155,15 @@ static const guint8 PBAP_TARGET[TARGET_SIZE] = {
 typedef int (*cache_sort_f) (struct cache_entry *entry, gpointer user_data);
 typedef void (*cache_element_f) (struct cache_entry *entry, gpointer user_data);
 
-static gint entry_handle_cmp(gconstpointer a, gconstpointer b)
-{
-	const struct cache_entry *entry = a;
-	const char* handle = b;
-
-	return strcmp(entry->handle, handle);
-}
-
 static void cache_entry_free(struct cache_entry *entry)
 {
-	g_free(entry->handle);
 	g_free(entry->id);
 	g_free(entry->name);
 	g_free(entry->sound);
 	g_free(entry->tel);
 	g_free(entry);
 }
-
+#if 0
 static void cache_foreach(struct cache *cache, cache_sort_f sort,
 		cache_element_f elem, gpointer user_data)
 {
@@ -188,28 +178,25 @@ static void cache_foreach(struct cache *cache, cache_sort_f sort,
 		elem(entry, user_data);
 	}
 }
+#endif
 
-static gpointer cache_find(struct cache *cache, const gchar *handle)
+static const gchar *cache_find(struct cache *cache, guint32 handle)
 {
-	struct cache_entry *entry;
 	GSList *l;
 
-	l = g_slist_find_custom(cache->entries, handle, entry_handle_cmp);
-	if (!l)
-		return NULL;
-
-	entry = l->data;
+	for (l = cache->entries; l; l = l->next) {
+		struct cache_entry *entry = l->data;
 
-	return entry->id;
-}
+		if (entry->handle == handle)
+			return entry->id;
+	}
 
-static void cache_add(struct cache *cache, struct cache_entry *entry)
-{
-	cache->entries = g_slist_append(cache->entries, entry);
+	return NULL;
 }
 
 static void cache_clear(struct cache *cache)
 {
+	g_free(cache->folder);
 	g_slist_foreach(cache->entries, (GFunc) cache_entry_free, NULL);
 	g_slist_free(cache->entries);
 	cache->entries = NULL;
@@ -226,8 +213,17 @@ static void phonebook_size_result(const gchar *buffer, size_t bufsize,
 				gint vcards, gint missed, gpointer user_data)
 {
 	struct pbap_session *pbap = user_data;
+	gchar aparam[4];
+	struct aparam_header *hdr = (struct aparam_header *) aparam;
+	guint16 phonebooksize;
+
+	phonebooksize = htons(vcards);
 
-	pbap->phonebooksize = vcards;
+	hdr->tag = PHONEBOOKSIZE_TAG;
+	hdr->len = PHONEBOOKSIZE_LEN;
+	memcpy(hdr->val, &phonebooksize, sizeof(phonebooksize));
+
+	pbap->buffer = g_string_new_len(aparam, sizeof(aparam));
 
 	obex_object_set_io_flags(pbap, G_IO_IN, 0);
 }
@@ -246,6 +242,63 @@ static void query_result(const gchar *buffer, size_t bufsize, gint vcards,
 	obex_object_set_io_flags(pbap, G_IO_IN, 0);
 }
 
+static void cache_entry_notify(const gchar *id, const gchar *name,
+		const gchar *sound, const gchar *tel, gpointer user_data)
+{
+	struct pbap_session *pbap = user_data;
+	struct cache_entry *entry = g_new0(struct cache_entry, 1);
+	struct cache *cache = &pbap->cache;
+
+	entry->handle = ++pbap->cache.index;
+	entry->id = g_strdup(id);
+	entry->name = g_strdup(name);
+	entry->sound = g_strdup(sound);
+	entry->tel = g_strdup(tel);
+
+	cache->entries = g_slist_append(cache->entries, entry);
+}
+
+static void cache_ready_notify(gpointer user_data)
+{
+	struct pbap_session *pbap = user_data;
+
+	if (pbap->params->maxlistcount == 0) {
+		/* Ignore all other parameter and return PhoneBookSize */
+		gchar aparam[4];
+		struct aparam_header *hdr = (struct aparam_header *) aparam;
+		guint16 size = g_slist_length(pbap->cache.entries);
+
+		hdr->tag = PHONEBOOKSIZE_TAG;
+		hdr->len = PHONEBOOKSIZE_LEN;
+		memcpy(hdr->val, &size, sizeof(size));
+
+		pbap->buffer = g_string_new_len(aparam, sizeof(aparam));
+	} else {
+		GSList *l;
+
+		pbap->buffer = g_string_new(VCARD_LISTING_BEGIN);
+		l = g_slist_nth(pbap->cache.entries,
+				pbap->params->liststartoffset);
+
+		for (; l; l = l->next) {
+			struct cache_entry *entry = l->data;
+
+			/* FIXME: check if the entry matches */
+
+			g_string_append_printf(pbap->buffer,
+					VCARD_LISTING_ELEMENT,
+					entry->handle, entry->name);
+		}
+
+		pbap->buffer = g_string_append(pbap->buffer, VCARD_LISTING_END);
+	}
+
+	if (!pbap->cache.valid) {
+		pbap->cache.valid = TRUE;
+		obex_object_set_io_flags(pbap, G_IO_IN, 0);
+	}
+}
+
 static struct apparam_field *parse_aparam(const guint8 *buffer, guint32 hlen)
 {
 	struct apparam_field *param;
@@ -351,10 +404,26 @@ static int pbap_get(struct obex_session *os, obex_object_t *obj,
 	if (type == NULL)
 		return -EBADR;
 
-	if (strcmp(type, PHONEBOOK_TYPE) == 0)
+	rsize = obex_aparam_read(os, obj, &buffer);
+	if (rsize < 0)
+		return -EBADR;
+
+	params = parse_aparam(buffer, rsize);
+	if (params == NULL)
+		return -EBADR;
+
+	if (pbap->params) {
+		g_free(pbap->params->searchval);
+		g_free(pbap->params);
+	}
+
+	pbap->params = params;
+
+	if (strcmp(type, PHONEBOOK_TYPE) == 0) {
 		/* Always contains the absolute path */
 		path = g_strdup(name);
-	else if (strcmp(type, VCARDLISTING_TYPE) == 0)
+		*stream = (params->maxlistcount == 0 ? FALSE : TRUE);
+	} else if (strcmp(type, VCARDLISTING_TYPE) == 0) {
 		/* Always relative */
 		if (!name || strlen(name) == 0)
 			/* Current folder */
@@ -363,46 +432,24 @@ static int pbap_get(struct obex_session *os, obex_object_t *obj,
 			/* Current folder + relative path */
 			path = g_build_filename(pbap->folder, name, NULL);
 
-	else if (strcmp(type, VCARDENTRY_TYPE) == 0)
-		/* Always relative */
-		path = g_build_filename(pbap->folder, name, NULL);
-	else
-		return -EBADR;
-
-	rsize = obex_aparam_read(os, obj, &buffer);
-	if (rsize < 0) {
-		ret = -EBADR;
-		goto failed;
-	}
-
-	params = parse_aparam(buffer, rsize);
-	if (params == NULL) {
-		ret = -EBADR;
-		goto failed;
-	}
-
-	if (pbap->params) {
-		g_free(pbap->params->searchval);
-		g_free(pbap->params);
-	}
-
-	if (params->maxlistcount == 0)
-		*stream = FALSE;
-	else
+		*stream = (params->maxlistcount == 0 ? FALSE : TRUE);
+	} else if (strcmp(type, VCARDENTRY_TYPE) == 0) {
+		/* File name only */
+		path = g_strdup(name);
 		*stream = TRUE;
+	} else
+		return -EBADR;
 
 	pbap->params = params;
-
 	ret = obex_get_stream_start(os, path, pbap);
-failed:
+
 	g_free(path);
 
 	return ret;
 }
 
-
 static int pbap_setpath(struct obex_session *os, obex_object_t *obj,
-							gpointer user_data)
+		gpointer user_data)
 {
 	struct pbap_session *pbap = user_data;
 	const gchar *name;
@@ -428,6 +475,13 @@ static int pbap_setpath(struct obex_session *os, obex_object_t *obj,
 
 	set_folder(pbap, fullname);
 
+	/*
+	 * FIXME: Define a criteria to mark the cache as invalid
+	 */
+	pbap->cache.valid = FALSE;
+	pbap->cache.index = 0;
+	cache_clear(&pbap->cache);
+
 	g_free(fullname);
 
 	return 0;
@@ -506,7 +560,6 @@ static gpointer vobject_list_open(const char *name, int oflag, mode_t mode,
 				gpointer context, size_t *size, int *err)
 {
 	struct pbap_session *pbap = context;
-	phonebook_cb cb;
 	int ret;
 
 	if (oflag != O_RDONLY) {
@@ -514,19 +567,24 @@ static gpointer vobject_list_open(const char *name, int oflag, mode_t mode,
 		goto fail;
 	}
 
-	if (pbap->params->maxlistcount == 0)
-		cb = phonebook_size_result;
-	else
-		cb = query_result;
+	/* PullvCardListing always get the contacts from the cache */
 
-	/* FIXME: PullvCardList should read data from the cache only */
-	cache_add(&pbap->cache, NULL);
-	cache_foreach(&pbap->cache, NULL, NULL, NULL);
+	/*
+	 * FIXME: When the name is given and the OBEX non header flags are
+	 * provided it is necessary to check if the cache still valid.
+	 */
+	if (pbap->cache.valid) {
+		cache_ready_notify(pbap);
+		goto done;
+	}
+
+	ret = phonebook_create_cache(name,
+		cache_entry_notify, cache_ready_notify, pbap);
 
-	ret = phonebook_list(name, pbap->params, cb, pbap);
 	if (ret < 0)
 		goto fail;
 
+done:
 	if (size)
 		*size = OBJECT_SIZE_UNKNOWN;
 
@@ -539,12 +597,12 @@ fail:
 	return NULL;
 }
 
-
 static gpointer vobject_vcard_open(const char *name, int oflag, mode_t mode,
-				gpointer context, size_t *size, int *err)
+		gpointer context, size_t *size, int *err)
 {
 	struct pbap_session *pbap = context;
-	gpointer id;
+	const gchar *id;
+	guint32 handle;
 	int ret;
 
 	if (oflag != O_RDONLY) {
@@ -552,14 +610,14 @@ static gpointer vobject_vcard_open(const char *name, int oflag, mode_t mode,
 		goto fail;
 	}
 
-	id = cache_find(&pbap->cache, name);
+	sscanf(name, "%u.vcf", &handle);
+	id = cache_find(&pbap->cache, handle);
 	if (!id) {
 		ret = -ENOENT;
 		goto fail;
 	}
 
-	/* FIXME: use id */
-	ret = phonebook_get_entry(name, pbap->params, query_result, pbap);
+	ret = phonebook_get_entry(id, pbap->params, query_result, pbap);
 	if (ret < 0)
 		goto fail;
 
@@ -575,33 +633,51 @@ fail:
 	return NULL;
 }
 
-static ssize_t vobject_read(gpointer object, void *buf, size_t count,
+static ssize_t vobject_pull_read(gpointer object, void *buf, size_t count,
 								guint8 *hi)
 {
 	struct pbap_session *pbap = object;
-	ssize_t ret = -EAGAIN;
+
+	if (!pbap->buffer)
+		return -EAGAIN;
 
 	/* PhoneBookSize */
-	if (pbap->params->maxlistcount == 0) {
-		guint8 data[4];
-		guint16 phonebooksize;
-		struct aparam_header *hdr = (struct aparam_header *) data;
+	if (pbap->params->maxlistcount == 0)
+		*hi = OBEX_HDR_APPARAM;
+	else
+		/* Stream data */
+		*hi = OBEX_HDR_BODY;
 
-		phonebooksize = htons(pbap->phonebooksize);
-		hdr->tag = PHONEBOOKSIZE_TAG;
-		hdr->len = PHONEBOOKSIZE_LEN;
-		memcpy(hdr->val, &phonebooksize, sizeof(phonebooksize));
+	return string_read(pbap->buffer, buf, count);
+}
+
+static ssize_t vobject_list_read(gpointer object, void *buf, size_t count,
+								guint8 *hi)
+{
+	struct pbap_session *pbap = object;
+
+	/* Backend still busy reading contacts */
+	if (!pbap->cache.valid)
+		return -EAGAIN;
 
-		memcpy(buf, data, sizeof(data));
+	if (pbap->params->maxlistcount == 0)
 		*hi = OBEX_HDR_APPARAM;
-		ret = 0;
-	} else if (pbap->buffer) {
-		/* Stream data */
+	else
 		*hi = OBEX_HDR_BODY;
-		ret = string_read(pbap->buffer, buf, count, hi);
-	}
 
-	return ret;
+	return string_read(pbap->buffer, buf, count);
+}
+
+static ssize_t vobject_vcard_read(gpointer object, void *buf, size_t count,
+								guint8 *hi)
+{
+	struct pbap_session *pbap = object;
+
+	if (!pbap->buffer)
+		return -EAGAIN;
+
+	*hi = OBEX_HDR_BODY;
+	return string_read(pbap->buffer, buf, count);
 }
 
 static int vobject_close(gpointer object)
@@ -621,7 +697,7 @@ static struct obex_mime_type_driver mime_pull = {
 	.mimetype	= "x-bt/phonebook",
 	.open		= vobject_pull_open,
 	.close		= vobject_close,
-	.read		= vobject_read,
+	.read		= vobject_pull_read,
 };
 
 static struct obex_mime_type_driver mime_list = {
@@ -629,7 +705,7 @@ static struct obex_mime_type_driver mime_list = {
 	.mimetype	= "x-bt/vcard-listing",
 	.open		= vobject_list_open,
 	.close		= vobject_close,
-	.read		= vobject_read,
+	.read		= vobject_list_read,
 };
 
 static struct obex_mime_type_driver mime_vcard = {
@@ -637,7 +713,7 @@ static struct obex_mime_type_driver mime_vcard = {
 	.mimetype	= "x-bt/vcard",
 	.open		= vobject_vcard_open,
 	.close		= vobject_close,
-	.read		= vobject_read,
+	.read		= vobject_vcard_read,
 };
 
 static int pbap_init(void)
diff --git a/obexd/plugins/phonebook-dummy.c b/obexd/plugins/phonebook-dummy.c
index 38736a2..02edf2a 100644
--- a/obexd/plugins/phonebook-dummy.c
+++ b/obexd/plugins/phonebook-dummy.c
@@ -85,14 +85,14 @@ int phonebook_pull(const gchar *name, const struct apparam_field *params,
 	return 0;
 }
 
-int phonebook_get_entry(const gchar *name, const struct apparam_field *params,
+int phonebook_get_entry(const gchar *id, const struct apparam_field *params,
 					phonebook_cb cb, gpointer user_data)
 {
 	return -1;
 }
 
-int phonebook_list(const gchar *name, const struct apparam_field *params,
-					phonebook_cb cb, gpointer user_data)
+int phonebook_create_cache(const gchar *name, phonebook_entry_cb entry_cb,
+		phonebook_cache_ready_cb ready_cb, gpointer user_data)
 {
-	return -1;
+	return 0;
 }
diff --git a/obexd/plugins/phonebook-ebook.c b/obexd/plugins/phonebook-ebook.c
index c401a28..7afcf09 100644
--- a/obexd/plugins/phonebook-ebook.c
+++ b/obexd/plugins/phonebook-ebook.c
@@ -41,24 +41,22 @@
 #include "service.h"
 #include "phonebook.h"
 
-#define EOL	"\r\n"
-#define VCARD_LISTING_BEGIN						\
-	"<?xml version=\"1.0\"?>" EOL					\
-	"<!DOCTYPE vcard-listing SYSTEM \"vcard-listing.dtd\">" EOL 	\
-	"<vCard-listing version=\"1.0\">" EOL
-#define VCARD_LISTING_ELEMENT	"<card handle = \"%d.vcf\" name = \"%s\"/>" EOL
-#define VCARD_LISTING_END	"</vCard-listing>"
-
 #define QUERY_FN "(contains \"family_name\" \"%s\")"
 #define QUERY_NAME "(contains \"given_name\" \"%s\")"
 #define QUERY_PHONE "(contains \"phone\" \"%s\")"
 
-struct query_data {
+struct contacts_query {
 	const struct apparam_field *params;
 	phonebook_cb cb;
 	gpointer user_data;
 };
 
+struct cache_query {
+	phonebook_entry_cb entry_cb;
+	phonebook_cache_ready_cb ready_cb;
+	gpointer user_data;
+};
+
 static EBook *ebook = NULL;
 
 static gchar *attribute_mask[] = {
@@ -130,13 +128,21 @@ static gchar *evcard_to_string(EVCard *evcard, guint format, guint64 filter)
 	return vcard;
 }
 
-static void ebookpull_cb(EBook *book, EBookStatus status, GList *contacts,
+static void ebookpull_cb(EBook *book, EBookStatus estatus, GList *contacts,
 							gpointer user_data)
 {
-	struct query_data *data = user_data;
+	struct contacts_query *data = user_data;
 	GString *string = g_string_new("");
-	GList *l = g_list_nth(contacts, data->params->liststartoffset);
-	guint count = 0, maxcount = data->params->maxlistcount;
+	guint count = 0, maxcount;
+	GList *l;
+
+	if (estatus != E_BOOK_ERROR_OK) {
+		error("E-Book query failed: status %d", estatus);
+		goto done;
+	}
+
+	maxcount = data->params->maxlistcount;
+	l = g_list_nth(contacts, data->params->liststartoffset);
 
 	/* FIXME: Missing 0.vcf */
 
@@ -145,19 +151,47 @@ static void ebookpull_cb(EBook *book, EBookStatus status, GList *contacts,
 		EVCard *evcard = E_VCARD(contact);
 		gchar *vcard;
 
-		vcard = evcard_to_string(evcard,
-				data->params->format, data->params->filter);
+		vcard = evcard_to_string(evcard, data->params->format,
+						data->params->filter);
 
 		string = g_string_append(string, vcard);
 		g_free(vcard);
 	}
 
+done:
 	data->cb(string->str, string->len, count, 0, data->user_data);
 
 	g_string_free(string, TRUE);
 	g_free(data);
 }
 
+static void ebook_entry_cb(EBook *book, EBookStatus estatus,
+			EContact *contact, gpointer user_data)
+{
+	struct contacts_query *data = user_data;
+	EVCard *evcard;
+	gchar *vcard;
+	size_t len;
+
+	if (estatus != E_BOOK_ERROR_OK) {
+		error("E-Book query failed: status %d", estatus);
+		data->cb(NULL, 0, 1, 0, data->user_data);
+		g_free(data);
+		return;
+	}
+
+	evcard = E_VCARD(contact);
+
+	vcard = evcard_to_string(evcard, data->params->format,
+					data->params->filter);
+
+	len = vcard ? strlen(vcard) : 0;
+
+	data->cb(vcard, len, 1, 0, data->user_data);
+
+	g_free(data);
+}
+
 static gchar *evcard_name_attribute_to_string(EVCard *evcard)
 {
 	EVCardAttribute *attrib;
@@ -188,36 +222,48 @@ static gchar *evcard_name_attribute_to_string(EVCard *evcard)
 	return g_string_free(name, FALSE);
 }
 
-static void ebooklist_cb(EBook *book, EBookStatus status, GList *contacts,
+static void cache_cb(EBook *book, EBookStatus estatus, GList *contacts,
 							gpointer user_data)
 {
-	struct query_data *data = user_data;
-	GString *string = g_string_new(VCARD_LISTING_BEGIN);
-	GList *l = g_list_nth(contacts, data->params->liststartoffset);
-	guint count = 0, maxcount = data->params->maxlistcount;
+	struct cache_query *data = user_data;
+	GList *l;
 
-	for (; l && count < maxcount; l = g_list_next(l), count++) {
+	if (estatus != E_BOOK_ERROR_OK) {
+		error("E-Book query failed: status %d", estatus);
+		goto done;
+	}
+
+	for (l = contacts; l; l = g_list_next(l)) {
 		EContact *contact = E_CONTACT(l->data);
 		EVCard *evcard = E_VCARD(contact);
-		gchar *element, *name;
+		EVCardAttribute *attrib;
+		gchar *uid, *tel, *name;
 
 		name = evcard_name_attribute_to_string(evcard);
 		if (!name)
 			continue;
 
-		element = g_strdup_printf(VCARD_LISTING_ELEMENT, count, name);
+		attrib = e_vcard_get_attribute(evcard, EVC_UID);
+		if (!attrib)
+			continue;
 
-		string = g_string_append(string, element);
-		g_free(name);
-		g_free(element);
-	}
+		uid =  e_vcard_attribute_get_value(attrib);
+		if (!uid)
+			continue;
 
-	string = g_string_append(string, VCARD_LISTING_END);
+		attrib = e_vcard_get_attribute(evcard, EVC_TEL);
+		if (!attrib)
+			continue;
 
-	data->cb(string->str, string->len, count, 0, data->user_data);
+		tel =  e_vcard_attribute_get_value(attrib);
 
-	g_string_free(string, TRUE);
-	g_free(data);
+		data->entry_cb(uid, name, NULL, tel, data->user_data);
+		g_free(name);
+		g_free(uid);
+		g_free(tel);
+	}
+done:
+	data->ready_cb(data->user_data);
 }
 
 int phonebook_init(void)
@@ -253,7 +299,7 @@ int phonebook_set_folder(const gchar *current_folder,
 	gboolean root, child;
 	int ret;
 
-	root = (!current_folder || strlen(current_folder) == 0);
+	root = (g_strcmp0("/", current_folder) == 0);
 	child = (new_folder && strlen(new_folder) != 0);
 
 	/* Evolution back-end will support telecom/pb folder only */
@@ -266,7 +312,7 @@ int phonebook_set_folder(const gchar *current_folder,
 		/* Go down 1 level */
 		if (root)
 			ret = (strcmp("telecom", new_folder) != 0) ? -EBADR: 0;
-		else if (strcmp("telecom", current_folder) == 0)
+		else if (strcmp("/telecom", current_folder) == 0)
 			ret = (strcmp("pb", new_folder) != 0) ? -EBADR: 0;
 		else
 			ret = -EBADR;
@@ -282,9 +328,9 @@ int phonebook_set_folder(const gchar *current_folder,
 			return 0;
 
 		/* /telecom or /telecom/pb */
-		if (strcmp("telecom", current_folder) == 0)
+		if (strcmp("/telecom", current_folder) == 0)
 			ret = (strcmp("telecom", new_folder) != 0) ? -EBADR : 0;
-		else if (strcmp("telecom/pb", current_folder) == 0)
+		else if (strcmp("/telecom/pb", current_folder) == 0)
 			ret = (strcmp("pb", new_folder) != 0) ? -EBADR : 0;
 		else
 			ret = -EBADR;
@@ -298,14 +344,14 @@ int phonebook_set_folder(const gchar *current_folder,
 }
 
 gint phonebook_pull(const gchar *name, const struct apparam_field *params,
-		phonebook_cb cb, gpointer user_data)
+					phonebook_cb cb, gpointer user_data)
 {
-	struct query_data *data;
+	struct contacts_query *data;
 	EBookQuery *query;
 
 	query = e_book_query_any_field_contains("");
 
-	data = g_new0(struct query_data, 1);
+	data = g_new0(struct contacts_query, 1);
 	data->cb = cb;
 	data->params = params;
 	data->user_data = user_data;
@@ -317,79 +363,41 @@ gint phonebook_pull(const gchar *name, const struct apparam_field *params,
 	return 0;
 }
 
-int phonebook_get_entry(const gchar *name, const struct apparam_field *params,
+int phonebook_get_entry(const gchar *id, const struct apparam_field *params,
 					phonebook_cb cb, gpointer user_data)
 {
-	return -1;
-}
-
-static EBookQuery *create_query(guint8 attrib, guint8 *searchval)
-{
-	EBookQuery *query;
-	gchar *fam, *str, **values;
-
-	if (!searchval || strlen((gchar *) searchval) == 0)
-		return e_book_query_any_field_contains("");
-
-	switch (attrib) {
-	case 0:
-		/* Name */
-		values = g_strsplit((gchar *) searchval, ";", 2);
-
-		str = NULL;
-
-		fam = (values[0] ? g_strdup_printf(QUERY_FN, values[0]) : NULL);
-
-		if (values[1]) {
-			char *given = g_strdup_printf(QUERY_NAME, values[1]);
-
-			str = fam ?
-				g_strconcat(fam, " and ", given, NULL) : given;
+	struct contacts_query *data;
 
-			g_free(fam);
-			g_free(given);
-		}
-
-		if (!str)
-			str = fam;
+	data = g_new0(struct contacts_query, 1);
+	data->cb = cb;
+	data->params = params;
+	data->user_data = user_data;
 
-		g_strfreev(values);
-		break;
-	case 1:
-		/* Number */
-		str = g_strdup_printf(QUERY_PHONE, searchval);
-		break;
-	case 2:
-		/* Sound */
-		/* TODO: not yet implemented */
-	default:
-		return NULL;
+	if (e_book_async_get_contact(ebook, id, ebook_entry_cb, data)) {
+		g_free(data);
+		return -EPERM;
 	}
 
-	query = e_book_query_from_string(str);
-	g_free(str);
-
-	return query;
+	return 0;
 }
 
-int phonebook_list(const gchar *name, const struct apparam_field *params,
-					phonebook_cb cb, gpointer user_data)
+int phonebook_create_cache(const gchar *name, phonebook_entry_cb entry_cb,
+		phonebook_cache_ready_cb ready_cb, gpointer user_data)
 {
-	struct query_data *data;
+	struct cache_query *data;
 	EBookQuery *query;
+	gboolean ret;
 
-	query = create_query(params->searchattrib, params->searchval);
-	if (!query)
-		return -EBADR;
+	query = e_book_query_any_field_contains("");
 
-	data = g_new0(struct query_data, 1);
-	data->cb = cb;
-	data->params = params;
+	data = g_new0(struct cache_query, 1);
+	data->entry_cb = entry_cb;
+	data->ready_cb = ready_cb;
 	data->user_data = user_data;
 
-	e_book_async_get_contacts(ebook, query, ebooklist_cb, data);
+	ret = e_book_async_get_contacts(ebook, query, cache_cb, data);
 
 	e_book_query_unref(query);
 
-	return 0;
+	return (ret == FALSE ? 0 : -EBADR);
 }
diff --git a/obexd/plugins/phonebook.h b/obexd/plugins/phonebook.h
index b0878fb..5d3e33d 100644
--- a/obexd/plugins/phonebook.h
+++ b/obexd/plugins/phonebook.h
@@ -21,6 +21,14 @@
  *
  */
 
+#define EOL	"\r\n"
+#define VCARD_LISTING_BEGIN \
+	"<?xml version=\"1.0\"?>"				EOL\
+	"<!DOCTYPE vcard-listing SYSTEM \"vcard-listing.dtd\">" EOL\
+	"<vCard-listing version=\"1.0\">"			EOL
+#define VCARD_LISTING_ELEMENT		"<card handle = \"%d.vcf\" name = \"%s\"/>" EOL
+#define VCARD_LISTING_END	"</vCard-listing>"
+
 struct apparam_field {
 	/* list and pull attributes */
 	guint16		maxlistcount;
@@ -36,20 +44,47 @@ struct apparam_field {
 	guint8		*searchval;
 };
 
+/*
+ * Interface between the PBAP core and backends to retrieve
+ * all contacts that match the application parameters rules.
+ * Contacts will be returned in the vcard format.
+ */
 typedef void (*phonebook_cb) (const gchar *buffer, size_t bufsize,
 		gint vcards, gint missed, gpointer user_data);
 
+/*
+ * Interface between the PBAP core and backends to
+ * append a new entry in the PBAP folder cache.
+ */
+typedef void (*phonebook_entry_cb) (const gchar *id, const gchar *name,
+		const gchar *sound, const gchar *tel, gpointer user_data);
+
+/*
+ * After notify all entries to PBAP core, the backend
+ * needs to notify that the operation has finished.
+ */
+typedef void (*phonebook_cache_ready_cb) (gpointer user_data);
+
+
 int phonebook_init(void);
 void phonebook_exit(void);
 
 int phonebook_set_folder(const gchar *current_folder,
 		const gchar *new_folder, guint8 flags);
 
+/*
+ * PullPhoneBook never use cached entries. PCE use this
+ * function to get all entries of a given folder.
+ */
 int phonebook_pull(const gchar *name, const struct apparam_field *params,
 		phonebook_cb cb, gpointer user_data);
 
-int phonebook_get_entry(const gchar *name, const struct apparam_field *params,
+int phonebook_get_entry(const gchar *id, const struct apparam_field *params,
 		phonebook_cb cb, gpointer user_data);
 
-int phonebook_list(const gchar *name, const struct apparam_field *params,
-		phonebook_cb cb, gpointer user_data);
+/*
+ * PBAP core will keep the contacts cache per folder. SetPhoneBook or
+ * PullvCardListing can invalidate the cache if the current folder changes.
+ */
+int phonebook_create_cache(const gchar *name, phonebook_entry_cb entry_cb,
+		phonebook_cache_ready_cb ready_cb, gpointer user_data);
diff --git a/obexd/plugins/syncevolution.c b/obexd/plugins/syncevolution.c
index a646cc6..7a76a49 100644
--- a/obexd/plugins/syncevolution.c
+++ b/obexd/plugins/syncevolution.c
@@ -339,8 +339,10 @@ static ssize_t synce_read(gpointer object, void *buf, size_t count, guint8 *hi)
 	gboolean authenticate;
 	DBusPendingCall *call;
 
-	if (context->buffer)
-		return string_read(context->buffer, buf, count, hi);
+	if (context->buffer) {
+		*hi = OBEX_HDR_BODY;
+		return string_read(context->buffer, buf, count);
+	}
 
 	conn = obex_dbus_get_connection();
 	if (conn == NULL)