---
 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(a);
+       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

_______________________________________________
ofono mailing list
[email protected]
http://lists.ofono.org/listinfo/ofono

Reply via email to