/* * This file is part of oFono - Open Source Telephony * * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Alexander Kanavin * * 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 #endif #include #include #include #include #include #include #include #include "driver.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 }; static GIsiClient *client = NULL; struct pb_data { }; static char *ucs2_to_utf8(const char *str, long len) { char *utf8; utf8 = g_convert(str, len, "UTF-8//TRANSLIT", "UCS-2BE", NULL, NULL, NULL); return utf8; } static struct pb_data *phonebook_create() { struct pb_data *pb = g_try_new0(struct pb_data, 1); return pb; } static void phonebook_destroy(struct pb_data *data) { g_free(data); } static int decode_read_response(const unsigned char *msg, size_t len, struct ofono_modem *modem) { unsigned int i, 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; dump_msg(msg, len); if (len < 3) goto error; messageid = msg[0]; servicetype = msg[1]; num_subblocks = msg[2]; if (messageid != SIM_PB_RESP_SIM_PB_READ || servicetype != SIM_PB_READ) goto error; p = 3; for (i=0; i < num_subblocks; i++) { unsigned int subblock_type; unsigned int subblock_len; if (p + 4 > len) goto error; 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 error; 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 error; 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 error; locsne = (msg[p + 4] << 8) + msg[p + 5]; snelength = msg[p + 6]; snefiller = msg[p + 7]; if (p + 8 + snelength * 2 > len) goto error; 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 error; locanr = (msg[p + 4] << 8) + msg[p + 5]; anrlength = msg[p + 6]; anrfiller = msg[p + 7]; if (p + 8 + anrlength * 2 > len) goto error; 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 error; locemail = (msg[p + 4] << 8) + msg[p + 5]; emaillength = msg[p + 6]; emailfiller = msg[p + 7]; if (p + 8 + emaillength * 2 > len) goto error; 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 error; 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(modem, -1, number, -1, name, -1, NULL, snr, -1, adn, email, NULL, NULL); return location; } else { return -1; } error: DBG("Malformed read response"); return -1; } static void read_next_entry(int location, GIsiResponseFunc read_cb, struct isi_cb_data *cbd) { ofono_generic_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_generic_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->modem); if (location != -1) { read_next_entry(location, read_resp_cb, cbd); return; } { DECLARE_SUCCESS(e); cb(&e, cbd->data); } goto out; error: { DECLARE_FAILURE(e); cb(&e, cbd->data); } out: g_free(cbd); return true; } static void isi_export_entries(struct ofono_modem *modem, const char *storage, ofono_generic_cb_t cb, void *data) { struct isi_cb_data *cbd = isi_cb_data_new(modem, 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; if (g_isi_request_make(client, msg, sizeof(msg), PHONEBOOK_TIMEOUT, read_resp_cb, cbd)) return; error: if (cbd) g_free(cbd); { DECLARE_FAILURE(error); cb(&error, data); } } static struct ofono_phonebook_ops ops = { .export_entries = isi_export_entries }; static void phonebook_not_supported(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_userdata(modem); ofono_error("Phonebook not supported by this modem. If this is in " "error please submit patches to support this hardware"); if (isi->pb) { phonebook_destroy(isi->pb); isi->pb = NULL; } } void isi_phonebook_init(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_userdata(modem); isi->pb = phonebook_create(); if (!client) { client = g_isi_client_create(PN_SIM); if (!client) goto error; } /* FIXME: If this is running on a phone itself, phonebook initialization needs to be done here */ ofono_phonebook_register(modem, &ops); return; error: phonebook_not_supported(modem); } void isi_phonebook_exit(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_userdata(modem); ofono_phonebook_unregister(modem); if (client) { g_isi_client_destroy(client); client = NULL; } if (!isi->pb) return; phonebook_destroy(isi->pb); isi->pb = NULL; }