---
 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

_______________________________________________
ofono mailing list
ofono@ofono.org
http://lists.ofono.org/listinfo/ofono

Reply via email to