Return-Path: From: Bartosz Szatkowski To: linux-bluetooth@vger.kernel.org Cc: Bartosz Szatkowski Subject: [PATCH obexd 4/8] MAP Tracker Add basic support for messages listing Date: Fri, 2 Sep 2011 10:57:56 +0200 Message-Id: <1314953880-4663-4-git-send-email-bulislaw@linux.com> In-Reply-To: <1314953880-4663-1-git-send-email-bulislaw@linux.com> References: <1314953880-4663-1-git-send-email-bulislaw@linux.com> Sender: linux-bluetooth-owner@vger.kernel.org List-ID: --- plugins/messages-tracker.c | 447 +++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 438 insertions(+), 9 deletions(-) diff --git a/plugins/messages-tracker.c b/plugins/messages-tracker.c index efcd007..96a180e 100644 --- a/plugins/messages-tracker.c +++ b/plugins/messages-tracker.c @@ -33,6 +33,81 @@ #include "log.h" #include "messages.h" +#define TRACKER_SERVICE "org.freedesktop.Tracker1" +#define TRACKER_RESOURCES_PATH "/org/freedesktop/Tracker1/Resources" +#define TRACKER_RESOURCES_INTERFACE "org.freedesktop.Tracker1.Resources" + +#define QUERY_RESPONSE_SIZE 13 +#define MESSAGE_HANDLE_SIZE 16 +#define MESSAGE_HANDLE_PREFIX_LEN 8 + +/* + * As stated in MAP errata bmessage-body-content-length-property should be + * length of: "BEGIN:MSG" + + "END:MSG" + */ +#define BMESSAGE_BASE_LEN (9 + 2 + 2 + 7 + 2) + +#define MESSAGE_HANDLE 0 +#define MESSAGE_SUBJECT 1 +#define MESSAGE_SDATE 2 +#define MESSAGE_RDATE 3 +#define MESSAGE_FROM_N 4 +#define MESSAGE_FROM_LASTN 5 +#define MESSAGE_FROM_PHONE 6 +#define MESSAGE_TO_N 7 +#define MESSAGE_TO_LASTN 8 +#define MESSAGE_TO_PHONE 9 +#define MESSAGE_READ 10 +#define MESSAGE_SENT 11 +#define MESSAGE_CONTENT 12 + +#define LIST_MESSAGES_QUERY \ +"SELECT " \ +"?msg " \ +"nmo:messageSubject(?msg) " \ +"nmo:sentDate(?msg) " \ +"nmo:receivedDate(?msg) " \ +"nco:nameGiven(?from_c) " \ +"nco:nameFamily(?from_c) " \ +"nco:phoneNumber(?from_phone) " \ +"nco:nameGiven(?to_c) " \ +"nco:nameFamily(?to_c) " \ +"nco:phoneNumber(?to_phone) " \ +"nmo:isRead(?msg) " \ +"nmo:isSent(?msg) " \ +"nie:plainTextContent(?msg) " \ +"WHERE { " \ + "?msg a nmo:SMSMessage . " \ + "%s " \ + "%s " \ + "OPTIONAL { " \ + "?msg nmo:from ?from . " \ + "?from nco:hasPhoneNumber ?from_phone . " \ + "?from_phone maemo:localPhoneNumber ?from_lphone . " \ + "OPTIONAL { " \ + "?from_c a nco:PersonContact . " \ + "OPTIONAL {?from_c nco:hasPhoneNumber ?phone .} "\ + "OPTIONAL {?from_c nco:hasAffiliation ?af . " \ + "?af nco:hasPhoneNumber ?phone . } " \ + "?phone maemo:localPhoneNumber ?from_lphone . " \ + "} " \ + "} " \ + "OPTIONAL { " \ + "?msg nmo:to ?to . " \ + "?to nco:hasPhoneNumber ?to_phone . " \ + "?to_phone maemo:localPhoneNumber ?to_lphone . " \ + "OPTIONAL { " \ + "?to_c a nco:PersonContact . " \ + "OPTIONAL {?to_c nco:hasPhoneNumber ?phone1 .} "\ + "OPTIONAL {?to_c nco:hasAffiliation ?af . " \ + "?af nco:hasPhoneNumber ?phone1 . } " \ + "?phone1 maemo:localPhoneNumber ?to_lphone " \ + "} " \ + "} " \ +"} ORDER BY DESC(nmo:sentDate(?msg)) " + +typedef void (*reply_list_foreach_cb)(const char **reply, void *user_data); + struct message_folder { char *name; GSList *subfolders; @@ -45,14 +120,183 @@ struct session { char *name; uint16_t max; uint16_t offset; + uint16_t size; void *user_data; - void (*folder_list_cb)(void *session, int err, uint16_t size, - const char *name, void *user_data); + gboolean count; + gboolean new_message; + gboolean aborted; + reply_list_foreach_cb generate_response; + union { + messages_folder_listing_cb folder_list; + messages_get_messages_listing_cb messages_list; + } cb; }; static struct message_folder *folder_tree = NULL; static DBusConnection *session_connection = NULL; +static gboolean trace_call(void *data) +{ + DBusPendingCall *call = data; + + if (dbus_pending_call_get_completed(call) == TRUE) { + dbus_pending_call_unref(call); + + return FALSE; + } + + return TRUE; +} + +static void new_call(DBusPendingCall *call) +{ + g_timeout_add_seconds(5, trace_call, call); +} + +static void free_msg_data(struct messages_message *msg) +{ + g_free(msg->handle); + g_free(msg->subject); + g_free(msg->datetime); + g_free(msg->sender_name); + g_free(msg->sender_addressing); + g_free(msg->replyto_addressing); + g_free(msg->recipient_name); + g_free(msg->recipient_addressing); + g_free(msg->type); + g_free(msg->reception_status); + g_free(msg->size); + g_free(msg->attachment_size); + + g_free(msg); +} + +static char **string_array_from_iter(DBusMessageIter iter, int array_len) +{ + DBusMessageIter sub; + char **result; + int i; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) + return NULL; + + result = g_new0(char *, array_len + 1); + + dbus_message_iter_recurse(&iter, &sub); + + i = 0; + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + char *arg; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) { + g_free(result); + + return NULL; + } + + dbus_message_iter_get_basic(&sub, &arg); + + result[i++] = arg; + + dbus_message_iter_next(&sub); + } + + return result; +} + +static void query_reply(DBusPendingCall *call, void *user_data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + struct session *session = user_data; + DBusMessageIter iter, element; + DBusError derr; + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + error("Replied with an error: %s, %s", derr.name, derr.message); + dbus_error_free(&derr); + + goto done; + } + + dbus_message_iter_init(reply, &iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { + error("SparqlQuery reply is not an array"); + + goto done; + } + + dbus_message_iter_recurse(&iter, &element); + + while (dbus_message_iter_get_arg_type(&element) != DBUS_TYPE_INVALID) { + char **node; + + if (dbus_message_iter_get_arg_type(&element) + != DBUS_TYPE_ARRAY) { + error("Element is not an array\n"); + + goto done; + } + + node = string_array_from_iter(element, QUERY_RESPONSE_SIZE); + + session->generate_response((const char **) node, session); + + g_free(node); + + dbus_message_iter_next(&element); + } + +done: + session->generate_response(NULL, session); + + dbus_message_unref(reply); +} + +static DBusPendingCall *query_tracker(char *query, void *user_data, int *err) +{ + DBusPendingCall *call; + DBusMessage *msg; + + msg = dbus_message_new_method_call(TRACKER_SERVICE, + TRACKER_RESOURCES_PATH, + TRACKER_RESOURCES_INTERFACE, + "SparqlQuery"); + if (msg == NULL) { + if (err) + *err = -EPERM; + + return NULL; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &query, + DBUS_TYPE_INVALID); + + if (dbus_connection_send_with_reply(session_connection, msg, &call, + -1) == FALSE) { + error("Could not send dbus message"); + + dbus_message_unref(msg); + if (err) + *err = -EPERM; + + return NULL; + } + + dbus_pending_call_set_notify(call, query_reply, user_data, NULL); + + dbus_message_unref(msg); + + return call; +} + +static char *folder2query(const struct message_folder *folder, + const char *query) +{ + return g_strdup_printf(query, folder->query, ""); +} + static struct message_folder *get_folder(const char *folder) { GSList *folders = folder_tree->subfolders; @@ -151,6 +395,24 @@ static void create_folder_tree() parent->subfolders = g_slist_append(parent->subfolders, child); } +static char *merge_names(const char *name, const char *lastname) +{ + char *tmp = NULL; + + if (strlen(lastname) != 0) { + if (strlen(name) == 0) + tmp = g_strdup(lastname); + else + tmp = g_strdup_printf("%s %s", name, lastname); + + } else if (strlen(name) != 0) + tmp = g_strdup(name); + else + tmp = g_strdup(""); + + return tmp; +} + static DBusConnection *dbus_get_connection(DBusBusType type) { DBusError err; @@ -171,6 +433,120 @@ static DBusConnection *dbus_get_connection(DBusBusType type) return tmp; } +static struct messages_message *pull_message_data(const char **reply) +{ + struct messages_message *data = g_new0(struct messages_message, 1); + + data->handle = g_strdup(reply[MESSAGE_HANDLE] + + MESSAGE_HANDLE_PREFIX_LEN); + + if (strlen(reply[MESSAGE_SUBJECT]) != 0) + data->subject = g_strdup(reply[MESSAGE_SUBJECT]); + else + data->subject = g_strdup(reply[MESSAGE_CONTENT]); + + data->mask |= PMASK_SUBJECT; + + if (strlen(reply[MESSAGE_SDATE]) != 0) { + char **date = g_strsplit_set(reply[MESSAGE_SDATE], ":-Z", -1); + + data->datetime = g_strjoinv(NULL, date); + g_strfreev(date); + } else if (strlen(reply[MESSAGE_RDATE]) != 0) { + char **date = g_strsplit_set(reply[MESSAGE_RDATE], ":-Z", -1); + + data->datetime = g_strjoinv(NULL, date); + g_strfreev(date); + } else { + data->datetime = g_strdup(""); + } + + data->mask |= PMASK_DATETIME; + + data->sender_name = merge_names(reply[MESSAGE_FROM_N], + reply[MESSAGE_FROM_LASTN]); + data->mask |= PMASK_SENDER_NAME; + + data->sender_addressing = g_strdup(reply[MESSAGE_FROM_PHONE]); + data->mask |= PMASK_SENDER_ADDRESSING; + + data->recipient_name = merge_names(reply[MESSAGE_TO_N], + reply[MESSAGE_TO_LASTN]); + data->mask |= PMASK_RECIPIENT_NAME; + + data->recipient_addressing = g_strdup(reply[MESSAGE_TO_PHONE]); + data->mask |= PMASK_RECIPIENT_ADDRESSING; + + data->type = g_strdup("SMS_GSM"); + data->mask |= PMASK_TYPE; + + data->size = g_strdup_printf("%d", strlen(reply[MESSAGE_CONTENT]) + + BMESSAGE_BASE_LEN); + data->mask |= PMASK_SIZE; + + data->text = TRUE; + data->mask |= PMASK_TEXT; + + data->reception_status = g_strdup("complete"); + data->mask |= PMASK_RECEPTION_STATUS; + + data->attachment_size = g_strdup("0"); + data->mask |= PMASK_ATTACHMENT_SIZE; + + data->priority = FALSE; + data->mask |= PMASK_PRIORITY; + + data->read = g_strcmp0(reply[MESSAGE_READ], "true") == 0 ? TRUE : FALSE; + data->mask |= PMASK_READ; + + data->sent = g_strcmp0(reply[MESSAGE_SENT], "true") == 0 ? TRUE : FALSE; + data->mask |= PMASK_SENT; + + data->protect = FALSE; + data->mask |= PMASK_PROTECTED; + + return data; +} + +static void get_messages_listing_resp(const char **reply, void *user_data) +{ + struct session *session = user_data; + struct messages_message *msg_data; + + DBG("reply %p", reply); + + if (reply == NULL) + goto done; + + if (session->aborted) + return; + + msg_data = pull_message_data(reply); + + session->size++; + + if (!msg_data->read) + session->new_message = TRUE; + + if (session->count == TRUE) { + free_msg_data(msg_data); + return; + } + + if (session->size > session->offset) + session->cb.messages_list(session, -EAGAIN, 1, + session->new_message, msg_data, + session->user_data); + + free_msg_data(msg_data); + return; + +done: + session->cb.messages_list(session, 0, session->size, + session->new_message, NULL, + session->user_data); +} + int messages_init(void) { DBusError err; @@ -302,12 +678,12 @@ static gboolean async_get_folder_listing(void *s) { struct message_folder *dir_data = dir->data; if (count == FALSE && session->offset <= folder_count) - session->folder_list_cb(session, -EAGAIN, 0, + session->cb.folder_list(session, -EAGAIN, 1, dir_data->name, session->user_data); } done: - session->folder_list_cb(session, 0, folder_count, NULL, + session->cb.folder_list(session, 0, folder_count, NULL, session->user_data); g_free(path); @@ -325,7 +701,7 @@ int messages_get_folder_listing(void *s, const char *name, session->name = g_strdup(name); session->max = max; session->offset = offset; - session->folder_list_cb = callback; + session->cb.folder_list = callback; session->user_data = user_data; g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, async_get_folder_listing, @@ -334,14 +710,64 @@ int messages_get_folder_listing(void *s, const char *name, return 0; } -int messages_get_messages_listing(void *session, - const char *name, +int messages_get_messages_listing(void *s, const char *name, uint16_t max, uint16_t offset, const struct messages_filter *filter, messages_get_messages_listing_cb callback, void *user_data) { - return -EINVAL; + struct session *session = s; + char *path, *query; + struct message_folder *folder = NULL; + DBusPendingCall *call; + int err = 0; + + if (name == NULL || strlen(name) == 0) { + path = g_strdup(session->cwd); + + folder = session->folder; + if (folder == NULL) + folder = get_folder(path); + } else { + if (strchr(name, '/') != NULL) + return -EBADR; + + path = g_build_filename(session->cwd, name, NULL); + folder = get_folder(path); + } + + g_free(path); + + if (folder == NULL) + return -ENOENT; + + query = folder2query(folder, LIST_MESSAGES_QUERY); + if (query == NULL) + return -ENOENT; + + session->generate_response = get_messages_listing_resp; + session->cb.messages_list = callback; + session->offset = offset; + session->max = max; + session->user_data = user_data; + session->new_message = FALSE; + session->count = FALSE; + session->size = 0; + session->aborted = FALSE; + + if (max == 0) { + session->max = 0xffff; + session->offset = 0; + session->count = TRUE; + } + + call = query_tracker(query, session, &err); + if (err == 0) + new_call(call); + + g_free(query); + + return err; } int messages_get_message(void *session, @@ -353,6 +779,9 @@ int messages_get_message(void *session, return -EINVAL; } -void messages_abort(void *session) +void messages_abort(void *s) { + struct session *session = s; + + session->aborted = TRUE; } -- 1.7.4.1