aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2021-04-04 23:27:50 +0200
committerAleksander Morgado <aleksander@aleksander.es>2021-04-29 10:13:22 +0000
commit49b63cc62b46777c419b6a8e63629d228953fe95 (patch)
treed9c464da26bc2e3e0ad1322fd06d3f81c1ed0674
parent887358663de820078f5632180cba53975809ebcf (diff)
icera: implement profile management support
Using AT%IPDPCFG for authentication related profile settings management (querying, updating), and relying on the parent implementation for the remaining ones. And using AT%IPDPACT for activation status management (deactivate), while relying on the parent implementation for the activated check.
-rw-r--r--plugins/icera/mm-broadband-bearer-icera.c202
-rw-r--r--plugins/icera/mm-broadband-modem-icera.c450
-rw-r--r--plugins/icera/mm-modem-helpers-icera.c103
-rw-r--r--plugins/icera/mm-modem-helpers-icera.h6
-rw-r--r--plugins/icera/tests/test-modem-helpers-icera.c75
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 ();
}