2011-02-09 07:38:45

by Radoslaw Jablonski

[permalink] [raw]
Subject: [PATCH 1/3] Add support for notyfying pbap about more parts from backend

Added new parameter to phonebook_cb - lastpart variable.
If backend want to notify that more parts of current
request will be delivered later, it should use lastpart=FALSE.
Because of that, PBAP core will 'know' that should not finalize
request immediately after receiving data and wait for more
parts to come.
If result is returned in one part and no more responses part
will be sent later, then backend should use lastparam=TRUE.
Previously results of request from backend was always returned
in one part to PBAP core.
---
plugins/irmc.c | 5 +++--
plugins/pbap.c | 10 +++++++---
plugins/phonebook-dummy.c | 4 ++--
plugins/phonebook-ebook.c | 7 ++++---
plugins/phonebook-tracker.c | 13 +++++++------
plugins/phonebook.h | 2 +-
6 files changed, 24 insertions(+), 17 deletions(-)

diff --git a/plugins/irmc.c b/plugins/irmc.c
index 0488cae..e1e83f9 100644
--- a/plugins/irmc.c
+++ b/plugins/irmc.c
@@ -133,7 +133,8 @@ static const char *owner_vcard =
"END:VCARD\r\n";

static void phonebook_size_result(const char *buffer, size_t bufsize,
- int vcards, int missed, void *user_data)
+ int vcards, int missed,
+ gboolean lastpart, void *user_data)
{
struct irmc_session *irmc = user_data;

@@ -148,7 +149,7 @@ static void phonebook_size_result(const char *buffer, size_t bufsize,
}

static void query_result(const char *buffer, size_t bufsize, int vcards,
- int missed, void *user_data)
+ int missed, gboolean lastpart, void *user_data)
{
struct irmc_session *irmc = user_data;
const char *s, *t;
diff --git a/plugins/pbap.c b/plugins/pbap.c
index 6579d09..5775eea 100644
--- a/plugins/pbap.c
+++ b/plugins/pbap.c
@@ -148,6 +148,7 @@ struct pbap_object {
GString *buffer;
GByteArray *aparams;
gboolean firstpacket;
+ gboolean lastpart;
struct pbap_session *session;
void *request;
};
@@ -254,7 +255,8 @@ static GByteArray *append_aparam_header(GByteArray *buf, uint8_t tag,
}

static void phonebook_size_result(const char *buffer, size_t bufsize,
- int vcards, int missed, void *user_data)
+ int vcards, int missed,
+ gboolean lastpart, void *user_data)
{
struct pbap_session *pbap = user_data;
uint16_t phonebooksize;
@@ -286,17 +288,19 @@ static void phonebook_size_result(const char *buffer, size_t bufsize,
}

static void query_result(const char *buffer, size_t bufsize, int vcards,
- int missed, void *user_data)
+ int missed, gboolean lastpart, void *user_data)
{
struct pbap_session *pbap = user_data;

DBG("");

- if (pbap->obj->request) {
+ if (pbap->obj->request && lastpart) {
phonebook_req_finalize(pbap->obj->request);
pbap->obj->request = NULL;
}

+ pbap->obj->lastpart = lastpart;
+
if (vcards <= 0) {
obex_object_set_io_flags(pbap->obj, G_IO_ERR, -ENOENT);
return;
diff --git a/plugins/phonebook-dummy.c b/plugins/phonebook-dummy.c
index 60b7640..76dd550 100644
--- a/plugins/phonebook-dummy.c
+++ b/plugins/phonebook-dummy.c
@@ -248,7 +248,7 @@ static gboolean read_dir(void *user_data)
closedir(dp);
done:
/* FIXME: Missing vCards fields filtering */
- dummy->cb(buffer->str, buffer->len, count, 0, dummy->user_data);
+ dummy->cb(buffer->str, buffer->len, count, 0, TRUE, dummy->user_data);

g_string_free(buffer, TRUE);

@@ -346,7 +346,7 @@ static gboolean read_entry(void *user_data)

/* FIXME: Missing vCards fields filtering */

- dummy->cb(buffer, count, 1, 0, dummy->user_data);
+ dummy->cb(buffer, count, 1, 0, TRUE, dummy->user_data);

return FALSE;
}
diff --git a/plugins/phonebook-ebook.c b/plugins/phonebook-ebook.c
index 70b9c02..6cc4f31 100644
--- a/plugins/phonebook-ebook.c
+++ b/plugins/phonebook-ebook.c
@@ -186,7 +186,8 @@ static void ebookpull_cb(EBook *book, EBookStatus estatus, GList *contacts,

done:
data->completed = TRUE;
- data->contacts_cb(string->str, string->len, count, 0, data->user_data);
+ data->contacts_cb(string->str, string->len, count, 0, TRUE,
+ data->user_data);

fail:
g_string_free(string, TRUE);
@@ -212,7 +213,7 @@ static void ebook_entry_cb(EBook *book, EBookStatus estatus,

if (estatus != E_BOOK_ERROR_OK) {
error("E-Book query failed: status %d", estatus);
- data->contacts_cb(NULL, 0, 1, 0, data->user_data);
+ data->contacts_cb(NULL, 0, 1, 0, TRUE, data->user_data);
goto fail;
}

@@ -223,7 +224,7 @@ static void ebook_entry_cb(EBook *book, EBookStatus estatus,

len = vcard ? strlen(vcard) : 0;

- data->contacts_cb(vcard, len, 1, 0, data->user_data);
+ data->contacts_cb(vcard, len, 1, 0, TRUE, data->user_data);

g_free(vcard);

diff --git a/plugins/phonebook-tracker.c b/plugins/phonebook-tracker.c
index f00d23c..e3f26a1 100644
--- a/plugins/phonebook-tracker.c
+++ b/plugins/phonebook-tracker.c
@@ -1342,7 +1342,7 @@ static int pull_contacts_size(const char **reply, int num_fields,
struct phonebook_data *data = user_data;

if (num_fields < 0) {
- data->cb(NULL, 0, num_fields, 0, data->user_data);
+ data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data);
return -1;
}

@@ -1351,7 +1351,8 @@ static int pull_contacts_size(const char **reply, int num_fields,
return 0;
}

- data->cb(NULL, 0, data->index, data->newmissedcalls, data->user_data);
+ data->cb(NULL, 0, data->index, data->newmissedcalls, TRUE,
+ data->user_data);

return -1;
/*
@@ -1569,7 +1570,7 @@ static int pull_contacts(const char **reply, int num_fields, void *user_data)
static char *temp_id = NULL;

if (num_fields < 0) {
- data->cb(NULL, 0, num_fields, 0, data->user_data);
+ data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data);
goto fail;
}

@@ -1649,7 +1650,7 @@ done:
vcards = gen_vcards(data->contacts, params);

data->cb(vcards->str, vcards->len, g_slist_length(data->contacts),
- data->newmissedcalls, data->user_data);
+ data->newmissedcalls, TRUE, data->user_data);

g_string_free(vcards, TRUE);
fail:
@@ -1876,7 +1877,7 @@ done:
data->contacts = NULL;

if (num_fields < 0) {
- data->cb(NULL, 0, num_fields, 0, data->user_data);
+ data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data);
return -1;
}

@@ -1892,7 +1893,7 @@ done:

query_tracker(query, col_amount, pull_cb, data, &err);
if (err < 0)
- data->cb(NULL, 0, err, 0, data->user_data);
+ data->cb(NULL, 0, err, 0, TRUE, data->user_data);

return -1;
}
diff --git a/plugins/phonebook.h b/plugins/phonebook.h
index bfbae0f..f6df164 100644
--- a/plugins/phonebook.h
+++ b/plugins/phonebook.h
@@ -50,7 +50,7 @@ struct apparam_field {
* Contacts will be returned in the vcard format.
*/
typedef void (*phonebook_cb) (const char *buffer, size_t bufsize,
- int vcards, int missed, void *user_data);
+ int vcards, int missed, gboolean lastpart, void *user_data);

/*
* Interface between the PBAP core and backends to
--
1.7.0.4



2011-02-09 22:00:28

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH 1/3] Add support for notyfying pbap about more parts from backend

Hi Radek,

On Wed, Feb 09, 2011, Radoslaw Jablonski wrote:
> Added new parameter to phonebook_cb - lastpart variable.
> If backend want to notify that more parts of current
> request will be delivered later, it should use lastpart=FALSE.
> Because of that, PBAP core will 'know' that should not finalize
> request immediately after receiving data and wait for more
> parts to come.
> If result is returned in one part and no more responses part
> will be sent later, then backend should use lastparam=TRUE.
> Previously results of request from backend was always returned
> in one part to PBAP core.
> ---
> plugins/irmc.c | 5 +++--
> plugins/pbap.c | 10 +++++++---
> plugins/phonebook-dummy.c | 4 ++--
> plugins/phonebook-ebook.c | 7 ++++---
> plugins/phonebook-tracker.c | 13 +++++++------
> plugins/phonebook.h | 2 +-
> 6 files changed, 24 insertions(+), 17 deletions(-)

It seems these patches depend on your previous ones which weren't ready
for upstream, so I suppose they'll need to be fixed and resent (once
you've done the necessary changes to the first ones).

Johan

2011-02-09 07:38:46

by Radoslaw Jablonski

[permalink] [raw]
Subject: [PATCH 2/3] Introduction of phonebook_pull_read

Previosly reading from backend was initialized in phonebook_pull.
Now phonebook_pull should be used only for preparing request data
and phonebook_pull_read for 'real' reading vcards data from back-end.
The back-end can return data in one response or it can return data in
many parts. After obtaining one part, PBAP core need to call
phonebook_pull_read with the same request again to get more results.
Using that, PBAP core has control of its the buffer size - it can
ask for new parts of data when buffer is empty or when its size
will be lower than some level.
---
plugins/pbap.c | 23 +++++++++++++++++++-
plugins/phonebook-dummy.c | 32 +++++++++++++++++++++------
plugins/phonebook-ebook.c | 24 ++++++++++++++++----
plugins/phonebook-tracker.c | 50 +++++++++++++++++++++++++++++-------------
plugins/phonebook.h | 18 ++++++++++++---
5 files changed, 114 insertions(+), 33 deletions(-)

diff --git a/plugins/pbap.c b/plugins/pbap.c
index 5775eea..7d46f91 100644
--- a/plugins/pbap.c
+++ b/plugins/pbap.c
@@ -806,6 +806,12 @@ static void *vobject_pull_open(const char *name, int oflag, mode_t mode,
if (ret < 0)
goto fail;

+ /* reading first part of results from backend */
+ phonebook_pull_read(request, &ret);
+
+ if (ret < 0)
+ goto fail;
+
if (err)
*err = 0;

@@ -962,6 +968,7 @@ static ssize_t vobject_pull_read(void *object, void *buf, size_t count,
{
struct pbap_object *obj = object;
struct pbap_session *pbap = obj->session;
+ int len, err;

DBG("buffer %p maxlistcount %d", obj->buffer,
pbap->params->maxlistcount);
@@ -987,7 +994,21 @@ static ssize_t vobject_pull_read(void *object, void *buf, size_t count,
*hi = OBEX_HDR_BODY;
if (flags)
*flags = 0;
- return string_read(obj->buffer, buf, count);
+
+ len = string_read(obj->buffer, buf, count);
+
+ if (len == 0 && !obj->lastpart) {
+ /* in case when buffer is empty and we know that more
+ * data is still available in backend, requesting new
+ * data part via phonebook_pull_read and returning
+ * -EAGAIN to suspend request for now */
+ phonebook_pull_read(obj->request, &err);
+ if (err)
+ return -EPERM;
+
+ return -EAGAIN;
+ } else
+ return len;
}
}

diff --git a/plugins/phonebook-dummy.c b/plugins/phonebook-dummy.c
index 76dd550..0bf9132 100644
--- a/plugins/phonebook-dummy.c
+++ b/plugins/phonebook-dummy.c
@@ -52,6 +52,7 @@ struct dummy_data {
const struct apparam_field *apparams;
char *folder;
int fd;
+ guint id;
};

struct cache_query {
@@ -449,9 +450,12 @@ done:

void phonebook_req_finalize(void *request)
{
- guint id = GPOINTER_TO_INT(request);
+ struct dummy_data *dummy = request;

- g_source_remove(id);
+ /* dummy_data will be cleaned when request will be finished via
+ * g_source_remove */
+ if (dummy && dummy->id)
+ g_source_remove(dummy->id);
}

void *phonebook_pull(const char *name, const struct apparam_field *params,
@@ -459,7 +463,6 @@ void *phonebook_pull(const char *name, const struct apparam_field *params,
{
struct dummy_data *dummy;
char *filename, *folder;
- guint ret;

/*
* Main phonebook objects will be created dinamically based on the
@@ -492,13 +495,28 @@ void *phonebook_pull(const char *name, const struct apparam_field *params,
dummy->folder = folder;
dummy->fd = -1;

- ret = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_dir, dummy,
- dummy_free);
-
if (err)
*err = 0;

- return GINT_TO_POINTER(ret);
+ return dummy;
+}
+
+void phonebook_pull_read(void *request, int *err)
+{
+ struct dummy_data *dummy = request;
+
+ if (!dummy) {
+ if (err)
+ *err = -ENOENT;
+
+ return;
+ }
+
+ dummy->id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_dir, dummy,
+ dummy_free);
+
+ if (err)
+ *err = 0;
}

void *phonebook_get_entry(const char *folder, const char *id,
diff --git a/plugins/phonebook-ebook.c b/plugins/phonebook-ebook.c
index 6cc4f31..4a222a8 100644
--- a/plugins/phonebook-ebook.c
+++ b/plugins/phonebook-ebook.c
@@ -453,23 +453,37 @@ void *phonebook_pull(const char *name, const struct apparam_field *params,
phonebook_cb cb, void *user_data, int *err)
{
struct query_context *data;
- EBookQuery *query;
-
- query = e_book_query_any_field_contains("");

data = g_new0(struct query_context, 1);
data->contacts_cb = cb;
data->params = params;
data->user_data = user_data;

+ if (err)
+ *err = 0;
+
+ return data;
+}
+
+void phonebook_pull_read(void *request, int *err)
+{
+ struct query_context *data = request;
+ EBookQuery *query;
+
+ if (!data) {
+ if (err)
+ *err = -ENOENT;
+
+ return;
+ }
+
+ query = e_book_query_any_field_contains("");
e_book_async_get_contacts(ebook, query, ebookpull_cb, data);

e_book_query_unref(query);

if (err)
*err = 0;
-
- return data;
}

void *phonebook_get_entry(const char *folder, const char *id,
diff --git a/plugins/phonebook-tracker.c b/plugins/phonebook-tracker.c
index e3f26a1..d632744 100644
--- a/plugins/phonebook-tracker.c
+++ b/plugins/phonebook-tracker.c
@@ -918,6 +918,7 @@ struct phonebook_data {
phonebook_entry_cb entry_cb;
int newmissedcalls;
GCancellable *query_canc;
+ char *req_name;
};

struct phonebook_index {
@@ -1827,6 +1828,7 @@ void phonebook_req_finalize(void *request)
}

g_slist_free(data->contacts);
+ g_free(data->req_name);
g_free(data);
}

@@ -1902,39 +1904,55 @@ void *phonebook_pull(const char *name, const struct apparam_field *params,
phonebook_cb cb, void *user_data, int *err)
{
struct phonebook_data *data;
- const char *query;
+
+ DBG("name %s", name);
+
+ data = g_new0(struct phonebook_data, 1);
+ data->params = params;
+ data->user_data = user_data;
+ data->cb = cb;
+ data->req_name = g_strdup(name);
+
+ if (err)
+ *err = 0;
+
+ return data;
+}
+
+void phonebook_pull_read(void *request, int *err)
+{
+ struct phonebook_data *data = request;
reply_list_foreach_t pull_cb;
+ const char *query;
int col_amount;

- DBG("name %s", name);
+ if(!data)
+ goto failed;

- if (g_strcmp0(name, "telecom/mch.vcf") == 0) {
+ if (g_strcmp0(data->req_name, "telecom/mch.vcf") == 0) {
query = NEW_MISSED_CALLS_LIST;
col_amount = PULL_QUERY_COL_AMOUNT;
pull_cb = pull_newmissedcalls;
- } else if (params->maxlistcount == 0) {
- query = name2count_query(name);
+ } else if (data->params->maxlistcount == 0) {
+ query = name2count_query(data->req_name);
col_amount = COUNT_QUERY_COL_AMOUNT;
pull_cb = pull_contacts_size;
} else {
- query = name2query(name);
+ query = name2query(data->req_name);
col_amount = PULL_QUERY_COL_AMOUNT;
pull_cb = pull_contacts;
}

- if (query == NULL) {
- if (err)
- *err = -ENOENT;
- return NULL;
- }
+ if (query == NULL)
+ goto failed;

- data = g_new0(struct phonebook_data, 1);
- data->params = params;
- data->user_data = user_data;
- data->cb = cb;
query_tracker(query, col_amount, pull_cb, data, err);

- return data;
+ return;
+
+failed:
+ if (err)
+ *err = -ENOENT;
}

void *phonebook_get_entry(const char *folder, const char *id,
diff --git a/plugins/phonebook.h b/plugins/phonebook.h
index f6df164..cbc0e38 100644
--- a/plugins/phonebook.h
+++ b/plugins/phonebook.h
@@ -82,17 +82,27 @@ char *phonebook_set_folder(const char *current_folder,
const char *new_folder, uint8_t flags, int *err);

/*
- * PullPhoneBook never use cached entries. PCE use this function to get all
- * entries of a given folder. The back-end MUST return only the content based
- * on the application parameters requested by the client.
+ * phonebook_pull should be used only to prepare pull request - prepared
+ * request data is returned by this function. Start of fetching data from
+ * back-end will be done only after calling phonebook_pull_read with this
+ * returned request given as a parameter.
*
- * Return value is a pointer to asynchronous request to phonebook back-end.
* phonebook_req_finalize MUST always be used to free associated resources.
*/
void *phonebook_pull(const char *name, const struct apparam_field *params,
phonebook_cb cb, void *user_data, int *err);

/*
+ * phonebook_pull_read should be used to start getting results from back-end.
+ * The back-end can return data as one response or can return it many parts.
+ * After obtaining one part, PBAP core need to call phonebook_pull_read with
+ * the same request again to get more results from back-end.
+ * The back-end MUST return only the content based on the application
+ * parameters requested by the client.
+ */
+void phonebook_pull_read(void *request, int *err);
+
+/*
* Function used to retrieve a contact from the backend. Only contacts
* found in the cache are requested to the back-ends. The back-end MUST
* return only the content based on the application parameters requested
--
1.7.0.4


2011-02-09 07:38:47

by Radoslaw Jablonski

[permalink] [raw]
Subject: [PATCH 3/3] Support for multipart response sending from phonebook-tracker

Now data are being sent with smaller parts - vcard amount for
one part is defined in VCARDS_PART_COUNT. When one part of
data is sent, then data download from tracker is stopped.
It will be resumed when phonebook_pull_read will be called again.
This is needed to start sending data from PBAP quicker - now
transfer can be started when first part of data is processed.
Previously transfer was started when all results for response
were downloaded, and (for large responses) some PBAP clients
were disconnecting due to timeout.
---
plugins/phonebook-tracker.c | 96 +++++++++++++++++++++++++++++++++++--------
1 files changed, 78 insertions(+), 18 deletions(-)

diff --git a/plugins/phonebook-tracker.c b/plugins/phonebook-tracker.c
index d632744..41643d8 100644
--- a/plugins/phonebook-tracker.c
+++ b/plugins/phonebook-tracker.c
@@ -81,6 +81,8 @@
#define MAIN_DELIM "\30" /* Main delimiter between phones, addresses, emails*/
#define SUB_DELIM "\31" /* Delimiter used in telephone number strings*/
#define MAX_FIELDS 100 /* Max amount of fields to be concatenated at once*/
+#define VCARDS_PART_COUNT 50 /* amount of vcards sent at once to PBAP core */
+#define QUERY_OFFSET_FORMAT "%s OFFSET %d"

#define CONTACTS_QUERY_ALL \
"SELECT " \
@@ -919,6 +921,8 @@ struct phonebook_data {
int newmissedcalls;
GCancellable *query_canc;
char *req_name;
+ int vcard_part_count;
+ int tracker_index;
};

struct phonebook_index {
@@ -1559,15 +1563,45 @@ static void contact_add_organization(struct phonebook_contact *contact,
add_affiliation(&contact->role, reply[COL_ORG_ROLE]);
}

+static void free_data_contacts(struct phonebook_data *data)
+{
+ GSList *l;
+
+ /* freeing contacts */
+ for (l = data->contacts; l; l = l->next) {
+ struct contact_data *c_data = l->data;
+
+ g_free(c_data->id);
+ phonebook_contact_free(c_data->contact);
+ g_free(c_data);
+ }
+
+ g_slist_free(data->contacts);
+ data->contacts = NULL;
+}
+
+static void send_pull_part(struct phonebook_data *data,
+ const struct apparam_field *params, gboolean lastpart)
+{
+ GString *vcards;
+
+ DBG("");
+ vcards = gen_vcards(data->contacts, params);
+ data->cb(vcards->str, vcards->len, g_slist_length(data->contacts),
+ data->newmissedcalls, lastpart, data->user_data);
+
+ free_data_contacts(data);
+ g_string_free(vcards, TRUE);
+}
+
static int pull_contacts(const char **reply, int num_fields, void *user_data)
{
struct phonebook_data *data = user_data;
const struct apparam_field *params = data->params;
struct phonebook_contact *contact;
struct contact_data *contact_data;
- GString *vcards;
int last_index, i;
- gboolean cdata_present = FALSE;
+ gboolean cdata_present = FALSE, part_sent = FALSE;
static char *temp_id = NULL;

if (num_fields < 0) {
@@ -1576,6 +1610,7 @@ static int pull_contacts(const char **reply, int num_fields, void *user_data)
}

DBG("reply %p", reply);
+ data->tracker_index++;

if (reply == NULL)
goto done;
@@ -1608,6 +1643,25 @@ static int pull_contacts(const char **reply, int num_fields, void *user_data)
data->index++;
g_free(temp_id);
temp_id = g_strdup(reply[CONTACTS_ID_COL]);
+
+ /* Increment counter for vcards in current part of data,
+ * but only if liststartoffset has been already reached */
+ if (data->index > params->liststartoffset)
+ data->vcard_part_count++;
+ }
+
+ if (data->vcard_part_count > VCARDS_PART_COUNT) {
+ DBG("Part of vcard data ready for sending...");
+ data->vcard_part_count = 0;
+ /* Sending part of data to PBAP core - more data can be still
+ * fetched, so marking lastpart as FALSE */
+ send_pull_part(data, params, FALSE);
+
+ /* Later, after adding contact data, need to return -1 to stop
+ * fetching more data for this request. Data will be downloaded
+ * again from this point, when phonebook_pull_read will be
+ * called again with current request as a parameter*/
+ part_sent = TRUE;
}

last_index = params->liststartoffset + params->maxlistcount;
@@ -1645,15 +1699,16 @@ add_numbers:
data->contacts = g_slist_append(data->contacts, contact_data);
}

+ if (part_sent)
+ return -1;
+
return 0;

done:
- vcards = gen_vcards(data->contacts, params);
+ /* Processing is end, this is definitely last part of transmission
+ * (marking lastpart as TRUE) */
+ send_pull_part(data, params, TRUE);

- data->cb(vcards->str, vcards->len, g_slist_length(data->contacts),
- data->newmissedcalls, TRUE, data->user_data);
-
- g_string_free(vcards, TRUE);
fail:
g_free(temp_id);
temp_id = NULL;
@@ -1805,7 +1860,6 @@ done:
void phonebook_req_finalize(void *request)
{
struct phonebook_data *data = request;
- GSList *l;

DBG("");

@@ -1818,16 +1872,7 @@ void phonebook_req_finalize(void *request)
g_object_unref(data->query_canc);
}

- /* freeing contacts */
- for (l = data->contacts; l; l = l->next) {
- struct contact_data *c_data = l->data;
-
- g_free(c_data->id);
- phonebook_contact_free(c_data->contact);
- g_free(c_data);
- }
-
- g_slist_free(data->contacts);
+ free_data_contacts(data);
g_free(data->req_name);
g_free(data);
}
@@ -1924,6 +1969,7 @@ void phonebook_pull_read(void *request, int *err)
struct phonebook_data *data = request;
reply_list_foreach_t pull_cb;
const char *query;
+ char *offset_query;
int col_amount;

if(!data)
@@ -1946,6 +1992,20 @@ void phonebook_pull_read(void *request, int *err)
if (query == NULL)
goto failed;

+ if (pull_cb == pull_contacts && data->tracker_index > 0) {
+ /* Adding offset to pull query to download next parts of data
+ * from tracker (phonebook_pull_read may be called many times
+ * from PBAP core to fetch data partially) */
+ offset_query = g_strdup_printf(QUERY_OFFSET_FORMAT,
+ name2query(data->req_name),
+ data->tracker_index);
+ query_tracker(offset_query, col_amount, pull_cb, data, err);
+
+ g_free(offset_query);
+
+ return;
+ }
+
query_tracker(query, col_amount, pull_cb, data, err);

return;
--
1.7.0.4