[PATCH obexd 4/8] MAP Tracker Add basic support for messages listing

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



---
 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

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux