---
Makefile.am | 3 +
plugins/context-provisioning.c | 418 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 421 insertions(+), 0 deletions(-)
create mode 100644 plugins/context-provisioning.c
diff --git a/Makefile.am b/Makefile.am
index 42ad86f..18938b6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -347,6 +347,9 @@ builtin_sources += plugins/smart-messaging.c
builtin_modules += push_notification
builtin_sources += plugins/push-notification.c
+builtin_modules += context_provisioning
+builtin_sources += plugins/context-provisioning.c
+
sbin_PROGRAMS = src/ofonod
src_ofonod_SOURCES = $(gdbus_sources) $(builtin_sources) src/ofono.ver \
diff --git a/plugins/context-provisioning.c b/plugins/context-provisioning.c
new file mode 100644
index 0000000..4dcb373
--- /dev/null
+++ b/plugins/context-provisioning.c
@@ -0,0 +1,418 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include <errno.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+
+#include "ofono.h"
+#include <ofono/modem.h>
+#include <ofono/gprs-provision.h>
+#include <ofono/types.h>
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+
+#define OPERATOR_SETTINGS_DIR "operator-settings"
+
+struct parser_data {
+ const gchar *mcc;
+ const gchar *mnc;
+ GSList *candidates;
+};
+
+struct candidate_data {
+ struct ofono_gprs_provision_data *entry;
+ gchar *spn;
+};
+
+static void candidate_data_free(struct candidate_data *cand)
+{
+ if (cand == NULL)
+ return;
+
+ if (cand->entry)
+ __ofono_gprs_provision_free_settings(cand->entry, 1);
+
+ g_free(cand->spn);
+ g_free(cand);
+}
+
+static enum ofono_gprs_context_type string_to_gprs_context_type(const char *str)
+{
+ if (str != NULL) {
+ if (strcasecmp(str, "internet") == 0)
+ return OFONO_GPRS_CONTEXT_TYPE_INTERNET;
+ if (strcasecmp(str, "mms") == 0)
+ return OFONO_GPRS_CONTEXT_TYPE_MMS;
+ if (strcasecmp(str, "wap") == 0)
+ return OFONO_GPRS_CONTEXT_TYPE_WAP;
+ if (strcasecmp(str, "ims") == 0)
+ return OFONO_GPRS_CONTEXT_TYPE_IMS;
+ }
+
+ return OFONO_GPRS_CONTEXT_TYPE_ANY;
+}
+
+static enum ofono_gprs_proto string_to_gprs_proto(const char *str)
+{
+ if (str != NULL) {
+ if (strcasecmp(str, "ipv6") == 0)
+ return OFONO_GPRS_PROTO_IPV6;
+ if (strcasecmp(str, "ipv4v6") == 0)
+ return OFONO_GPRS_PROTO_IPV6; // FIXME when possible
+ }
+
+ return OFONO_GPRS_PROTO_IP;
+}
+
+static struct candidate_data *candidate_data_new(const char *spn,
+ const struct ofono_gprs_provision_data *data)
+{
+ struct candidate_data *cand;
+ cand = g_try_malloc0(sizeof(*cand));
+ if (cand == NULL)
+ return NULL;
+
+ cand->entry = g_try_malloc0(sizeof(*cand->entry));
+ if (cand->entry == NULL) {
+ g_free(cand);
+ return NULL;
+ }
+
+ cand->spn = g_strdup(spn);
+ cand->entry->type = data->type;
+ cand->entry->apn = g_strdup(data->apn);
+ cand->entry->name = g_strdup(data->name);
+ cand->entry->username = g_strdup(data->username);
+ cand->entry->password = g_strdup(data->password);
+ cand->entry->proto = data->proto;
+ cand->entry->message_proxy = g_strdup(data->message_proxy);
+ cand->entry->message_center = g_strdup(data->message_center);
+
+ return cand;
+}
+
+/*
+ * Parse <access> element.
+ * Mandatory attributes in <access>: mcc, mnc, type, name
+ * If MCC/MNC matches, add entry to candidate entry list.
+ */
+static void settings_start_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ int i;
+ struct parser_data *parser = user_data;
+ const char *mcc = NULL, *mnc = NULL, *spn = NULL;
+ struct ofono_gprs_provision_data entry = {0};
+
+ if (strcasecmp(element_name, "access") != 0)
+ return;
+
+ for (i = 0; attribute_names[i] != NULL; i++) {
+ const char *name = attribute_names[i];
+ const char *value = attribute_values[i];
+
+ if (strcasecmp(name, "mcc") == 0) {
+ if (strcmp(value, parser->mcc) != 0)
+ break;
+
+ mcc = value;
+ }
+
+ if (strcasecmp(name, "mnc") == 0) {
+ /*
+ * Match MNC as integers, since settings files
+ * do not always contain leading zeros in MNC values...
+ */
+ if (atoi(value) != atoi(parser->mnc))
+ break;
+
+ mnc = value;
+ }
+
+ if (strcasecmp(name, "spn") == 0)
+ spn = value;
+
+ if (strcasecmp(name, "type") == 0)
+ entry.type = string_to_gprs_context_type(value);
+
+ if (strcasecmp(name, "apn") == 0)
+ entry.apn = (char *) value;
+
+ if (strcasecmp(name, "name") == 0)
+ entry.name = (char *) value;
+
+ if (strcasecmp(name, "username") == 0)
+ entry.username = (char *) value;
+
+ if (strcasecmp(name, "password") == 0)
+ entry.password = (char *) value;
+
+ if (strcasecmp(name, "protocol") == 0)
+ entry.proto = string_to_gprs_proto(value);
+
+ if (strcasecmp(name, "proxy") == 0)
+ entry.message_proxy = (char *) value;
+
+ if (strcasecmp(name, "mmsc") == 0)
+ entry.message_center = (char *) value;
+ }
+
+ if (mcc != NULL && mnc != NULL && entry.type != 0 &&
+ entry.name != NULL && entry.apn != NULL) {
+ struct candidate_data *cand = candidate_data_new(spn, &entry);
+ if (cand == NULL)
+ return;
+
+ parser->candidates = g_slist_append(parser->candidates, cand);
+ }
+}
+
+/*
+ * Returns list of candidate settings matching mcc/mnc from operator
+ * access settings file
+ */
+static GSList *read_access_settings_file(const char *filename,
+ const char *mcc, const char *mnc)
+{
+ gchar *contents;
+ gsize length;
+ GMarkupParseContext *context;
+
+ struct parser_data parser = {0};
+ GMarkupParser settings_parser = { settings_start_element_handler,
+ NULL, NULL, NULL, NULL };
+
+ if (filename == NULL || mcc == NULL || mnc == NULL)
+ return NULL;
+
+ if (g_file_get_contents(filename, &contents, &length,
+ NULL) == FALSE) {
+ ofono_warn("Error reading access settings file %s", filename);
+ return NULL;
+ }
+
+ DBG("Reading access settings file %s", filename);
+
+ parser.mcc = mcc;
+ parser.mnc = mnc;
+
+ context = g_markup_parse_context_new(&settings_parser, 0, &parser,
+ NULL);
+
+ if (g_markup_parse_context_parse(context, contents, length,
+ NULL) == FALSE) {
+ ofono_warn("Error parsing XML file %s", filename);
+ }
+
+ g_markup_parse_context_free(context);
+ g_free(contents);
+
+ return parser.candidates;
+}
+
+static void append_to_settings(struct ofono_gprs_provision_data **settings,
+ int *count,
+ struct ofono_gprs_provision_data **data)
+{
+ *settings = g_try_renew(struct ofono_gprs_provision_data, *settings,
+ *count + 1);
+ if (*settings == NULL) {
+ *count = 0;
+ return;
+ }
+
+ memcpy(*settings + *count, *data, sizeof(**data));
+ *count = *count + 1;
+
+ g_free(*data);
+ *data = NULL;
+
+ return;
+}
+
+/* Compare type and spn (case-insensitively) to a candidate entry */
+static gboolean is_spn_match(struct candidate_data *cand,
+ enum ofono_gprs_context_type type,
+ gchar *spn_casefold)
+{
+ gboolean ret = FALSE;
+ gchar *candspn_casefold = NULL;
+
+ if (spn_casefold == NULL || cand->spn == NULL)
+ return FALSE;
+
+ candspn_casefold = g_utf8_casefold(cand->spn, -1);
+ if (type == cand->entry->type && strcmp(spn_casefold,
+ candspn_casefold) == 0)
+ ret = TRUE;
+
+ g_free(candspn_casefold);
+ return ret;
+}
+
+/* Find an entry from candidates matching type and optionally spn */
+static gboolean get_entry(GSList * candidates,
+ enum ofono_gprs_context_type type,
+ const char *spn,
+ struct ofono_gprs_provision_data **settings,
+ int *count)
+{
+ GSList *lp;
+ gchar *spn_casefold = NULL;
+ gboolean success = FALSE;
+
+ if (spn != NULL)
+ spn_casefold = g_utf8_casefold(spn, -1);
+
+ for (lp = candidates; lp != NULL; lp = lp->next) {
+ struct candidate_data *cand = lp->data;
+
+ if (cand->entry == NULL)
+ continue;
+
+ if ((spn_casefold == NULL && cand->entry->type == type) ||
+ is_spn_match(cand, type, spn_casefold)) {
+ DBG("Found: %s", cand->entry->name);
+
+ append_to_settings(settings, count, &cand->entry);
+ success = TRUE;
+ break;
+ }
+ }
+
+ g_free(spn_casefold);
+
+ /* If match with using spn failed, retry without */
+ if (success == FALSE && spn != NULL)
+ return get_entry(candidates, type, NULL, settings, count);
+
+ return success;
+}
+
+
+static void read_operator_settings(const char *filename, const char *mcc,
+ const char *mnc, const char *spn,
+ struct ofono_gprs_provision_data **settings,
+ int *count)
+{
+ GSList *candidates;
+ candidates = read_access_settings_file(filename, mcc, mnc);
+
+ /* Get settings for internet and mms contexts */
+ get_entry(candidates, OFONO_GPRS_CONTEXT_TYPE_INTERNET, spn,
+ settings, count);
+ get_entry(candidates, OFONO_GPRS_CONTEXT_TYPE_MMS, spn,
+ settings, count);
+ /* TODO: add wap and ims types */
+
+ g_slist_foreach(candidates, (GFunc) candidate_data_free, NULL);
+ g_slist_free(candidates);
+}
+
+/*
+ * Returns GPRS context settings (internet and mms types) based on
+ * SIM provided MCC,MNC and Service Provider Name (SPN) values.
+ * Operator settings for Internet and MMS access points are stored
+ * in XML formatted files (*.xml) under $CONFIGDIR/operator-settings
+ */
+static int get_settings(const char *mcc, const char *mnc, const char *spn,
+ struct ofono_gprs_provision_data **settings,
+ int *count)
+{
+ GDir *dir;
+ GSList *files = NULL, *lp;
+ const gchar *filename;
+
+ *count = 0;
+ *settings = NULL;
+
+ ofono_debug("Provisioning settings for MCC %s, MNC %s SPN '%s'",
+ mcc, mnc, spn);
+
+ /* Find all .xml-files from settings directory */
+ dir = g_dir_open(CONFIGDIR "/" OPERATOR_SETTINGS_DIR, 0, NULL);
+ if (dir == NULL) {
+ ofono_warn("Error opening settings directory");
+ return -ENOENT;
+ }
+
+ while ((filename = g_dir_read_name(dir)) != NULL) {
+ if (g_str_has_suffix(filename, ".xml")) {
+ gchar *fn = g_build_filename(CONFIGDIR,
+ OPERATOR_SETTINGS_DIR,
+ filename, NULL);
+ files = g_slist_append(files, fn);
+ }
+ }
+
+ /* Process files in sorted order */
+ files = g_slist_sort(files, (GCompareFunc) g_strcmp0);
+
+ for (lp = files; lp != NULL; lp = lp->next) {
+ gchar *fn = lp->data;
+ read_operator_settings(fn, mcc, mnc, spn, settings, count);
+
+ if (*count > 0)
+ break;
+ }
+
+ g_slist_foreach(files, (GFunc) g_free, NULL);
+ g_slist_free(files);
+ g_dir_close(dir);
+
+ return *count > 0 ? 0 : -ENOENT;
+}
+
+static struct ofono_gprs_provision_driver provision_driver = {
+ .name = "Connection Context Provisioning Plugin",
+ .priority = OFONO_PLUGIN_PRIORITY_DEFAULT,
+ .get_settings = get_settings,
+};
+
+static int provision_init(void)
+{
+ return ofono_gprs_provision_driver_register(&provision_driver);
+}
+
+static void provision_exit(void)
+{
+ ofono_gprs_provision_driver_unregister(&provision_driver);
+}
+
+OFONO_PLUGIN_DEFINE(context_provisioning,
+ "Connection Context Provisioning Plugin",
+ VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT,
+ provision_init,
+ provision_exit)
+
--
1.7.1