Files
ModemManager/libmm-glib/mm-common-helpers.c
Aleksander Morgado ff8e21b535 api,bearer: new 'apn-type' setting
This new setting allows the user setting up the connection to specify
the purpose of the connection being brought up.

Until now, we would always assume that connections are exclusively
brought up for connecting to the Internet, also limited by the
inability to connect to multiple different APNs at the same time.

But that may really not be true as there may be additional services
that may be accessed through other APNs, like MMS services or even
private networks for companies that have their own APNs on a given
operator (e.g. not that uncommon with banks and connected cars).

The new APN type setting will not change the way the bearer is
connected, but will allow the connection manager to decide what kind
of networking setup the specific connection needs.

This new setting can be provided by the user itself, or implicitly
read from the device if the device stores this information.
2021-04-29 10:13:22 +00:00

1881 lines
51 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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:
*
* Copyright (C) 2010 - 2012 Red Hat, Inc.
* Copyright (C) 2011 - 2012 Google, Inc.
*/
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <gio/gio.h>
#include <ModemManager.h>
#include "mm-enums-types.h"
#include "mm-errors-types.h"
#include "mm-common-helpers.h"
static gint
_enum_from_string (GType type,
const gchar *str,
gint error_value,
GError **error)
{
GEnumClass *enum_class;
gint value;
guint i;
enum_class = G_ENUM_CLASS (g_type_class_ref (type));
for (i = 0; enum_class->values[i].value_nick; i++) {
if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick)) {
value = enum_class->values[i].value;
g_type_class_unref (enum_class);
return value;
}
}
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Couldn't match '%s' with a valid %s value",
str,
g_type_name (type));
g_type_class_unref (enum_class);
return error_value;
}
static guint
_flags_from_string (GType type,
const gchar *str,
guint error_value,
GError **error)
{
GFlagsClass *flags_class;
guint value;
guint i;
flags_class = G_FLAGS_CLASS (g_type_class_ref (type));
for (i = 0; flags_class->values[i].value_nick; i++) {
if (!g_ascii_strcasecmp (str, flags_class->values[i].value_nick)) {
value = flags_class->values[i].value;
g_type_class_unref (flags_class);
return value;
}
}
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Couldn't match '%s' with a valid %s value",
str,
g_type_name (type));
g_type_class_unref (flags_class);
return error_value;
}
gchar *
mm_common_build_capabilities_string (const MMModemCapability *capabilities,
guint n_capabilities)
{
gboolean first = TRUE;
GString *str;
guint i;
if (!capabilities || !n_capabilities)
return g_strdup ("none");
str = g_string_new ("");
for (i = 0; i < n_capabilities; i++) {
gchar *tmp;
tmp = mm_modem_capability_build_string_from_mask (capabilities[i]);
g_string_append_printf (str, "%s%s",
first ? "" : "\n",
tmp);
g_free (tmp);
if (first)
first = FALSE;
}
return g_string_free (str, FALSE);
}
gchar *
mm_common_build_bands_string (const MMModemBand *bands,
guint n_bands)
{
gboolean first = TRUE;
GString *str;
guint i;
if (!bands || !n_bands)
return g_strdup ("none");
str = g_string_new ("");
for (i = 0; i < n_bands; i++) {
g_string_append_printf (str, "%s%s",
first ? "" : ", ",
mm_modem_band_get_string (bands[i]));
if (first)
first = FALSE;
}
return g_string_free (str, FALSE);
}
gchar *
mm_common_build_ports_string (const MMModemPortInfo *ports,
guint n_ports)
{
gboolean first = TRUE;
GString *str;
guint i;
if (!ports || !n_ports)
return g_strdup ("none");
str = g_string_new ("");
for (i = 0; i < n_ports; i++) {
g_string_append_printf (str, "%s%s (%s)",
first ? "" : ", ",
ports[i].name,
mm_modem_port_type_get_string (ports[i].type));
if (first)
first = FALSE;
}
return g_string_free (str, FALSE);
}
gchar *
mm_common_build_sms_storages_string (const MMSmsStorage *storages,
guint n_storages)
{
gboolean first = TRUE;
GString *str;
guint i;
if (!storages || !n_storages)
return g_strdup ("none");
str = g_string_new ("");
for (i = 0; i < n_storages; i++) {
g_string_append_printf (str, "%s%s",
first ? "" : ", ",
mm_sms_storage_get_string (storages[i]));
if (first)
first = FALSE;
}
return g_string_free (str, FALSE);
}
gchar *
mm_common_build_mode_combinations_string (const MMModemModeCombination *modes,
guint n_modes)
{
gboolean first = TRUE;
GString *str;
guint i;
if (!modes || !n_modes)
return g_strdup ("none");
str = g_string_new ("");
for (i = 0; i < n_modes; i++) {
gchar *allowed;
gchar *preferred;
allowed = mm_modem_mode_build_string_from_mask (modes[i].allowed);
preferred = mm_modem_mode_build_string_from_mask (modes[i].preferred);
g_string_append_printf (str, "%sallowed: %s; preferred: %s",
first ? "" : "\n",
allowed,
preferred);
g_free (allowed);
g_free (preferred);
if (first)
first = FALSE;
}
return g_string_free (str, FALSE);
}
GArray *
mm_common_sms_storages_variant_to_garray (GVariant *variant)
{
GArray *array = NULL;
if (variant) {
GVariantIter iter;
guint n;
g_variant_iter_init (&iter, variant);
n = g_variant_iter_n_children (&iter);
if (n > 0) {
guint32 storage;
array = g_array_sized_new (FALSE, FALSE, sizeof (MMSmsStorage), n);
while (g_variant_iter_loop (&iter, "u", &storage))
g_array_append_val (array, storage);
}
}
return array;
}
MMSmsStorage *
mm_common_sms_storages_variant_to_array (GVariant *variant,
guint *n_storages)
{
GArray *array;
array = mm_common_sms_storages_variant_to_garray (variant);
if (n_storages)
*n_storages = array->len;
return (MMSmsStorage *) g_array_free (array, FALSE);
}
GVariant *
mm_common_sms_storages_array_to_variant (const MMSmsStorage *storages,
guint n_storages)
{
GVariantBuilder builder;
guint i;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
for (i = 0; i < n_storages; i++)
g_variant_builder_add_value (&builder,
g_variant_new_uint32 ((guint32)storages[i]));
return g_variant_builder_end (&builder);
}
GVariant *
mm_common_sms_storages_garray_to_variant (GArray *array)
{
if (array)
return mm_common_sms_storages_array_to_variant ((const MMSmsStorage *)array->data,
array->len);
return mm_common_sms_storages_array_to_variant (NULL, 0);
}
static void
clear_modem_port_info (MMModemPortInfo *info)
{
g_free (info->name);
}
GArray *
mm_common_ports_variant_to_garray (GVariant *variant)
{
GArray *array = NULL;
if (variant) {
guint i;
guint n;
n = g_variant_n_children (variant);
if (n > 0) {
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemPortInfo), n);
g_array_set_clear_func (array, (GDestroyNotify) clear_modem_port_info);
for (i = 0; i < n; i++) {
MMModemPortInfo info;
g_variant_get_child (variant, i, "(su)", &info.name, &info.type);
g_array_append_val (array, info);
}
}
}
return array;
}
MMModemPortInfo *
mm_common_ports_variant_to_array (GVariant *variant,
guint *n_ports)
{
GArray *array;
array = mm_common_ports_variant_to_garray (variant);
if (n_ports)
*n_ports = array->len;
return (MMModemPortInfo *) g_array_free (array, FALSE);
}
GVariant *
mm_common_ports_array_to_variant (const MMModemPortInfo *ports,
guint n_ports)
{
GVariantBuilder builder;
guint i;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(su)"));
for (i = 0; i < n_ports; i++) {
GVariant *tuple[2];
tuple[0] = g_variant_new_string (ports[i].name);
tuple[1] = g_variant_new_uint32 ((guint32)ports[i].type);
g_variant_builder_add_value (&builder, g_variant_new_tuple (tuple, 2));
}
return g_variant_builder_end (&builder);
}
GVariant *
mm_common_ports_garray_to_variant (GArray *array)
{
if (array)
return mm_common_ports_array_to_variant ((const MMModemPortInfo *)array->data,
array->len);
return mm_common_ports_array_to_variant (NULL, 0);
}
MMModemCapability
mm_common_get_capabilities_from_string (const gchar *str,
GError **error)
{
GError *inner_error = NULL;
MMModemCapability capabilities;
gchar **capability_strings;
GFlagsClass *flags_class;
capabilities = MM_MODEM_CAPABILITY_NONE;
flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_MODEM_CAPABILITY));
capability_strings = g_strsplit (str, "|", -1);
if (capability_strings) {
guint i;
for (i = 0; capability_strings[i]; i++) {
guint j;
gboolean found = FALSE;
for (j = 0; flags_class->values[j].value_nick; j++) {
if (!g_ascii_strcasecmp (capability_strings[i], flags_class->values[j].value_nick)) {
capabilities |= flags_class->values[j].value;
found = TRUE;
break;
}
}
if (!found) {
inner_error = g_error_new (
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Couldn't match '%s' with a valid MMModemCapability value",
capability_strings[i]);
break;
}
}
}
if (inner_error) {
g_propagate_error (error, inner_error);
capabilities = MM_MODEM_CAPABILITY_NONE;
}
g_type_class_unref (flags_class);
g_strfreev (capability_strings);
return capabilities;
}
MMModemMode
mm_common_get_modes_from_string (const gchar *str,
GError **error)
{
GError *inner_error = NULL;
MMModemMode modes;
gchar **mode_strings;
GFlagsClass *flags_class;
modes = MM_MODEM_MODE_NONE;
flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_MODEM_MODE));
mode_strings = g_strsplit (str, "|", -1);
if (mode_strings) {
guint i;
for (i = 0; mode_strings[i]; i++) {
guint j;
gboolean found = FALSE;
for (j = 0; flags_class->values[j].value_nick; j++) {
if (!g_ascii_strcasecmp (mode_strings[i], flags_class->values[j].value_nick)) {
modes |= flags_class->values[j].value;
found = TRUE;
break;
}
}
if (!found) {
inner_error = g_error_new (
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Couldn't match '%s' with a valid MMModemMode value",
mode_strings[i]);
break;
}
}
}
if (inner_error) {
g_propagate_error (error, inner_error);
modes = MM_MODEM_MODE_NONE;
}
g_type_class_unref (flags_class);
g_strfreev (mode_strings);
return modes;
}
GArray *
mm_common_capability_combinations_variant_to_garray (GVariant *variant)
{
GArray *array = NULL;
if (variant) {
GVariantIter iter;
guint n;
g_variant_iter_init (&iter, variant);
n = g_variant_iter_n_children (&iter);
if (n > 0) {
guint32 capability;
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), n);
while (g_variant_iter_loop (&iter, "u", &capability))
g_array_append_val (array, capability);
}
}
/* If nothing set, fallback to default */
if (!array) {
guint32 capability = MM_MODEM_CAPABILITY_NONE;
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 1);
g_array_append_val (array, capability);
}
return array;
}
MMModemCapability *
mm_common_capability_combinations_variant_to_array (GVariant *variant,
guint *n_capabilities)
{
GArray *array;
array = mm_common_capability_combinations_variant_to_garray (variant);
if (n_capabilities)
*n_capabilities = array->len;
return (MMModemCapability *) g_array_free (array, FALSE);
}
GVariant *
mm_common_capability_combinations_array_to_variant (const MMModemCapability *capabilities,
guint n_capabilities)
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
if (n_capabilities > 0) {
guint i;
for (i = 0; i < n_capabilities; i++)
g_variant_builder_add_value (&builder,
g_variant_new_uint32 ((guint32)capabilities[i]));
} else
g_variant_builder_add_value (&builder,
g_variant_new_uint32 (MM_MODEM_CAPABILITY_NONE));
return g_variant_builder_end (&builder);
}
GVariant *
mm_common_capability_combinations_garray_to_variant (GArray *array)
{
if (array)
return mm_common_capability_combinations_array_to_variant ((const MMModemCapability *)array->data,
array->len);
return mm_common_capability_combinations_array_to_variant (NULL, 0);
}
GVariant *
mm_common_build_capability_combinations_none (void)
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
g_variant_builder_add_value (&builder,
g_variant_new_uint32 (MM_MODEM_CAPABILITY_NONE));
return g_variant_builder_end (&builder);
}
void
mm_common_get_bands_from_string (const gchar *str,
MMModemBand **bands,
guint *n_bands,
GError **error)
{
GError *inner_error = NULL;
GArray *array;
gchar **band_strings;
GEnumClass *enum_class;
array = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_MODEM_BAND));
band_strings = g_strsplit (str, "|", -1);
if (band_strings) {
guint i;
for (i = 0; band_strings[i]; i++) {
guint j;
gboolean found = FALSE;
for (j = 0; enum_class->values[j].value_nick; j++) {
if (!g_ascii_strcasecmp (band_strings[i], enum_class->values[j].value_nick)) {
g_array_append_val (array, enum_class->values[j].value);
found = TRUE;
break;
}
}
if (!found) {
inner_error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Couldn't match '%s' with a valid MMModemBand value",
band_strings[i]);
break;
}
}
}
if (inner_error) {
g_propagate_error (error, inner_error);
g_array_free (array, TRUE);
*n_bands = 0;
*bands = NULL;
} else {
if (!array->len) {
GEnumValue *value;
value = g_enum_get_value (enum_class, MM_MODEM_BAND_UNKNOWN);
g_array_append_val (array, value->value);
}
*n_bands = array->len;
*bands = (MMModemBand *)g_array_free (array, FALSE);
}
g_type_class_unref (enum_class);
g_strfreev (band_strings);
}
GArray *
mm_common_bands_variant_to_garray (GVariant *variant)
{
GArray *array = NULL;
if (variant) {
GVariantIter iter;
guint n;
g_variant_iter_init (&iter, variant);
n = g_variant_iter_n_children (&iter);
if (n > 0) {
guint32 band;
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n);
while (g_variant_iter_loop (&iter, "u", &band))
g_array_append_val (array, band);
}
}
/* If nothing set, fallback to default */
if (!array) {
guint32 band = MM_MODEM_BAND_UNKNOWN;
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
g_array_append_val (array, band);
}
return array;
}
MMModemBand *
mm_common_bands_variant_to_array (GVariant *variant,
guint *n_bands)
{
GArray *array;
array = mm_common_bands_variant_to_garray (variant);
if (n_bands)
*n_bands = array->len;
return (MMModemBand *) g_array_free (array, FALSE);
}
GVariant *
mm_common_bands_array_to_variant (const MMModemBand *bands,
guint n_bands)
{
if (n_bands > 0) {
GVariantBuilder builder;
guint i;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
for (i = 0; i < n_bands; i++)
g_variant_builder_add_value (&builder,
g_variant_new_uint32 ((guint32)bands[i]));
return g_variant_builder_end (&builder);
}
return mm_common_build_bands_unknown ();
}
GVariant *
mm_common_bands_garray_to_variant (GArray *array)
{
if (array)
return mm_common_bands_array_to_variant ((const MMModemBand *)array->data,
array->len);
return mm_common_bands_array_to_variant (NULL, 0);
}
static guint
cmp_band (MMModemBand *a, MMModemBand *b)
{
return (*a - *b);
}
gboolean
mm_common_bands_garray_cmp (GArray *a, GArray *b)
{
GArray *dup_a;
GArray *dup_b;
guint i;
gboolean different;
if (a->len != b->len)
return FALSE;
dup_a = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), a->len);
g_array_append_vals (dup_a, a->data, a->len);
dup_b = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), b->len);
g_array_append_vals (dup_b, b->data, b->len);
g_array_sort (dup_a, (GCompareFunc)cmp_band);
g_array_sort (dup_b, (GCompareFunc)cmp_band);
different = FALSE;
for (i = 0; !different && i < a->len; i++) {
if (g_array_index (dup_a, MMModemBand, i) != g_array_index (dup_b, MMModemBand, i))
different = TRUE;
}
g_array_unref (dup_a);
g_array_unref (dup_b);
return !different;
}
gboolean
mm_common_bands_garray_lookup (GArray *array,
MMModemBand value)
{
guint i;
for (i = 0; i < array->len; i++) {
if (value == g_array_index (array, MMModemBand, i))
return TRUE;
}
return FALSE;
}
void
mm_common_bands_garray_sort (GArray *array)
{
g_array_sort (array, (GCompareFunc) cmp_band);
}
gboolean
mm_common_band_is_gsm (MMModemBand band)
{
return ((band >= MM_MODEM_BAND_EGSM && band <= MM_MODEM_BAND_G850) ||
(band >= MM_MODEM_BAND_G450 && band <= MM_MODEM_BAND_G810));
}
gboolean
mm_common_band_is_utran (MMModemBand band)
{
return ((band >= MM_MODEM_BAND_UTRAN_1 && band <= MM_MODEM_BAND_UTRAN_7) ||
(band >= MM_MODEM_BAND_UTRAN_10 && band <= MM_MODEM_BAND_UTRAN_32));
}
gboolean
mm_common_band_is_eutran (MMModemBand band)
{
return (band >= MM_MODEM_BAND_EUTRAN_1 && band <= MM_MODEM_BAND_EUTRAN_71);
}
gboolean
mm_common_band_is_cdma (MMModemBand band)
{
return (band >= MM_MODEM_BAND_CDMA_BC0 && band <= MM_MODEM_BAND_CDMA_BC19);
}
GArray *
mm_common_mode_combinations_variant_to_garray (GVariant *variant)
{
GArray *array = NULL;
if (variant) {
GVariantIter iter;
guint n;
g_variant_iter_init (&iter, variant);
n = g_variant_iter_n_children (&iter);
if (n > 0) {
MMModemModeCombination mode;
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), n);
while (g_variant_iter_loop (&iter, "(uu)", &mode.allowed, &mode.preferred))
g_array_append_val (array, mode);
}
}
/* If nothing set, fallback to default */
if (!array) {
MMModemModeCombination default_mode;
default_mode.allowed = MM_MODEM_MODE_ANY;
default_mode.preferred = MM_MODEM_MODE_NONE;
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
g_array_append_val (array, default_mode);
}
return array;
}
MMModemModeCombination *
mm_common_mode_combinations_variant_to_array (GVariant *variant,
guint *n_modes)
{
GArray *array;
array = mm_common_mode_combinations_variant_to_garray (variant);
if (n_modes)
*n_modes = array->len;
return (MMModemModeCombination *) g_array_free (array, FALSE);
}
GVariant *
mm_common_mode_combinations_array_to_variant (const MMModemModeCombination *modes,
guint n_modes)
{
if (n_modes > 0) {
GVariantBuilder builder;
guint i;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)"));
for (i = 0; i < n_modes; i++)
g_variant_builder_add_value (&builder,
g_variant_new ("(uu)",
((guint32)modes[i].allowed),
((guint32)modes[i].preferred)));
return g_variant_builder_end (&builder);
}
return mm_common_build_mode_combinations_default ();
}
GVariant *
mm_common_mode_combinations_garray_to_variant (GArray *array)
{
if (array)
return mm_common_mode_combinations_array_to_variant ((const MMModemModeCombination *)array->data,
array->len);
return mm_common_mode_combinations_array_to_variant (NULL, 0);
}
GVariant *
mm_common_build_mode_combinations_default (void)
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)"));
g_variant_builder_add_value (&builder,
g_variant_new ("(uu)",
MM_MODEM_MODE_ANY,
MM_MODEM_MODE_NONE));
return g_variant_builder_end (&builder);
}
MMModem3gppEpsUeModeOperation
mm_common_get_eps_ue_mode_operation_from_string (const gchar *str,
GError **error)
{
return _enum_from_string (MM_TYPE_MODEM_3GPP_EPS_UE_MODE_OPERATION,
str,
MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN,
error);
}
MMBearerMultiplexSupport
mm_common_get_multiplex_support_from_string (const gchar *str,
GError **error)
{
return _enum_from_string (MM_TYPE_BEARER_MULTIPLEX_SUPPORT,
str,
MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN,
error);
}
MMBearerApnType
mm_common_get_apn_type_from_string (const gchar *str,
GError **error)
{
return _flags_from_string (MM_TYPE_BEARER_APN_TYPE,
str,
MM_BEARER_APN_TYPE_NONE,
error);
}
GArray *
mm_common_oma_pending_network_initiated_sessions_variant_to_garray (GVariant *variant)
{
GArray *array = NULL;
if (variant) {
GVariantIter iter;
guint n;
g_variant_iter_init (&iter, variant);
n = g_variant_iter_n_children (&iter);
if (n > 0) {
MMOmaPendingNetworkInitiatedSession session;
array = g_array_sized_new (FALSE, FALSE, sizeof (MMOmaPendingNetworkInitiatedSession), n);
while (g_variant_iter_loop (&iter, "(uu)", &session.session_type, &session.session_id))
g_array_append_val (array, session);
}
}
/* If nothing set, fallback to empty */
if (!array)
array = g_array_new (FALSE, FALSE, sizeof (MMOmaPendingNetworkInitiatedSession));
return array;
}
MMOmaPendingNetworkInitiatedSession *
mm_common_oma_pending_network_initiated_sessions_variant_to_array (GVariant *variant,
guint *n_sessions)
{
GArray *array;
array = mm_common_oma_pending_network_initiated_sessions_variant_to_garray (variant);
if (n_sessions)
*n_sessions = array->len;
return (MMOmaPendingNetworkInitiatedSession *) g_array_free (array, FALSE);
}
GVariant *
mm_common_oma_pending_network_initiated_sessions_array_to_variant (const MMOmaPendingNetworkInitiatedSession *sessions,
guint n_sessions)
{
if (n_sessions > 0) {
GVariantBuilder builder;
guint i;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)"));
for (i = 0; i < n_sessions; i++)
g_variant_builder_add_value (&builder,
g_variant_new ("(uu)",
((guint32)sessions[i].session_type),
((guint32)sessions[i].session_id)));
return g_variant_builder_end (&builder);
}
return mm_common_build_oma_pending_network_initiated_sessions_default ();
}
GVariant *
mm_common_oma_pending_network_initiated_sessions_garray_to_variant (GArray *array)
{
if (array)
return mm_common_oma_pending_network_initiated_sessions_array_to_variant ((const MMOmaPendingNetworkInitiatedSession *)array->data,
array->len);
return mm_common_oma_pending_network_initiated_sessions_array_to_variant (NULL, 0);
}
GVariant *
mm_common_build_oma_pending_network_initiated_sessions_default (void)
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)"));
return g_variant_builder_end (&builder);
}
gboolean
mm_common_get_boolean_from_string (const gchar *value,
GError **error)
{
if (!g_ascii_strcasecmp (value, "true") || g_str_equal (value, "1") || !g_ascii_strcasecmp (value, "yes"))
return TRUE;
if (!g_ascii_strcasecmp (value, "false") || g_str_equal (value, "0") || !g_ascii_strcasecmp (value, "no"))
return FALSE;
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Cannot get boolean from string '%s'", value);
return FALSE;
}
MMModemCdmaRmProtocol
mm_common_get_rm_protocol_from_string (const gchar *str,
GError **error)
{
return _enum_from_string (MM_TYPE_MODEM_CDMA_RM_PROTOCOL,
str,
MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN,
error);
}
MMBearerIpFamily
mm_common_get_ip_type_from_string (const gchar *str,
GError **error)
{
return _flags_from_string (MM_TYPE_BEARER_IP_FAMILY,
str,
MM_BEARER_IP_FAMILY_NONE,
error);
}
MMBearerAllowedAuth
mm_common_get_allowed_auth_from_string (const gchar *str,
GError **error)
{
GError *inner_error = NULL;
MMBearerAllowedAuth allowed_auth;
gchar **strings;
GFlagsClass *flags_class;
allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_BEARER_ALLOWED_AUTH));
strings = g_strsplit (str, "|", -1);
if (strings) {
guint i;
for (i = 0; strings[i]; i++) {
guint j;
gboolean found = FALSE;
for (j = 0; flags_class->values[j].value_nick; j++) {
if (!g_ascii_strcasecmp (strings[i], flags_class->values[j].value_nick)) {
allowed_auth |= flags_class->values[j].value;
found = TRUE;
break;
}
}
if (!found) {
inner_error = g_error_new (
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Couldn't match '%s' with a valid MMBearerAllowedAuth value",
strings[i]);
break;
}
}
}
if (inner_error) {
g_propagate_error (error, inner_error);
allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
}
g_type_class_unref (flags_class);
g_strfreev (strings);
return allowed_auth;
}
MMSmsStorage
mm_common_get_sms_storage_from_string (const gchar *str,
GError **error)
{
return _enum_from_string (MM_TYPE_SMS_STORAGE,
str,
MM_SMS_STORAGE_UNKNOWN,
error);
}
MMSmsCdmaTeleserviceId
mm_common_get_sms_cdma_teleservice_id_from_string (const gchar *str,
GError **error)
{
return _enum_from_string (MM_TYPE_SMS_CDMA_TELESERVICE_ID,
str,
MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN,
error);
}
MMSmsCdmaServiceCategory
mm_common_get_sms_cdma_service_category_from_string (const gchar *str,
GError **error)
{
return _enum_from_string (MM_TYPE_SMS_CDMA_SERVICE_CATEGORY,
str,
MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
error);
}
MMCallDirection
mm_common_get_call_direction_from_string (const gchar *str,
GError **error)
{
return _enum_from_string (MM_TYPE_CALL_DIRECTION,
str,
MM_CALL_DIRECTION_UNKNOWN,
error);
}
MMCallState
mm_common_get_call_state_from_string (const gchar *str,
GError **error)
{
return _enum_from_string (MM_TYPE_CALL_STATE,
str,
MM_CALL_STATE_UNKNOWN,
error);
}
MMCallStateReason
mm_common_get_call_state_reason_from_string (const gchar *str,
GError **error)
{
return _enum_from_string (MM_TYPE_CALL_STATE_REASON,
str,
MM_CALL_STATE_REASON_UNKNOWN,
error);
}
MMOmaFeature
mm_common_get_oma_features_from_string (const gchar *str,
GError **error)
{
GError *inner_error = NULL;
MMOmaFeature features;
gchar **feature_strings;
GFlagsClass *flags_class;
features = MM_OMA_FEATURE_NONE;
flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_OMA_FEATURE));
feature_strings = g_strsplit (str, "|", -1);
if (feature_strings) {
guint i;
for (i = 0; feature_strings[i]; i++) {
guint j;
gboolean found = FALSE;
for (j = 0; flags_class->values[j].value_nick; j++) {
if (!g_ascii_strcasecmp (feature_strings[i], flags_class->values[j].value_nick)) {
features |= flags_class->values[j].value;
found = TRUE;
break;
}
}
if (!found) {
inner_error = g_error_new (
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Couldn't match '%s' with a valid MMOmaFeature value",
feature_strings[i]);
break;
}
}
}
if (inner_error) {
g_propagate_error (error, inner_error);
features = MM_OMA_FEATURE_NONE;
}
g_type_class_unref (flags_class);
g_strfreev (feature_strings);
return features;
}
MMOmaSessionType
mm_common_get_oma_session_type_from_string (const gchar *str,
GError **error)
{
return _enum_from_string (MM_TYPE_OMA_SESSION_TYPE,
str,
MM_OMA_SESSION_TYPE_UNKNOWN,
error);
}
GVariant *
mm_common_build_bands_unknown (void)
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
g_variant_builder_add_value (&builder,
g_variant_new_uint32 (MM_MODEM_BAND_UNKNOWN));
return g_variant_builder_end (&builder);
}
GVariant *
mm_common_build_bands_any (void)
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
g_variant_builder_add_value (&builder,
g_variant_new_uint32 (MM_MODEM_BAND_ANY));
return g_variant_builder_end (&builder);
}
/* 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
mm_common_parse_key_value_string (const gchar *str,
GError **error,
MMParseKeyValueForeachFn 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 (MM_CORE_ERROR,
MM_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 (MM_CORE_ERROR,
MM_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 (MM_CORE_ERROR,
MM_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 (MM_CORE_ERROR,
MM_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, user_data)) {
/* We were told to abort */
break;
}
if (keep_iteration)
continue;
/* Check if no more key/value pairs expected */
if (*p == '\0')
break;
inner_error = g_error_new (MM_CORE_ERROR,
MM_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;
}
MMModemAccessTechnology
mm_common_get_access_technology_from_string (const gchar *str,
GError **error)
{
GError *inner_error = NULL;
MMModemAccessTechnology technologies;
gchar **technology_strings;
GFlagsClass *flags_class;
technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_MODEM_ACCESS_TECHNOLOGY));
technology_strings = g_strsplit (str, "|", -1);
if (technology_strings) {
guint i;
for (i = 0; technology_strings[i]; i++) {
guint j;
gboolean found = FALSE;
for (j = 0; flags_class->values[j].value_nick; j++) {
if (!g_ascii_strcasecmp (technology_strings[i], flags_class->values[j].value_nick)) {
technologies |= flags_class->values[j].value;
found = TRUE;
break;
}
}
if (!found) {
inner_error = g_error_new (
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Couldn't match '%s' with a valid MMModemAccessTechnology value",
technology_strings[i]);
break;
}
}
}
if (inner_error) {
g_propagate_error (error, inner_error);
technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
}
g_type_class_unref (flags_class);
g_strfreev (technology_strings);
return technologies;
}
/*****************************************************************************/
gboolean
mm_get_int_from_str (const gchar *str,
gint *out)
{
glong num;
guint eol = 0;
if (!str)
return FALSE;
/* ignore all leading whitespaces */
while (str[0] == ' ')
str++;
if (!str[0])
return FALSE;
for (num = 0; str[num]; num++) {
if (str[num] != '+' && str[num] != '-' && !g_ascii_isdigit (str[num])) {
/* ignore \r\n at the end of the string */
if ((str[num] == '\r') || (str[num] == '\n')) {
eol++;
continue;
}
return FALSE;
}
/* if eol found before a valid char, the string is not parseable */
if (eol)
return FALSE;
}
/* if all characters were eol, the string is not parseable */
if (eol == num)
return FALSE;
errno = 0;
num = strtol (str, NULL, 10);
if (!errno && num >= G_MININT && num <= G_MAXINT) {
*out = (gint)num;
return TRUE;
}
return FALSE;
}
gboolean
mm_get_int_from_match_info (GMatchInfo *match_info,
guint32 match_index,
gint *out)
{
g_autofree gchar *s = NULL;
s = mm_get_string_unquoted_from_match_info (match_info, match_index);
return (s ? mm_get_int_from_str (s, out) : FALSE);
}
gboolean
mm_get_uint_from_str (const gchar *str,
guint *out)
{
guint64 num;
if (!mm_get_u64_from_str (str, &num) || num > G_MAXUINT)
return FALSE;
*out = (guint)num;
return TRUE;
}
gboolean
mm_get_u64_from_str (const gchar *str,
guint64 *out)
{
guint64 num;
guint eol = 0;
if (!str)
return FALSE;
/* ignore all leading whitespaces */
while (str[0] == ' ')
str++;
if (!str[0])
return FALSE;
for (num = 0; str[num]; num++) {
if (!g_ascii_isdigit (str[num])) {
/* ignore \r\n at the end of the string */
if ((str[num] == '\r') || (str[num] == '\n')) {
eol++;
continue;
}
return FALSE;
}
/* if eol found before a valid char, the string is not parseable */
if (eol)
return FALSE;
}
/* if all characters were eol, the string is not parseable */
if (eol == num)
return FALSE;
errno = 0;
num = (guint64) strtoull (str, NULL, 10);
if (!errno) {
*out = num;
return TRUE;
}
return FALSE;
}
gboolean
mm_get_uint_from_hex_str (const gchar *str,
guint *out)
{
guint64 num;
if (!mm_get_u64_from_hex_str (str, &num) || num > G_MAXUINT)
return FALSE;
*out = (guint)num;
return TRUE;
}
gboolean
mm_get_u64_from_hex_str (const gchar *str,
guint64 *out)
{
guint64 num;
guint eol = 0;
if (!str)
return FALSE;
/* ignore all leading whitespaces */
while (str[0] == ' ')
str++;
if (g_str_has_prefix (str, "0x"))
str = &str[2];
if (!str[0])
return FALSE;
for (num = 0; str[num]; num++) {
if (!g_ascii_isxdigit (str[num])) {
/* ignore \r\n at the end of the string */
if ((str[num] == '\r') || (str[num] == '\n')) {
eol++;
continue;
}
return FALSE;
}
/* if eol found before a valid char, the string is not parseable */
if (eol)
return FALSE;
}
/* if all characters were eol, the string is not parseable */
if (eol == num)
return FALSE;
errno = 0;
num = (guint64) strtoull (str, NULL, 16);
if (!errno) {
*out = num;
return TRUE;
}
return FALSE;
}
gboolean
mm_get_uint_from_match_info (GMatchInfo *match_info,
guint32 match_index,
guint *out)
{
guint64 num;
if (!mm_get_u64_from_match_info (match_info, match_index, &num) || num > G_MAXUINT)
return FALSE;
*out = (guint)num;
return TRUE;
}
gboolean
mm_get_u64_from_match_info (GMatchInfo *match_info,
guint32 match_index,
guint64 *out)
{
g_autofree gchar *s = NULL;
s = mm_get_string_unquoted_from_match_info (match_info, match_index);
return (s ? mm_get_u64_from_str (s, out) : FALSE);
}
gboolean
mm_get_uint_from_hex_match_info (GMatchInfo *match_info,
guint32 match_index,
guint *out)
{
guint64 num;
if (!mm_get_u64_from_hex_match_info (match_info, match_index, &num) || num > G_MAXUINT)
return FALSE;
*out = (guint)num;
return TRUE;
}
gboolean
mm_get_u64_from_hex_match_info (GMatchInfo *match_info,
guint32 match_index,
guint64 *out)
{
g_autofree gchar *s = NULL;
s = mm_get_string_unquoted_from_match_info (match_info, match_index);
return (s ? mm_get_u64_from_hex_str (s, out) : FALSE);
}
gboolean
mm_get_double_from_str (const gchar *str,
gdouble *out)
{
gdouble num;
guint i;
guint eol = 0;
if (!str || !str[0])
return FALSE;
for (i = 0; str[i]; i++) {
/* we don't really expect numbers in scientific notation, so
* don't bother looking for exponents and such */
if ((str[i] != '-') && (str[i] != '.') && !g_ascii_isdigit (str[i])) {
/* ignore \r\n at the end of the string */
if ((str[i] == '\r') || (str[i] == '\n')) {
eol++;
continue;
}
return FALSE;
}
/* if eol found before a valid char, the string is not parseable */
if (eol)
return FALSE;
}
/* if all characters were eol, the string is not parseable */
if (eol == i)
return FALSE;
errno = 0;
num = g_ascii_strtod (str, NULL);
if (!errno) {
*out = num;
return TRUE;
}
return FALSE;
}
gboolean
mm_get_double_from_match_info (GMatchInfo *match_info,
guint32 match_index,
gdouble *out)
{
g_autofree gchar *s = NULL;
s = mm_get_string_unquoted_from_match_info (match_info, match_index);
return (s ? mm_get_double_from_str (s, out) : FALSE);
}
gchar *
mm_get_string_unquoted_from_match_info (GMatchInfo *match_info,
guint32 match_index)
{
gchar *str;
gsize len;
str = g_match_info_fetch (match_info, match_index);
if (!str)
return NULL;
len = strlen (str);
/* Unquote the item if needed */
if ((len >= 2) && (str[0] == '"') && (str[len - 1] == '"')) {
str[0] = ' ';
str[len - 1] = ' ';
str = g_strstrip (str);
}
if (!str[0]) {
g_free (str);
return NULL;
}
return str;
}
gboolean
mm_is_string_mccmnc (const gchar *str)
{
gsize len;
guint i;
if (!str)
return FALSE;
len = strlen (str);
if (len < 5 || len > 6)
return FALSE;
for (i = 0; i < len; i++)
if (str[i] < '0' || str[i] > '9')
return FALSE;
return TRUE;
}
/*****************************************************************************/
const gchar *
mm_sms_delivery_state_get_string_extended (guint delivery_state)
{
if (delivery_state > 0x02 && delivery_state < 0x20) {
if (delivery_state < 0x10)
return "completed-reason-reserved";
else
return "completed-sc-specific-reason";
}
if (delivery_state > 0x25 && delivery_state < 0x40) {
if (delivery_state < 0x30)
return "temporary-error-reason-reserved";
else
return "temporary-error-sc-specific-reason";
}
if (delivery_state > 0x49 && delivery_state < 0x60) {
if (delivery_state < 0x50)
return "error-reason-reserved";
else
return "error-sc-specific-reason";
}
if (delivery_state > 0x65 && delivery_state < 0x80) {
if (delivery_state < 0x70)
return "temporary-fatal-error-reason-reserved";
else
return "temporary-fatal-error-sc-specific-reason";
}
if (delivery_state >= 0x80 && delivery_state < 0x100)
return "unknown-reason-reserved";
if (delivery_state >= 0x100)
return "unknown";
/* Otherwise, use the MMSmsDeliveryState enum as we can match the known
* value */
return mm_sms_delivery_state_get_string ((MMSmsDeliveryState)delivery_state);
}
/*****************************************************************************/
/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */
static gint
hex2num (gchar c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
gint
mm_utils_hex2byte (const gchar *hex)
{
gint a, b;
a = hex2num (*hex++);
if (a < 0)
return -1;
b = hex2num (*hex++);
if (b < 0)
return -1;
return (a << 4) | b;
}
guint8 *
mm_utils_hexstr2bin (const gchar *hex,
gssize len,
gsize *out_len,
GError **error)
{
const gchar *ipos = hex;
g_autofree guint8 *buf = NULL;
gsize i;
gint a;
guint8 *opos;
if (len < 0)
len = strlen (hex);
if (len == 0) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
"Hex conversion failed: empty string");
return NULL;
}
/* Length must be a multiple of 2 */
if ((len % 2) != 0) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
"Hex conversion failed: invalid input length");
return NULL;
}
opos = buf = g_malloc0 (len / 2);
for (i = 0; i < len; i += 2) {
a = mm_utils_hex2byte (ipos);
if (a < 0) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
"Hex byte conversion from '%c%c' failed",
ipos[0], ipos[1]);
return NULL;
}
*opos++ = (guint8)a;
ipos += 2;
}
*out_len = len / 2;
return g_steal_pointer (&buf);
}
/* End from hostap */
gboolean
mm_utils_ishexstr (const gchar *hex)
{
gsize len;
gsize i;
/* Empty string or length not multiple of 2? */
len = strlen (hex);
if (len == 0 || (len % 2) != 0)
return FALSE;
for (i = 0; i < len; i++) {
/* Non-hex char? */
if (hex[i] >= '0' && hex[i] <= '9')
continue;
if (hex[i] >= 'a' && hex[i] <= 'f')
continue;
if (hex[i] >= 'A' && hex[i] <= 'F')
continue;
return FALSE;
}
return TRUE;
}
gchar *
mm_utils_bin2hexstr (const guint8 *bin, gsize len)
{
GString *ret;
gsize i;
g_return_val_if_fail (bin != NULL, NULL);
ret = g_string_sized_new (len * 2 + 1);
for (i = 0; i < len; i++)
g_string_append_printf (ret, "%.2X", bin[i]);
return g_string_free (ret, FALSE);
}
gboolean
mm_utils_check_for_single_value (guint32 value)
{
gboolean found = FALSE;
guint32 i;
for (i = 1; i <= 32; i++) {
if (value & 0x1) {
if (found)
return FALSE; /* More than one bit set */
found = TRUE;
}
value >>= 1;
}
return TRUE;
}