diff options
-rw-r--r-- | plugins/icera/mm-broadband-bearer-icera.c | 202 | ||||
-rw-r--r-- | plugins/icera/mm-broadband-modem-icera.c | 450 | ||||
-rw-r--r-- | plugins/icera/mm-modem-helpers-icera.c | 103 | ||||
-rw-r--r-- | plugins/icera/mm-modem-helpers-icera.h | 6 | ||||
-rw-r--r-- | plugins/icera/tests/test-modem-helpers-icera.c | 75 |
5 files changed, 649 insertions, 187 deletions
diff --git a/plugins/icera/mm-broadband-bearer-icera.c b/plugins/icera/mm-broadband-bearer-icera.c index 1d6859c0..e4b9d552 100644 --- a/plugins/icera/mm-broadband-bearer-icera.c +++ b/plugins/icera/mm-broadband-bearer-icera.c @@ -640,187 +640,6 @@ activate_ready (MMBaseModem *modem, g_object_unref (self); } -static void authenticate (GTask *task); - -static gboolean -retry_authentication_cb (GTask *task) -{ - authenticate (task); - return G_SOURCE_REMOVE; -} - -static void -authenticate_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - MMBroadbandBearerIcera *self; - Dial3gppContext *ctx; - GError *error = NULL; - gchar *command; - - /* If cancelled, complete */ - if (g_task_return_error_if_cancelled (task)) { - g_object_unref (task); - return; - } - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - if (!mm_base_modem_at_command_full_finish (modem, res, &error)) { - /* Retry configuring the context. It sometimes fails with a 583 - * error ["a profile (CID) is currently active"] if a connect - * is attempted too soon after a disconnect. */ - if (++ctx->authentication_retries < 3) { - mm_obj_dbg (self, "authentication failed: %s; retrying...", error->message); - g_error_free (error); - g_timeout_add_seconds (1, (GSourceFunc)retry_authentication_cb, task); - return; - } - - /* Return an error */ - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - /* The unsolicited response to %IPDPACT may come before the OK does. - * We will keep the connection context in the bearer private data so - * that it is accessible from the unsolicited message handler. Note - * also that we do NOT pass the ctx to the GAsyncReadyCallback, as it - * may not be valid any more when the callback is called (it may be - * already completed in the unsolicited handling) */ - g_assert (self->priv->connect_pending == NULL); - self->priv->connect_pending = task; - - command = g_strdup_printf ("%%IPDPACT=%d,1", ctx->cid); - mm_base_modem_at_command_full (ctx->modem, - ctx->primary, - command, - MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, - FALSE, - FALSE, /* raw */ - NULL, /* cancellable */ - (GAsyncReadyCallback) activate_ready, - g_object_ref (self)); /* we pass the bearer object! */ - g_free (command); -} - -static void -authenticate (GTask *task) -{ - MMBroadbandBearerIcera *self; - Dial3gppContext *ctx; - gchar *command; - const gchar *user; - const gchar *password; - MMBearerAllowedAuth allowed_auth; - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); - password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); - allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); - - /* Both user and password are required; otherwise firmware returns an error */ - if (!user || !password || allowed_auth == MM_BEARER_ALLOWED_AUTH_NONE) { - mm_obj_dbg (self, "not using authentication"); - command = g_strdup_printf ("%%IPDPCFG=%d,0,0,\"\",\"\"", ctx->cid); - } else { - gchar *quoted_user; - gchar *quoted_password; - guint icera_auth; - - if (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) { - mm_obj_dbg (self, "using default (CHAP) authentication method"); - icera_auth = 2; - } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_CHAP) { - mm_obj_dbg (self, "using CHAP authentication method"); - icera_auth = 2; - } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) { - mm_obj_dbg (self, "using PAP authentication method"); - icera_auth = 1; - } else { - gchar *str; - - str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth); - g_task_return_new_error (task, - MM_CORE_ERROR, - MM_CORE_ERROR_UNSUPPORTED, - "Cannot use any of the specified authentication methods (%s)", - str); - g_object_unref (task); - g_free (str); - return; - } - - quoted_user = mm_port_serial_at_quote_string (user); - quoted_password = mm_port_serial_at_quote_string (password); - command = g_strdup_printf ("%%IPDPCFG=%d,0,%u,%s,%s", - ctx->cid, - icera_auth, - quoted_user, - quoted_password); - g_free (quoted_user); - g_free (quoted_password); - } - - mm_base_modem_at_command_full (ctx->modem, - ctx->primary, - command, - 60, - FALSE, - FALSE, /* raw */ - NULL, /* cancellable */ - (GAsyncReadyCallback)authenticate_ready, - task); - g_free (command); -} - -static void -deactivate_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - /* - * Ignore any error here; %IPDPACT=ctx,0 will produce an error 767 - * if the context is not, in fact, connected. This is annoying but - * harmless. - */ - mm_base_modem_at_command_full_finish (modem, res, NULL); - - authenticate (task); -} - -static void -connect_deactivate (GTask *task) -{ - Dial3gppContext *ctx; - gchar *command; - - ctx = g_task_get_task_data (task); - - /* Deactivate the context we want to use before we try to activate - * it. This handles the case where ModemManager crashed while - * connected and is now trying to reconnect. (Should some part of - * the core or modem driver have made sure of this already?) - */ - command = g_strdup_printf ("%%IPDPACT=%d,0", ctx->cid); - mm_base_modem_at_command_full ( - ctx->modem, - ctx->primary, - command, - MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, - FALSE, - FALSE, /* raw */ - NULL, /* cancellable */ - (GAsyncReadyCallback)deactivate_ready, - task); - g_free (command); -} - static void dial_3gpp (MMBroadbandBearer *_self, MMBaseModem *modem, @@ -833,6 +652,7 @@ dial_3gpp (MMBroadbandBearer *_self, MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (_self); GTask *task; Dial3gppContext *ctx; + g_autofree gchar *cmd = NULL; g_assert (primary != NULL); @@ -855,7 +675,25 @@ dial_3gpp (MMBroadbandBearer *_self, return; } - connect_deactivate (task); + /* The unsolicited response to %IPDPACT may come before the OK does. + * We will keep the connection context in the bearer private data so + * that it is accessible from the unsolicited message handler. Note + * also that we do NOT pass the ctx to the GAsyncReadyCallback, as it + * may not be valid any more when the callback is called (it may be + * already completed in the unsolicited handling) */ + g_assert (self->priv->connect_pending == NULL); + self->priv->connect_pending = task; + + cmd = g_strdup_printf ("%%IPDPACT=%d,1", ctx->cid); + mm_base_modem_at_command_full (ctx->modem, + ctx->primary, + cmd, + MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, + FALSE, + FALSE, /* raw */ + NULL, /* cancellable */ + (GAsyncReadyCallback) activate_ready, + g_object_ref (self)); /* we pass the bearer object! */ } /*****************************************************************************/ diff --git a/plugins/icera/mm-broadband-modem-icera.c b/plugins/icera/mm-broadband-modem-icera.c index 8df73b6c..e7bc2dc7 100644 --- a/plugins/icera/mm-broadband-modem-icera.c +++ b/plugins/icera/mm-broadband-modem-icera.c @@ -30,22 +30,27 @@ #include "mm-errors-types.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" +#include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-iface-modem-time.h" #include "mm-base-modem-at.h" #include "mm-bearer-list.h" #include "mm-broadband-bearer-icera.h" #include "mm-broadband-modem-icera.h" +#include "mm-modem-helpers-icera.h" -static void iface_modem_init (MMIfaceModem *iface); -static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); -static void iface_modem_time_init (MMIfaceModemTime *iface); +static void iface_modem_init (MMIfaceModem *iface); +static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); +static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface); +static void iface_modem_time_init (MMIfaceModemTime *iface); -static MMIfaceModem *iface_modem_parent; -static MMIfaceModem3gpp *iface_modem_3gpp_parent; +static MMIfaceModem *iface_modem_parent; +static MMIfaceModem3gpp *iface_modem_3gpp_parent; +static MMIfaceModem3gppProfileManager *iface_modem_3gpp_profile_manager_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemIcera, mm_broadband_modem_icera, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)) enum { @@ -1676,6 +1681,424 @@ modem_time_load_network_timezone (MMIfaceModemTime *self, } /*****************************************************************************/ +/* List profiles (3GPP profile management interface) */ + +typedef struct { + GList *profiles; +} ListProfilesContext; + +static void +list_profiles_context_free (ListProfilesContext *ctx) +{ + mm_3gpp_profile_list_free (ctx->profiles); + g_slice_free (ListProfilesContext, ctx); +} + +static gboolean +modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self, + GAsyncResult *res, + GList **out_profiles, + GError **error) +{ + ListProfilesContext *ctx; + + if (!g_task_propagate_boolean (G_TASK (res), error)) + return FALSE; + + ctx = g_task_get_task_data (G_TASK (res)); + if (out_profiles) + *out_profiles = g_steal_pointer (&ctx->profiles); + return TRUE; +} + +static void +profile_manager_ipdpcfg_query_ready (MMBaseModem *self, + GAsyncResult *res, + GTask *task) +{ + ListProfilesContext *ctx; + const gchar *response; + g_autoptr(GError) error = NULL; + + ctx = g_task_get_task_data (task); + + response = mm_base_modem_at_command_finish (self, res, &error); + if (!response) + mm_obj_warn (self, "couldn't load PDP context auth settings: %s", error->message); + else if (!mm_icera_parse_ipdpcfg_query_response (response, ctx->profiles, self, &error)) + mm_obj_warn (self, "couldn't update profile list with PDP context auth settings: %s", error->message); + + /* complete successfully anyway */ + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +profile_manager_parent_list_profiles_ready (MMIfaceModem3gppProfileManager *self, + GAsyncResult *res, + GTask *task) +{ + ListProfilesContext *ctx; + GError *error = NULL; + + ctx = g_slice_new0 (ListProfilesContext); + g_task_set_task_data (task, ctx, (GDestroyNotify) list_profiles_context_free); + + if (!iface_modem_3gpp_profile_manager_parent->list_profiles_finish (self, res, &ctx->profiles, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (!ctx->profiles) { + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + mm_base_modem_at_command ( + MM_BASE_MODEM (self), + "%IPDPCFG?", + 3, + FALSE, + (GAsyncReadyCallback)profile_manager_ipdpcfg_query_ready, + task); +} + +static void +modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (self, NULL, callback, user_data); + + iface_modem_3gpp_profile_manager_parent->list_profiles ( + self, + (GAsyncReadyCallback)profile_manager_parent_list_profiles_ready, + task); +} + +typedef struct { + gboolean new_id; + gint min_profile_id; + gint max_profile_id; + GEqualFunc apn_cmp; + MM3gppProfileCmpFlags profile_cmp_flags; +} CheckFormatContext; + +static void +check_format_context_free (CheckFormatContext *ctx) +{ + g_slice_free (CheckFormatContext, ctx); +} + +static gboolean +modem_3gpp_profile_manager_check_format_finish (MMIfaceModem3gppProfileManager *self, + GAsyncResult *res, + gboolean *new_id, + gint *min_profile_id, + gint *max_profile_id, + GEqualFunc *apn_cmp, + MM3gppProfileCmpFlags *profile_cmp_flags, + GError **error) +{ + CheckFormatContext *ctx; + + if (!g_task_propagate_boolean (G_TASK (res), error)) + return FALSE; + + ctx = g_task_get_task_data (G_TASK (res)); + if (new_id) + *new_id = ctx->new_id; + if (min_profile_id) + *min_profile_id = (gint) ctx->min_profile_id; + if (max_profile_id) + *max_profile_id = (gint) ctx->max_profile_id; + if (apn_cmp) + *apn_cmp = ctx->apn_cmp; + if (profile_cmp_flags) + *profile_cmp_flags = ctx->profile_cmp_flags; + return TRUE; +} + +static void +profile_manager_parent_check_format_ready (MMIfaceModem3gppProfileManager *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + CheckFormatContext *ctx; + + ctx = g_task_get_task_data (task); + + if (!iface_modem_3gpp_profile_manager_parent->check_format_finish (self, + res, + &ctx->new_id, + &ctx->min_profile_id, + &ctx->max_profile_id, + &ctx->apn_cmp, + &ctx->profile_cmp_flags, + &error)) { + g_task_return_error (task, error); + } else { + /* the icera implementation supports AUTH, so unset that cmp flag */ + ctx->profile_cmp_flags &= ~MM_3GPP_PROFILE_CMP_FLAGS_NO_AUTH; + g_task_return_boolean (task, TRUE); + } + g_object_unref (task); +} + +static void +modem_3gpp_profile_manager_check_format (MMIfaceModem3gppProfileManager *self, + MMBearerIpFamily ip_type, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + CheckFormatContext *ctx; + + task = g_task_new (self, NULL, callback, user_data); + ctx = g_slice_new0 (CheckFormatContext); + g_task_set_task_data (task, ctx, (GDestroyNotify)check_format_context_free); + + iface_modem_3gpp_profile_manager_parent->check_format ( + self, + ip_type, + (GAsyncReadyCallback)profile_manager_parent_check_format_ready, + task); +} + +/*****************************************************************************/ +/* Deactivate profile (3GPP profile management interface) */ + +static gboolean +modem_3gpp_profile_manager_deactivate_profile_finish (MMIfaceModem3gppProfileManager *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +deactivate_profile_ipdpact_set_ready (MMBaseModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + + if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +modem_3gpp_profile_manager_deactivate_profile (MMIfaceModem3gppProfileManager *self, + MM3gppProfile *profile, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + g_autofree gchar *cmd = NULL; + gint profile_id; + + task = g_task_new (self, NULL, callback, user_data); + + profile_id = mm_3gpp_profile_get_profile_id (profile); + mm_obj_dbg (self, "deactivating profile '%d'...", profile_id); + + cmd = g_strdup_printf ("%%IPDPACT=%d,0", profile_id); + mm_base_modem_at_command ( + MM_BASE_MODEM (self), + cmd, + MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, + FALSE, + (GAsyncReadyCallback)deactivate_profile_ipdpact_set_ready, + task); +} + +/*****************************************************************************/ +/* Set profile (3GPP profile management interface) */ + +#define IPDPCFG_SET_MAX_ATTEMPTS 3 +#define IPDPCFG_SET_RETRY_TIMEOUT_SECS 1 + +typedef struct { + MM3gppProfile *profile; + gchar *cmd; + gint profile_id; + guint n_attempts; +} StoreProfileContext; + +static void +store_profile_context_free (StoreProfileContext *ctx) +{ + g_free (ctx->cmd); + g_clear_object (&ctx->profile); + g_slice_free (StoreProfileContext, ctx); +} + +static gint +modem_3gpp_profile_manager_store_profile_finish (MMIfaceModem3gppProfileManager *self, + GAsyncResult *res, + GError **error) +{ + StoreProfileContext *ctx; + + if (!g_task_propagate_boolean (G_TASK (res), error)) + return MM_3GPP_PROFILE_ID_UNKNOWN; + + ctx = g_task_get_task_data (G_TASK (res)); + return ctx->profile_id; +} + +static void profile_manager_store_profile_auth_settings (GTask *task); + +static gboolean +profile_manager_ipdpcfg_set_retry (GTask *task) +{ + profile_manager_store_profile_auth_settings (task); + return G_SOURCE_REMOVE; +} + +static void +profile_manager_ipdpcfg_set_ready (MMBaseModem *self, + GAsyncResult *res, + GTask *task) +{ + StoreProfileContext *ctx; + g_autoptr(GError) error = NULL; + + ctx = g_task_get_task_data (task); + + if (!mm_base_modem_at_command_finish (self, res, &error)) { + /* Retry configuring the context. It sometimes fails with a 583 + * error ["a profile (CID) is currently active"] if a connect + * is attempted too soon after a disconnect. */ + if (ctx->n_attempts < IPDPCFG_SET_MAX_ATTEMPTS) { + mm_obj_dbg (self, "couldn't store auth settings in profile '%d': %s; retrying...", + ctx->profile_id, error->message); + g_timeout_add_seconds (IPDPCFG_SET_RETRY_TIMEOUT_SECS, (GSourceFunc)profile_manager_ipdpcfg_set_retry, task); + return; + } + g_task_return_error (task, g_steal_pointer (&error)); + } else + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +profile_manager_store_profile_auth_settings (GTask *task) +{ + MMIfaceModem3gppProfileManager *self; + StoreProfileContext *ctx; + g_autofree gchar *cmd = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + if (!ctx->cmd) { + const gchar *user; + const gchar *password; + MMBearerAllowedAuth allowed_auth; + + user = mm_3gpp_profile_get_user (ctx->profile); + password = mm_3gpp_profile_get_password (ctx->profile); + allowed_auth = mm_3gpp_profile_get_allowed_auth (ctx->profile); + + /* Both user and password are required; otherwise firmware returns an error */ + if (!user || !password || allowed_auth == MM_BEARER_ALLOWED_AUTH_NONE) { + mm_obj_dbg (self, "not using authentication"); + ctx->cmd = g_strdup_printf ("%%IPDPCFG=%d,0,0,\"\",\"\"", ctx->profile_id); + } else { + g_autofree gchar *quoted_user = NULL; + g_autofree gchar *quoted_password = NULL; + guint icera_auth; + + if (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) { + mm_obj_dbg (self, "using default (CHAP) authentication method"); + icera_auth = 2; + } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_CHAP) { + mm_obj_dbg (self, "using CHAP authentication method"); + icera_auth = 2; + } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) { + mm_obj_dbg (self, "using PAP authentication method"); + icera_auth = 1; + } else { + g_autofree gchar *str = NULL; + + str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth); + g_task_return_new_error (task, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Cannot use any of the specified authentication methods (%s)", + str); + g_object_unref (task); + return; + } + + quoted_user = mm_port_serial_at_quote_string (user); + quoted_password = mm_port_serial_at_quote_string (password); + ctx->cmd = g_strdup_printf ("%%IPDPCFG=%d,0,%u,%s,%s", + ctx->profile_id, + icera_auth, + quoted_user, + quoted_password); + } + } + + ctx->n_attempts++; + mm_base_modem_at_command (MM_BASE_MODEM (self), + ctx->cmd, + 6, + FALSE, + (GAsyncReadyCallback)profile_manager_ipdpcfg_set_ready, + task); +} + +static void +profile_manager_parent_store_profile_ready (MMIfaceModem3gppProfileManager *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + + if (iface_modem_3gpp_profile_manager_parent->store_profile_finish (self, res, &error) == MM_3GPP_PROFILE_ID_UNKNOWN) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + profile_manager_store_profile_auth_settings (task); +} + +static void +modem_3gpp_profile_manager_store_profile (MMIfaceModem3gppProfileManager *self, + MM3gppProfile *profile, + GAsyncReadyCallback callback, + gpointer user_data) +{ + StoreProfileContext *ctx; + GTask *task; + + task = g_task_new (self, NULL, callback, user_data); + ctx = g_slice_new0 (StoreProfileContext); + ctx->profile = g_object_ref (profile); + ctx->profile_id = mm_3gpp_profile_get_profile_id (ctx->profile); + g_assert (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN); + g_task_set_task_data (task, ctx, (GDestroyNotify) store_profile_context_free); + + iface_modem_3gpp_profile_manager_parent->store_profile ( + self, + profile, + (GAsyncReadyCallback)profile_manager_parent_store_profile_ready, + task); +} + +/*****************************************************************************/ /* Load network time (Time interface) */ static gchar * @@ -1884,6 +2307,23 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface) } static void +iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface) +{ + iface_modem_3gpp_profile_manager_parent = g_type_interface_peek_parent (iface); + + iface->list_profiles = modem_3gpp_profile_manager_list_profiles; + iface->list_profiles_finish = modem_3gpp_profile_manager_list_profiles_finish; + iface->check_format = modem_3gpp_profile_manager_check_format; + iface->check_format_finish = modem_3gpp_profile_manager_check_format_finish; + /* note: the parent check_activated_profile() implementation using +CGACT? seems to + * be perfectly valid. */ + iface->deactivate_profile = modem_3gpp_profile_manager_deactivate_profile; + iface->deactivate_profile_finish = modem_3gpp_profile_manager_deactivate_profile_finish; + iface->store_profile = modem_3gpp_profile_manager_store_profile; + iface->store_profile_finish = modem_3gpp_profile_manager_store_profile_finish; +} + +static void iface_modem_time_init (MMIfaceModemTime *iface) { iface->check_support = modem_time_check_support; diff --git a/plugins/icera/mm-modem-helpers-icera.c b/plugins/icera/mm-modem-helpers-icera.c index 57963aba..da1cd873 100644 --- a/plugins/icera/mm-modem-helpers-icera.c +++ b/plugins/icera/mm-modem-helpers-icera.c @@ -25,6 +25,7 @@ #define _LIBMM_INSIDE_MM #include <libmm-glib.h> +#include "mm-log.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-icera.h" @@ -284,3 +285,105 @@ out: *out_ip6_config = ip6_config; return success; } + +/*****************************************************************************/ +/* %IPDPCFG? response parser. + * Modifies the input list of profiles in place + * + * AT%IPDPCFG? + * %IPDPCFG: 1,0,0,,,0 + * %IPDPCFG: 2,0,0,,,0 + * %IPDPCFG: 3,0,2,"user","pass",0 + * %IPDPCFG: 4,0,0,,,0 + * OK + */ + +gboolean +mm_icera_parse_ipdpcfg_query_response (const gchar *str, + GList *profiles, + gpointer log_object, + GError **error) +{ + g_autoptr(GRegex) r = NULL; + g_autoptr(GError) inner_error = NULL; + g_autoptr(GMatchInfo) match_info = NULL; + guint n_updates = 0; + guint n_profiles; + + n_profiles = g_list_length (profiles); + + r = g_regex_new ("%IPDPCFG:\\s*(\\d+),(\\d+),(\\d+),([^,]*),([^,]*),(\\d+)", + G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, + 0, NULL); + g_assert (r != NULL); + + g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error); + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + /* Parse the results */ + while (g_match_info_matches (match_info)) { + guint cid; + guint auth; + MMBearerAllowedAuth allowed_auth; + g_autofree gchar *user = NULL; + g_autofree gchar *password = NULL; + GList *l; + + if (!mm_get_uint_from_match_info (match_info, 1, &cid)) { + mm_obj_warn (log_object, "couldn't parse cid from %%IPDPCFG line"); + goto next; + } + + if (!mm_get_uint_from_match_info (match_info, 3, &auth)) { + mm_obj_warn (log_object, "couldn't parse auth from %%IPDPCFG line"); + goto next; + } + + switch (auth) { + case 0: + allowed_auth = MM_BEARER_ALLOWED_AUTH_NONE; + break; + case 1: + allowed_auth = MM_BEARER_ALLOWED_AUTH_PAP; + break; + case 2: + allowed_auth = MM_BEARER_ALLOWED_AUTH_CHAP; + break; + default: + mm_obj_warn (log_object, "unexpected icera auth setting: %u", auth); + goto next; + } + + user = mm_get_string_unquoted_from_match_info (match_info, 4); + password = mm_get_string_unquoted_from_match_info (match_info, 5); + + mm_obj_dbg (log_object, "found icera auth settings for profile with id '%u'", cid); + + /* Find profile and update in place */ + for (l = profiles; l; l = g_list_next (l)) { + MM3gppProfile *iter = l->data; + + if (mm_3gpp_profile_get_profile_id (iter) == (gint) cid) { + n_updates++; + mm_3gpp_profile_set_allowed_auth (iter, allowed_auth); + mm_3gpp_profile_set_user (iter, user); + mm_3gpp_profile_set_password (iter, password); + break; + } + } + if (!l) + mm_obj_warn (log_object, "couldn't update auth settings in profile with id '%d': not found", cid); + + next: + g_match_info_next (match_info, NULL); + } + + if (n_updates != n_profiles) + mm_obj_warn (log_object, "couldn't update auth settings in all profiles: %u/%u updated", + n_updates, n_profiles); + + return TRUE; +} diff --git a/plugins/icera/mm-modem-helpers-icera.h b/plugins/icera/mm-modem-helpers-icera.h index 8a7a87a2..fa4f9016 100644 --- a/plugins/icera/mm-modem-helpers-icera.h +++ b/plugins/icera/mm-modem-helpers-icera.h @@ -25,4 +25,10 @@ gboolean mm_icera_parse_ipdpaddr_response (const gchar *response, MMBearerIpConfig **out_ip6_config, GError **error); +/* %IPDPCFG? response parser */ +gboolean mm_icera_parse_ipdpcfg_query_response (const gchar *response, + GList *profiles, + gpointer log_object, + GError **error); + #endif /* MM_MODEM_HELPERS_HUAWEI_H */ diff --git a/plugins/icera/tests/test-modem-helpers-icera.c b/plugins/icera/tests/test-modem-helpers-icera.c index 518e9afd..dceb1e2b 100644 --- a/plugins/icera/tests/test-modem-helpers-icera.c +++ b/plugins/icera/tests/test-modem-helpers-icera.c @@ -174,6 +174,80 @@ test_ipdpaddr (void) } /*****************************************************************************/ +/* Test %IPDPCFG responses */ + +static void +test_ipdpcfg (void) +{ + MM3gppProfile *profile; + GList *profiles = NULL; + GList *l; + GError *error = NULL; + gboolean result; + gboolean cid_1_validated = FALSE; + gboolean cid_2_validated = FALSE; + gboolean cid_5_validated = FALSE; + const gchar *response = + "%IPDPCFG: 1,0,0,,,0\r\n" + "%IPDPCFG: 2,0,1,\"aaaa\",\"bbbbb\",0\r\n" + "%IPDPCFG: 5,0,2,\"user\",\"pass\",0"; /* last line without CRLF */ + + profile = mm_3gpp_profile_new (); + mm_3gpp_profile_set_profile_id (profile, 1); + mm_3gpp_profile_set_apn (profile, "internet"); + mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV4); + profiles = g_list_append (profiles, profile); + + profile = mm_3gpp_profile_new (); + mm_3gpp_profile_set_profile_id (profile, 2); + mm_3gpp_profile_set_apn (profile, "internet2"); + mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV4V6); + profiles = g_list_append (profiles, profile); + + profile = mm_3gpp_profile_new (); + mm_3gpp_profile_set_profile_id (profile, 5); + mm_3gpp_profile_set_apn (profile, "internet3"); + mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV6); + profiles = g_list_append (profiles, profile); + + result = mm_icera_parse_ipdpcfg_query_response (response, profiles, NULL, &error); + g_assert_no_error (error); + g_assert (result); + + for (l = profiles; l; l = g_list_next (l)) { + MM3gppProfile *iter = l->data; + + switch (mm_3gpp_profile_get_profile_id (iter)) { + case 1: + cid_1_validated = TRUE; + g_assert_cmpuint (mm_3gpp_profile_get_allowed_auth (iter), ==, MM_BEARER_ALLOWED_AUTH_NONE); + g_assert (!mm_3gpp_profile_get_user (iter)); + g_assert (!mm_3gpp_profile_get_password (iter)); + break; + case 2: + cid_2_validated = TRUE; + g_assert_cmpuint (mm_3gpp_profile_get_allowed_auth (iter), ==, MM_BEARER_ALLOWED_AUTH_PAP); + g_assert_cmpstr (mm_3gpp_profile_get_user (iter), ==, "aaaa"); + g_assert_cmpstr (mm_3gpp_profile_get_password (iter), ==, "bbbbb"); + break; + case 5: + cid_5_validated = TRUE; + g_assert_cmpuint (mm_3gpp_profile_get_allowed_auth (iter), ==, MM_BEARER_ALLOWED_AUTH_CHAP); + g_assert_cmpstr (mm_3gpp_profile_get_user (iter), ==, "user"); + g_assert_cmpstr (mm_3gpp_profile_get_password (iter), ==, "pass"); + break; + default: + g_assert_not_reached (); + } + } + g_assert (cid_1_validated); + g_assert (cid_2_validated); + g_assert (cid_5_validated); + + g_list_free_full (profiles, (GDestroyNotify)g_object_unref); +} + +/*****************************************************************************/ int main (int argc, char **argv) { @@ -182,6 +256,7 @@ int main (int argc, char **argv) g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/icera/ipdpaddr", test_ipdpaddr); + g_test_add_func ("/MM/icera/ipdpcfg", test_ipdpcfg); return g_test_run (); } |