/* * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "ofono.h" #include "dbus-gsm.h" #include "modem.h" #include "driver.h" #include "common.h" #define PHONEBOOK_SERVICES_INTERFACE "org.ofono.Phonebook" #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define LEN_MAX 128 #define TYPE_INTERNATIONAL 145 struct phonebook_data { struct ofono_phonebook_ops *ops; DBusMessage *pending; int storage_support_index; /* go through all supported storage */ int cached; GString entries_vcard; /* entries with vcard 3.0 format */ }; static DBusMessage *export_entries_one_storage(DBusConnection *conn, DBusMessage *msg, void *data); static const char *storage_support[] = { "\"SM\"", "\"ME\"", NULL }; static struct phonebook_data *phonebook_create() { struct phonebook_data *phonebook; phonebook = g_try_new0(struct phonebook_data, 1); return phonebook; } static void phonebook_destroy(gpointer data) { struct ofono_modem *modem = data; struct phonebook_data *phonebook = modem->phonebook; g_free(phonebook); modem->phonebook = NULL; } /* according to RFC 2425, the output string may need folding */ static void vcard_printf(GString *str, const char *fmt, ...) { char buf[1024]; va_list ap; int len_temp, line_number, i; unsigned int line_delimit = 75; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); line_number = strlen(buf) / line_delimit + 1; for (i = 0; i < line_number; i++) { len_temp = MIN(line_delimit, strlen(buf) - line_delimit * i); g_string_append_len(str, buf + line_delimit * i, len_temp); if (i != line_number - 1) g_string_append(str, "\r\n "); } g_string_append(str, "\r\n"); } /* According to RFC 2426, we need escape following characters: * '\n', '\r', ';', ',', '\'. */ static void add_slash(char *dest, char *src, int len_max, int len) { int i, j; for (i = 0, j = 0; i < len && j < len_max; i++, j++) { switch (src[i]) { case '\n': dest[j++] = '\\'; dest[j] = 'n'; break; case '\r': dest[j++] = '\\'; dest[j] = 'r'; break; case '\\': case ';': case ',': dest[j++] = '\\'; default: dest[j] = src[i]; break; } } dest[j] = 0; return; } static void vcard_printf_number(GString *entries_vcard_pointer, int type, char *number, int prefer) { char *pref = "", *intl = ""; char buf[128]; if (prefer) pref = "PREF,"; if ((type == TYPE_INTERNATIONAL) && (number[0] != '+')) intl = "+"; sprintf(buf, "TEL;TYPE=\%sVOICE:\%s\%s", pref, intl, number); vcard_printf(entries_vcard_pointer, buf, number); } static void entry_to_vcard(GString *entries_vcard_pointer, struct ofono_phonebook_entry *entry) { char field[LEN_MAX]; vcard_printf(entries_vcard_pointer, "BEGIN:VCARD"); vcard_printf(entries_vcard_pointer, "VERSION:3.0"); add_slash(field, entry->text, LEN_MAX, strlen(entry->text)); vcard_printf(entries_vcard_pointer, "FN:%s", field); vcard_printf_number(entries_vcard_pointer, entry->type, entry->number, 1); if (entry->group) { add_slash(field, entry->group, LEN_MAX, strlen(entry->group)); vcard_printf(entries_vcard_pointer, "CATEGORIES:%s", field); } if (entry->adtype && entry->adnumber) { vcard_printf_number(entries_vcard_pointer, entry->adtype, entry->adnumber, 0); } if (entry->email) { add_slash(field, entry->email, LEN_MAX, strlen(entry->email)); vcard_printf(entries_vcard_pointer, "EMAIL;TYPE=INTERNET:%s", field); } if (entry->sip_uri) { add_slash(field, entry->sip_uri, LEN_MAX, strlen(entry->sip_uri)); vcard_printf(entries_vcard_pointer, "IMPP;TYPE=SIP:%s", field); } vcard_printf(entries_vcard_pointer, "END:VCARD"); vcard_printf(entries_vcard_pointer, ""); } static DBusMessage *generate_export_entries_reply(struct ofono_modem *modem) { struct phonebook_data *phonebook = modem->phonebook; DBusMessage *reply; DBusMessageIter iter; DBusConnection *conn = dbus_gsm_connection(); reply = dbus_message_new_method_return(phonebook->pending); if (!reply) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &(phonebook->entries_vcard)); g_dbus_send_message(conn, reply); return NULL; } static void export_entries_one_storage_cb(const struct ofono_error *error, int num_entries, const struct ofono_phonebook_entry *entries, void *data) { struct ofono_modem *modem = data; struct phonebook_data *phonebook = modem->phonebook; DBusConnection *conn = dbus_gsm_connection(); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) ofono_error("export_entries_one_storage_cb with %s failed", storage_support[phonebook->storage_support_index]); else { int num = 0; GString *entries_vcard_pointer = &(phonebook->entries_vcard); for (num = 0; num < num_entries; num++) entry_to_vcard(entries_vcard_pointer, (struct ofono_phonebook_entry *)&entries[num]); } phonebook->storage_support_index++; export_entries_one_storage(conn, phonebook->pending, modem); return; } static DBusMessage *export_entries_one_storage(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_modem *modem = data; struct phonebook_data *phonebook = modem->phonebook; if (storage_support[phonebook->storage_support_index] != NULL) phonebook->ops->export_entries(modem, (char *)storage_support[phonebook->storage_support_index], export_entries_one_storage_cb, modem); else { phonebook->cached = 1; generate_export_entries_reply(modem); dbus_message_unref(phonebook->pending); phonebook->pending = NULL; } return NULL; } static DBusMessage *export_entries(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_modem *modem = data; struct phonebook_data *phonebook = modem->phonebook; if (phonebook->pending) { DBusMessage *reply; reply = dbus_gsm_busy(phonebook->pending); g_dbus_send_message(conn, reply); return NULL; } phonebook->pending = dbus_message_ref(msg); if (phonebook->cached) { generate_export_entries_reply(modem); dbus_message_unref(phonebook->pending); phonebook->pending = NULL; return NULL; } g_string_set_size(&(phonebook->entries_vcard), 0); phonebook->storage_support_index = 0; export_entries_one_storage(conn, msg, data); return NULL; } static GDBusMethodTable phonebook_methods[] = { { "ExportEntries", "", "s", export_entries, G_DBUS_METHOD_FLAG_ASYNC }, { } }; static GDBusSignalTable phonebook_signals[] = { { } }; int ofono_phonebook_register(struct ofono_modem *modem, struct ofono_phonebook_ops *ops) { DBusConnection *conn = dbus_gsm_connection(); if (modem == NULL) return -1; if (ops == NULL) return -1; modem->phonebook = phonebook_create(); if (modem->phonebook == NULL) return -1; modem->phonebook->ops = ops; if (!g_dbus_register_interface(conn, modem->path, PHONEBOOK_SERVICES_INTERFACE, phonebook_methods, phonebook_signals, NULL, modem, phonebook_destroy)) { ofono_error("Could not register Phonebook %s", modem->path); phonebook_destroy(modem->phonebook); return -1; } modem_add_interface(modem, PHONEBOOK_SERVICES_INTERFACE); return 0; } void ofono_phonebook_unregister(struct ofono_modem *modem) { DBusConnection *conn = dbus_gsm_connection(); if (modem->phonebook == NULL) return; modem_remove_interface(modem, PHONEBOOK_SERVICES_INTERFACE); g_dbus_unregister_interface(conn, modem->path, PHONEBOOK_SERVICES_INTERFACE); }