diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2019-09-11 14:34:48 +0200 |
---|---|---|
committer | Lubomir Rintel <lkundrak@v3.sk> | 2019-09-11 14:34:48 +0200 |
commit | beab85520afa091392947b8cab128d513de194b7 (patch) | |
tree | e005df8fb2ef256f63d4f28cab8898d046623c1e | |
parent | 5730b0ff463f3530db407dac8e159a345b2bf867 (diff) | |
parent | 7c53930ceb1561182deb8f47dd9927e049fc396d (diff) | |
download | NetworkManager-beab85520afa091392947b8cab128d513de194b7.tar.gz |
merge: branch 'lr/gsm-default-apn'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/98
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.am | 31 | ||||
-rw-r--r-- | clients/common/nm-meta-setting-desc.c | 3 | ||||
-rw-r--r-- | clients/common/settings-docs.h.in | 1 | ||||
-rw-r--r-- | clients/tests/test-client.check-on-disk/test_003.expected | 10 | ||||
-rw-r--r-- | config.h.meson | 3 | ||||
-rw-r--r-- | configure.ac | 10 | ||||
-rwxr-xr-x | contrib/fedora/REQUIRED_PACKAGES | 1 | ||||
-rw-r--r-- | contrib/fedora/rpm/NetworkManager.spec | 3 | ||||
-rw-r--r-- | libnm-core/nm-connection.c | 84 | ||||
-rw-r--r-- | libnm-core/nm-setting-gsm.c | 63 | ||||
-rw-r--r-- | libnm-core/nm-setting-gsm.h | 5 | ||||
-rw-r--r-- | libnm-core/tests/test-general.c | 2 | ||||
-rw-r--r-- | libnm/libnm.ver | 5 | ||||
-rw-r--r-- | meson.build | 3 | ||||
-rw-r--r-- | src/devices/wwan/meson.build | 9 | ||||
-rw-r--r-- | src/devices/wwan/nm-modem-broadband.c | 150 | ||||
-rw-r--r-- | src/devices/wwan/nm-service-providers.c | 460 | ||||
-rw-r--r-- | src/devices/wwan/nm-service-providers.h | 24 | ||||
-rw-r--r-- | src/devices/wwan/tests/meson.build | 13 | ||||
-rw-r--r-- | src/devices/wwan/tests/test-service-providers.c | 124 | ||||
-rw-r--r-- | src/devices/wwan/tests/test-service-providers.xml | 73 |
22 files changed, 1000 insertions, 78 deletions
diff --git a/.gitignore b/.gitignore index d74b40c08d..55e8317e31 100644 --- a/.gitignore +++ b/.gitignore @@ -209,6 +209,7 @@ test-*.trs /src/devices/tests/test-acd /src/devices/tests/test-lldp /src/devices/wifi/tests/test-devices-wifi +/src/devices/wwan/tests/test-service-providers /src/dhcp/nm-dhcp-helper /src/dhcp/tests/test-dhcp-dhclient /src/dhcp/tests/test-dhcp-options diff --git a/Makefile.am b/Makefile.am index ef275bb440..7eb6a82674 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3315,7 +3315,10 @@ src_devices_wwan_libnm_wwan_la_SOURCES = \ src/devices/wwan/nm-modem-manager.c \ src/devices/wwan/nm-modem-manager.h \ src/devices/wwan/nm-modem.c \ - src/devices/wwan/nm-modem.h + src/devices/wwan/nm-modem.h \ + src/devices/wwan/nm-service-providers.c \ + src/devices/wwan/nm-service-providers.h \ + $(NULL) if WITH_OFONO src_devices_wwan_libnm_wwan_la_SOURCES += \ @@ -3364,11 +3367,35 @@ check-local-devices-wwan: src/devices/wwan/libnm-device-plugin-wwan.la src/devic check_local += check-local-devices-wwan +src_devices_wwan_tests_test_service_providers_SOURCES = \ + src/devices/wwan/tests/test-service-providers.c \ + src/devices/wwan/nm-service-providers.c \ + src/devices/wwan/nm-service-providers.h \ + $(NULL) + +src_devices_wwan_tests_test_service_providers_CPPFLAGS = \ + $(src_cppflags_base_test) \ + -I$(srcdir)/src/devices/wwan \ + $(NULL) + +src_devices_wwan_tests_test_service_providers_LDFLAGS = \ + $(SANITIZER_EXEC_LDFLAGS) \ + $(NULL) + +src_devices_wwan_tests_test_service_providers_LDADD = \ + src/libNetworkManagerTest.la \ + $(GLIB_LIBS) \ + $(NULL) + +check_programs += src/devices/wwan/tests/test-service-providers + endif EXTRA_DIST += \ src/devices/wwan/libnm-wwan.ver \ - src/devices/wwan/meson.build + src/devices/wwan/meson.build \ + src/devices/wwan/tests/test-service-providers.xml \ + $(NULL) ############################################################################### # src/devices/bluetooth diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 6451fe3378..40faa485ed 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -5365,6 +5365,9 @@ static const NMMetaPropertyInfo *const property_infos_ETHTOOL[] = { #undef _CURRENT_NM_META_SETTING_TYPE #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_GSM static const NMMetaPropertyInfo *const property_infos_GSM[] = { + PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_AUTO_CONFIG, + .property_type = &_pt_gobject_bool, + ), PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_NUMBER, .property_type = &_pt_gobject_string, ), diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in index d49331993b..518381077b 100644 --- a/clients/common/settings-docs.h.in +++ b/clients/common/settings-docs.h.in @@ -170,6 +170,7 @@ #define DESCRIBE_DOC_NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH N_("An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the priority may use all of the bandwidth allocated to its assigned group.") #define DESCRIBE_DOC_NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS N_("An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the traffic class (0 - 7) to which the priority is mapped.") #define DESCRIBE_DOC_NM_SETTING_GSM_APN N_("The GPRS Access Point Name specifying the APN used when establishing a data session with the GSM-based network. The APN often determines how the user will be billed for their network usage and whether the user has access to the Internet or just a provider-specific walled-garden, so it is important to use the correct APN for the user's mobile broadband plan. The APN may only be composed of the characters a-z, 0-9, ., and - per GSM 03.60 Section 14.9.") +#define DESCRIBE_DOC_NM_SETTING_GSM_AUTO_CONFIG N_("When TRUE, the settings such as APN, username, or password will default to values that match the network the modem will register to in the Mobile Broadband Provider database.") #define DESCRIBE_DOC_NM_SETTING_GSM_DEVICE_ID N_("The device unique identifier (as given by the WWAN management service) which this connection applies to. If given, the connection will only apply to the specified device.") #define DESCRIBE_DOC_NM_SETTING_GSM_HOME_ONLY N_("When TRUE, only connections to the home network will be allowed. Connections to roaming networks will not be made.") #define DESCRIBE_DOC_NM_SETTING_GSM_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames.") diff --git a/clients/tests/test-client.check-on-disk/test_003.expected b/clients/tests/test-client.check-on-disk/test_003.expected index c8cb4903b5..8bffc5b126 100644 --- a/clients/tests/test-client.check-on-disk/test_003.expected +++ b/clients/tests/test-client.check-on-disk/test_003.expected @@ -150,12 +150,12 @@ id path uuid <<< -size: 4116 +size: 4159 location: clients/tests/test-client.py:911:test_003()/12 cmd: $NMCLI con s con-gsm1 lang: C returncode: 0 -stdout: 3982 bytes +stdout: 4025 bytes >>> connection.id: con-gsm1 connection.uuid: UUID-con-gsm1-REPLACED-REPLACED-REPL @@ -228,6 +228,7 @@ serial.bits: 8 serial.parity: even serial.stopbits: 1 serial.send-delay: 100 +gsm.auto-config: no gsm.number: -- gsm.username: -- gsm.password: <hidden> @@ -247,12 +248,12 @@ proxy.pac-url: -- proxy.pac-script: -- <<< -size: 4145 +size: 4189 location: clients/tests/test-client.py:911:test_003()/13 cmd: $NMCLI con s con-gsm1 lang: pl_PL.UTF-8 returncode: 0 -stdout: 4001 bytes +stdout: 4045 bytes >>> connection.id: con-gsm1 connection.uuid: UUID-con-gsm1-REPLACED-REPLACED-REPL @@ -325,6 +326,7 @@ serial.bits: 8 serial.parity: even serial.stopbits: 1 serial.send-delay: 100 +gsm.auto-config: nie gsm.number: -- gsm.username: -- gsm.password: <hidden> diff --git a/config.h.meson b/config.h.meson index a8c694011f..7ba9f9af56 100644 --- a/config.h.meson +++ b/config.h.meson @@ -73,6 +73,9 @@ /* Define to path of the kernel firmware directory */ #mesondefine KERNEL_FIRMWARE_DIR +/* Mobile Broadband Service Provider Information Database location */ +#mesondefine MOBILE_BROADBAND_PROVIDER_INFO_DATABASE + /* Path to netconfig */ #mesondefine NETCONFIG_PATH diff --git a/configure.ac b/configure.ac index 951f51b9a0..68507e2884 100644 --- a/configure.ac +++ b/configure.ac @@ -747,10 +747,20 @@ if (test "${with_modem_manager_1}" != "no"); then fi else with_modem_manager_1="yes" + + PKG_CHECK_MODULES(MOBILE_BROADBAND_PROVIDER_INFO, [mobile-broadband-provider-info], + [MOBILE_BROADBAND_PROVIDER_INFO_DATABASE=`$PKG_CONFIG --variable=database mobile-broadband-provider-info`], + [MOBILE_BROADBAND_PROVIDER_INFO_DATABASE="$prefix/share/mobile-broadband-provider-info/serviceproviders.xml"]) + AC_DEFINE_UNQUOTED([MOBILE_BROADBAND_PROVIDER_INFO_DATABASE], + ["$MOBILE_BROADBAND_PROVIDER_INFO_DATABASE"], + [Mobile Broadband Service Provider Information Database location]) fi fi AM_CONDITIONAL(WITH_MODEM_MANAGER_1, test "${with_modem_manager_1}" = "yes") + + + # Bluez5 DUN support PKG_CHECK_MODULES(BLUEZ5, [bluez >= 5], [have_bluez5=yes],[have_bluez5=no]) AC_ARG_ENABLE(bluez5-dun, diff --git a/contrib/fedora/REQUIRED_PACKAGES b/contrib/fedora/REQUIRED_PACKAGES index 00164cc669..7579e9a983 100755 --- a/contrib/fedora/REQUIRED_PACKAGES +++ b/contrib/fedora/REQUIRED_PACKAGES @@ -22,6 +22,7 @@ install \ \ ModemManager-devel \ ModemManager-glib-devel \ + mobile-broadband-provider-info-devel \ audit-libs-devel \ bash-completion \ bluez-libs-devel \ diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec index b7957d602c..4eddef793a 100644 --- a/contrib/fedora/rpm/NetworkManager.spec +++ b/contrib/fedora/rpm/NetworkManager.spec @@ -219,6 +219,9 @@ BuildRequires: libndp-devel >= 1.0 %if 0%{?with_modem_manager_1} BuildRequires: ModemManager-glib-devel >= 1.0 %endif +%if %{with wwan} +BuildRequires: mobile-broadband-provider-info-devel +%endif %if %{with nmtui} BuildRequires: newt-devel %endif diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c index 2c69d2a998..36600a89c4 100644 --- a/libnm-core/nm-connection.c +++ b/libnm-core/nm-connection.c @@ -1041,7 +1041,7 @@ _normalize_ip_config (NMConnection *self, GHashTable *parameters) } static gboolean -_normalize_infiniband_mtu (NMConnection *self, GHashTable *parameters) +_normalize_infiniband_mtu (NMConnection *self) { NMSettingInfiniband *s_infini = nm_connection_get_setting_infiniband (self); @@ -1056,7 +1056,7 @@ _normalize_infiniband_mtu (NMConnection *self, GHashTable *parameters) } static gboolean -_normalize_bond_mode (NMConnection *self, GHashTable *parameters) +_normalize_bond_mode (NMConnection *self) { NMSettingBond *s_bond = nm_connection_get_setting_bond (self); @@ -1077,7 +1077,7 @@ _normalize_bond_mode (NMConnection *self, GHashTable *parameters) } static gboolean -_normalize_bond_options (NMConnection *self, GHashTable *parameters) +_normalize_bond_options (NMConnection *self) { NMSettingBond *s_bond = nm_connection_get_setting_bond (self); gboolean changed = FALSE; @@ -1107,7 +1107,7 @@ again: } static gboolean -_normalize_wireless_mac_address_randomization (NMConnection *self, GHashTable *parameters) +_normalize_wireless_mac_address_randomization (NMConnection *self) { NMSettingWireless *s_wifi = nm_connection_get_setting_wireless (self); const char *cloned_mac_address; @@ -1153,7 +1153,7 @@ _normalize_wireless_mac_address_randomization (NMConnection *self, GHashTable *p } static gboolean -_normalize_macsec (NMConnection *self, GHashTable *parameters) +_normalize_macsec (NMConnection *self) { NMSettingMacsec *s_macsec = nm_connection_get_setting_macsec (self); gboolean changed = FALSE; @@ -1176,7 +1176,7 @@ _normalize_macsec (NMConnection *self, GHashTable *parameters) } static gboolean -_normalize_team_config (NMConnection *self, GHashTable *parameters) +_normalize_team_config (NMConnection *self) { NMSettingTeam *s_team = nm_connection_get_setting_team (self); @@ -1192,7 +1192,7 @@ _normalize_team_config (NMConnection *self, GHashTable *parameters) } static gboolean -_normalize_team_port_config (NMConnection *self, GHashTable *parameters) +_normalize_team_port_config (NMConnection *self) { NMSettingTeamPort *s_team_port = nm_connection_get_setting_team_port (self); @@ -1208,7 +1208,7 @@ _normalize_team_port_config (NMConnection *self, GHashTable *parameters) } static gboolean -_normalize_bluetooth_type (NMConnection *self, GHashTable *parameters) +_normalize_bluetooth_type (NMConnection *self) { const char *type = _nm_connection_detect_bluetooth_type (self); @@ -1222,7 +1222,7 @@ _normalize_bluetooth_type (NMConnection *self, GHashTable *parameters) } static gboolean -_normalize_ovs_interface_type (NMConnection *self, GHashTable *parameters) +_normalize_ovs_interface_type (NMConnection *self) { NMSettingOvsInterface *s_ovs_interface = nm_connection_get_setting_ovs_interface (self); gboolean modified; @@ -1243,7 +1243,7 @@ _normalize_ovs_interface_type (NMConnection *self, GHashTable *parameters) } static gboolean -_normalize_ip_tunnel_wired_setting (NMConnection *self, GHashTable *parameters) +_normalize_ip_tunnel_wired_setting (NMConnection *self) { NMSettingIPTunnel *s_ip_tunnel; @@ -1263,7 +1263,7 @@ _normalize_ip_tunnel_wired_setting (NMConnection *self, GHashTable *parameters) } static gboolean -_normalize_sriov_vf_order (NMConnection *self, GHashTable *parameters) +_normalize_sriov_vf_order (NMConnection *self) { NMSettingSriov *s_sriov; @@ -1275,7 +1275,7 @@ _normalize_sriov_vf_order (NMConnection *self, GHashTable *parameters) } static gboolean -_normalize_bridge_vlan_order (NMConnection *self, GHashTable *parameters) +_normalize_bridge_vlan_order (NMConnection *self) { NMSettingBridge *s_bridge; @@ -1287,7 +1287,7 @@ _normalize_bridge_vlan_order (NMConnection *self, GHashTable *parameters) } static gboolean -_normalize_bridge_port_vlan_order (NMConnection *self, GHashTable *parameters) +_normalize_bridge_port_vlan_order (NMConnection *self) { NMSettingBridgePort *s_port; @@ -1299,7 +1299,30 @@ _normalize_bridge_port_vlan_order (NMConnection *self, GHashTable *parameters) } static gboolean -_normalize_required_settings (NMConnection *self, GHashTable *parameters) +_normalize_gsm_auto_config (NMConnection *self) +{ + NMSettingGsm *s_gsm; + + s_gsm = nm_connection_get_setting_gsm (self); + if (!s_gsm) + return FALSE; + + if (!nm_setting_gsm_get_auto_config (s_gsm)) + return FALSE; + + if ( !nm_setting_gsm_get_apn (s_gsm) + && !nm_setting_gsm_get_username (s_gsm) + && !nm_setting_gsm_get_password (s_gsm)) + return FALSE; + + g_object_set (s_gsm, + NM_SETTING_GSM_AUTO_CONFIG, FALSE, + NULL); + return TRUE; +} + +static gboolean +_normalize_required_settings (NMConnection *self) { NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth (self); NMSetting *s_bridge; @@ -1323,7 +1346,7 @@ _normalize_required_settings (NMConnection *self, GHashTable *parameters) } static gboolean -_normalize_invalid_slave_port_settings (NMConnection *self, GHashTable *parameters) +_normalize_invalid_slave_port_settings (NMConnection *self) { NMSettingConnection *s_con = nm_connection_get_setting_connection (self); const char *slave_type; @@ -1597,23 +1620,24 @@ _connection_normalize (NMConnection *connection, was_modified |= _normalize_connection_uuid (connection); was_modified |= _normalize_connection_type (connection); was_modified |= _normalize_connection_slave_type (connection); - was_modified |= _normalize_required_settings (connection, parameters); - was_modified |= _normalize_invalid_slave_port_settings (connection, parameters); + was_modified |= _normalize_required_settings (connection); + was_modified |= _normalize_invalid_slave_port_settings (connection); was_modified |= _normalize_ip_config (connection, parameters); was_modified |= _normalize_ethernet_link_neg (connection); - was_modified |= _normalize_infiniband_mtu (connection, parameters); - was_modified |= _normalize_bond_mode (connection, parameters); - was_modified |= _normalize_bond_options (connection, parameters); - was_modified |= _normalize_wireless_mac_address_randomization (connection, parameters); - was_modified |= _normalize_macsec (connection, parameters); - was_modified |= _normalize_team_config (connection, parameters); - was_modified |= _normalize_team_port_config (connection, parameters); - was_modified |= _normalize_bluetooth_type (connection, parameters); - was_modified |= _normalize_ovs_interface_type (connection, parameters); - was_modified |= _normalize_ip_tunnel_wired_setting (connection, parameters); - was_modified |= _normalize_sriov_vf_order (connection, parameters); - was_modified |= _normalize_bridge_vlan_order (connection, parameters); - was_modified |= _normalize_bridge_port_vlan_order (connection, parameters); + was_modified |= _normalize_infiniband_mtu (connection); + was_modified |= _normalize_bond_mode (connection); + was_modified |= _normalize_bond_options (connection); + was_modified |= _normalize_wireless_mac_address_randomization (connection); + was_modified |= _normalize_macsec (connection); + was_modified |= _normalize_team_config (connection); + was_modified |= _normalize_team_port_config (connection); + was_modified |= _normalize_bluetooth_type (connection); + was_modified |= _normalize_ovs_interface_type (connection); + was_modified |= _normalize_ip_tunnel_wired_setting (connection); + was_modified |= _normalize_sriov_vf_order (connection); + was_modified |= _normalize_bridge_vlan_order (connection); + was_modified |= _normalize_bridge_port_vlan_order (connection); + was_modified |= _normalize_gsm_auto_config (connection); was_modified = !!was_modified; diff --git a/libnm-core/nm-setting-gsm.c b/libnm-core/nm-setting-gsm.c index dd5e5ee37a..b4ca97e6d8 100644 --- a/libnm-core/nm-setting-gsm.c +++ b/libnm-core/nm-setting-gsm.c @@ -24,6 +24,7 @@ /*****************************************************************************/ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( + PROP_AUTO_CONFIG, PROP_NUMBER, PROP_USERNAME, PROP_PASSWORD, @@ -40,6 +41,8 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( ); typedef struct { + gboolean auto_config; + char *number; /* For dialing, duh */ char *username; char *password; @@ -67,6 +70,22 @@ G_DEFINE_TYPE (NMSettingGsm, nm_setting_gsm, NM_TYPE_SETTING) /*****************************************************************************/ /** + * nm_setting_gsm_get_auto_config: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingGsm:auto-config property of the setting + * + * Since: 1.22 + **/ +gboolean +nm_setting_gsm_get_auto_config (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), FALSE); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->auto_config; +} + +/** * nm_setting_gsm_get_number: * @setting: the #NMSettingGsm * @@ -273,10 +292,10 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } if (priv->apn) { - guint32 apn_len = strlen (priv->apn); - guint32 i; + gsize apn_len = strlen (priv->apn); + gsize i; - if (apn_len < 1 || apn_len > 64) { + if (apn_len > 64) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -320,7 +339,8 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } - if (priv->username && !strlen (priv->username)) { + if ( priv->username + && priv->username[0] == '\0') { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -330,8 +350,8 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } if (priv->network_id) { - guint32 nid_len = strlen (priv->network_id); - guint32 i; + gsize nid_len = strlen (priv->network_id); + gsize i; /* Accept both 5 and 6 digit MCC/MNC codes */ if ((nid_len < 5) || (nid_len > 6)) { @@ -400,6 +420,16 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } + if ( priv->auto_config + && (priv->apn || priv->username || priv->password)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("can't be enabled when manual configuration is present")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_AUTO_CONFIG); + return NM_SETTING_VERIFY_NORMALIZABLE_ERROR; + } + return TRUE; } @@ -440,6 +470,9 @@ get_property (GObject *object, guint prop_id, NMSettingGsm *setting = NM_SETTING_GSM (object); switch (prop_id) { + case PROP_AUTO_CONFIG: + g_value_set_boolean (value, nm_setting_gsm_get_auto_config (setting)); + break; case PROP_NUMBER: g_value_set_string (value, nm_setting_gsm_get_number (setting)); break; @@ -493,6 +526,9 @@ set_property (GObject *object, guint prop_id, char *tmp; switch (prop_id) { + case PROP_AUTO_CONFIG: + priv->auto_config = g_value_get_boolean (value); + break; case PROP_NUMBER: g_free (priv->number); priv->number = g_value_dup_string (value); @@ -609,6 +645,21 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass) setting_class->need_secrets = need_secrets; /** + * NMSettingGsm:auto-config: + * + * When %TRUE, the settings such as APN, username, or password will + * default to values that match the network the modem will register + * to in the Mobile Broadband Provider database. + * + * Since: 1.22 + **/ + obj_properties[PROP_AUTO_CONFIG] = + g_param_spec_boolean (NM_SETTING_GSM_AUTO_CONFIG, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + /** * NMSettingGsm:number: * * Legacy setting that used to help establishing PPP data sessions for diff --git a/libnm-core/nm-setting-gsm.h b/libnm-core/nm-setting-gsm.h index 057ccf9e75..8a6fabd7f8 100644 --- a/libnm-core/nm-setting-gsm.h +++ b/libnm-core/nm-setting-gsm.h @@ -24,6 +24,7 @@ G_BEGIN_DECLS #define NM_SETTING_GSM_SETTING_NAME "gsm" +#define NM_SETTING_GSM_AUTO_CONFIG "auto-config" #define NM_SETTING_GSM_USERNAME "username" #define NM_SETTING_GSM_PASSWORD "password" #define NM_SETTING_GSM_PASSWORD_FLAGS "password-flags" @@ -59,6 +60,10 @@ typedef struct { GType nm_setting_gsm_get_type (void); NMSetting *nm_setting_gsm_new (void); + +NM_AVAILABLE_IN_1_22 +gboolean nm_setting_gsm_get_auto_config (NMSettingGsm *setting); + const char *nm_setting_gsm_get_username (NMSettingGsm *setting); const char *nm_setting_gsm_get_password (NMSettingGsm *setting); const char *nm_setting_gsm_get_apn (NMSettingGsm *setting); diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 4df97b3469..c477844c55 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -1747,7 +1747,7 @@ test_setting_gsm_apn_bad_chars (void) /* 0 characters long */ g_object_set (s_gsm, NM_SETTING_GSM_APN, "", NULL); - g_assert (!nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL)); + g_assert (nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL)); /* 65-character long */ g_object_set (s_gsm, NM_SETTING_GSM_APN, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl1", NULL); diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 51e0d87241..68eaf745e9 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1628,3 +1628,8 @@ global: nm_setting_wireguard_get_ip6_auto_default_route; nm_settings_add_connection2_flags_get_type; } libnm_1_18_0; + +libnm_1_22_0 { +global: + nm_setting_gsm_get_auto_config; +} libnm_1_20_0; diff --git a/meson.build b/meson.build index 9fc9ad2684..db0fcbd29f 100644 --- a/meson.build +++ b/meson.build @@ -502,6 +502,9 @@ config_h.set10('WITH_PPP', enable_ppp) enable_modem_manager = get_option('modem_manager') if enable_modem_manager mm_glib_dep = dependency('mm-glib', version: '>= 0.7.991') + + service_provider_db = dependency('mobile-broadband-provider-info').get_pkgconfig_variable('database') + config_h.set_quoted('MOBILE_BROADBAND_PROVIDER_INFO_DATABASE', service_provider_db) endif # Bluez5 DUN support diff --git a/src/devices/wwan/meson.build b/src/devices/wwan/meson.build index 482dc205fa..363e522bf1 100644 --- a/src/devices/wwan/meson.build +++ b/src/devices/wwan/meson.build @@ -2,6 +2,7 @@ sources = files( 'nm-modem-broadband.c', 'nm-modem.c', 'nm-modem-manager.c', + 'nm-service-providers.c', ) deps = [ @@ -28,8 +29,10 @@ libnm_wwan = shared_module( install_dir: nm_plugindir, ) +wwan_inc = include_directories('.') + libnm_wwan_dep = declare_dependency( - include_directories: include_directories('.'), + include_directories: wwan_inc, link_with: libnm_wwan, ) @@ -74,3 +77,7 @@ check-local-devices-wwan: src/devices/wwan/libnm-device-plugin-wwan.la src/devic $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wwan/.libs/libnm-wwan.so "$(srcdir)/src/devices/wwan/libnm-wwan.ver" $(call check_so_symbols,$(builddir)/src/devices/wwan/.libs/libnm-wwan.so) ''' + +if enable_tests + subdir('tests') +endif diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 056d302e96..820c343a31 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -7,6 +7,7 @@ #include "nm-default.h" #include "nm-modem-broadband.h" +#include "nm-service-providers.h" #include <arpa/inet.h> #include <libmm-glib.h> @@ -261,7 +262,10 @@ create_cdma_connect_properties (NMConnection *connection) } static MMSimpleConnectProperties * -create_gsm_connect_properties (NMConnection *connection) +create_gsm_connect_properties (NMConnection *connection, + const char *apn, + const char *username, + const char *password) { NMSettingGsm *setting; NMSettingPpp *s_ppp; @@ -269,11 +273,14 @@ create_gsm_connect_properties (NMConnection *connection) const char *str; setting = nm_connection_get_setting_gsm (connection); + properties = mm_simple_connect_properties_new (); - /* Blank APN ("") means the default subscription APN */ - str = nm_setting_gsm_get_apn (setting); - mm_simple_connect_properties_set_apn (properties, str ?: ""); + mm_simple_connect_properties_set_apn (properties, apn ?: ""); + if (username) + mm_simple_connect_properties_set_user (properties, username); + if (password) + mm_simple_connect_properties_set_password (properties, password); str = nm_setting_gsm_get_network_id (setting); if (str) @@ -283,14 +290,6 @@ create_gsm_connect_properties (NMConnection *connection) if (str) mm_simple_connect_properties_set_pin (properties, str); - str = nm_setting_gsm_get_username (setting); - if (str) - mm_simple_connect_properties_set_user (properties, str); - - str = nm_setting_gsm_get_password (setting); - if (str) - mm_simple_connect_properties_set_password (properties, str); - /* Roaming */ if (nm_setting_gsm_get_home_only (setting)) mm_simple_connect_properties_set_allow_roaming (properties, FALSE); @@ -444,6 +443,95 @@ send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self) } static void +find_gsm_apn_cb (const char *apn, + const char *username, + const char *password, + const char *gateway, + const char *auth_method, + const GSList *dns, + GError *error, + gpointer user_data) +{ + NMModemBroadband *self = user_data; + NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self); + ConnectContext *ctx = priv->ctx; + + if (error) { + _LOGW ("failed to connect '%s': APN not found: %s", + nm_connection_get_id (ctx->connection), error->message); + + nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_GSM_APN_FAILED); + connect_context_clear (self); + return; + } + + /* Blank APN ("") means the default subscription APN */ + ctx->connect_properties = create_gsm_connect_properties (ctx->connection, + apn, + username, + password); + g_return_if_fail (ctx->connect_properties); + connect_context_step (self); +} + +static gboolean +try_create_connect_properties (NMModemBroadband *self) +{ + NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self); + ConnectContext *ctx = priv->ctx; + + if (MODEM_CAPS_3GPP (ctx->caps)) { + NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (ctx->connection); + + if (!s_gsm || nm_setting_gsm_get_auto_config (s_gsm)) { + gs_unref_object MMModem3gpp *modem_3gpp = NULL; + const char *network_id = NULL; + + s_gsm = nm_connection_get_setting_gsm (ctx->connection); + if (s_gsm) + network_id = nm_setting_gsm_get_network_id (s_gsm); + if (!network_id) { + if (mm_modem_get_state (self->_priv.modem_iface) < MM_MODEM_STATE_REGISTERED) + return FALSE; + modem_3gpp = mm_object_get_modem_3gpp (priv->modem_object); + network_id = mm_modem_3gpp_get_operator_code (modem_3gpp); + } + if (!network_id) { + _LOGW ("failed to connect '%s': unable to determine the network id", + nm_connection_get_id (ctx->connection)); + goto out; + } + + nm_service_providers_find_gsm_apn (MOBILE_BROADBAND_PROVIDER_INFO_DATABASE, + network_id, + ctx->cancellable, + find_gsm_apn_cb, + self); + } else { + ctx->connect_properties = create_gsm_connect_properties (ctx->connection, + nm_setting_gsm_get_apn (s_gsm), + nm_setting_gsm_get_username (s_gsm), + nm_setting_gsm_get_password (s_gsm)); + g_return_val_if_fail (ctx->connect_properties, TRUE); + } + + return TRUE; + } else if (MODEM_CAPS_3GPP2 (ctx->caps)) { + ctx->connect_properties = create_cdma_connect_properties (ctx->connection); + g_return_val_if_fail (ctx->connect_properties, FALSE); + return TRUE; + } else { + _LOGW ("failed to connect '%s': not a mobile broadband modem", + nm_connection_get_id (ctx->connection)); + } + +out: + nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); + connect_context_clear (self); + return TRUE; +} + +static void connect_context_step (NMModemBroadband *self) { ConnectContext *ctx = self->_priv.ctx; @@ -487,22 +575,8 @@ connect_context_step (NMModemBroadband *self) if (mm_modem_get_state (self->_priv.modem_iface) <= MM_MODEM_STATE_LOCKED) break; - /* Create core connect properties based on the modem capabilities */ - g_assert (!ctx->connect_properties); - - if (MODEM_CAPS_3GPP (ctx->caps)) - ctx->connect_properties = create_gsm_connect_properties (ctx->connection); - else if (MODEM_CAPS_3GPP2 (ctx->caps)) - ctx->connect_properties = create_cdma_connect_properties (ctx->connection); - else { - _LOGW ("failed to connect '%s': not a mobile broadband modem", - nm_connection_get_id (ctx->connection)); - - nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); - connect_context_clear (self); + if (!try_create_connect_properties (self)) break; - } - g_assert (ctx->connect_properties); /* Build up list of IP types that we need to use in the retries */ ctx->ip_types = nm_modem_get_connection_ip_type (NM_MODEM (self), ctx->connection, &error); @@ -521,6 +595,9 @@ connect_context_step (NMModemBroadband *self) } /* fall through */ case CONNECT_STEP_CONNECT: + if (!ctx->connect_properties) + break; + if (ctx->ip_types_i < ctx->ip_types->len) { NMModemIPType current; @@ -533,7 +610,7 @@ connect_context_step (NMModemBroadband *self) else if (current == NM_MODEM_IP_TYPE_IPV4V6) mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4V6); else - g_assert_not_reached (); + g_return_if_reached (); _nm_modem_set_apn (NM_MODEM (self), mm_simple_connect_properties_get_apn (ctx->connect_properties)); @@ -676,6 +753,9 @@ complete_connection (NMModem *modem, if (!s_gsm) { s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); nm_connection_add_setting (connection, NM_SETTING (s_gsm)); + g_object_set (G_OBJECT (s_gsm), + NM_SETTING_GSM_AUTO_CONFIG, TRUE, + NULL); } if (!nm_setting_gsm_get_device_id (s_gsm)) { @@ -863,8 +943,8 @@ static_stage3_ip4_done (NMModemBroadband *self) guint32 ip4_route_table, ip4_route_metric; NMPlatformIP4Route *r; - g_assert (self->_priv.ipv4_config); - g_assert (self->_priv.bearer); + g_return_val_if_fail (self->_priv.ipv4_config, FALSE); + g_return_val_if_fail (self->_priv.bearer, FALSE); self->_priv.idle_id_ip4 = 0; @@ -895,7 +975,7 @@ static_stage3_ip4_done (NMModemBroadband *self) } data_port = mm_bearer_get_interface (self->_priv.bearer); - g_assert (data_port); + g_return_val_if_fail (data_port, FALSE); config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); @@ -970,7 +1050,7 @@ stage3_ip6_done (NMModemBroadband *self) const char **dns; guint i; - g_assert (self->_priv.ipv6_config); + g_return_val_if_fail (self->_priv.ipv6_config, FALSE); self->_priv.idle_id_ip6 = 0; memset (&address, 0, sizeof (address)); @@ -1002,7 +1082,8 @@ stage3_ip6_done (NMModemBroadband *self) _LOGI ("IPv6 base configuration:"); data_port = mm_bearer_get_interface (self->_priv.bearer); - g_assert (data_port); + g_return_val_if_fail (data_port, FALSE); + config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); @@ -1370,8 +1451,9 @@ set_property (GObject *object, /* construct-only */ self->_priv.modem_object = g_value_dup_object (value); self->_priv.modem_iface = mm_object_get_modem (self->_priv.modem_object); + g_return_if_fail (self->_priv.modem_iface); self->_priv.modem_3gpp_iface = mm_object_get_modem_3gpp (self->_priv.modem_object); - g_assert (self->_priv.modem_iface != NULL); + g_signal_connect (self->_priv.modem_iface, "state-changed", G_CALLBACK (modem_state_changed), diff --git a/src/devices/wwan/nm-service-providers.c b/src/devices/wwan/nm-service-providers.c new file mode 100644 index 0000000000..f363ebe506 --- /dev/null +++ b/src/devices/wwan/nm-service-providers.c @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: LGPL-2.1+ +/* + * Copyright (C) 2009 Novell, Inc. + * Author: Tambet Ingo (tambet@gmail.com). + * + * Copyright (C) 2009 - 2019 Red Hat, Inc. + * Copyright (C) 2012 Lanedo GmbH + */ + +#include "nm-default.h" + +#include "nm-service-providers.h" + +typedef enum { + PARSER_TOPLEVEL = 0, + PARSER_COUNTRY, + PARSER_PROVIDER, + PARSER_METHOD_GSM, + PARSER_METHOD_GSM_APN, + PARSER_METHOD_CDMA, + PARSER_DONE, + PARSER_ERROR +} ParseContextState; + +typedef struct { + char *mccmnc; + NMServiceProvidersGsmApnCallback callback; + gpointer user_data; + GCancellable *cancellable; + GMarkupParseContext *ctx; + char buffer[4096]; + + char *text_buffer; + ParseContextState state; + + gboolean mccmnc_matched; + gboolean found_internet_apn; + char *apn; + char *username; + char *password; + char *gateway; + char *auth_method; + GSList *dns; +} ParseContext; + +/*****************************************************************************/ + +static void +parser_toplevel_start (ParseContext *parse_context, + const char *name, + const char **attribute_names, + const char **attribute_values) +{ + int i; + + if (strcmp (name, "serviceproviders") == 0) { + for (i = 0; attribute_names && attribute_names[i]; i++) { + if (strcmp (attribute_names[i], "format") == 0) { + if (strcmp (attribute_values[i], "2.0")) { + g_warning ("%s: mobile broadband provider database format '%s'" + " not supported.", __func__, attribute_values[i]); + parse_context->state = PARSER_ERROR; + break; + } + } + } + } else if (strcmp (name, "country") == 0) { + parse_context->state = PARSER_COUNTRY; + } +} + +static void +parser_country_start (ParseContext *parse_context, + const char *name, + const char **attribute_names, + const char **attribute_values) +{ + if (strcmp (name, "provider") == 0) + parse_context->state = PARSER_PROVIDER; +} + +static void +parser_provider_start (ParseContext *parse_context, + const char *name, + const char **attribute_names, + const char **attribute_values) +{ + parse_context->mccmnc_matched = FALSE; + if (strcmp (name, "gsm") == 0) + parse_context->state = PARSER_METHOD_GSM; + else if (strcmp (name, "cdma") == 0) + parse_context->state = PARSER_METHOD_CDMA; +} + +static void +parser_gsm_start (ParseContext *parse_context, + const char *name, + const char **attribute_names, + const char **attribute_values) +{ + int i; + + if (strcmp (name, "network-id") == 0) { + const char *mcc = NULL, *mnc = NULL; + + for (i = 0; attribute_names && attribute_names[i]; i++) { + if (strcmp (attribute_names[i], "mcc") == 0) + mcc = attribute_values[i]; + else if (strcmp (attribute_names[i], "mnc") == 0) + mnc = attribute_values[i]; + if (mcc && strlen (mcc) && mnc && strlen (mnc)) { + char *mccmnc = g_strdup_printf ("%s%s", mcc, mnc); + + if (strcmp (mccmnc, parse_context->mccmnc) == 0) + parse_context->mccmnc_matched = TRUE; + g_free (mccmnc); + break; + } + } + } else if (strcmp (name, "apn") == 0) { + parse_context->found_internet_apn = FALSE; + g_clear_pointer (&parse_context->apn, g_free); + g_clear_pointer (&parse_context->username, g_free); + g_clear_pointer (&parse_context->password, g_free); + g_clear_pointer (&parse_context->gateway, g_free); + g_clear_pointer (&parse_context->auth_method, g_free); + g_slist_free_full (parse_context->dns, g_free); + parse_context->dns = NULL; + + for (i = 0; attribute_names && attribute_names[i]; i++) { + if (strcmp (attribute_names[i], "value") == 0) { + parse_context->state = PARSER_METHOD_GSM_APN; + parse_context->apn = g_strstrip (g_strdup (attribute_values[i])); + break; + } + } + } +} + +static void +parser_gsm_apn_start (ParseContext *parse_context, + const char *name, + const char **attribute_names, + const char **attribute_values) +{ + int i; + + if (strcmp (name, "usage") == 0) { + for (i = 0; attribute_names && attribute_names[i]; i++) { + if ( (strcmp (attribute_names[i], "type") == 0) + && (strcmp (attribute_values[i], "internet") == 0)) { + parse_context->found_internet_apn = TRUE; + break; + } + } + } else if (strcmp (name, "authentication") == 0) { + for (i = 0; attribute_names && attribute_names[i]; i++) { + if (strcmp (attribute_names[i], "method") == 0) { + g_clear_pointer (&parse_context->auth_method, g_free); + parse_context->auth_method = g_strstrip (g_strdup (attribute_values[i])); + break; + } + } + } +} + +static void +parser_start_element (GMarkupParseContext *context, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + gpointer user_data, + GError **error) +{ + ParseContext *parse_context = user_data; + + g_clear_pointer (&parse_context->text_buffer, g_free); + + switch (parse_context->state) { + case PARSER_TOPLEVEL: + parser_toplevel_start (parse_context, element_name, attribute_names, attribute_values); + break; + case PARSER_COUNTRY: + parser_country_start (parse_context, element_name, attribute_names, attribute_values); + break; + case PARSER_PROVIDER: + parser_provider_start (parse_context, element_name, attribute_names, attribute_values); + break; + case PARSER_METHOD_GSM: + parser_gsm_start (parse_context, element_name, attribute_names, attribute_values); + break; + case PARSER_METHOD_GSM_APN: + parser_gsm_apn_start (parse_context, element_name, attribute_names, attribute_values); + break; + case PARSER_METHOD_CDMA: + break; + case PARSER_ERROR: + break; + case PARSER_DONE: + break; + } +} + +static void +parser_country_end (ParseContext *parse_context, + const char *name) +{ + if (strcmp (name, "country") == 0) { + g_clear_pointer (&parse_context->text_buffer, g_free); + parse_context->state = PARSER_TOPLEVEL; + } +} + +static void +parser_provider_end (ParseContext *parse_context, + const char *name) +{ + if (strcmp (name, "provider") == 0) { + g_clear_pointer (&parse_context->text_buffer, g_free); + parse_context->state = PARSER_COUNTRY; + } +} + +static void +parser_gsm_end (ParseContext *parse_context, + const char *name) +{ + if (strcmp (name, "gsm") == 0) { + g_clear_pointer (&parse_context->text_buffer, g_free); + parse_context->state = PARSER_PROVIDER; + } +} + +static void +parser_gsm_apn_end (ParseContext *parse_context, + const char *name) +{ + if (strcmp (name, "username") == 0) { + g_clear_pointer (&parse_context->username, g_free); + parse_context->username = g_steal_pointer (&parse_context->text_buffer); + } else if (strcmp (name, "password") == 0) { + g_clear_pointer (&parse_context->password, g_free); + parse_context->password = g_steal_pointer (&parse_context->text_buffer); + } else if (strcmp (name, "dns") == 0) { + parse_context->dns = g_slist_prepend (parse_context->dns, + g_steal_pointer (&parse_context->text_buffer)); + } else if (strcmp (name, "gateway") == 0) { + g_clear_pointer (&parse_context->gateway, g_free); + parse_context->gateway = g_steal_pointer (&parse_context->text_buffer); + } else if (strcmp (name, "apn") == 0) { + g_clear_pointer (&parse_context->text_buffer, g_free); + + if (parse_context->mccmnc_matched && parse_context->found_internet_apn) + parse_context->state = PARSER_DONE; + else + parse_context->state = PARSER_METHOD_GSM; + + } +} + +static void +parser_cdma_end (ParseContext *parse_context, + const char *name) +{ + if (strcmp (name, "cdma") == 0) { + g_clear_pointer (&parse_context->text_buffer, g_free); + parse_context->state = PARSER_PROVIDER; + } +} + +static void +parser_end_element (GMarkupParseContext *context, + const char *element_name, + gpointer user_data, + GError **error) +{ + ParseContext *parse_context = user_data; + + switch (parse_context->state) { + case PARSER_TOPLEVEL: + break; + case PARSER_COUNTRY: + parser_country_end (parse_context, element_name); + break; + case PARSER_PROVIDER: + parser_provider_end (parse_context, element_name); + break; + case PARSER_METHOD_GSM: + parser_gsm_end (parse_context, element_name); + break; + case PARSER_METHOD_GSM_APN: + parser_gsm_apn_end (parse_context, element_name); + break; + case PARSER_METHOD_CDMA: + parser_cdma_end (parse_context, element_name); + break; + case PARSER_ERROR: + break; + case PARSER_DONE: + break; + } +} + +static void +parser_text (GMarkupParseContext *context, + const char *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + ParseContext *parse_context = user_data; + + g_free (parse_context->text_buffer); + parse_context->text_buffer = g_strdup (text); +} + +static const GMarkupParser parser = { + .start_element = parser_start_element, + .end_element = parser_end_element, + .text = parser_text, + .passthrough = NULL, + .error = NULL, +}; + +/*****************************************************************************/ + +static void +finish_parse_context (ParseContext *parse_context, GError *error) +{ + if (parse_context->callback) { + if (error) { + parse_context->callback (NULL, NULL, NULL, NULL, NULL, + NULL, error, + parse_context->user_data); + } else { + parse_context->callback (parse_context->apn, + parse_context->username, + parse_context->password, + parse_context->gateway, + parse_context->auth_method, + parse_context->dns, + error, + parse_context->user_data); + } + } + + g_free (parse_context->mccmnc); + g_markup_parse_context_free (parse_context->ctx); + + g_free (parse_context->text_buffer); + g_free (parse_context->apn); + g_free (parse_context->username); + g_free (parse_context->password); + g_free (parse_context->gateway); + g_free (parse_context->auth_method); + g_slist_free_full (parse_context->dns, g_free); + + g_slice_free (ParseContext, parse_context); +} + +static void +read_next_chunk (GInputStream *stream, ParseContext *parse_context); + +static void +stream_read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GInputStream *stream = G_INPUT_STREAM (source_object); + ParseContext *parse_context = user_data; + gssize len; + GError *error = NULL; + + len = g_input_stream_read_finish (stream, res, &error); + if (len == -1) { + g_prefix_error (&error, "Error reading service provider database: "); + finish_parse_context (parse_context, error); + g_clear_error (&error); + return; + } + + if (len == 0) { + g_set_error (&error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "Operator ID '%s' not found in service provider database", + parse_context->mccmnc); + finish_parse_context (parse_context, error); + g_clear_error (&error); + return; + } + + if (!g_markup_parse_context_parse (parse_context->ctx, parse_context->buffer, len, &error)) { + g_prefix_error (&error, "Error parsing service provider database: "); + finish_parse_context (parse_context, error); + g_clear_error (&error); + return; + } + + if (parse_context->state == PARSER_DONE) { + finish_parse_context (parse_context, NULL); + return; + } + + read_next_chunk (stream, parse_context); +} + +static void +read_next_chunk (GInputStream *stream, ParseContext *parse_context) +{ + g_input_stream_read_async (stream, + parse_context->buffer, + sizeof (parse_context->buffer), + G_PRIORITY_DEFAULT, + parse_context->cancellable, + stream_read_cb, + parse_context); +} + +static void +file_read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GFile *file = G_FILE (source_object); + ParseContext *parse_context = user_data; + GFileInputStream *stream; + gs_free_error GError *error = NULL; + + stream = g_file_read_finish (file, res, &error); + if (!stream) { + g_prefix_error (&error, "Error opening service provider database: "); + finish_parse_context (parse_context, error); + return; + } + + read_next_chunk (G_INPUT_STREAM (stream), parse_context); + + g_object_unref (stream); +} + +/*****************************************************************************/ + +void +nm_service_providers_find_gsm_apn (const char *service_providers, + const char *mccmnc, + GCancellable *cancellable, + NMServiceProvidersGsmApnCallback callback, + gpointer user_data) +{ + GFile *file; + ParseContext *parse_context; + + parse_context = g_slice_new0 (ParseContext); + parse_context->mccmnc = g_strdup (mccmnc); + parse_context->cancellable = cancellable; + parse_context->callback = callback; + parse_context->user_data = user_data; + parse_context->ctx = g_markup_parse_context_new (&parser, 0, parse_context, NULL); + + file = g_file_new_for_path (service_providers); + + g_file_read_async (file, G_PRIORITY_DEFAULT, cancellable, file_read_cb, parse_context); + + g_object_unref (file); +} diff --git a/src/devices/wwan/nm-service-providers.h b/src/devices/wwan/nm-service-providers.h new file mode 100644 index 0000000000..35ad2fc120 --- /dev/null +++ b/src/devices/wwan/nm-service-providers.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: LGPL-2.1+ +/* + * Copyright (C) 2019 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_SERVICE_PROVIDERS_H__ +#define __NETWORKMANAGER_SERVICE_PROVIDERS_H__ + +typedef void (*NMServiceProvidersGsmApnCallback) (const char *apn, + const char *username, + const char *password, + const char *gateway, + const char *auth_method, + const GSList *dns, + GError *error, + gpointer user_data); + +void nm_service_providers_find_gsm_apn (const char *service_providers, + const char *mccmnc, + GCancellable *cancellable, + NMServiceProvidersGsmApnCallback callback, + gpointer user_data); + +#endif /* __NETWORKMANAGER_SERVICE_PROVIDERS_H__ */ diff --git a/src/devices/wwan/tests/meson.build b/src/devices/wwan/tests/meson.build new file mode 100644 index 0000000000..64dd76742a --- /dev/null +++ b/src/devices/wwan/tests/meson.build @@ -0,0 +1,13 @@ +exe = executable( + 'test-service-providers', + sources: files('test-service-providers.c', + '../nm-service-providers.c'), + dependencies: test_nm_dep, + include_directories: wwan_inc, +) +test( + 'wwan/test-service-providers', + test_script, + timeout: default_test_timeout, + args: test_args + [exe.full_path()], +) diff --git a/src/devices/wwan/tests/test-service-providers.c b/src/devices/wwan/tests/test-service-providers.c new file mode 100644 index 0000000000..33402cd52c --- /dev/null +++ b/src/devices/wwan/tests/test-service-providers.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: LGPL-2.1+ +/* + * Copyright (C) 2019 Red Hat + */ + +#include "nm-default.h" + +#include "nm-service-providers.h" + +#include "nm-test-utils-core.h" + +static void +test_positive_cb (const char *apn, + const char *username, + const char *password, + const char *gateway, + const char *auth_method, + const GSList *dns, + GError *error, + gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_main_loop_quit (loop); + g_assert_no_error (error); + g_assert_cmpstr (apn, ==, "gprs.example.com"); + g_assert_cmpstr (username, ==, "praise"); + g_assert_cmpstr (password, ==, "santa"); + g_assert_cmpstr (gateway, ==, "192.0.2.3"); + g_assert_cmpstr (auth_method, ==, "pap"); + + g_assert_nonnull (dns); + g_assert_cmpstr (dns->data, ==, "192.0.2.2"); + dns = dns->next; + g_assert_nonnull (dns); + g_assert_cmpstr (dns->data, ==, "192.0.2.1"); + g_assert_null (dns->next); +} + +static void +test_positive (void) +{ + GMainLoop *loop = g_main_loop_new (NULL, FALSE); + + nm_service_providers_find_gsm_apn (NM_BUILD_SRCDIR"/src/devices/wwan/tests/test-service-providers.xml", + "13337", NULL, test_positive_cb, loop); + g_main_loop_run (loop); + g_main_loop_unref (loop); +} + +/*****************************************************************************/ + +static void +test_negative_cb (const char *apn, + const char *username, + const char *password, + const char *gateway, + const char *auth_method, + const GSList *dns, + GError *error, + gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_main_loop_quit (loop); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN); +} + +static void +test_negative (void) +{ + GMainLoop *loop = g_main_loop_new (NULL, FALSE); + + nm_service_providers_find_gsm_apn (NM_BUILD_SRCDIR"/src/devices/wwan/tests/test-service-providers.xml", + "78130", NULL, test_negative_cb, loop); + g_main_loop_run (loop); + g_main_loop_unref (loop); +} + +/*****************************************************************************/ + +static void +test_nonexistent_cb (const char *apn, + const char *username, + const char *password, + const char *gateway, + const char *auth_method, + const GSList *dns, + GError *error, + gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_main_loop_quit (loop); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_AGAIN); +} + +static void +test_nonexistent (void) +{ + GMainLoop *loop = g_main_loop_new (NULL, FALSE); + + nm_service_providers_find_gsm_apn ("nonexistent.xml", "13337", NULL, + test_nonexistent_cb, loop); + g_main_loop_run (loop); + g_main_loop_unref (loop); +} + +/*****************************************************************************/ + +NMTST_DEFINE (); + +int +main (int argc, char **argv) +{ + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); + + g_test_add_func ("/service-providers/positive", test_positive); + g_test_add_func ("/service-providers/negative", test_negative); + g_test_add_func ("/service-providers/nonexistent", test_nonexistent); + + return g_test_run (); +} + diff --git a/src/devices/wwan/tests/test-service-providers.xml b/src/devices/wwan/tests/test-service-providers.xml new file mode 100644 index 0000000000..f0ca2debce --- /dev/null +++ b/src/devices/wwan/tests/test-service-providers.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding='utf-8'?> +<!DOCTYPE serviceproviders SYSTEM "serviceproviders.2.dtd"> + +<serviceproviders format="2.0"> + +<country code="feh"> + <provider> + <name>Sophia</name> + <gsm> + <network-id mcc="666" mnc="999"/> + <apn value="access.example.com"> + <plan type="postpaid"/> + <usage type="internet"/> + <name>APN</name> + <dns>192.0.2.1</dns> + <dns>192.0.2.2</dns> + </apn> + </gsm> + </provider> +</country> + +<country code="meh"> + <provider> + <name>Demiurge</name> + <gsm> + <network-id mcc="133" mnc="37"/> + <network-id mcc="133" mnc="666"/> + <apn value="mms"> + <usage type="mms"/> + <name>Unsolicited Nudes MMS</name> + <username>mms</username> + <password>mms</password> + <mmsc>http://mms.example.com/</mmsc> + <mmsproxy>192.0.2.1:8080</mmsproxy> + </apn> + <apn value="gprs.example.com"> + <plan type="postpaid"/> + <usage type="internet"/> + <name>GPRS</name> + <username>praise</username> + <password>santa</password> + <dns>192.0.2.1</dns> + <dns>192.0.2.2</dns> + <gateway>192.0.2.3</gateway> + <authentication method="pap"/> + </apn> + <apn value="second.example.com"> + <plan type="postpaid"/> + <usage type="internet"/> + <name>Second</name> + <username>worship</username> + <password>doom</password> + </apn> + </gsm> + </provider> + + <provider> + <name>Personal</name> + <gsm> + <network-id mcc="666" mnc="999"/> + <apn value="access.example.com"> + <plan type="postpaid"/> + <usage type="internet"/> + <name>APN</name> + <dns>192.0.2.1</dns> + <dns>192.0.2.2</dns> + </apn> + </gsm> + </provider> +</country> + +</serviceproviders> + |