diff --git a/obexd/plugins/irmc.c b/obexd/plugins/irmc.c
index f7ad33b..a281341 100644
--- a/obexd/plugins/irmc.c
+++ b/obexd/plugins/irmc.c
char did[DID_LEN];
char manu[DID_LEN];
char model[DID_LEN];
+ void *request;
};
#define IRMC_TARGET_SIZE 9
DBG("vcards %d", vcards);
irmc->params->maxlistcount = vcards;
+
+ if (irmc->request) {
+ phonebook_req_finalize(irmc->request);
+ irmc->request = NULL;
+ }
}
static void query_result(const char *buffer, size_t bufsize, int vcards,
DBG("bufsize %zu vcards %d missed %d", bufsize, vcards, missed);
+ if (irmc->request) {
+ phonebook_req_finalize(irmc->request);
+ irmc->request = NULL;
+ }
+
/* first add a 'owner' vcard */
if (!irmc->buffer)
irmc->buffer = g_string_new(owner_vcard);
param->maxlistcount = 0; /* to count the number of vcards... */
param->filter = 0x200085; /* UID TEL N VERSION */
irmc->params = param;
- phonebook_pull("telecom/pb.vcf", irmc->params, phonebook_size_result,
- irmc);
-
- if (err)
- *err = 0;
+ irmc->request = phonebook_pull("telecom/pb.vcf", irmc->params,
+ phonebook_size_result, irmc, err);
return irmc;
}
if (!g_strcmp0(name, ".vcf")) {
/* how can we tell if the vcard count call already finished? */
- ret = phonebook_pull("telecom/pb.vcf", irmc->params,
- query_result, irmc);
+ irmc->request = phonebook_pull("telecom/pb.vcf", irmc->params,
+ query_result, irmc, &ret);
if (ret < 0) {
DBG("phonebook_pull failed...");
goto fail;
irmc->buffer = NULL;
}
+ if (irmc->request) {
+ phonebook_req_finalize(irmc->request);
+ irmc->request = NULL;
+ }
+
return 0;
}
diff --git a/obexd/plugins/pbap.c b/obexd/plugins/pbap.c
index 4086227..820e35c 100644
--- a/obexd/plugins/pbap.c
+++ b/obexd/plugins/pbap.c
struct pbap_object {
GString *buffer;
struct pbap_session *session;
+ void *request;
};
static const uint8_t PBAP_TARGET[TARGET_SIZE] = {
struct aparam_header *hdr = (struct aparam_header *) aparam;
uint16_t phonebooksize;
+ if (pbap->obj->request) {
+ phonebook_req_finalize(pbap->obj->request);
+ pbap->obj->request = NULL;
+ }
+
if (vcards < 0)
vcards = 0;
DBG("");
+ if (pbap->obj->request) {
+ phonebook_req_finalize(pbap->obj->request);
+ pbap->obj->request = NULL;
+ }
+
if (vcards <= 0) {
obex_object_set_io_flags(pbap->obj, G_IO_ERR, -ENOENT);
return;
return;
}
- ret = phonebook_get_entry(pbap->folder, id, pbap->params,
- query_result, pbap);
+ /* Unref previous request, associated data will be freed. */
+ phonebook_req_finalize(pbap->obj->request);
+ /* Get new pointer to pending call. */
+ pbap->obj->request = phonebook_get_entry(pbap->folder, id,
+ pbap->params, query_result, pbap, &ret);
if (ret < 0)
obex_object_set_io_flags(pbap->obj, G_IO_ERR, ret);
}
.chkput = pbap_chkput
};
-static struct pbap_object *vobject_create(struct pbap_session *pbap)
+static struct pbap_object *vobject_create(struct pbap_session *pbap,
+ void *request)
{
struct pbap_object *obj;
obj = g_new0(struct pbap_object, 1);
obj->session = pbap;
pbap->obj = obj;
+ obj->request = request;
return obj;
}
struct pbap_session *pbap = context;
phonebook_cb cb;
int ret;
+ void *request;
DBG("name %s context %p maxlistcount %d", name, context,
pbap->params->maxlistcount);
else
cb = query_result;
- ret = phonebook_pull(name, pbap->params, cb, pbap);
+ request = phonebook_pull(name, pbap->params, cb, pbap, &ret);
+
if (ret < 0)
goto fail;
- return vobject_create(pbap);
+ if (err)
+ *err = 0;
+
+ return vobject_create(pbap, request);
fail:
if (err)
if (obj->buffer)
g_string_free(obj->buffer, TRUE);
+ if (obj->request) {
+ phonebook_req_finalize(obj->request);
+ obj->request = NULL;
+ }
+
g_free(obj);
return 0;
struct pbap_session *pbap = context;
struct pbap_object *obj = NULL;
int ret;
+ void *request;
DBG("name %s context %p valid %d", name, context, pbap->cache.valid);
/* PullvCardListing always get the contacts from the cache */
- obj = vobject_create(pbap);
-
- if (pbap->cache.valid)
+ if (pbap->cache.valid) {
+ obj = vobject_create(pbap, NULL);
ret = generate_response(pbap);
- else
- ret = phonebook_create_cache(name, cache_entry_notify,
- cache_ready_notify, pbap);
+ } else {
+ request = phonebook_create_cache(name, cache_entry_notify,
+ cache_ready_notify, pbap, &ret);
+ if (ret == 0)
+ obj = vobject_create(pbap, request);
+ }
if (ret < 0)
goto fail;
const char *id;
uint32_t handle;
int ret;
+ void *request;
DBG("name %s context %p valid %d", name, context, pbap->cache.valid);
if (pbap->cache.valid == FALSE) {
pbap->find_handle = handle;
- ret = phonebook_create_cache(pbap->folder, cache_entry_notify,
- cache_entry_done, pbap);
+ request = phonebook_create_cache(pbap->folder,
+ cache_entry_notify, cache_entry_done, pbap, &ret);
goto done;
}
goto fail;
}
- ret = phonebook_get_entry(pbap->folder, id, pbap->params, query_result,
- pbap);
+ request = phonebook_get_entry(pbap->folder, id, pbap->params,
+ query_result, pbap, &ret);
done:
if (ret < 0)
goto fail;
- return vobject_create(pbap);
+ if (err)
+ *err = 0;
+
+ return vobject_create(pbap, request);
fail:
if (err)
diff --git a/obexd/plugins/phonebook-dummy.c b/obexd/plugins/phonebook-dummy.c
index 7c549fa..60b7640 100644
--- a/obexd/plugins/phonebook-dummy.c
+++ b/obexd/plugins/phonebook-dummy.c
return relative;
}
-int phonebook_pull(const char *name, const struct apparam_field *params,
- phonebook_cb cb, void *user_data)
+void phonebook_req_finalize(void *request)
+{
+ guint id = GPOINTER_TO_INT(request);
+
+ g_source_remove(id);
+}
+
+void *phonebook_pull(const char *name, const struct apparam_field *params,
+ phonebook_cb cb, void *user_data, int *err)
{
struct dummy_data *dummy;
char *filename, *folder;
+ guint ret;
/*
* Main phonebook objects will be created dinamically based on the
if (!g_str_has_suffix(filename, ".vcf")) {
g_free(filename);
- return -EBADR;
+ if (err)
+ *err = -EBADR;
+ return NULL;
}
folder = g_strndup(filename, strlen(filename) - 4);
g_free(filename);
if (!is_dir(folder)) {
g_free(folder);
- return -ENOENT;
+ if (err)
+ *err = -ENOENT;
+ return NULL;
}
dummy = g_new0(struct dummy_data, 1);
dummy->folder = folder;
dummy->fd = -1;
- g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_dir, dummy, dummy_free);
+ ret = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_dir, dummy,
+ dummy_free);
- return 0;
+ if (err)
+ *err = 0;
+
+ return GINT_TO_POINTER(ret);
}
-int phonebook_get_entry(const char *folder, const char *id,
- const struct apparam_field *params,
- phonebook_cb cb, void *user_data)
+void *phonebook_get_entry(const char *folder, const char *id,
+ const struct apparam_field *params, phonebook_cb cb,
+ void *user_data, int *err)
{
struct dummy_data *dummy;
char *filename;
int fd;
+ guint ret;
filename = g_build_filename(root_folder, folder, id, NULL);
fd = open(filename, O_RDONLY);
if (fd < 0) {
- int err = errno;
- DBG("open(): %s(%d)", strerror(err), err);
- return -ENOENT;
+ DBG("open(): %s(%d)", strerror(errno), errno);
+ if (err)
+ *err = -ENOENT;
+ return NULL;
}
dummy = g_new0(struct dummy_data, 1);
dummy->apparams = params;
dummy->fd = fd;
- g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_entry, dummy, dummy_free);
+ ret = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_entry, dummy,
+ dummy_free);
- return 0;
+ if (err)
+ *err = 0;
+
+ return GINT_TO_POINTER(ret);
}
-int phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
- phonebook_cache_ready_cb ready_cb, void *user_data)
+void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+ phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
{
struct cache_query *query;
char *foldername;
DIR *dp;
+ guint ret;
foldername = g_build_filename(root_folder, name, NULL);
dp = opendir(foldername);
g_free(foldername);
if (dp == NULL) {
- int err = errno;
- DBG("opendir(): %s(%d)", strerror(err), err);
- return -ENOENT;
+ DBG("opendir(): %s(%d)", strerror(errno), errno);
+ if (err)
+ *err = -ENOENT;
+ return NULL;
}
query = g_new0(struct cache_query, 1);
query->user_data = user_data;
query->dp = dp;
- g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, create_cache, query,
+ ret = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, create_cache, query,
query_free);
- return 0;
+
+ if (err)
+ *err = 0;
+
+ return GINT_TO_POINTER(ret);
}
diff --git a/obexd/plugins/phonebook-ebook.c b/obexd/plugins/phonebook-ebook.c
index 80398ef..70b9c02 100644
--- a/obexd/plugins/phonebook-ebook.c
+++ b/obexd/plugins/phonebook-ebook.c
#define QUERY_NAME "(contains \"given_name\" \"%s\")"
#define QUERY_PHONE "(contains \"phone\" \"%s\")"
+
struct query_context {
+ gboolean completed;
const struct apparam_field *params;
phonebook_cb contacts_cb;
phonebook_entry_cb entry_cb;
unsigned int count = 0, maxcount;
GList *l;
+ if (estatus == E_BOOK_ERROR_CANCELLED) {
+ error("E-Book operation was cancelled: status %d", estatus);
+ goto fail;
+ }
+
if (estatus != E_BOOK_ERROR_OK) {
error("E-Book query failed: status %d", estatus);
goto done;
done:
+ data->completed = TRUE;
data->contacts_cb(string->str, string->len, count, 0, data->user_data);
+fail:
g_string_free(string, TRUE);
- g_free(data);
+
+ if (data->completed)
+ g_free(data);
}
static void ebook_entry_cb(EBook *book, EBookStatus estatus,
char *vcard;
size_t len;
+ if (estatus == E_BOOK_ERROR_CANCELLED) {
+ error("E-Book operation was cancelled: status %d", estatus);
+ goto fail;
+ }
+
+ data->completed = TRUE;
+
if (estatus != E_BOOK_ERROR_OK) {
error("E-Book query failed: status %d", estatus);
data->contacts_cb(NULL, 0, 1, 0, data->user_data);
- g_free(data);
- return;
+ goto fail;
}
evcard = E_VCARD(contact);
data->contacts_cb(vcard, len, 1, 0, data->user_data);
g_free(vcard);
- g_free(data);
+
+fail:
+ if (data->completed)
+ g_free(data);
}
static char *evcard_name_attribute_to_string(EVCard *evcard)
struct query_context *data = user_data;
GList *l;
+ if (estatus == E_BOOK_ERROR_CANCELLED) {
+ error("E-Book operation was cancelled: status %d", estatus);
+ goto fail;
+ }
+
+ data->completed = TRUE;
+
if (estatus != E_BOOK_ERROR_OK) {
error("E-Book query failed: status %d", estatus);
goto done;
}
done:
data->ready_cb(data->user_data);
+
+fail:
+ if (data->completed)
+ g_free(data);
}
int phonebook_init(void)
return fullname;
}
-int phonebook_pull(const char *name, const struct apparam_field *params,
- phonebook_cb cb, void *user_data)
+void phonebook_req_finalize(void *request)
+{
+ struct query_context *data = request;
+
+ if (!data)
+ return;
+
+ if (!data->completed) {
+ data->completed = TRUE;
+ e_book_cancel_async_op(ebook, NULL);
+ }
+}
+
+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;
e_book_query_unref(query);
- return 0;
+ if (err)
+ *err = 0;
+
+ return data;
}
-int phonebook_get_entry(const char *folder, const char *id,
- const struct apparam_field *params,
- phonebook_cb cb, void *user_data)
+void *phonebook_get_entry(const char *folder, const char *id,
+ const struct apparam_field *params,
+ phonebook_cb cb, void *user_data, int *err)
{
struct query_context *data;
if (e_book_async_get_contact(ebook, id, ebook_entry_cb, data)) {
g_free(data);
- return -ENOENT;
+ if (err)
+ *err = -ENOENT;
+ return NULL;
}
- return 0;
+ if (err)
+ *err = 0;
+
+ return data;
}
-int phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
- phonebook_cache_ready_cb ready_cb, void *user_data)
+void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+ phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
{
struct query_context *data;
EBookQuery *query;
gboolean ret;
- if (g_strcmp0("/telecom/pb", name) != 0)
- return -ENOENT;
+ if (g_strcmp0("/telecom/pb", name) != 0) {
+ if (err)
+ *err = -ENOENT;
+ return NULL;
+ }
query = e_book_query_any_field_contains("");
e_book_query_unref(query);
if (ret != FALSE) {
g_free(data);
- return -EFAULT;
+ if (err)
+ *err = -EFAULT;
+ return NULL;
}
- return 0;
+ if (err)
+ *err = 0;
+
+ return data;
}
diff --git a/obexd/plugins/phonebook-tracker.c b/obexd/plugins/phonebook-tracker.c
index cdc1008..0a023aa 100644
--- a/obexd/plugins/phonebook-tracker.c
+++ b/obexd/plugins/phonebook-tracker.c
reply_list_foreach_t callback;
void *user_data;
int num_fields;
+ GDestroyNotify destroy;
};
struct contact_data {
pending->callback(NULL, err, pending->user_data);
dbus_message_unref(reply);
+
+ /*
+ * pending data is freed in query_free_data after call is unreffed.
+ * Same holds for pending->user_data which is not freed in callback
+ * but in query_free_data.
+ */
+}
+
+static void query_free_data(void *user_data)
+{
+ struct pending_reply *pending = user_data;
+
+ if (!pending)
+ return;
+
+ if (pending->destroy)
+ pending->destroy(pending->user_data);
+
g_free(pending);
}
-static int query_tracker(const char *query, int num_fields,
- reply_list_foreach_t callback, void *user_data)
+static DBusPendingCall *query_tracker(const char *query, int num_fields,
+ reply_list_foreach_t callback, void *user_data,
+ GDestroyNotify destroy, int *err)
{
struct pending_reply *pending;
DBusPendingCall *call;
-1) == FALSE) {
error("Could not send dbus message");
dbus_message_unref(msg);
- return -EPERM;
+ if (err)
+ *err = -EPERM;
+ /* user_data is freed otherwise only if call was sent */
+ g_free(user_data);
+ return NULL;
}
pending = g_new0(struct pending_reply, 1);
pending->callback = callback;
pending->user_data = user_data;
pending->num_fields = num_fields;
+ pending->destroy = destroy;
- dbus_pending_call_set_notify(call, query_reply, pending, NULL);
- dbus_pending_call_unref(call);
+ dbus_pending_call_set_notify(call, query_reply, pending,
+ query_free_data);
dbus_message_unref(msg);
- return 0;
+ if (err)
+ *err = 0;
+
+ return call;
}
static char *iso8601_utc_to_localtime(const char *datetime)
if (num_fields < 0) {
data->cb(NULL, 0, num_fields, 0, data->user_data);
- goto fail;
+ return;
}
if (reply != NULL) {
data->cb(NULL, 0, data->index, 0, data->user_data);
-fail:
- g_free(data);
+ /*
+ * phonebook_data is freed in query_free_data after call is unreffed.
+ * It is accessible by pointer from data (pending) associated to call.
+ * Useful in cases when call was terminated.
+ */
}
static void add_affiliation(char **field, const char *value)
g_string_free(vcards, TRUE);
fail:
g_slist_free(data->contacts);
- g_free(data);
g_free(temp_id);
temp_id = NULL;
+
+ /*
+ * phonebook_data is freed in query_free_data after call is unreffed.
+ * It is accessible by pointer from data (pending) associated to call.
+ * Useful in cases when call was terminated.
+ */
}
static void add_to_cache(char **reply, int num_fields, void *user_data)
if (num_fields <= 0)
cache->ready_cb(cache->user_data);
- g_free(cache);
+ /*
+ * cache is freed in query_free_data after call is unreffed.
+ * It is accessible by pointer from data (pending) associated to call.
+ * Useful in cases when call was terminated.
+ */
}
int phonebook_init(void)
return path;
}
-int phonebook_pull(const char *name, const struct apparam_field *params,
- phonebook_cb cb, void *user_data)
+void phonebook_req_finalize(void *request)
+{
+ struct DBusPendingCall *call = request;
+
+ DBG("");
+
+ if (!dbus_pending_call_get_completed(call))
+ dbus_pending_call_cancel(call);
+
+ dbus_pending_call_unref(call);
+}
+
+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;
pull_cb = pull_contacts;
}
- if (query == NULL)
- return -ENOENT;
+ if (query == NULL) {
+ if (err)
+ *err = -ENOENT;
+ return NULL;
+ }
data = g_new0(struct phonebook_data, 1);
data->params = params;
data->user_data = user_data;
data->cb = cb;
- return query_tracker(query, col_amount, pull_cb, data);
+ return query_tracker(query, col_amount, pull_cb, data, g_free, err);
}
-int phonebook_get_entry(const char *folder, const char *id,
- const struct apparam_field *params,
- phonebook_cb cb, void *user_data)
+void *phonebook_get_entry(const char *folder, const char *id,
+ const struct apparam_field *params,
+ phonebook_cb cb, void *user_data, int *err)
{
struct phonebook_data *data;
char *query;
- int ret;
+ DBusPendingCall *call;
DBG("folder %s id %s", folder, id);
query = g_strdup_printf(CONTACTS_OTHER_QUERY_FROM_URI,
id, id, id);
- ret = query_tracker(query, PULL_QUERY_COL_AMOUNT, pull_contacts, data);
+ call = query_tracker(query, PULL_QUERY_COL_AMOUNT, pull_contacts,
+ data, g_free, err);
g_free(query);
- return ret;
+ return call;
}
-int phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
- phonebook_cache_ready_cb ready_cb, void *user_data)
+void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+ phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
{
struct cache_data *cache;
const char *query;
DBG("name %s", name);
query = folder2query(name);
- if (query == NULL)
- return -ENOENT;
+ if (query == NULL) {
+ if (err)
+ *err = -ENOENT;
+ return NULL;
+ }
cache = g_new0(struct cache_data, 1);
cache->entry_cb = entry_cb;
cache->ready_cb = ready_cb;
cache->user_data = user_data;
- return query_tracker(query, 7, add_to_cache, cache);
+ return query_tracker(query, 7, add_to_cache, cache, g_free, err);
}
diff --git a/obexd/plugins/phonebook.h b/obexd/plugins/phonebook.h
index d7cfa46..bfbae0f 100644
--- a/obexd/plugins/phonebook.h
+++ b/obexd/plugins/phonebook.h
* 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.
+ *
+ * Return value is a pointer to asynchronous request to phonebook back-end.
+ * phonebook_req_finalize MUST always be used to free associated resources.
*/
-int phonebook_pull(const char *name, const struct apparam_field *params,
- phonebook_cb cb, void *user_data);
+void *phonebook_pull(const char *name, const struct apparam_field *params,
+ phonebook_cb cb, void *user_data, 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
* by the client.
+ *
+ * Return value is a pointer to asynchronous request to phonebook back-end.
+ * phonebook_req_finalize MUST always be used to free associated resources.
*/
-int phonebook_get_entry(const char *folder, const char *id,
+void *phonebook_get_entry(const char *folder, const char *id,
const struct apparam_field *params,
- phonebook_cb cb, void *user_data);
+ phonebook_cb cb, void *user_data, int *err);
/*
* PBAP core will keep the contacts cache per folder. SetPhoneBook or
* PullvCardListing can invalidate the cache if the current folder changes.
* Cache will store only the necessary information required to reply to
- * PullvCardListing request and verify if a given contact belongs to the source.
+ * PullvCardListing request and verify if a given contact belongs to the
+ * source.
+ *
+ * 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_create_cache(const char *name, phonebook_entry_cb entry_cb,
+ phonebook_cache_ready_cb ready_cb, void *user_data, int *err);
+
+/*
+ * Finalizes request to phonebook back-end and deallocates associated
+ * resources. Operation is canceled if not completed. This function MUST
+ * always be used after any of phonebook_pull, phonebook_get_entry, and
+ * phonebook_create_cache invoked.
+ *
+ * request is a pointer to asynchronous operation returned by phonebook_pull,
+ * phonebook_get_entry, and phonebook_create_cache.
*/
-int phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
- phonebook_cache_ready_cb ready_cb, void *user_data);
+void phonebook_req_finalize(void *request);