ofono/drivers/atmodem/phonebook.c

563 lines
13 KiB
C
Raw Normal View History

2009-06-15 08:18:48 +00:00
/*
* oFono - GSM Telephony Stack for Linux
*
* Copyright (C) 2008-2009 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
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <glib.h>
#include <ofono/log.h>
2009-07-29 19:33:58 +00:00
#include <ofono/modem.h>
#include <ofono/phonebook.h>
2009-06-15 08:18:48 +00:00
#include "util.h"
#include "gatchat.h"
#include "gatresult.h"
#include "at.h"
#define INDEX_INVALID -1
2009-07-16 07:05:33 +00:00
#define CHARSET_UTF8 1
#define CHARSET_UCS2 2
#define CHARSET_IRA 4
2009-07-16 07:05:33 +00:00
#define CHARSET_SUPPORT (CHARSET_UTF8 | CHARSET_UCS2)
2009-06-15 08:18:48 +00:00
static const char *none_prefix[] = { NULL };
2009-07-16 22:17:54 +00:00
static const char *cpbr_prefix[] = { "+CPBR:", NULL };
static const char *cscs_prefix[] = { "+CSCS:", NULL };
static const char *cpbs_prefix[] = { "+CPBS:", NULL };
2009-07-16 07:05:33 +00:00
struct pb_data {
int index_min, index_max;
2009-07-16 22:17:54 +00:00
char *old_charset;
int supported;
GAtChat *chat;
2009-07-16 07:05:33 +00:00
};
2009-07-16 22:17:54 +00:00
static char *ucs2_to_utf8(const char *str)
2009-07-16 07:05:33 +00:00
{
long len;
unsigned char *ucs2;
2009-07-16 22:17:54 +00:00
char *utf8;
2009-07-16 07:05:33 +00:00
ucs2 = decode_hex(str, -1, &len, 0);
2009-07-16 22:17:54 +00:00
utf8 = g_convert((char *)ucs2, len, "UTF-8//TRANSLIT", "UCS-2BE",
2009-07-16 07:05:33 +00:00
NULL, NULL, NULL);
g_free(ucs2);
return utf8;
}
2009-06-15 08:18:48 +00:00
2009-07-16 22:17:54 +00:00
static const char *best_charset(int supported)
{
const char *charset = "Invalid";
if (supported & CHARSET_IRA)
charset = "IRA";
2009-07-16 22:17:54 +00:00
if (supported & CHARSET_UCS2)
charset = "UCS2";
if (supported & CHARSET_UTF8)
charset = "UTF-8";
return charset;
}
static void at_cpbr_notify(GAtResult *result, gpointer user_data)
2009-06-15 08:18:48 +00:00
{
struct cb_data *cbd = user_data;
struct ofono_phonebook *pb = cbd->user;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
2009-06-15 08:18:48 +00:00
GAtResultIter iter;
2009-07-16 22:17:54 +00:00
int current;
dump_response("at_cbpr_notify", 1, result);
if (pbd->supported & CHARSET_IRA)
current = CHARSET_IRA;
if (pbd->supported & CHARSET_UCS2)
2009-07-16 22:17:54 +00:00
current = CHARSET_UCS2;
if (pbd->supported & CHARSET_UTF8)
2009-07-16 22:17:54 +00:00
current = CHARSET_UTF8;
2009-07-16 07:05:33 +00:00
2009-06-15 08:18:48 +00:00
g_at_result_iter_init(&iter, result);
while (g_at_result_iter_next(&iter, "+CPBR:")) {
int index;
const char *number;
int type;
const char *text;
int hidden = -1;
const char *group = NULL;
const char *adnumber = NULL;
int adtype = -1;
const char *secondtext = NULL;
const char *email = NULL;
const char *sip_uri = NULL;
const char *tel_uri = NULL;
2009-06-15 08:18:48 +00:00
if (!g_at_result_iter_next_number(&iter, &index))
continue;
if (!g_at_result_iter_next_string(&iter, &number))
continue;
if (!g_at_result_iter_next_number(&iter, &type))
2009-06-15 08:18:48 +00:00
continue;
if (!g_at_result_iter_next_string(&iter, &text))
2009-06-15 08:18:48 +00:00
continue;
g_at_result_iter_next_number(&iter, &hidden);
g_at_result_iter_next_string(&iter, &group);
g_at_result_iter_next_string(&iter, &adnumber);
g_at_result_iter_next_number(&iter, &adtype);
g_at_result_iter_next_string(&iter, &secondtext);
g_at_result_iter_next_string(&iter, &email);
g_at_result_iter_next_string(&iter, &sip_uri);
g_at_result_iter_next_string(&iter, &tel_uri);
2009-07-16 07:05:33 +00:00
/* charset_current is either CHARSET_UCS2 or CHARSET_UTF8 */
2009-07-16 22:17:54 +00:00
if (current == CHARSET_UCS2) {
char *text_utf8;
char *group_utf8 = NULL;
char *secondtext_utf8 = NULL;
char *email_utf8 = NULL;
char *sip_uri_utf8 = NULL;
char *tel_uri_utf8 = NULL;
2009-07-16 07:05:33 +00:00
text_utf8 = ucs2_to_utf8(text);
if (text_utf8 == NULL)
ofono_warn("Name field conversion to UTF8"
" failed, this can indicate a"
" problem with modem"
" integration, as this field"
" is required by 27.007."
" Contents of name reported"
" by modem: %s", text);
2009-07-16 07:05:33 +00:00
if (group)
group_utf8 = ucs2_to_utf8(group);
if (secondtext)
secondtext_utf8 = ucs2_to_utf8(secondtext);
if (email)
email_utf8 = ucs2_to_utf8(email);
if (sip_uri)
sip_uri_utf8 = ucs2_to_utf8(sip_uri);
if (tel_uri)
tel_uri_utf8 = ucs2_to_utf8(tel_uri);
ofono_phonebook_entry(pb, index, number, type,
2009-07-16 07:05:33 +00:00
text_utf8, hidden, group_utf8, adnumber,
adtype, secondtext_utf8, email_utf8,
sip_uri_utf8, tel_uri_utf8);
2009-07-16 22:17:54 +00:00
g_free(text_utf8);
g_free(group_utf8);
g_free(secondtext_utf8);
g_free(email_utf8);
g_free(sip_uri_utf8);
g_free(tel_uri_utf8);
} else {
/* In the case of IRA charset, assume these are Latin1
* characters, same as in UTF8
*/
ofono_phonebook_entry(pb, index, number, type,
2009-07-16 22:17:54 +00:00
text, hidden, group, adnumber,
adtype, secondtext, email,
sip_uri, tel_uri);
2009-07-16 07:05:33 +00:00
}
}
}
2009-06-15 08:18:48 +00:00
2009-07-16 22:17:54 +00:00
static void export_failed(struct cb_data *cbd)
{
struct ofono_phonebook *pb = cbd->user;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
ofono_phonebook_cb_t cb = cbd->cb;
2009-07-16 22:17:54 +00:00
{
DECLARE_FAILURE(error);
cb(&error, cbd->data);
}
g_free(cbd);
if (pbd->old_charset) {
g_free(pbd->old_charset);
pbd->old_charset = NULL;
2009-07-16 22:17:54 +00:00
}
}
static void at_read_entries_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct cb_data *cbd = user_data;
struct ofono_phonebook *pb = cbd->user;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
ofono_phonebook_cb_t cb = cbd->cb;
2009-07-16 22:17:54 +00:00
const char *charset;
struct ofono_error error;
char buf[32];
2009-06-15 08:18:48 +00:00
2009-07-16 22:17:54 +00:00
decode_at_error(&error, g_at_result_final_response(result));
cb(&error, cbd->data);
g_free(cbd);
charset = best_charset(pbd->supported);
2009-07-16 22:17:54 +00:00
if (strcmp(pbd->old_charset, charset)) {
sprintf(buf, "AT+CSCS=\"%s\"", pbd->old_charset);
g_at_chat_send(pbd->chat, buf, none_prefix, NULL, NULL, NULL);
2009-07-16 07:05:33 +00:00
}
2009-07-16 22:17:54 +00:00
g_free(pbd->old_charset);
pbd->old_charset = NULL;
2009-06-15 08:18:48 +00:00
}
2009-07-16 22:17:54 +00:00
static void at_read_entries(struct cb_data *cbd)
2009-06-15 08:18:48 +00:00
{
struct ofono_phonebook *pb = cbd->user;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
2009-06-15 08:18:48 +00:00
char buf[32];
sprintf(buf, "AT+CPBR=%d,%d", pbd->index_min, pbd->index_max);
if (g_at_chat_send_listing(pbd->chat, buf, cpbr_prefix,
at_cpbr_notify, at_read_entries_cb,
2009-07-16 22:17:54 +00:00
cbd, NULL) > 0)
2009-06-15 08:18:48 +00:00
return;
2009-07-16 22:17:54 +00:00
/* If we get here, then most likely connection to the modem dropped
* and we can't really restore the charset anyway
*/
export_failed(cbd);
2009-07-16 07:05:33 +00:00
}
2009-07-16 22:17:54 +00:00
static void at_set_charset_cb(gboolean ok, GAtResult *result,
gpointer user_data)
2009-07-16 07:05:33 +00:00
{
struct cb_data *cbd = user_data;
2009-07-16 22:17:54 +00:00
if (!ok) {
export_failed(cbd);
2009-07-16 07:05:33 +00:00
return;
}
2009-07-16 22:17:54 +00:00
at_read_entries(cbd);
2009-07-16 07:05:33 +00:00
}
2009-07-16 22:17:54 +00:00
static void at_read_charset_cb(gboolean ok, GAtResult *result,
gpointer user_data)
2009-07-16 07:05:33 +00:00
{
2009-07-16 22:17:54 +00:00
struct cb_data *cbd = user_data;
struct ofono_phonebook *pb = cbd->user;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
2009-07-16 22:17:54 +00:00
GAtResultIter iter;
2009-07-16 07:05:33 +00:00
const char *charset;
2009-07-16 22:17:54 +00:00
char buf[32];
2009-07-16 07:05:33 +00:00
2009-07-16 22:17:54 +00:00
dump_response("at_read_charset_cb", ok, result);
if (!ok)
2009-07-16 07:05:33 +00:00
goto error;
2009-07-16 22:17:54 +00:00
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CSCS:"))
goto error;
2009-07-16 07:05:33 +00:00
2009-07-16 22:17:54 +00:00
g_at_result_iter_next_string(&iter, &charset);
pbd->old_charset = g_strdup(charset);
2009-07-16 22:17:54 +00:00
charset = best_charset(pbd->supported);
2009-07-16 22:17:54 +00:00
if (!strcmp(pbd->old_charset, charset)) {
2009-07-16 22:17:54 +00:00
at_read_entries(cbd);
return;
}
sprintf(buf, "AT+CSCS=\"%s\"", charset);
if (g_at_chat_send(pbd->chat, buf, none_prefix,
2009-07-16 22:17:54 +00:00
at_set_charset_cb, cbd, NULL) > 0)
2009-07-16 07:05:33 +00:00
return;
2009-06-15 08:18:48 +00:00
error:
2009-07-16 22:17:54 +00:00
export_failed(cbd);
2009-06-15 08:18:48 +00:00
}
static void at_list_indices_cb(gboolean ok, GAtResult *result,
2009-06-16 00:29:10 +00:00
gpointer user_data)
2009-06-15 08:18:48 +00:00
{
struct cb_data *cbd = user_data;
struct ofono_phonebook *pb = cbd->user;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
2009-06-15 08:18:48 +00:00
GAtResultIter iter;
2009-07-16 07:05:33 +00:00
if (!ok)
goto error;
2009-06-15 08:18:48 +00:00
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CPBR:"))
2009-07-16 07:05:33 +00:00
goto error;
if (!g_at_result_iter_open_list(&iter))
2009-07-16 07:05:33 +00:00
goto error;
2009-06-15 08:18:48 +00:00
/* retrieve index_min and index_max from indices
* which seems like "(1-150),32,16"
*/
if (!g_at_result_iter_next_range(&iter, &pbd->index_min,
&pbd->index_max))
2009-07-16 07:05:33 +00:00
goto error;
if (!g_at_result_iter_close_list(&iter))
2009-07-16 07:05:33 +00:00
goto error;
2009-06-15 08:18:48 +00:00
if (g_at_chat_send(pbd->chat, "AT+CSCS?", cscs_prefix,
2009-07-16 22:17:54 +00:00
at_read_charset_cb, cbd, NULL) > 0)
2009-06-15 08:18:48 +00:00
return;
error:
2009-07-16 22:17:54 +00:00
export_failed(cbd);
2009-06-15 08:18:48 +00:00
}
static void at_select_storage_cb(gboolean ok, GAtResult *result,
2009-06-16 00:29:10 +00:00
gpointer user_data)
2009-06-15 08:18:48 +00:00
{
struct cb_data *cbd = user_data;
struct ofono_phonebook *pb = cbd->user;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
2009-06-15 08:18:48 +00:00
2009-07-16 22:17:54 +00:00
dump_response("at_select_storage_cb", ok, result);
if (!ok)
goto error;
if (g_at_chat_send(pbd->chat, "AT+CPBR=?", cpbr_prefix,
2009-07-16 22:17:54 +00:00
at_list_indices_cb, cbd, NULL) > 0)
2009-06-15 08:18:48 +00:00
return;
2009-07-16 22:17:54 +00:00
error:
export_failed(cbd);
2009-06-15 08:18:48 +00:00
}
static void at_export_entries(struct ofono_phonebook *pb, const char *storage,
ofono_phonebook_cb_t cb, void *data)
2009-06-15 08:18:48 +00:00
{
struct pb_data *pbd = ofono_phonebook_get_data(pb);
2009-08-19 23:15:46 +00:00
struct cb_data *cbd = cb_data_new(cb, data);
2009-06-15 08:18:48 +00:00
char buf[32];
if (!cbd)
goto error;
cbd->user = pb;
2009-07-16 22:17:54 +00:00
sprintf(buf, "AT+CPBS=\"%s\"", storage);
if (g_at_chat_send(pbd->chat, buf, none_prefix,
2009-07-16 22:17:54 +00:00
at_select_storage_cb, cbd, NULL) > 0)
2009-06-15 08:18:48 +00:00
return;
error:
if (cbd)
g_free(cbd);
2009-07-16 07:05:33 +00:00
{
DECLARE_FAILURE(error);
2009-07-16 22:17:54 +00:00
cb(&error, data);
2009-07-16 07:05:33 +00:00
}
}
static void phonebook_not_supported(struct ofono_phonebook *pb)
2009-07-16 07:05:33 +00:00
{
2009-07-16 22:17:54 +00:00
ofono_error("Phonebook not supported by this modem. If this is in "
"error please submit patches to support this hardware");
ofono_phonebook_remove(pb);
2009-07-16 22:17:54 +00:00
}
static void at_list_storages_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct ofono_phonebook *pb = user_data;
2009-07-16 22:17:54 +00:00
gboolean sm_supported = FALSE;
gboolean me_supported = FALSE;
gboolean in_list = FALSE;
2009-07-16 07:05:33 +00:00
GAtResultIter iter;
2009-07-16 22:17:54 +00:00
const char *storage;
dump_response("at_list_storages_cb", ok, result);
2009-07-16 07:05:33 +00:00
if (!ok)
goto error;
g_at_result_iter_init(&iter, result);
2009-07-16 22:17:54 +00:00
if (!g_at_result_iter_next(&iter, "+CPBS:"))
2009-07-16 07:05:33 +00:00
goto error;
2009-07-16 22:17:54 +00:00
/* Some modems don't report CPBS in a proper list */
if (g_at_result_iter_open_list(&iter))
in_list = TRUE;
2009-07-16 07:05:33 +00:00
2009-07-16 22:17:54 +00:00
while (g_at_result_iter_next_string(&iter, &storage)) {
if (!strcmp(storage, "ME"))
me_supported = TRUE;
else if (!strcmp(storage, "SM"))
sm_supported = TRUE;
2009-07-16 07:05:33 +00:00
}
2009-07-16 22:17:54 +00:00
if (in_list && !g_at_result_iter_close_list(&iter))
goto error;
2009-07-16 07:05:33 +00:00
2009-07-16 22:17:54 +00:00
if (!me_supported && !sm_supported)
goto error;
2009-07-16 07:05:33 +00:00
ofono_phonebook_register(pb);
2009-07-16 22:17:54 +00:00
return;
2009-07-16 07:05:33 +00:00
2009-07-16 22:17:54 +00:00
error:
phonebook_not_supported(pb);
2009-07-16 07:05:33 +00:00
}
static void at_list_charsets_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct ofono_phonebook *pb = user_data;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
2009-07-16 22:17:54 +00:00
gboolean in_list = FALSE;
2009-07-16 07:05:33 +00:00
GAtResultIter iter;
const char *charset;
2009-07-16 22:17:54 +00:00
dump_response("at_list_charsets_cb", ok, result);
2009-07-16 07:05:33 +00:00
if (!ok)
goto error;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CSCS:"))
goto error;
2009-07-27 05:56:22 +00:00
/* Some modems don't report CSCS in a proper list */
2009-07-16 22:17:54 +00:00
if (g_at_result_iter_open_list(&iter))
in_list = TRUE;
2009-07-16 07:05:33 +00:00
while (g_at_result_iter_next_string(&iter, &charset)) {
if (!strcmp(charset, "UTF-8"))
pbd->supported |= CHARSET_UTF8;
2009-07-16 07:05:33 +00:00
else if (!strcmp(charset, "UCS2"))
pbd->supported |= CHARSET_UCS2;
else if (!strcmp(charset, "IRA"))
pbd->supported |= CHARSET_IRA;
2009-07-16 07:05:33 +00:00
}
if (in_list && !g_at_result_iter_close_list(&iter))
2009-07-16 07:05:33 +00:00
goto error;
if (!(pbd->supported & CHARSET_SUPPORT)) {
/* Some modems, like the Google G1, do not support UCS2 or UTF8
* Such modems are effectively junk, but we can still get some
* useful information out of them by using IRA charset, which
* is essentially Latin1. Still, all bets are off if a SIM
* with UCS2 encoded entries is present.
*/
if (pbd->supported & CHARSET_IRA) {
ofono_error("This modem does not support UCS2 or UTF8 "
"character sets. This means no i18n "
"phonebook is possible on this modem,"
" if this is in error, submit patches "
"to properly support this hardware");
} else
goto error;
}
2009-07-16 07:05:33 +00:00
if (g_at_chat_send(pbd->chat, "AT+CPBS=?", cpbs_prefix,
at_list_storages_cb, pb, NULL) > 0)
2009-07-16 07:05:33 +00:00
return;
2009-07-16 22:17:54 +00:00
error:
phonebook_not_supported(pb);
2009-07-16 07:05:33 +00:00
}
static void at_list_charsets(struct ofono_phonebook *pb)
2009-07-16 07:05:33 +00:00
{
struct pb_data *pbd = ofono_phonebook_get_data(pb);
2009-07-16 07:05:33 +00:00
if (g_at_chat_send(pbd->chat, "AT+CSCS=?", cscs_prefix,
at_list_charsets_cb, pb, NULL) > 0)
2009-07-16 22:17:54 +00:00
return;
2009-07-16 07:05:33 +00:00
phonebook_not_supported(pb);
2009-06-15 08:18:48 +00:00
}
static int at_phonebook_probe(struct ofono_phonebook *pb, unsigned int vendor,
void *data)
2009-06-15 08:18:48 +00:00
{
GAtChat *chat = data;
struct pb_data *pbd;
2009-07-16 07:05:33 +00:00
pbd = g_new0(struct pb_data, 1);
pbd->chat = chat;
ofono_phonebook_set_data(pb, pbd);
at_list_charsets(pb);
return 0;
2009-06-15 08:18:48 +00:00
}
static void at_phonebook_remove(struct ofono_phonebook *pb)
2009-06-15 08:18:48 +00:00
{
struct pb_data *pbd = ofono_phonebook_get_data(pb);
2009-07-16 07:05:33 +00:00
if (pbd->old_charset)
g_free(pbd->old_charset);
2009-07-16 07:05:33 +00:00
g_free(pbd);
}
static struct ofono_phonebook_driver driver = {
.name = "atmodem",
.probe = at_phonebook_probe,
.remove = at_phonebook_remove,
.export_entries = at_export_entries
};
void at_phonebook_init()
{
ofono_phonebook_driver_register(&driver);
}
void at_phonebook_exit()
{
ofono_phonebook_driver_unregister(&driver);
2009-06-15 08:18:48 +00:00
}