aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2015-10-10 13:51:53 +0200
committerAleksander Morgado <aleksander@aleksander.es>2015-10-12 14:04:18 +0200
commitfe0d1020bfc3f7b931b250eaf89b02e200d2c8f3 (patch)
tree3af168085ccebc5782a46fb4341475571e18b054 /src
parent84e48d701010baebd1beae2d1edd2d0108bae816 (diff)
qmicli,wds: setup new key-value format based Start Network command
This would be equivalent to the new format used in mbimcli's --connect operation, and will allow us to add new parameters easily. Note that The old legacy format will still be supported, for backwards compatibility.
Diffstat (limited to 'src')
-rw-r--r--src/qmicli/qmicli-helpers.c168
-rw-r--r--src/qmicli/qmicli-helpers.h12
-rw-r--r--src/qmicli/qmicli-wds.c161
3 files changed, 305 insertions, 36 deletions
diff --git a/src/qmicli/qmicli-helpers.c b/src/qmicli/qmicli-helpers.c
index d5e9eee..3b62358 100644
--- a/src/qmicli/qmicli-helpers.c
+++ b/src/qmicli/qmicli-helpers.c
@@ -411,6 +411,24 @@ qmicli_read_autoconnect_setting_roaming_from_string (const gchar *str,
}
gboolean
+qmicli_read_authentication_from_string (const gchar *str,
+ QmiWdsAuthentication *out)
+{
+ if (g_ascii_strcasecmp (str, "PAP") == 0)
+ *out = QMI_WDS_AUTHENTICATION_PAP;
+ else if (g_ascii_strcasecmp (str, "CHAP") == 0)
+ *out = QMI_WDS_AUTHENTICATION_CHAP;
+ else if (g_ascii_strcasecmp (str, "BOTH") == 0)
+ *out = (QMI_WDS_AUTHENTICATION_PAP | QMI_WDS_AUTHENTICATION_CHAP);
+ else if (!str[0] || g_ascii_strcasecmp (str, "NONE") == 0)
+ *out = QMI_WDS_AUTHENTICATION_NONE;
+ else
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
qmicli_read_uint_from_string (const gchar *str,
guint *out)
{
@@ -523,3 +541,153 @@ qmicli_earfcn_to_eutra_band_string (guint16 earfcn)
}
return "unknown";
}
+
+/* Expecting input as:
+ * key1=string,key2=true,key3=false...
+ * Strings may also be passed enclosed between double or single quotes, like:
+ * key1="this is a string", key2='and so is this'
+ *
+ * Based on libmbim's mbimcli_parse_key_value_string().
+ */
+gboolean
+qmicli_parse_key_value_string (const gchar *str,
+ GError **error,
+ QmiParseKeyValueForeachFn callback,
+ gpointer user_data)
+{
+ GError *inner_error = NULL;
+ gchar *dupstr, *p, *key, *key_end, *value, *value_end, quote;
+
+ g_return_val_if_fail (callback != NULL, FALSE);
+ g_return_val_if_fail (str != NULL, FALSE);
+
+ /* Allow empty strings, we'll just return with success */
+ while (g_ascii_isspace (*str))
+ str++;
+ if (!str[0])
+ return TRUE;
+
+ dupstr = g_strdup (str);
+ p = dupstr;
+
+ while (TRUE) {
+ gboolean keep_iteration = FALSE;
+
+ /* Skip leading spaces */
+ while (g_ascii_isspace (*p))
+ p++;
+
+ /* Key start */
+ key = p;
+ if (!g_ascii_isalnum (*key)) {
+ inner_error = g_error_new (QMI_CORE_ERROR,
+ QMI_CORE_ERROR_FAILED,
+ "Key must start with alpha/num, starts with '%c'",
+ *key);
+ break;
+ }
+
+ /* Key end */
+ while (g_ascii_isalnum (*p) || (*p == '-') || (*p == '_'))
+ p++;
+ key_end = p;
+ if (key_end == key) {
+ inner_error = g_error_new (QMI_CORE_ERROR,
+ QMI_CORE_ERROR_FAILED,
+ "Couldn't find a proper key");
+ break;
+ }
+
+ /* Skip whitespaces, if any */
+ while (g_ascii_isspace (*p))
+ p++;
+
+ /* Equal sign must be here */
+ if (*p != '=') {
+ inner_error = g_error_new (QMI_CORE_ERROR,
+ QMI_CORE_ERROR_FAILED,
+ "Couldn't find equal sign separator");
+ break;
+ }
+ /* Skip the equal */
+ p++;
+
+ /* Skip whitespaces, if any */
+ while (g_ascii_isspace (*p))
+ p++;
+
+ /* Do we have a quote-enclosed string? */
+ if (*p == '\"' || *p == '\'') {
+ quote = *p;
+ /* Skip the quote */
+ p++;
+ /* Value start */
+ value = p;
+ /* Find the closing quote */
+ p = strchr (p, quote);
+ if (!p) {
+ inner_error = g_error_new (QMI_CORE_ERROR,
+ QMI_CORE_ERROR_FAILED,
+ "Unmatched quotes in string value");
+ break;
+ }
+
+ /* Value end */
+ value_end = p;
+ /* Skip the quote */
+ p++;
+ } else {
+ /* Value start */
+ value = p;
+
+ /* Value end */
+ while ((*p != ',') && (*p != '\0') && !g_ascii_isspace (*p))
+ p++;
+ value_end = p;
+ }
+
+ /* Note that we allow value == value_end here */
+
+ /* Skip whitespaces, if any */
+ while (g_ascii_isspace (*p))
+ p++;
+
+ /* If a comma is found, we should keep the iteration */
+ if (*p == ',') {
+ /* skip the comma */
+ p++;
+ keep_iteration = TRUE;
+ }
+
+ /* Got key and value, prepare them and run the callback */
+ *value_end = '\0';
+ *key_end = '\0';
+ if (!callback (key, value, &inner_error, user_data)) {
+ /* We were told to abort */
+ break;
+ }
+ g_assert (!inner_error);
+
+ if (keep_iteration)
+ continue;
+
+ /* Check if no more key/value pairs expected */
+ if (*p == '\0')
+ break;
+
+ inner_error = g_error_new (QMI_CORE_ERROR,
+ QMI_CORE_ERROR_FAILED,
+ "Unexpected content (%s) after value",
+ p);
+ break;
+ }
+
+ g_free (dupstr);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/src/qmicli/qmicli-helpers.h b/src/qmicli/qmicli-helpers.h
index 0e6d567..6b19827 100644
--- a/src/qmicli/qmicli-helpers.h
+++ b/src/qmicli/qmicli-helpers.h
@@ -53,6 +53,8 @@ gboolean qmicli_read_autoconnect_setting_from_string (const gchar *str,
QmiWdsAutoconnectSetting *out);
gboolean qmicli_read_autoconnect_setting_roaming_from_string (const gchar *str,
QmiWdsAutoconnectSettingRoaming *out);
+gboolean qmicli_read_authentication_from_string (const gchar *str,
+ QmiWdsAuthentication *out);
gboolean qmicli_read_non_empty_string (const gchar *str,
const gchar *description,
@@ -65,4 +67,14 @@ gchar *qmicli_get_supported_messages_list (const guint8 *data,
const char *qmicli_earfcn_to_eutra_band_string (guint16 earfcn);
+typedef gboolean (*QmiParseKeyValueForeachFn) (const gchar *key,
+ const gchar *value,
+ GError **error,
+ gpointer user_data);
+
+gboolean qmicli_parse_key_value_string (const gchar *str,
+ GError **error,
+ QmiParseKeyValueForeachFn callback,
+ gpointer user_data);
+
#endif /* __QMICLI_H__ */
diff --git a/src/qmicli/qmicli-wds.c b/src/qmicli/qmicli-wds.c
index bba1c9b..77b01cb 100644
--- a/src/qmicli/qmicli-wds.c
+++ b/src/qmicli/qmicli-wds.c
@@ -67,8 +67,8 @@ static gboolean noop_flag;
static GOptionEntry entries[] = {
{ "wds-start-network", 0, 0, G_OPTION_ARG_STRING, &start_network_str,
- "Start network (Authentication, Username and Password are optional)",
- "[(APN),(PAP|CHAP|BOTH),(Username),(Password)]"
+ "Start network (allowed keys: apn, auth (PAP|CHAP|BOTH), username, password)",
+ "[\"key=value,...\"]"
},
{ "wds-follow-network", 0, 0, G_OPTION_ARG_NONE, &follow_network_flag,
"Follow the network status until disconnected. Use with `--wds-start-network'",
@@ -327,63 +327,152 @@ packet_status_timeout (void)
return TRUE;
}
+typedef struct {
+ gchar *apn;
+ QmiWdsAuthentication auth;
+ gboolean auth_set;
+ gchar *username;
+ gchar *password;
+} StartNetworkProperties;
+
+static gboolean
+start_network_properties_handle (const gchar *key,
+ const gchar *value,
+ GError **error,
+ gpointer user_data)
+{
+ StartNetworkProperties *props = user_data;
+
+ if (!value || !value[0]) {
+ g_set_error (error,
+ QMI_CORE_ERROR,
+ QMI_CORE_ERROR_FAILED,
+ "key '%s' required a value",
+ key);
+ return FALSE;
+ }
+
+ if (g_ascii_strcasecmp (key, "apn") == 0 && !props->apn) {
+ props->apn = g_strdup (value);
+ return TRUE;
+ }
+
+ if (g_ascii_strcasecmp (key, "auth") == 0 && !props->auth_set) {
+ if (!qmicli_read_authentication_from_string (value, &(props->auth))) {
+ g_set_error (error,
+ QMI_CORE_ERROR,
+ QMI_CORE_ERROR_FAILED,
+ "unknown auth protocol '%s'",
+ value);
+ return FALSE;
+ }
+ props->auth_set = TRUE;
+ return TRUE;
+ }
+
+ if (g_ascii_strcasecmp (key, "username") == 0 && !props->username) {
+ props->username = g_strdup (value);
+ return TRUE;
+ }
+
+ if (g_ascii_strcasecmp (key, "password") == 0 && !props->password) {
+ props->password = g_strdup (value);
+ return TRUE;
+ }
+
+ g_set_error (error,
+ QMI_CORE_ERROR,
+ QMI_CORE_ERROR_FAILED,
+ "unrecognized or duplicate option '%s'",
+ key);
+ return FALSE;
+}
+
static QmiMessageWdsStartNetworkInput *
start_network_input_create (const gchar *str)
{
- QmiMessageWdsStartNetworkInput *input = NULL;
- const gchar *apn = NULL;
- const gchar *auth = NULL;
- const gchar *username = NULL;
- const gchar *password = NULL;
+ gchar *aux_auth_str = NULL;
gchar **split = NULL;
+ QmiMessageWdsStartNetworkInput *input = NULL;
+ StartNetworkProperties props = {
+ .apn = NULL,
+ .auth = QMI_WDS_AUTHENTICATION_NONE,
+ .auth_set = FALSE,
+ .username = NULL,
+ .password = NULL,
+ };
/* An empty string is totally valid (i.e. no TLVs) */
if (!str[0])
return NULL;
+ /* New key=value format */
+ if (strchr (str, '=')) {
+ GError *error = NULL;
+
+ if (!qmicli_parse_key_value_string (str,
+ &error,
+ start_network_properties_handle,
+ &props)) {
+ g_printerr ("error: couldn't parse input string: %s\n", error->message);
+ g_error_free (error);
+ goto out;
+ }
+ }
+ /* Old non key=value format, like this:
+ * "[(APN),(PAP|CHAP|BOTH),(Username),(Password)]"
+ */
+ else {
+ /* Parse input string into the expected fields */
+ split = g_strsplit (str, ",", 0);
+
+ props.apn = g_strdup (split[0]);
+
+ if (props.apn && split[1]) {
+ if (!qmicli_read_authentication_from_string (split[1], &(props.auth))) {
+ g_printerr ("error: unknown auth protocol '%s'\n", split[1]);
+ goto out;
+ }
+ props.auth_set = TRUE;
+ }
+
+ props.username = (props.auth_set ? g_strdup (split[2]) : NULL);
+ props.password = (props.username ? g_strdup (split[3]) : NULL);
+ }
+
/* Create input bundle */
input = qmi_message_wds_start_network_input_new ();
- /* Parse input string into the expected fields */
- split = g_strsplit (str, ",", 0);
- apn = split[0];
- auth = (apn ? split[1] : NULL);
- username = (auth ? split[2] : NULL);
- password = (username ? split[3] : NULL);
-
/* Set APN */
- qmi_message_wds_start_network_input_set_apn (input, apn, NULL);
+ if (props.apn)
+ qmi_message_wds_start_network_input_set_apn (input, props.apn, NULL);
/* Set authentication method */
- if (auth) {
- QmiWdsAuthentication qmiwdsauth;
-
- if (g_ascii_strcasecmp (auth, "PAP") == 0)
- qmiwdsauth = QMI_WDS_AUTHENTICATION_PAP;
- else if (g_ascii_strcasecmp (auth, "CHAP") == 0)
- qmiwdsauth = QMI_WDS_AUTHENTICATION_CHAP;
- else if (g_ascii_strcasecmp (auth, "BOTH") == 0)
- qmiwdsauth = (QMI_WDS_AUTHENTICATION_PAP | QMI_WDS_AUTHENTICATION_CHAP);
- else
- qmiwdsauth = QMI_WDS_AUTHENTICATION_NONE;
- qmi_message_wds_start_network_input_set_authentication_preference (input, qmiwdsauth, NULL);
+ if (props.auth_set) {
+ aux_auth_str = qmi_wds_authentication_build_string_from_mask (props.auth);
+ qmi_message_wds_start_network_input_set_authentication_preference (input, props.auth, NULL);
}
/* Set username, avoid empty strings */
- if (username && username[0])
- qmi_message_wds_start_network_input_set_username (input, username, NULL);
+ if (props.username && props.username[0])
+ qmi_message_wds_start_network_input_set_username (input, props.username, NULL);
/* Set password, avoid empty strings */
- if (password && password[0])
- qmi_message_wds_start_network_input_set_password (input, password, NULL);
+ if (props.password && props.password[0])
+ qmi_message_wds_start_network_input_set_password (input, props.password, NULL);
- g_debug ("Network start parameters set (apn: '%s', auth: '%s', user: '%s', pass: '%s')",
- apn ? apn : "unspecified",
- auth ? auth : "unspecified",
- (username && username[0]) ? username : "unspecified",
- (password && password[0]) ? password : "unspecified");
+ g_debug ("Network start parameters set (apn: '%s', auth: '%s', username: '%s', password: '%s')",
+ props.apn ? props.apn : "unspecified",
+ aux_auth_str ? aux_auth_str : "unspecified",
+ (props.username && props.username[0]) ? props.username : "unspecified",
+ (props.password && props.password[0]) ? props.password : "unspecified");
+out:
g_strfreev (split);
+ g_free (props.apn);
+ g_free (props.username);
+ g_free (props.password);
+ g_free (aux_auth_str);
return input;
}