summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2015-08-17 12:19:05 -0500
committerDan Williams <dcbw@redhat.com>2015-08-18 12:04:02 -0500
commit4e58451a744dbc3f086e9c1dd2d7469ff70d50ef (patch)
tree953c05522ecd0a4af7512e71a342acc7cc1af8e1
parent8b6316667b438bb2aa99c8029f95542d74ee7136 (diff)
downloadlibmbim-4e58451a744dbc3f086e9c1dd2d7469ff70d50ef.tar.gz
mbimcli: add support for Basic Connect session IDs
--query-connection-state=[SessionID] --disconnect=[SessionID] --connect=["key=value,..."] As part of enabling session IDs, we must also convert --connect over to a key=value format for all its arguments, but still preserve backwards compat with the old format.
-rw-r--r--src/mbimcli/mbimcli-basic-connect.c284
-rw-r--r--src/mbimcli/mbimcli-helpers.c148
-rw-r--r--src/mbimcli/mbimcli-helpers.h10
3 files changed, 385 insertions, 57 deletions
diff --git a/src/mbimcli/mbimcli-basic-connect.c b/src/mbimcli/mbimcli-basic-connect.c
index 5cbd3ec..8fdff82 100644
--- a/src/mbimcli/mbimcli-basic-connect.c
+++ b/src/mbimcli/mbimcli-basic-connect.c
@@ -24,6 +24,7 @@
#include <stdlib.h>
#include <locale.h>
#include <string.h>
+#include <errno.h>
#include <glib.h>
#include <gio/gio.h>
@@ -61,9 +62,9 @@ static gboolean query_signal_state_flag;
static gboolean query_packet_service_flag;
static gboolean set_packet_service_attach_flag;
static gboolean set_packet_service_detach_flag;
-static gboolean query_connect_flag;
+static gchar *query_connect_str;
static gchar *set_connect_activate_str;
-static gboolean set_connect_deactivate_flag;
+static gchar *set_connect_deactivate_str;
static gboolean query_packet_statistics_flag;
static GOptionEntry entries[] = {
@@ -147,17 +148,17 @@ static GOptionEntry entries[] = {
"Detach from the packet service",
NULL
},
- { "query-connection-state", 0, 0, G_OPTION_ARG_NONE, &query_connect_flag,
- "Query connection state",
- NULL
+ { "query-connection-state", 0, 0, G_OPTION_ARG_NONE, &query_connect_str,
+ "Query connection state (SessionID is optional, defaults to 0)",
+ "[SessionID]"
},
{ "connect", 0, 0, G_OPTION_ARG_STRING, &set_connect_activate_str,
- "Connect (Authentication, Username and Password are optional)",
- "[(APN),(PAP|CHAP|MSCHAPV2),(Username),(Password)]"
+ "Connect (allowed keys: session-id, apn, auth (PAP|CHAP|MSCHAPV2), username, password)",
+ "[\"key=value,...\"]"
},
- { "disconnect", 0, 0, G_OPTION_ARG_NONE, &set_connect_deactivate_flag,
- "Disconnect",
- NULL
+ { "disconnect", 0, 0, G_OPTION_ARG_STRING, &set_connect_deactivate_str,
+ "Disconnect (SessionID is optional, defaults to 0)",
+ "[SessionID]"
},
{ "query-packet-statistics", 0, 0, G_OPTION_ARG_NONE, &query_packet_statistics_flag,
"Query packet statistics",
@@ -210,9 +211,9 @@ mbimcli_basic_connect_options_enabled (void)
query_packet_service_flag +
set_packet_service_attach_flag +
set_packet_service_detach_flag +
- query_connect_flag +
+ !!query_connect_str +
!!set_connect_activate_str +
- set_connect_deactivate_flag +
+ !!set_connect_deactivate_str +
query_packet_statistics_flag);
if (n_actions > 1) {
@@ -791,66 +792,217 @@ connect_ready (MbimDevice *device,
}
static gboolean
+mbim_auth_protocol_from_string (const gchar *str,
+ MbimAuthProtocol *auth_protocol)
+{
+ if (g_ascii_strcasecmp (str, "PAP") == 0) {
+ *auth_protocol = MBIM_AUTH_PROTOCOL_PAP;
+ return TRUE;
+ } else if (g_ascii_strcasecmp (str, "CHAP") == 0) {
+ *auth_protocol = MBIM_AUTH_PROTOCOL_CHAP;
+ return TRUE;
+ } else if (g_ascii_strcasecmp (str, "MSCHAPV2") == 0) {
+ *auth_protocol = MBIM_AUTH_PROTOCOL_MSCHAPV2;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+connect_session_id_parse (const gchar *str,
+ gboolean allow_empty,
+ guint *session_id,
+ GError **error)
+{
+ gchar *endptr = NULL;
+ gint64 n;
+
+ g_assert (str != NULL);
+ g_assert (session_id != NULL);
+
+ if (!str[0]) {
+ if (allow_empty) {
+ *session_id = 0;
+ return TRUE;
+ }
+ g_set_error_literal (error,
+ MBIM_CORE_ERROR,
+ MBIM_CORE_ERROR_FAILED,
+ "missing session ID (must be 0 - 255)");
+ return FALSE;
+ }
+
+ errno = 0;
+ n = g_ascii_strtoll (str, &endptr, 10);
+ if (errno || n < 0 || n > 255 || ((endptr - str) < strlen (str))) {
+ g_set_error (error,
+ MBIM_CORE_ERROR,
+ MBIM_CORE_ERROR_FAILED,
+ "couldn't parse session ID '%s' (must be 0 - 255)",
+ str);
+ return FALSE;
+ }
+ *session_id = (guint) n;
+
+ return TRUE;
+}
+
+typedef struct {
+ guint session_id;
+ gchar *apn;
+ MbimAuthProtocol auth_protocol;
+ gchar *username;
+ gchar *password;
+} ConnectActivateProperties;
+
+static gboolean connect_activate_properties_handle (const gchar *key,
+ const gchar *value,
+ GError **error,
+ gpointer user_data)
+{
+ ConnectActivateProperties *props = user_data;
+
+ if (!value || !value[0]) {
+ g_set_error (error,
+ MBIM_CORE_ERROR,
+ MBIM_CORE_ERROR_FAILED,
+ "key '%s' required a value",
+ key);
+ return FALSE;
+ }
+
+ if (g_ascii_strcasecmp (key, "session-id") == 0) {
+ if (!connect_session_id_parse (value, FALSE, &props->session_id, error))
+ return FALSE;
+ } else if (g_ascii_strcasecmp (key, "apn") == 0 && !props->apn) {
+ props->apn = g_strdup (value);
+ } else if (g_ascii_strcasecmp (key, "auth") == 0) {
+ if (!mbim_auth_protocol_from_string (value, &props->auth_protocol)) {
+ g_set_error (error,
+ MBIM_CORE_ERROR,
+ MBIM_CORE_ERROR_FAILED,
+ "unknown auth protocol '%s'",
+ value);
+ return FALSE;
+ }
+ } else if (g_ascii_strcasecmp (key, "username") == 0 && !props->username) {
+ props->username = g_strdup (value);
+ } else if (g_ascii_strcasecmp (key, "password") == 0 && !props->password) {
+ props->password = g_strdup (value);
+ } else {
+ g_set_error (error,
+ MBIM_CORE_ERROR,
+ MBIM_CORE_ERROR_FAILED,
+ "unrecognized or duplicate option '%s'",
+ key);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
set_connect_activate_parse (const gchar *str,
+ guint *session_id,
gchar **apn,
MbimAuthProtocol *auth_protocol,
gchar **username,
gchar **password)
{
- gchar **split;
-
+ ConnectActivateProperties props = {
+ .session_id = 0,
+ .apn = NULL,
+ .auth_protocol = MBIM_AUTH_PROTOCOL_NONE,
+ .username = NULL,
+ .password = NULL
+ };
+ gchar **split = NULL;
+
+ g_assert (session_id != NULL);
g_assert (apn != NULL);
g_assert (auth_protocol != NULL);
g_assert (username != NULL);
g_assert (password != NULL);
- /* Format of the string is:
- * "[(APN),(PAP|CHAP|MSCHAPV2),(Username),(Password)]"
- */
- split = g_strsplit (str, ",", -1);
+ if (strchr (str, '=')) {
+ GError *error = NULL;
- if (g_strv_length (split) > 4) {
- g_printerr ("error: couldn't parse input string, too many arguments\n");
- g_strfreev (split);
- return FALSE;
- }
+ /* New key=value format */
+ if (!mbimcli_parse_key_value_string (str,
+ &error,
+ connect_activate_properties_handle,
+ &props)) {
+ g_printerr ("error: couldn't parse input string: %s\n", error->message);
+ g_error_free (error);
+ goto error;
+ }
+ } else {
+ /* Old non key=value format, like this:
+ * "[(APN),(PAP|CHAP|MSCHAPV2),(Username),(Password)]"
+ */
+ split = g_strsplit (str, ",", -1);
+
+ if (g_strv_length (split) > 4) {
+ g_printerr ("error: couldn't parse input string, too many arguments\n");
+ goto error;
+ }
- if (g_strv_length (split) < 1) {
- g_printerr ("error: couldn't parse input string, missing arguments\n");
- g_strfreev (split);
- return FALSE;
- }
+ if (g_strv_length (split) < 1) {
+ g_printerr ("error: couldn't parse input string, missing arguments\n");
+ goto error;
+ }
- /* APN */
- *apn = g_strdup (split[0]);
-
- /* Some defaults */
- *auth_protocol = MBIM_AUTH_PROTOCOL_NONE;
- *username = NULL;
- *password = NULL;
-
- /* Use authentication method */
- if (split[1]) {
- if (g_ascii_strcasecmp (split[1], "PAP") == 0)
- *auth_protocol = MBIM_AUTH_PROTOCOL_PAP;
- else if (g_ascii_strcasecmp (split[1], "CHAP") == 0)
- *auth_protocol = MBIM_AUTH_PROTOCOL_CHAP;
- else if (g_ascii_strcasecmp (split[1], "MSCHAPV2") == 0)
- *auth_protocol = MBIM_AUTH_PROTOCOL_MSCHAPV2;
- else
- *auth_protocol = MBIM_AUTH_PROTOCOL_NONE;
+ /* APN */
+ props.apn = g_strdup (split[0]);
- /* Username */
- if (split[2]) {
- *username = g_strdup (split[2]);
+ /* Use authentication method */
+ if (split[1]) {
+ if (!mbim_auth_protocol_from_string (split[1], &props.auth_protocol)) {
+ g_printerr ("error: couldn't parse input string, unknown auth protocol '%s'\n", split[1]);
+ goto error;
+ }
+
+ /* Username */
+ if (split[2]) {
+ props.username = g_strdup (split[2]);
- /* Password */
- *password = g_strdup (split[3]);
+ /* Password */
+ props.password = g_strdup (split[3]);
+ }
}
}
- g_strfreev (split);
+ if (props.auth_protocol == MBIM_AUTH_PROTOCOL_NONE) {
+ if (username || password) {
+ g_printerr ("error: username or password requires an auth protocol\n");
+ goto error;
+ }
+ } else {
+ if (!username) {
+ g_printerr ("error: auth protocol requires a username\n");
+ goto error;
+ }
+ }
+
+ *session_id = props.session_id;
+ *apn = props.apn;
+ *auth_protocol = props.auth_protocol;
+ *username = props.username;
+ *password = props.password;
+
+ if (split)
+ g_strfreev (split);
+
return TRUE;
+
+error:
+ if (split)
+ g_strfreev (split);
+ g_free (props.apn);
+ g_free (props.username);
+ g_free (props.password);
+ return FALSE;
}
static void
@@ -1679,11 +1831,19 @@ mbimcli_basic_connect_run (MbimDevice *device,
}
/* Query connection status? */
- if (query_connect_flag) {
+ if (query_connect_str) {
MbimMessage *request;
GError *error = NULL;
+ guint session_id = 0;
- request = mbim_message_connect_query_new (0,
+ if (!connect_session_id_parse (query_connect_str, TRUE, &session_id, &error)) {
+ g_printerr ("error: couldn't parse session ID: %s\n", error->message);
+ g_error_free (error);
+ shutdown (FALSE);
+ return;
+ }
+
+ request = mbim_message_connect_query_new (session_id,
MBIM_ACTIVATION_STATE_UNKNOWN,
MBIM_VOICE_CALL_STATE_NONE,
MBIM_CONTEXT_IP_TYPE_DEFAULT,
@@ -1711,12 +1871,14 @@ mbimcli_basic_connect_run (MbimDevice *device,
if (set_connect_activate_str) {
MbimMessage *request;
GError *error = NULL;
+ guint session_id = 0;
gchar *apn;
MbimAuthProtocol auth_protocol;
gchar *username = NULL;
gchar *password = NULL;
if (!set_connect_activate_parse (set_connect_activate_str,
+ &session_id,
&apn,
&auth_protocol,
&username,
@@ -1725,7 +1887,7 @@ mbimcli_basic_connect_run (MbimDevice *device,
return;
}
- request = mbim_message_connect_set_new (0,
+ request = mbim_message_connect_set_new (session_id,
MBIM_ACTIVATION_COMMAND_ACTIVATE,
apn,
username,
@@ -1757,11 +1919,19 @@ mbimcli_basic_connect_run (MbimDevice *device,
}
/* Disconnect? */
- if (set_connect_deactivate_flag) {
+ if (set_connect_deactivate_str) {
MbimMessage *request;
GError *error = NULL;
+ guint session_id = 0;
+
+ if (!connect_session_id_parse (set_connect_deactivate_str, TRUE, &session_id, &error)) {
+ g_printerr ("error: couldn't parse session ID: %s\n", error->message);
+ g_error_free (error);
+ shutdown (FALSE);
+ return;
+ }
- request = mbim_message_connect_set_new (0,
+ request = mbim_message_connect_set_new (session_id,
MBIM_ACTIVATION_COMMAND_DEACTIVATE,
NULL,
NULL,
diff --git a/src/mbimcli/mbimcli-helpers.c b/src/mbimcli/mbimcli-helpers.c
index 21aea9b..d4485b0 100644
--- a/src/mbimcli/mbimcli-helpers.c
+++ b/src/mbimcli/mbimcli-helpers.c
@@ -189,3 +189,151 @@ mbimcli_print_ip_config (MbimDevice *device,
return TRUE;
}
+/* 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'
+ */
+gboolean
+mbimcli_parse_key_value_string (const gchar *str,
+ GError **error,
+ MbimParseKeyValueForeachFn callback,
+ gpointer user_data)
+{
+ GError *inner_error = NULL;
+ gchar *dup, *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;
+
+ dup = g_strdup (str);
+ p = dup;
+
+ 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 (MBIM_CORE_ERROR,
+ MBIM_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 (MBIM_CORE_ERROR,
+ MBIM_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 (MBIM_CORE_ERROR,
+ MBIM_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 (MBIM_CORE_ERROR,
+ MBIM_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 (MBIM_CORE_ERROR,
+ MBIM_CORE_ERROR_FAILED,
+ "Unexpected content (%s) after value",
+ p);
+ break;
+ }
+
+ g_free (dup);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
diff --git a/src/mbimcli/mbimcli-helpers.h b/src/mbimcli/mbimcli-helpers.h
index 969f416..61d1483 100644
--- a/src/mbimcli/mbimcli-helpers.h
+++ b/src/mbimcli/mbimcli-helpers.h
@@ -32,4 +32,14 @@ gboolean mbimcli_print_ip_config (MbimDevice *device,
MbimMessage *response,
GError **error);
+typedef gboolean (*MbimParseKeyValueForeachFn) (const gchar *key,
+ const gchar *value,
+ GError **error,
+ gpointer user_data);
+
+gboolean mbimcli_parse_key_value_string (const gchar *str,
+ GError **error,
+ MbimParseKeyValueForeachFn callback,
+ gpointer user_data);
+
#endif /* __MBIMCLI_H__ */