From: Bartosz Szatkowski <bulislaw@linux.com>
To: linux-bluetooth@vger.kernel.org
Cc: Bartosz Szatkowski <bulislaw@linux.com>
Subject: [PATCH obexd 4/8] MAP Tracker Add basic support for messages listing
Date: Fri, 2 Sep 2011 10:57:56 +0200 [thread overview]
Message-ID: <1314953880-4663-4-git-send-email-bulislaw@linux.com> (raw)
In-Reply-To: <1314953880-4663-1-git-send-email-bulislaw@linux.com>
---
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<CRLF>" + <message content> + "END:MSG<CRLF>"
+ */
+#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
next prev parent reply other threads:[~2011-09-02 8:57 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-09-02 8:57 [PATCH obexd 1/8] MAP Tracker: Fix folder and path handling Bartosz Szatkowski
2011-09-02 8:57 ` [PATCH obexd 2/8] MAP Tracker: Fix memory issue in folder listing Bartosz Szatkowski
2011-09-02 8:57 ` [PATCH obexd 3/8] MAP Tracker: Add support for DBus connection Bartosz Szatkowski
2011-09-06 13:44 ` Johan Hedberg
2011-09-02 8:57 ` Bartosz Szatkowski [this message]
2011-09-02 8:57 ` [PATCH obexd 5/8] MAP Tracker Add filter support in messages listing Bartosz Szatkowski
2011-09-02 8:57 ` [PATCH obexd 6/8] MAP Tracker: Add support for pulling messages Bartosz Szatkowski
2011-09-02 13:36 ` [PATCH " Bartosz Szatkowski
2011-09-02 8:57 ` [PATCH obexd 7/8] MAP Tracker: Add abort support in folder listing Bartosz Szatkowski
2011-09-02 8:58 ` [PATCH obexd 8/8] MAP Tracker: Refactor session struct Bartosz Szatkowski
2011-09-27 9:46 ` [PATCH obexd 1/8] MAP Tracker: Fix folder and path handling Johan Hedberg
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1314953880-4663-4-git-send-email-bulislaw@linux.com \
--to=bulislaw@linux.com \
--cc=linux-bluetooth@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).