summaryrefslogtreecommitdiffstats
path: root/drivers/isimodem/phonebook.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/isimodem/phonebook.c')
-rw-r--r--drivers/isimodem/phonebook.c432
1 files changed, 432 insertions, 0 deletions
diff --git a/drivers/isimodem/phonebook.c b/drivers/isimodem/phonebook.c
new file mode 100644
index 00000000..7c544741
--- /dev/null
+++ b/drivers/isimodem/phonebook.c
@@ -0,0 +1,432 @@
+/*
+ * This file is part of oFono - Open Source Telephony
+ *
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Alexander Kanavin <alexander.kanavin@nokia.com>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gisi/client.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/phonebook.h>
+#include "util.h"
+
+#include "isi.h"
+
+#define PHONEBOOK_TIMEOUT 5
+#define PN_SIM 0x09
+
+enum pb_message_id {
+ SIM_PB_REQ_SIM_PB_READ = 0xDC,
+ SIM_PB_RESP_SIM_PB_READ = 0xDD
+};
+
+enum pb_service_types {
+ SIM_PB_READ = 0x0F
+};
+
+enum pb_sub_block_id {
+ SIM_PB_INFO_REQUEST = 0xE4,
+ SIM_PB_STATUS = 0xFB,
+ SIM_PB_LOCATION = 0xFE,
+ SIM_PB_LOCATION_SEARCH = 0xFF
+};
+
+enum pb_type {
+ SIM_PB_ADN = 0xC8
+};
+
+enum pb_tag {
+ SIM_PB_ANR = 0xCA,
+ SIM_PB_EMAIL = 0xDD,
+ SIM_PB_SNE = 0xF7
+};
+
+enum pb_status {
+ SIM_SERV_OK = 0x01
+};
+
+struct pb_data {
+ GIsiClient *client;
+};
+
+static char *ucs2_to_utf8(const unsigned char *str, long len)
+{
+ char *utf8;
+ utf8 = g_convert((const char *)str, len, "UTF-8//TRANSLIT", "UCS-2BE",
+ NULL, NULL, NULL);
+ return utf8;
+}
+
+static int decode_read_response(const unsigned char *msg, size_t len,
+ struct ofono_phonebook *pb)
+{
+ int retval = -1;
+
+ unsigned int i;
+ unsigned int p;
+
+ char *name = NULL;
+ char *number = NULL;
+ char *adn = NULL;
+ char *snr = NULL;
+ char *email = NULL;
+
+ unsigned int location = 0;
+ unsigned int status = 0;
+
+ unsigned int messageid;
+ unsigned int servicetype;
+ unsigned int num_subblocks;
+
+ if (len < 3)
+ goto cleanup;
+
+ messageid = msg[0];
+ servicetype = msg[1];
+ num_subblocks = msg[2];
+
+ if (messageid != SIM_PB_RESP_SIM_PB_READ || servicetype != SIM_PB_READ)
+ goto cleanup;
+
+ p = 3;
+ for (i=0; i < num_subblocks; i++) {
+ unsigned int subblock_type;
+ unsigned int subblock_len;
+
+ if (p + 4 > len)
+ goto cleanup;
+
+ subblock_type = (msg[p] << 8) + msg[p + 1];
+ subblock_len = (msg[p + 2] << 8) + msg[p + 3];
+
+ switch (subblock_type) {
+
+ case SIM_PB_ADN: {
+ unsigned int namelength;
+ unsigned int numberlength;
+
+ if (p + 8 > len)
+ goto cleanup;
+
+ location = (msg[p + 4] << 8) + msg[p + 5];
+ namelength = msg[p + 6];
+ numberlength = msg[p + 7];
+
+ if (p + 8 + namelength * 2 + numberlength * 2 > len)
+ goto cleanup;
+
+ name = ucs2_to_utf8(msg + p + 8, namelength * 2);
+ number = ucs2_to_utf8(msg + p + 8 + namelength * 2,
+ numberlength * 2);
+ DBG("ADN subblock: name %s number %s location %i",
+ name, number, location);
+ break;
+ }
+
+ case SIM_PB_SNE: {
+ unsigned int locsne;
+ unsigned int snelength;
+ unsigned int snefiller;
+
+ if (p + 8 > len)
+ goto cleanup;
+
+ locsne = (msg[p + 4] << 8) + msg[p + 5];
+ snelength = msg[p + 6];
+ snefiller = msg[p + 7];
+
+ if (p + 8 + snelength * 2 > len)
+ goto cleanup;
+
+ adn = ucs2_to_utf8(msg + p + 8, snelength * 2);
+ DBG("SNE subblock: name %s", adn);
+ break;
+ }
+
+ case SIM_PB_ANR: {
+ unsigned int locanr;
+ unsigned int anrlength;
+ unsigned int anrfiller;
+
+ if (p + 8 > len)
+ goto cleanup;
+
+ locanr = (msg[p + 4] << 8) + msg[p + 5];
+ anrlength = msg[p + 6];
+ anrfiller = msg[p + 7];
+
+ if (p + 8 + anrlength * 2 > len)
+ goto cleanup;
+
+ snr = ucs2_to_utf8(msg + p + 8, anrlength * 2);
+ DBG("ANR subblock: number %s", snr);
+ break;
+ }
+
+ case SIM_PB_EMAIL: {
+ unsigned int locemail;
+ unsigned int emaillength;
+ unsigned int emailfiller;
+
+ if (p + 8 > len)
+ goto cleanup;
+
+ locemail = (msg[p + 4] << 8) + msg[p + 5];
+ emaillength = msg[p + 6];
+ emailfiller = msg[p + 7];
+
+ if (p + 8 + emaillength * 2 > len)
+ goto cleanup;
+
+ email = ucs2_to_utf8(msg + p + 8, emaillength * 2);
+ DBG("EMAIL subblock: email %s", email);
+ break;
+ }
+
+ case SIM_PB_STATUS:
+ if (p + 5 > len)
+ goto cleanup;
+
+ status = msg[p + 4];
+ DBG("STATUS subblock: status %i", status);
+ break;
+
+ default:
+ DBG("Unknown subblock in read response: type %i length %i",
+ subblock_type, subblock_len);
+ break;
+ }
+
+ p += subblock_len;
+ }
+
+ if (status == SIM_SERV_OK) {
+
+ ofono_phonebook_entry(pb, -1, number, -1, name, -1, NULL,
+ snr, -1, adn, email, NULL, NULL);
+ retval = location;
+ }
+
+cleanup:
+ g_free(name);
+ g_free(number);
+ g_free(adn);
+ g_free(snr);
+ g_free(email);
+
+ return retval;
+}
+
+static void read_next_entry(GIsiClient *client, int location, GIsiResponseFunc read_cb, struct isi_cb_data *cbd)
+{
+ ofono_phonebook_cb_t cb = cbd->cb;
+ const unsigned char msg[] = {
+ SIM_PB_REQ_SIM_PB_READ,
+ SIM_PB_READ,
+ 2, /* number of subblocks */
+ 0, SIM_PB_LOCATION_SEARCH, /* subblock id */
+ 0, 8, /* subblock size */
+ 0, SIM_PB_ADN,
+ location >> 8, location & 0xFF, /* read next entry after specified by location */
+ 0, SIM_PB_INFO_REQUEST, /* subblock id */
+ 0, 16, /* subblock size */
+ 4, /* number of tags */
+ 0, /* filler */
+ 0, SIM_PB_ADN, /* tags */
+ 0, SIM_PB_SNE,
+ 0, SIM_PB_ANR,
+ 0, SIM_PB_EMAIL,
+ 0, 0 /* filler */
+ };
+
+ if (location < 0)
+ goto error;
+
+ if (!cbd)
+ goto error;
+
+ if (g_isi_request_make(client, msg, sizeof(msg), PHONEBOOK_TIMEOUT,
+ read_cb, cbd))
+ return;
+
+error:
+ {
+ DECLARE_FAILURE(error);
+ cb(&error, cbd->data);
+ g_free(cbd);
+ }
+}
+
+static bool read_resp_cb(GIsiClient *client, const void *restrict data,
+ size_t len, uint16_t object, void *opaque)
+{
+ const unsigned char *msg = data;
+ struct isi_cb_data *cbd = opaque;
+ ofono_phonebook_cb_t cb = cbd->cb;
+ int location;
+
+ if(!msg) {
+ DBG("ISI client error: %d", g_isi_client_error(client));
+ goto error;
+ }
+
+ location = decode_read_response(data, len, cbd->user);
+ if (location != -1) {
+ read_next_entry(client, location, read_resp_cb, cbd);
+ return true;
+ }
+
+ {
+ DECLARE_SUCCESS(error);
+ cb(&error, cbd->data);
+ goto out;
+ }
+
+error:
+ {
+ DECLARE_FAILURE(error);
+ cb(&error, cbd->data);
+ }
+
+out:
+ g_free(cbd);
+ return true;
+}
+
+static void isi_export_entries(struct ofono_phonebook *pb, const char *storage,
+ ofono_phonebook_cb_t cb, void *data)
+{
+ struct pb_data *pbd = ofono_phonebook_get_data(pb);
+ struct isi_cb_data *cbd = isi_cb_data_new(NULL, cb, data);
+ const unsigned char msg[] = {
+ SIM_PB_REQ_SIM_PB_READ,
+ SIM_PB_READ,
+ 2, /* number of subblocks */
+ 0, SIM_PB_LOCATION, /* subblock id */
+ 0, 8, /* subblock size */
+ 0, SIM_PB_ADN,
+ 0xFF, 0xFF, /* read first entry in the phonebook */
+ 0, SIM_PB_INFO_REQUEST, /* subblock id */
+ 0, 16, /* subblock size */
+ 4, /* number of tags */
+ 0, /* filler */
+ 0, SIM_PB_ADN, /* tags */
+ 0, SIM_PB_SNE,
+ 0, SIM_PB_ANR,
+ 0, SIM_PB_EMAIL,
+ 0, 0 /* filler */
+ };
+
+ if (!cbd)
+ goto error;
+
+ if (strcmp(storage, "SM"))
+ goto error;
+
+ cbd->user = pb;
+
+ if (g_isi_request_make(pbd->client, msg, sizeof(msg), PHONEBOOK_TIMEOUT,
+ read_resp_cb, cbd))
+ return;
+
+error:
+ if (cbd)
+ g_free(cbd);
+
+ {
+ DECLARE_FAILURE(error);
+ cb(&error, data);
+ }
+}
+
+static gboolean isi_phonebook_register(gpointer user)
+{
+ struct ofono_phonebook *pb = user;
+
+ ofono_phonebook_register(pb);
+
+ return FALSE;
+}
+
+static int isi_phonebook_probe(struct ofono_phonebook *pb)
+{
+ GIsiModem *idx = ofono_phonebook_get_data(pb);
+ struct pb_data *data = g_try_new0(struct pb_data, 1);
+
+ if (!data)
+ return -ENOMEM;
+
+ DBG("idx=%p", idx);
+
+ data->client = g_isi_client_create(idx, PN_SIM);
+ if (!data->client) {
+ g_free(data);
+ return -ENOMEM;
+ }
+
+ ofono_phonebook_set_data(pb, data);
+
+ /* FIXME: If this is running on a phone itself, phonebook
+ * initialization needs to be done here */
+
+ g_idle_add(isi_phonebook_register, pb);
+
+ return 0;
+}
+
+static int isi_phonebook_remove(struct ofono_phonebook *pb)
+{
+ struct pb_data *data = ofono_phonebook_get_data(pb);
+
+ if (data) {
+ g_isi_client_destroy(data->client);
+ g_free(data);
+ }
+
+ return 0;
+}
+
+static struct ofono_phonebook_driver driver = {
+ .name = "isi",
+ .probe = isi_phonebook_probe,
+ .remove = isi_phonebook_remove,
+ .export_entries = isi_export_entries
+};
+
+void isi_phonebook_init()
+{
+ ofono_phonebook_driver_register(&driver);
+}
+
+void isi_phonebook_exit()
+{
+ ofono_phonebook_driver_unregister(&driver);
+}