All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC v2] modules: Add support for history agent interface
@ 2011-02-07  2:06 Marcel Holtmann
  0 siblings, 0 replies; only message in thread
From: Marcel Holtmann @ 2011-02-07  2:06 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 17240 bytes --]

---
 modules/history-agent.c |  686 +++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 686 insertions(+), 0 deletions(-)
 create mode 100644 modules/history-agent.c

diff --git a/modules/history-agent.c b/modules/history-agent.c
new file mode 100644
index 0000000..6979195
--- /dev/null
+++ b/modules/history-agent.c
@@ -0,0 +1,686 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  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 version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <glib.h>
+#include <gdbus.h>
+#include <ofono.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/dbus.h>
+#include "common.h"
+#include "storage.h"
+
+#define HISTORY_SPOOL_MODE 0600
+#define HISTORY_SPOOL_BASE STORAGEDIR "/%s/history"
+#define HISTORY_SPOOL_PATH HISTORY_SPOOL_BASE "/%08x"
+
+#define HISTORY_INTERFACE "org.ofono.History"
+#define AGENT_INTERFACE "org.ofono.HistoryAgent"
+
+struct history_agent {
+	DBusConnection *conn;
+	const char *path;
+	char *imsi;
+
+	char *agent_path;
+	char *agent_owner;
+	guint agent_watch;
+
+	GQueue *queue;
+	guint32 next_idx;
+	guint32 current_idx;
+	DBusPendingCall *agent_call;
+};
+
+static void process_queue(struct history_agent *history);
+
+static void agent_reply_cb(DBusPendingCall *call, void *data)
+{
+	struct history_agent *history = data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError error;
+	char *path;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, reply) == TRUE) {
+		ofono_error("Agent method failed: %s", error.name);
+		dbus_error_free(&error);
+		goto done;
+	}
+
+	DBG("index: %08x", history->current_idx);
+
+	path = g_strdup_printf(HISTORY_SPOOL_PATH, history->imsi,
+						history->current_idx);
+	unlink(path);
+	g_free(path);
+
+done:
+	dbus_message_unref(reply);
+
+	history->agent_call = NULL;
+
+	process_queue(history);
+}
+
+static const char *buffer_next(const unsigned char **buffer, size_t *size)
+{
+	const void *ptr = *buffer;
+	guint32 len;
+
+	if (*size < 5)
+		return NULL;
+
+	len = *((guint32 *) ptr);
+	if (len == 0)
+		return NULL;
+
+	if (len + 4 > *size)
+		return NULL;
+
+	*size -= len + 4;
+	*buffer += len + 4;
+
+	return ptr + 4;
+}
+
+static void process_history(struct history_agent *history,
+				const unsigned char *buffer, size_t size)
+{
+	const unsigned char *ptr = buffer;
+	size_t len = size;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict;
+	const char *method;
+
+	method = buffer_next(&ptr, &len);
+	if (method == NULL)
+		return;
+
+	DBG("method: %s", method);
+
+	msg = dbus_message_new_method_call(history->agent_owner,
+						history->agent_path,
+						AGENT_INTERFACE, method);
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+				OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict);
+
+	while (1) {
+		const char *key, *val;
+
+		key = buffer_next(&ptr, &len);
+		if (key == NULL)
+			break;
+
+		val = buffer_next(&ptr, &len);
+		if (val == NULL)
+			break;
+
+		ofono_dbus_dict_append(&dict, key, DBUS_TYPE_STRING, &val);
+        }
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	if (dbus_connection_send_with_reply(history->conn, msg,
+					&history->agent_call, -1) == FALSE) {
+		ofono_error("Sending D-Bus method failed");
+		goto done;
+	}
+
+	dbus_pending_call_set_notify(history->agent_call, agent_reply_cb,
+							history, NULL);
+
+	dbus_pending_call_unref(history->agent_call);
+
+done:
+	dbus_message_unref(msg);
+}
+
+static void process_queue(struct history_agent *history)
+{
+	int fd;
+	struct stat st;
+	unsigned char *addr;
+	char *path;
+	gpointer head;
+
+	DBG("owner: %s", history->agent_owner);
+
+	if (history->agent_owner == NULL)
+		return;
+
+	if (history->agent_call != NULL)
+		return;
+
+	head = g_queue_pop_head(history->queue);
+	if (head == NULL) {
+		history->next_idx = 0;
+		return;
+	}
+
+	history->current_idx = GPOINTER_TO_UINT(head);
+
+	DBG("index: %08x", history->current_idx);
+
+	path = g_strdup_printf(HISTORY_SPOOL_PATH, history->imsi,
+						history->current_idx);
+	fd = TFR(open(path, O_RDONLY));
+	g_free(path);
+
+	if (fd < 0) {
+		ofono_error("Failed to open history index %08x",
+						history->current_idx);
+		return;
+	}
+
+	if (TFR(fstat(fd, &st)) < 0)
+		goto done;
+
+	addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (addr == MAP_FAILED)
+		goto done;
+
+	process_history(history, addr, st.st_size);
+
+	munmap(addr, st.st_size);
+
+done:
+	TFR(close(fd));
+}
+
+static gint compare_index(gconstpointer a, gconstpointer b,
+						gpointer user_data)
+{
+	guint32 one = GPOINTER_TO_UINT@;
+	guint32 two = GPOINTER_TO_UINT(b);
+
+	return one - two;
+}
+
+static void process_spool(struct history_agent *history)
+{
+	GDir *dir;
+	char *path;
+	gpointer tail;
+
+	path = g_strdup_printf(HISTORY_SPOOL_BASE, history->imsi);
+
+	DBG("path: %s", path);
+
+	dir = g_dir_open(path, 0, NULL);
+
+	g_free(path);
+
+	if (dir == NULL)
+		return;
+
+	while (1) {
+		const char *path;
+		guint32 idx;
+
+		path = g_dir_read_name(dir);
+		if (path == NULL)
+			break;
+
+		idx = strtol(path, NULL, 16);
+		if (errno == ERANGE || errno == EINVAL)
+			continue;
+
+		DBG("index: %08x", idx);
+
+		g_queue_insert_sorted(history->queue, GUINT_TO_POINTER(idx),
+							compare_index, NULL);
+	}
+
+	g_dir_close(dir);
+
+	tail = g_queue_peek_tail(history->queue);
+	if (tail == NULL)
+		return;
+
+	history->next_idx = GPOINTER_TO_UINT(tail);
+}
+
+static void array_append(GByteArray *array, const char *str)
+{
+	guint32 len;
+
+	if (str == NULL) {
+		len = 0;
+		g_byte_array_append(array, (guint8 *) &len, 4);
+	} else {
+		len = strlen(str) + 1;
+		g_byte_array_append(array, (guint8 *) &len, 4);
+		g_byte_array_append(array, (guint8 *) str, len);
+	}
+}
+
+static void create_history(struct history_agent *history,
+					const char *method, ...)
+{
+	GByteArray *array;
+	va_list args;
+
+	DBG("method: %s", method);
+
+	array = g_byte_array_sized_new(128);
+	if (array == NULL)
+		return;
+
+	array_append(array, method);
+
+	va_start(args, method);
+
+	while (1) {
+		const char *key, *val;
+
+		key = va_arg(args, const char *);
+		if (key == NULL)
+			break;
+
+		array_append(array, key);
+
+		val = va_arg(args, const char *);
+
+		array_append(array, val);
+	}
+
+	va_end(args);
+
+	array_append(array, NULL);
+
+	history->next_idx++;
+
+	DBG("index: %08x", history->next_idx);
+
+	write_file(array->data, array->len, HISTORY_SPOOL_MODE,
+			HISTORY_SPOOL_PATH, history->imsi, history->next_idx);
+
+	g_byte_array_free(array, TRUE);
+
+	g_queue_push_tail(history->queue, GUINT_TO_POINTER(history->next_idx));
+
+	process_queue(history);
+}
+
+static void free_agent_data(struct history_agent *history)
+{
+	if (history->agent_call != NULL) {
+		dbus_pending_call_cancel(history->agent_call);
+		history->agent_call = NULL;
+	}
+
+	g_free(history->agent_path);
+	history->agent_path = NULL;
+
+	g_free(history->agent_owner);
+	history->agent_owner = NULL;
+}
+
+static void agent_disconnect_cb(DBusConnection *conn, void *data)
+{
+	struct history_agent *history = data;
+
+	history->agent_watch = 0;
+
+	free_agent_data(history);
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct history_agent *history = data;
+	const char *path, *sender;
+
+	sender = dbus_message_get_sender(msg);
+
+	DBG("sender: %s", sender);
+
+	if (history->agent_owner != NULL)
+		return __ofono_error_busy(msg);
+
+	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+						DBUS_TYPE_INVALID) == FALSE)
+		return __ofono_error_invalid_args(msg);
+
+	if (!__ofono_dbus_valid_object_path(path))
+		return __ofono_error_invalid_format(msg);
+
+	history->agent_path = g_strdup(path);
+	history->agent_owner = g_strdup(sender);
+
+	history->agent_watch = g_dbus_add_disconnect_watch(history->conn,
+						sender, agent_disconnect_cb,
+						history, NULL);
+
+	process_queue(history);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct history_agent *history = data;
+	const char *path, *sender;
+
+	sender = dbus_message_get_sender(msg);
+
+	DBG("sender: %s", sender);
+
+	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+						DBUS_TYPE_INVALID) == FALSE)
+		return __ofono_error_invalid_args(msg);
+
+	if (history->agent_owner == NULL)
+		return __ofono_error_not_available(msg);
+
+	if (g_str_equal(sender, history->agent_owner) == FALSE)
+		return __ofono_error_access_denied(msg);
+
+	g_dbus_remove_watch(history->conn, history->agent_watch);
+	history->agent_watch = 0;
+
+	free_agent_data(history);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable history_methods[] = {
+	{ "RegisterAgent",   "o", "",  register_agent   },
+	{ "UnregisterAgent", "o", "",  unregister_agent },
+	{ }
+};
+
+static void send_release(struct history_agent *history)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(history->agent_owner,
+						history->agent_path,
+						AGENT_INTERFACE, "Release");
+	if (msg == NULL)
+		return;
+
+	dbus_message_set_no_reply(msg, TRUE);
+
+	g_dbus_send_message(history->conn, msg);
+}
+
+static void free_history(struct history_agent *history)
+{
+	g_queue_free(history->queue);
+
+	g_free(history->imsi);
+	g_free(history);
+}
+
+static int history_probe(struct ofono_history_context *context)
+{
+	struct history_agent *history;
+	struct ofono_atom *atom;
+	struct ofono_sim *sim;
+
+	DBG("modem: %p", context->modem);
+
+	history = g_try_new0(struct history_agent, 1);
+	if (history == NULL)
+		return -ENOMEM;
+
+	history->conn = ofono_dbus_get_connection();
+	history->path = ofono_modem_get_path(context->modem);
+
+	atom = __ofono_modem_find_atom(context->modem, OFONO_ATOM_TYPE_SIM);
+	sim = __ofono_atom_get_data(atom);
+
+	history->imsi = g_strdup(ofono_sim_get_imsi(sim));
+
+	DBG("imsi: %s", history->imsi);
+
+	history->queue = g_queue_new();
+
+	process_spool(history);
+
+	if (g_dbus_register_interface(history->conn, history->path,
+					HISTORY_INTERFACE, history_methods,
+					NULL, NULL, history, NULL) == FALSE) {
+		ofono_error("Could not create %s interface",
+						HISTORY_INTERFACE);
+		free_history(history);
+		return -EIO;
+	}
+
+	ofono_modem_add_interface(context->modem, HISTORY_INTERFACE);
+
+	context->data = history;
+
+	return 0;
+}
+
+static void history_remove(struct ofono_history_context *context)
+{
+	struct history_agent *history = context->data;
+
+	DBG("modem: %p", context->modem);
+
+	ofono_modem_remove_interface(context->modem, HISTORY_INTERFACE);
+
+	if (history->agent_watch > 0) {
+		g_dbus_remove_watch(history->conn, history->agent_watch);
+
+		send_release(history);
+	}
+
+	free_agent_data(history);
+
+	g_dbus_unregister_interface(history->conn, history->path,
+						HISTORY_INTERFACE);
+
+	free_history(history);
+}
+
+static void history_call_ended(struct ofono_history_context *context,
+					const struct ofono_call *call,
+					time_t start, time_t end)
+{
+	struct history_agent *history = context->data;
+	char stime_str[29], etime_str[29];
+	const char *number_str, *name_str, *dir_str;
+
+	strftime(stime_str, 28, "%Y-%m-%dT%H:%M:%S%z", localtime(&start));
+	stime_str[28] = '\0';
+
+	strftime(etime_str, 28, "%Y-%m-%dT%H:%M:%S%z", localtime(&end));
+	etime_str[28] = '\0';
+
+	dir_str = call->direction == 0 ? "outgoing" : "incoming";
+
+	if (call->clip_validity == 0)
+		number_str = phone_number_to_string(&call->phone_number);
+	else
+		number_str = "";
+
+	if (call->cnap_validity == 0)
+		name_str = call->name;
+	else
+		name_str = "";
+
+	create_history(history, "CallEnded", "Number", number_str,
+						"Name", name_str,
+						"Direction", dir_str,
+						"StartTime", stime_str,
+						"EndTime", etime_str, NULL);
+}
+
+static void history_call_missed(struct ofono_history_context *context,
+					const struct ofono_call *call,
+					time_t when)
+{
+	struct history_agent *history = context->data;
+	char time_str[29];
+	const char *number_str, *name_str;
+
+	strftime(time_str, 28, "%Y-%m-%dT%H:%M:%S%z", localtime(&when));
+	time_str[28] = '\0';
+
+	if (call->clip_validity == 0)
+		number_str = phone_number_to_string(&call->phone_number);
+	else
+		number_str = "";
+
+	if (call->cnap_validity == 0)
+		name_str = call->name;
+	else
+		name_str = "";
+
+	create_history(history, "CallMissed", "Number", number_str,
+						"Name", name_str,
+						"LocalTime", time_str, NULL);
+}
+
+static void history_sms_received(struct ofono_history_context *context,
+						const struct ofono_uuid *uuid,
+						const char *from,
+						const struct tm *remote,
+						const struct tm *local,
+						const char *text)
+{
+	struct history_agent *history = context->data;
+	char ltime_str[29], rtime_str[29];
+	const char *uuid_str;
+
+	strftime(rtime_str, 28, "%Y-%m-%dT%H:%M:%S%z", remote);
+	rtime_str[28] = '\0';
+
+	strftime(ltime_str, 28, "%Y-%m-%dT%H:%M:%S%z", local);
+	ltime_str[28] = '\0';
+
+	uuid_str = ofono_uuid_to_str(uuid);
+
+	create_history(history, "MessageReceived", "Identifier", uuid_str,
+						"Sender", from,
+						"Text", text,
+						"RemoteTime", rtime_str,
+						"LocalTime", ltime_str, NULL);
+}
+
+static void history_sms_send_pending(struct ofono_history_context *context,
+						const struct ofono_uuid *uuid,
+						const char *to, time_t when,
+						const char *text)
+{
+	struct history_agent *history = context->data;
+	char time_str[29];
+	const char *uuid_str;
+
+	strftime(time_str, 28, "%Y-%m-%dT%H:%M:%S%z", localtime(&when));
+	time_str[28] = '\0';
+
+	uuid_str = ofono_uuid_to_str(uuid);
+
+	create_history(history, "MessageSubmitted", "Identifier", uuid_str,
+						"Receiver", to,
+						"Text", text,
+						"LocalTime", time_str, NULL);
+}
+
+static void history_sms_send_status(struct ofono_history_context *context,
+				const struct ofono_uuid *uuid, time_t when,
+				enum ofono_history_sms_status status)
+{
+	struct history_agent *history = context->data;
+	char time_str[29];
+	const char *uuid_str, *status_str = NULL;
+
+	switch (status) {
+	case OFONO_HISTORY_SMS_STATUS_PENDING:
+		status_str = "pending";
+		break;
+	case OFONO_HISTORY_SMS_STATUS_SUBMITTED:
+		status_str = "submitted";
+		break;
+	case OFONO_HISTORY_SMS_STATUS_SUBMIT_FAILED:
+		status_str = "submit-failed";
+		break;
+	case OFONO_HISTORY_SMS_STATUS_DELIVERED:
+		status_str = "delivered";
+		break;
+	case OFONO_HISTORY_SMS_STATUS_DELIVER_FAILED:
+		status_str = "deliver-failed";
+		break;
+	}
+
+	if (status_str == NULL)
+		return;
+
+	strftime(time_str, 28, "%Y-%m-%dT%H:%M:%S%z", localtime(&when));
+	time_str[28] = '\0';
+
+	uuid_str = ofono_uuid_to_str(uuid);
+
+	create_history(history, "MessageStatus", "Identifier", uuid_str,
+						"Status", status_str,
+						"LocalTime", time_str, NULL);
+}
+
+static struct ofono_history_driver history_driver = {
+	.name			= "History Agent",
+	.probe			= history_probe,
+	.remove			= history_remove,
+	.call_ended		= history_call_ended,
+	.call_missed		= history_call_missed,
+	.sms_received		= history_sms_received,
+	.sms_send_pending	= history_sms_send_pending,
+	.sms_send_status	= history_sms_send_status,
+};
+
+static int history_agent_init(void)
+{
+	return ofono_history_driver_register(&history_driver);
+}
+
+static void history_agent_exit(void)
+{
+	ofono_history_driver_unregister(&history_driver);
+}
+
+OFONO_PLUGIN_DEFINE(history_agent, "History Agent Plugin",
+				VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT,
+				history_agent_init, history_agent_exit)
-- 
1.7.4


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2011-02-07  2:06 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-02-07  2:06 [RFC v2] modules: Add support for history agent interface Marcel Holtmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.