Diff between bff5b432c2471655409e0560d0df99931d8878c0 and 98359ac58234f9e81ea63a3d9da73c6734f9490f

Changed Files

File Additions Deletions Status
obexd/plugins/phonebook-tracker.c +423 -0 added
obexd/plugins/vcard.c +179 -0 added
obexd/plugins/vcard.h +31 -0 added

Full Patch

diff --git a/obexd/plugins/phonebook-tracker.c b/obexd/plugins/phonebook-tracker.c
new file mode 100644
index 0000000..9b431d8
--- /dev/null
+++ b/obexd/plugins/phonebook-tracker.c
@@ -0,0 +1,423 @@
+/*
+ *  Phonebook access through D-Bus vCard and call history service
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include "logging.h"
+#include "obex.h"
+#include "service.h"
+#include "mimetype.h"
+#include "phonebook.h"
+#include "dbus.h"
+#include "vcard.h"
+
+#define TRACKER_SERVICE "org.freedesktop.Tracker1"
+#define TRACKER_RESOURCES_PATH "/org/freedesktop/Tracker1/Resources"
+#define TRACKER_RESOURCES_INTERFACE "org.freedesktop.Tracker1.Resources"
+
+#define TRACKER_DEFAULT_CONTACT_ME "<urn:nco:default-contact-me>"
+
+#define CONTACTS_QUERY_ALL \
+	"SELECT ?phone ?family ?given ?additional ?prefix "		\
+		"?suffix ?email "					\
+	"WHERE { "							\
+		"?contact a nco:PersonContact ; "			\
+		"nco:nameFamily ?family ; "				\
+		"nco:nameGiven ?given ; "				\
+		"nco:hasPhoneNumber ?phone ."				\
+	"OPTIONAL { ?contact nco:hasEmailAddress ?email } "		\
+	"OPTIONAL { ?contact nco:nameAdditional ?additional } "		\
+	"OPTIONAL { ?contact nco:nameHonorificPrefix ?prefix } "	\
+	"OPTIONAL { ?contact nco:nameHonorificSuffix ?suffix } "	\
+	"}"
+
+#define CONTACTS_QUERY_ALL_LIST						\
+	"SELECT ?contact ?family ?given ?additional ?prefix "		\
+		"?suffix ?phone "					\
+	"WHERE { "							\
+		"?contact a nco:PersonContact ; "			\
+		"nco:nameFamily ?family ; "				\
+		"nco:nameGiven ?given ; "				\
+		"nco:hasPhoneNumber ?phone ."				\
+	"OPTIONAL { ?contact nco:nameAdditional ?additional } "		\
+	"OPTIONAL { ?contact nco:nameHonorificPrefix ?prefix } "	\
+	"OPTIONAL { ?contact nco:nameHonorificSuffix ?suffix } "	\
+	"}"
+
+#define MISSED_CALLS_QUERY						\
+	"SELECT ?contact ?family ?given ?additional ?prefix "		\
+		"?suffix ?phone ?email "				\
+	"WHERE { "							\
+		"?call a nmo:Call ; "					\
+		"nmo:from ?contact ; "					\
+		"nmo:to <nco:default-contact-me> ; "			\
+		"nmo:isRead false ."					\
+		"?contact a nco:PersonContact ; "			\
+		"nco:nameFamily ?family ; "				\
+		"nco:nameGiven ?given ; "				\
+		"nco:hasPhoneNumber ?phone ."				\
+	"OPTIONAL { ?contact nco:hasEmailAddress ?email } "		\
+	"OPTIONAL { ?contact nco:nameAdditional ?additional } "		\
+	"OPTIONAL { ?contact nco:nameHonorificPrefix ?prefix } "	\
+	"OPTIONAL { ?contact nco:nameHonorificSuffix ?suffix } "	\
+	"}"
+
+#define INCOMING_CALLS_QUERY						\
+	"SELECT ?contact ?family ?given ?additional ?prefix "		\
+		"?suffix ?phone ?fullname ?email "			\
+	"WHERE { "							\
+		"?call a nmo:Call ; "					\
+		"nmo:from ?contact ; "					\
+		"nmo:to " TRACKER_DEFAULT_CONTACT_ME " . "		\
+		"?contact a nco:PersonContact ; "			\
+		"nco:nameFamily ?family ; "				\
+		"nco:nameGiven ?given ; "				\
+		"nco:hasPhoneNumber ?phone ."				\
+	"OPTIONAL { ?contact nco:hasEmailAddress ?email } "		\
+	"OPTIONAL { ?contact nco:nameAdditional ?additional } "		\
+	"OPTIONAL { ?contact nco:nameHonorificPrefix ?prefix } "	\
+	"OPTIONAL { ?contact nco:nameHonorificSuffix ?suffix } "	\
+	"}"
+
+#define OUTGOING_CALLS_QUERY						\
+	"SELECT ?contact ?family ?given ?additional ?prefix "		\
+		"?suffix ?phone ?fullname ?email "			\
+	"WHERE { "							\
+		"?call a nmo:Call ; "					\
+		"nmo:to ?contact ; "					\
+		"nmo:from " TRACKER_DEFAULT_CONTACT_ME " . "		\
+			"?contact a nco:PersonContact ; "		\
+		"nco:nameFamily ?family ; "				\
+		"nco:nameGiven ?given ; "				\
+		"nco:hasPhoneNumber ?phone ."				\
+	"OPTIONAL { ?contact nco:hasEmailAddress ?email } "		\
+	"OPTIONAL { ?contact nco:nameAdditional ?additional } "		\
+	"OPTIONAL { ?contact nco:nameHonorificPrefix ?prefix } "	\
+	"OPTIONAL { ?contact nco:nameHonorificSuffix ?suffix } "	\
+	"}"
+
+/* FIXME: still not sure about how to implement this */
+#define COMBINED_CALLS_QUERY						\
+	"SELECT ?contact "						\
+	"WHERE { "							\
+		"?call a nmo:Call . "					\
+	"}"
+
+#define CONTACTS_QUERY_FROM_URI \
+	"SELECT ?phone ?family ?given ?additional ?prefix "		\
+	"	?suffix ?email "					\
+	"WHERE { "							\
+		"<%s> a nco:PersonContact ; "				\
+		"nco:nameFamily ?family ; "				\
+		"nco:nameGiven ?given ; "				\
+		"nco:hasPhoneNumber ?phone ."				\
+	"OPTIONAL { <%s> nco:hasEmailAddress ?email } "			\
+	"OPTIONAL { <%s> nco:nameAdditional ?additional } "		\
+	"OPTIONAL { <%s> nco:nameHonorificPrefix ?prefix } "		\
+	"OPTIONAL { <%s> nco:nameHonorificSuffix ?suffix } "		\
+	"}"
+
+typedef void (*reply_list_foreach_t) (char **reply, int num_fields,
+		void *user_data);
+
+struct pending_reply {
+	reply_list_foreach_t callback;
+	void *user_data;
+	int num_fields;
+};
+
+struct phonebook_data {
+	GString *vcards;
+	phonebook_cb cb;
+	void *user_data;
+	int index;
+};
+
+struct cache_data {
+	phonebook_cache_ready_cb ready_cb;
+	phonebook_entry_cb entry_cb;
+	void *user_data;
+	GString *listing;
+	int index;
+};
+
+struct phonebook_index {
+	GArray *phonebook;
+	int index;
+};
+
+static DBusConnection *connection = NULL;
+
+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);
+
+	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)
+			goto error;
+
+		dbus_message_iter_get_basic(&sub, &arg);
+
+		result[i] = arg;
+
+		i++;
+		dbus_message_iter_next(&sub);
+	}
+
+	return result;
+
+error:
+	g_free(result);
+
+	return NULL;
+}
+
+static void query_reply(DBusPendingCall *call, void *user_data)
+{
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	struct pending_reply *pending = user_data;
+	DBusMessageIter iter, element;
+	DBusError derr;
+	int err;
+
+	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);
+
+		err = -1;
+		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");
+
+		err = -1;
+		goto done;
+	}
+
+	dbus_message_iter_recurse(&iter, &element);
+
+	err = 0;
+
+	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");
+			goto done;
+		}
+
+		node = string_array_from_iter(element, pending->num_fields);
+		pending->callback(node, pending->num_fields,
+							pending->user_data);
+		g_free(node);
+
+		dbus_message_iter_next(&element);
+	}
+
+done:
+	/* This is the last entry */
+	pending->callback(NULL, err, pending->user_data);
+
+	dbus_message_unref(reply);
+	g_free(pending);
+}
+
+static int query_tracker(const char* query, int num_fields,
+				reply_list_foreach_t callback, void *user_data)
+{
+	struct pending_reply *pending;
+	DBusPendingCall *call;
+	DBusMessage *msg;
+
+	if (connection == NULL)
+		connection = obex_dbus_get_connection();
+
+	msg = dbus_message_new_method_call(TRACKER_SERVICE,
+			TRACKER_RESOURCES_PATH, TRACKER_RESOURCES_INTERFACE,
+								"SparqlQuery");
+
+	dbus_message_append_args(msg, DBUS_TYPE_STRING, &query,
+						DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(connection, msg, &call,
+							-1) == FALSE) {
+		error("Could not send dbus message");
+		dbus_message_unref(msg);
+		return -EPERM;
+	}
+
+	pending = g_new0(struct pending_reply, 1);
+	pending->callback = callback;
+	pending->user_data = user_data;
+	pending->num_fields = num_fields;
+
+	dbus_pending_call_set_notify(call, query_reply, pending, NULL);
+	dbus_pending_call_unref(call);
+	dbus_message_unref(msg);
+
+	return 0;
+}
+
+static void pull_contacts(char **reply, int num_fields, void *user_data)
+{
+	struct phonebook_data *data = user_data;
+	GString *vcards = data->vcards;
+	char *formatted;
+
+	if (reply == NULL)
+		goto done;
+
+	formatted = g_strdup_printf("%s;%s;%s;%s;%s", reply[1], reply[2],
+						reply[3], reply[4], reply[5]);
+
+	phonebook_add_entry(vcards, reply[0], TEL_TYPE_HOME, formatted,
+								reply[6]);
+
+	g_free(formatted);
+
+	data->index++;
+
+	return;
+
+done:
+	if (num_fields == 0)
+		data->cb(vcards->str, vcards->len, data->index, 0, data->user_data);
+
+	g_string_free(vcards, TRUE);
+}
+
+static void add_to_cache(char **reply, int num_fields, void *user_data)
+{
+	struct cache_data *cache = user_data;
+	char *formatted;
+
+	if (reply == NULL)
+		goto done;
+
+	formatted = g_strdup_printf("%s;%s;%s;%s;%s", reply[1], reply[2],
+						reply[3], reply[4], reply[5]);
+
+	cache->entry_cb(reply[0], PHONEBOOK_INVALID_HANDLE, formatted, "",
+						reply[6], cache->user_data);
+
+	g_free(formatted);
+
+	return;
+
+done:
+	if (num_fields == 0)
+		cache->ready_cb(cache->user_data);
+}
+
+int phonebook_init(void)
+{
+	return 0;
+}
+
+void phonebook_exit(void)
+{
+}
+
+char *phonebook_set_folder(const char *current_folder, const char *new_folder,
+							uint8_t flags, int *err)
+{
+	char *folder;
+
+	if (err)
+		*err = 0;
+
+	folder = g_build_path(current_folder, new_folder, NULL);
+
+	return folder;
+}
+
+int phonebook_pull(const char *name, const struct apparam_field *params,
+					phonebook_cb cb, void *user_data)
+{
+	struct phonebook_data *data;
+
+	data = g_new0(struct phonebook_data, 1);
+	data->vcards = g_string_new(NULL);
+	data->user_data = user_data;
+	data->cb = cb;
+
+	return query_tracker(CONTACTS_QUERY_ALL, 7, pull_contacts, data);
+}
+
+int phonebook_get_entry(const char *folder, const char *id,
+					const struct apparam_field *params,
+					phonebook_cb cb, void *user_data)
+{
+	struct phonebook_data *data;
+	char *query;
+	int ret;
+
+	data = g_new0(struct phonebook_data, 1);
+	data->vcards = g_string_new(NULL);
+	data->user_data = user_data;
+	data->cb = cb;
+
+	query = g_strdup_printf(CONTACTS_QUERY_FROM_URI, id, id, id, id, id);
+
+	ret = query_tracker(query, 8, pull_contacts, data);;
+
+	g_free(query);
+
+	return ret;
+}
+
+int phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+			phonebook_cache_ready_cb ready_cb, void *user_data)
+{
+	struct cache_data *cache;
+
+	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(CONTACTS_QUERY_ALL_LIST, 7, add_to_cache, cache);
+}
diff --git a/obexd/plugins/vcard.c b/obexd/plugins/vcard.c
new file mode 100644
index 0000000..41a0995
--- /dev/null
+++ b/obexd/plugins/vcard.c
@@ -0,0 +1,179 @@
+/*
+ * OBEX Server
+ *
+ * Copyright (C) 2008-2010 Intel Corporation.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "vcard.h"
+
+#define LEN_MAX 128
+#define TYPE_INTERNATIONAL 145
+
+#define PHONEBOOK_FLAG_CACHED 0x1
+
+/* according to RFC 2425, the output string may need folding */
+static void vcard_printf(GString *str, const char *fmt, ...)
+{
+	char buf[1024];
+	va_list ap;
+	int len_temp, line_number, i;
+	unsigned int line_delimit = 75;
+
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	va_end(ap);
+
+	line_number = strlen(buf) / line_delimit + 1;
+
+	for (i = 0; i < line_number; i++) {
+		len_temp = MIN(line_delimit, strlen(buf) - line_delimit * i);
+		g_string_append_len(str,  buf + line_delimit * i, len_temp);
+		if (i != line_number - 1)
+			g_string_append(str, "\r\n ");
+	}
+
+	g_string_append(str, "\r\n");
+}
+
+/* According to RFC 2426, we need escape following characters:
+ *  '\n', '\r', ';', ',', '\'.
+ */
+static void add_slash(char *dest, const char *src, int len_max, int len)
+{
+	int i, j;
+
+	for (i = 0, j = 0; i < len && j < len_max; i++, j++) {
+		switch (src[i]) {
+		case '\n':
+			dest[j++] = '\\';
+			dest[j] = 'n';
+			break;
+		case '\r':
+			dest[j++] = '\\';
+			dest[j] = 'r';
+			break;
+		case '\\':
+		case ';':
+		case ',':
+			dest[j++] = '\\';
+		default:
+			dest[j] = src[i];
+			break;
+		}
+	}
+	dest[j] = 0;
+	return;
+}
+
+static void vcard_printf_begin(GString *vcards)
+{
+	vcard_printf(vcards, "BEGIN:VCARD");
+	vcard_printf(vcards, "VERSION:3.0");
+}
+
+static void vcard_printf_text(GString *vcards, const char *text)
+{
+	char field[LEN_MAX];
+	add_slash(field, text, LEN_MAX, strlen(text));
+	vcard_printf(vcards, "FN:%s", field);
+}
+
+static void vcard_printf_number(GString *vcards, const char *number, int type,
+					enum phonebook_number_type category)
+{
+	char *pref = "", *intl = "", *category_string = "";
+	char buf[128];
+
+	if (!number || !strlen(number) || !type)
+		return;
+
+	switch (category) {
+	case TEL_TYPE_HOME:
+		category_string = "HOME,VOICE";
+		break;
+	case TEL_TYPE_MOBILE:
+		category_string = "CELL,VOICE";
+		break;
+	case TEL_TYPE_FAX:
+		category_string = "FAX";
+		break;
+	case TEL_TYPE_WORK:
+		category_string = "WORK,VOICE";
+		break;
+	case TEL_TYPE_OTHER:
+		category_string = "VOICE";
+		break;
+	}
+
+	if ((type == TYPE_INTERNATIONAL) && (number[0] != '+'))
+		intl = "+";
+
+	snprintf(buf, sizeof(buf), "TEL;TYPE=\%s%s:\%s\%s", pref,
+			category_string, intl, number);
+	vcard_printf(vcards, buf, number);
+}
+
+static void vcard_printf_email(GString *vcards, const char *email)
+{
+	int len = 0;
+
+	if (email)
+		len = strlen(email);
+
+	if (len) {
+		char field[LEN_MAX];
+		add_slash(field, email, LEN_MAX, len);
+		vcard_printf(vcards,
+				"EMAIL;TYPE=INTERNET:%s", field);
+	}
+}
+
+static void vcard_printf_end(GString *vcards)
+{
+	vcard_printf(vcards, "END:VCARD");
+	vcard_printf(vcards, "");
+}
+
+void phonebook_add_entry(GString *vcards, const char *number, int type,
+					const char *text, const char *email)
+{
+	/* There's really nothing to do */
+	if ((number == NULL || number[0] == '\0') &&
+			(text == NULL || text[0] == '\0'))
+		return;
+
+	vcard_printf_begin(vcards);
+
+	if (text == NULL || text[0] == '\0')
+		vcard_printf_text(vcards, number);
+	else
+		vcard_printf_text(vcards, text);
+
+	vcard_printf_email(vcards, email);
+	vcard_printf_number(vcards, number, type, TEL_TYPE_OTHER);
+	vcard_printf_end(vcards);
+}
diff --git a/obexd/plugins/vcard.h b/obexd/plugins/vcard.h
new file mode 100644
index 0000000..7b47984
--- /dev/null
+++ b/obexd/plugins/vcard.h
@@ -0,0 +1,31 @@
+/*
+ * OBEX Server
+ *
+ * Copyright (C) 2008-2010 Intel Corporation.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+enum phonebook_number_type {
+	TEL_TYPE_HOME,
+	TEL_TYPE_MOBILE,
+	TEL_TYPE_FAX,
+	TEL_TYPE_WORK,
+	TEL_TYPE_OTHER,
+};
+
+void phonebook_add_entry(GString *vcards, const char *number, int type,
+					const char *text, const char *email);