Files
ModemManager/libmm-common/mm-bearer-properties.c
Tore Anderson 6b9ee7c833 broadband-bearer: derive PDP type from the ip-type bearer property
This patch makes it possible to use MM to set up PDP contexts with
PDP types other than 'IP', which is particularly useful when trying
to use the 'IPV6' or 'IPV4V6' PDP types defined in recent 3GPP specs.

If ip-type isn't specified, 'IP' will be used by default, due to the
fact that modem support for the 'IPV4V6' type is still rather scarce.

The patch applies to Aleksander's 'bearer-properties' branch. It has
been tested using mmcli in this fashion:

mmcli -m 0 --simple-connect=apn=internet # default to 'IP'
mmcli -m 0 --simple-connect=apn=internet,ip-type=IP
mmcli -m 0 --simple-connect=apn=internet,ip-type=IPV6
mmcli -m 0 --simple-connect=apn=internet,ip-type=IPV4V6
2012-05-06 17:12:36 +02:00

508 lines
16 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) 2011 Aleksander Morgado <aleksander@gnu.org>
*/
#include <string.h>
#include "mm-errors-types.h"
#include "mm-common-helpers.h"
#include "mm-bearer-properties.h"
G_DEFINE_TYPE (MMBearerProperties, mm_bearer_properties, G_TYPE_OBJECT);
#define PROPERTY_APN "apn"
#define PROPERTY_USER "user"
#define PROPERTY_PASSWORD "password"
#define PROPERTY_IP_TYPE "ip-type"
#define PROPERTY_NUMBER "number"
#define PROPERTY_ALLOW_ROAMING "allow-roaming"
#define PROPERTY_RM_PROTOCOL "rm-protocol"
struct _MMBearerPropertiesPrivate {
/* APN */
gchar *apn;
/* IP type */
gchar *ip_type;
/* Number */
gchar *number;
/* User */
gchar *user;
/* Password */
gchar *password;
/* Roaming allowance */
gboolean allow_roaming_set;
gboolean allow_roaming;
/* Protocol of the Rm interface */
MMModemCdmaRmProtocol rm_protocol;
};
/*****************************************************************************/
void
mm_bearer_properties_set_apn (MMBearerProperties *self,
const gchar *apn)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
g_free (self->priv->apn);
self->priv->apn = g_strdup (apn);
}
void
mm_bearer_properties_set_user (MMBearerProperties *self,
const gchar *user)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
g_free (self->priv->user);
self->priv->user = g_strdup (user);
}
void
mm_bearer_properties_set_password (MMBearerProperties *self,
const gchar *password)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
g_free (self->priv->password);
self->priv->password = g_strdup (password);
}
void
mm_bearer_properties_set_ip_type (MMBearerProperties *self,
const gchar *ip_type)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
g_free (self->priv->ip_type);
self->priv->ip_type = g_strdup (ip_type);
}
void
mm_bearer_properties_set_allow_roaming (MMBearerProperties *self,
gboolean allow_roaming)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
self->priv->allow_roaming = allow_roaming;
self->priv->allow_roaming_set = TRUE;
}
void
mm_bearer_properties_set_number (MMBearerProperties *self,
const gchar *number)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
g_free (self->priv->number);
self->priv->number = g_strdup (number);
}
void
mm_bearer_properties_set_rm_protocol (MMBearerProperties *self,
MMModemCdmaRmProtocol protocol)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
self->priv->rm_protocol = protocol;
}
/*****************************************************************************/
const gchar *
mm_bearer_properties_get_apn (MMBearerProperties *self)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL);
return self->priv->apn;
}
const gchar *
mm_bearer_properties_get_user (MMBearerProperties *self)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL);
return self->priv->user;
}
const gchar *
mm_bearer_properties_get_password (MMBearerProperties *self)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL);
return self->priv->password;
}
const gchar *
mm_bearer_properties_get_ip_type (MMBearerProperties *self)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL);
return self->priv->ip_type;
}
gboolean
mm_bearer_properties_get_allow_roaming (MMBearerProperties *self)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), FALSE);
return self->priv->allow_roaming;
}
const gchar *
mm_bearer_properties_get_number (MMBearerProperties *self)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL);
return self->priv->number;
}
MMModemCdmaRmProtocol
mm_bearer_properties_get_rm_protocol (MMBearerProperties *self)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN);
return self->priv->rm_protocol;
}
/*****************************************************************************/
GVariant *
mm_bearer_properties_get_dictionary (MMBearerProperties *self)
{
GVariantBuilder builder;
/* We do allow NULL */
if (!self)
return NULL;
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL);
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
if (self->priv->apn)
g_variant_builder_add (&builder,
"{sv}",
PROPERTY_APN,
g_variant_new_string (self->priv->apn));
if (self->priv->user)
g_variant_builder_add (&builder,
"{sv}",
PROPERTY_USER,
g_variant_new_string (self->priv->user));
if (self->priv->password)
g_variant_builder_add (&builder,
"{sv}",
PROPERTY_PASSWORD,
g_variant_new_string (self->priv->password));
if (self->priv->ip_type)
g_variant_builder_add (&builder,
"{sv}",
PROPERTY_IP_TYPE,
g_variant_new_string (self->priv->ip_type));
if (self->priv->number)
g_variant_builder_add (&builder,
"{sv}",
PROPERTY_NUMBER,
g_variant_new_string (self->priv->number));
if (self->priv->allow_roaming_set)
g_variant_builder_add (&builder,
"{sv}",
PROPERTY_ALLOW_ROAMING,
g_variant_new_boolean (self->priv->allow_roaming));
if (self->priv->rm_protocol)
g_variant_builder_add (&builder,
"{sv}",
PROPERTY_RM_PROTOCOL,
g_variant_new_uint32 (self->priv->rm_protocol));
return g_variant_ref_sink (g_variant_builder_end (&builder));
}
/*****************************************************************************/
gboolean
mm_bearer_properties_consume_string (MMBearerProperties *self,
const gchar *key,
const gchar *value,
GError **error)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), FALSE);
if (g_str_equal (key, PROPERTY_APN))
mm_bearer_properties_set_apn (self, value);
else if (g_str_equal (key, PROPERTY_USER))
mm_bearer_properties_set_user (self, value);
else if (g_str_equal (key, PROPERTY_PASSWORD))
mm_bearer_properties_set_password (self, value);
else if (g_str_equal (key, PROPERTY_IP_TYPE))
mm_bearer_properties_set_ip_type (self, value);
else if (g_str_equal (key, PROPERTY_ALLOW_ROAMING)) {
GError *inner_error = NULL;
gboolean allow_roaming;
allow_roaming = mm_common_get_boolean_from_string (value, &inner_error);
if (inner_error) {
g_propagate_error (error, inner_error);
return FALSE;
}
mm_bearer_properties_set_allow_roaming (self, allow_roaming);
} else if (g_str_equal (key, PROPERTY_NUMBER))
mm_bearer_properties_set_number (self, value);
else if (g_str_equal (key, PROPERTY_RM_PROTOCOL)) {
GError *inner_error = NULL;
MMModemCdmaRmProtocol protocol;
protocol = mm_common_get_rm_protocol_from_string (value, &inner_error);
if (inner_error) {
g_propagate_error (error, inner_error);
return FALSE;
}
mm_bearer_properties_set_rm_protocol (self, protocol);
} else {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Invalid properties string, unexpected key '%s'",
key);
return FALSE;
}
return TRUE;
}
typedef struct {
MMBearerProperties *properties;
GError *error;
} ParseKeyValueContext;
static gboolean
key_value_foreach (const gchar *key,
const gchar *value,
ParseKeyValueContext *ctx)
{
return mm_bearer_properties_consume_string (ctx->properties,
key,
value,
&ctx->error);
}
MMBearerProperties *
mm_bearer_properties_new_from_string (const gchar *str,
GError **error)
{
ParseKeyValueContext ctx;
ctx.error = NULL;
ctx.properties = mm_bearer_properties_new ();
mm_common_parse_key_value_string (str,
&ctx.error,
(MMParseKeyValueForeachFn)key_value_foreach,
&ctx);
/* If error, destroy the object */
if (ctx.error) {
g_propagate_error (error, ctx.error);
g_object_unref (ctx.properties);
ctx.properties = NULL;
}
return ctx.properties;
}
/*****************************************************************************/
gboolean
mm_bearer_properties_consume_variant (MMBearerProperties *properties,
const gchar *key,
GVariant *value,
GError **error)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (properties), FALSE);
if (g_str_equal (key, PROPERTY_APN))
mm_bearer_properties_set_apn (
properties,
g_variant_get_string (value, NULL));
else if (g_str_equal (key, PROPERTY_USER))
mm_bearer_properties_set_user (
properties,
g_variant_get_string (value, NULL));
else if (g_str_equal (key, PROPERTY_PASSWORD))
mm_bearer_properties_set_password (
properties,
g_variant_get_string (value, NULL));
else if (g_str_equal (key, PROPERTY_IP_TYPE))
mm_bearer_properties_set_ip_type (
properties,
g_variant_get_string (value, NULL));
else if (g_str_equal (key, PROPERTY_NUMBER))
mm_bearer_properties_set_number (
properties,
g_variant_get_string (value, NULL));
else if (g_str_equal (key, PROPERTY_ALLOW_ROAMING))
mm_bearer_properties_set_allow_roaming (
properties,
g_variant_get_boolean (value));
else {
/* Set error */
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Invalid properties dictionary, unexpected key '%s'",
key);
return FALSE;
}
return TRUE;
}
MMBearerProperties *
mm_bearer_properties_new_from_dictionary (GVariant *dictionary,
GError **error)
{
GError *inner_error = NULL;
GVariantIter iter;
gchar *key;
GVariant *value;
MMBearerProperties *properties;
properties = mm_bearer_properties_new ();
if (!dictionary)
return properties;
if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Cannot create Bearer properties from dictionary: "
"invalid variant type received");
g_object_unref (properties);
return NULL;
}
g_variant_iter_init (&iter, dictionary);
while (!inner_error &&
g_variant_iter_next (&iter, "{sv}", &key, &value)) {
mm_bearer_properties_consume_variant (properties,
key,
value,
&inner_error);
g_free (key);
g_variant_unref (value);
}
/* If error, destroy the object */
if (inner_error) {
g_propagate_error (error, inner_error);
g_object_unref (properties);
properties = NULL;
}
return properties;
}
/*****************************************************************************/
MMBearerProperties *
mm_bearer_properties_dup (MMBearerProperties *orig)
{
GVariant *dict;
MMBearerProperties *copy;
GError *error = NULL;
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (orig), NULL);
dict = mm_bearer_properties_get_dictionary (orig);
copy = mm_bearer_properties_new_from_dictionary (dict, &error);
g_assert_no_error (error);
g_variant_unref (dict);
return copy;
}
/*****************************************************************************/
gboolean
mm_bearer_properties_cmp (MMBearerProperties *a,
MMBearerProperties *b)
{
return ((!g_strcmp0 (a->priv->apn, b->priv->apn)) &&
(!g_strcmp0 (a->priv->ip_type, b->priv->ip_type)) &&
(!g_strcmp0 (a->priv->number, b->priv->number)) &&
(!g_strcmp0 (a->priv->user, b->priv->user)) &&
(!g_strcmp0 (a->priv->password, b->priv->password)) &&
(a->priv->allow_roaming == b->priv->allow_roaming) &&
(a->priv->allow_roaming_set == b->priv->allow_roaming_set) &&
(a->priv->rm_protocol == b->priv->rm_protocol));
}
/*****************************************************************************/
MMBearerProperties *
mm_bearer_properties_new (void)
{
return (MM_BEARER_PROPERTIES (
g_object_new (MM_TYPE_BEARER_PROPERTIES, NULL)));
}
static void
mm_bearer_properties_init (MMBearerProperties *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
MM_TYPE_BEARER_PROPERTIES,
MMBearerPropertiesPrivate);
/* Some defaults */
self->priv->allow_roaming = TRUE;
self->priv->rm_protocol = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN;
/* At some point in the future, this default should probably be changed
* to IPV4V6. However, presently support for this PDP type is rare. An
* even better approach would likely be to query which PDP types the
* modem supports (using AT+CGDCONT=?), and set the default accordingly
*/
self->priv->ip_type = g_strdup ("IP");
}
static void
finalize (GObject *object)
{
MMBearerProperties *self = MM_BEARER_PROPERTIES (object);
g_free (self->priv->apn);
g_free (self->priv->user);
g_free (self->priv->password);
g_free (self->priv->ip_type);
g_free (self->priv->number);
G_OBJECT_CLASS (mm_bearer_properties_parent_class)->finalize (object);
}
static void
mm_bearer_properties_class_init (MMBearerPropertiesClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMBearerPropertiesPrivate));
object_class->finalize = finalize;
}