diff options
-rw-r--r-- | data/meson.build | 4 | ||||
-rw-r--r-- | data/org.gnome.settings-daemon.plugins.wwan.gschema.xml.in | 10 | ||||
-rw-r--r-- | meson.build | 7 | ||||
-rw-r--r-- | meson_options.txt | 1 | ||||
-rw-r--r-- | plugins/meson.build | 4 | ||||
-rw-r--r-- | plugins/wwan/gsd-wwan-device.c | 237 | ||||
-rw-r--r-- | plugins/wwan/gsd-wwan-device.h | 38 | ||||
-rw-r--r-- | plugins/wwan/gsd-wwan-manager.c | 388 | ||||
-rw-r--r-- | plugins/wwan/gsd-wwan-manager.h | 36 | ||||
-rw-r--r-- | plugins/wwan/gsd-wwan-pinentry.c | 296 | ||||
-rw-r--r-- | plugins/wwan/gsd-wwan-pinentry.h | 28 | ||||
-rw-r--r-- | plugins/wwan/main.c | 7 | ||||
-rw-r--r-- | plugins/wwan/meson.build | 22 | ||||
-rw-r--r-- | plugins/wwan/org.gnome.SettingsDaemon.Wwan.desktop.in | 9 |
14 files changed, 1087 insertions, 0 deletions
diff --git a/data/meson.build b/data/meson.build index e93ba641..d53eb26c 100644 --- a/data/meson.build +++ b/data/meson.build @@ -12,6 +12,10 @@ schemas = [ 'org.gnome.settings-daemon.plugins.xsettings.gschema.xml' ] +if enable_wwan + schemas += 'org.gnome.settings-daemon.plugins.wwan.gschema.xml' +endif + schema_conf = configuration_data() schema_conf.set('GETTEXT_PACKAGE', meson.project_name()) diff --git a/data/org.gnome.settings-daemon.plugins.wwan.gschema.xml.in b/data/org.gnome.settings-daemon.plugins.wwan.gschema.xml.in new file mode 100644 index 00000000..283b77b5 --- /dev/null +++ b/data/org.gnome.settings-daemon.plugins.wwan.gschema.xml.in @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<schemalist> + <schema gettext-domain="@GETTEXT_PACKAGE@" id="org.gnome.settings-daemon.plugins.wwan" path="/org/gnome/settings-daemon/plugins/wwan/"> + <key name="unlock-sim" type="b"> + <default>false</default> + <summary>Unlock sim cards</summary> + <description>Unlock any sim cards right away.</description> + </key> + </schema> +</schemalist> diff --git a/meson.build b/meson.build index f56984d5..1cf16b3a 100644 --- a/meson.build +++ b/meson.build @@ -191,6 +191,13 @@ if enable_rfkill endif endif +# wwan +enable_wwan = get_option('wwan') +if enable_wwan + gcr_base_dep = dependency('gcr-base-3', version: '>= 3.7.5') + mm_glib_dep = dependency('mm-glib', version: '>= 1.0') +endif + # Sharing plugin enable_network_manager = get_option('network_manager') assert(enable_network_manager or not host_is_linux, 'NetworkManager support is not optional on Linux platforms') diff --git a/meson_options.txt b/meson_options.txt index 50bd1749..7da7421a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -8,3 +8,4 @@ option('network_manager', type: 'boolean', value: true, description: 'build with option('rfkill', type: 'boolean', value: true, description: 'build with rfkill support (not optional on Linux platforms)') option('smartcard', type: 'boolean', value: true, description: 'build with smartcard support') option('wayland', type: 'boolean', value: true, description: 'build with Wayland support') +option('wwan', type: 'boolean', value: true, description: 'build with WWAN support') diff --git a/plugins/meson.build b/plugins/meson.build index 53db07db..4dd2738b 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -29,6 +29,10 @@ if enable_rfkill enabled_plugins += [['rfkill', 'Rfkill']] endif +if enable_wwan + enabled_plugins += [['wwan', 'Wwan']] +endif + plugins_conf = configuration_data() plugins_conf.set('libexecdir', gsd_libexecdir) diff --git a/plugins/wwan/gsd-wwan-device.c b/plugins/wwan/gsd-wwan-device.c new file mode 100644 index 00000000..193c58ef --- /dev/null +++ b/plugins/wwan/gsd-wwan-device.c @@ -0,0 +1,237 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2019 Purism SPC + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Guido Günther <agx@sigxcpu.org> + * + */ + +#include "config.h" + +#include <gio/gio.h> +#include <glib.h> +#include <glib/gi18n.h> + +#include <libmm-glib.h> + +#include "gsd-wwan-device.h" + + +struct _GsdWwanDevice +{ + GObject parent; + + MMModem *mm_modem; + MMSim *mm_sim; + MMObject *mm_object; +}; + + +enum { + PROP_0, + PROP_MM_OBJECT, + PROP_MM_MODEM, + PROP_MM_SIM, + PROP_LAST_PROP, +}; +static GParamSpec *props[PROP_LAST_PROP]; + +enum { + SIM_NEEDS_UNLOCK, + N_SIGNALS +}; +static guint signals[N_SIGNALS]; + +G_DEFINE_TYPE (GsdWwanDevice, gsd_wwan_device, G_TYPE_OBJECT) + + +static void +modem_get_sim_ready (MMModem *modem, GAsyncResult *res, GsdWwanDevice *self) +{ + self->mm_sim = mm_modem_get_sim_finish (modem, res, NULL); + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_MM_SIM]); + g_return_if_fail (MM_IS_SIM (self->mm_sim)); + + g_debug ("Need to unlock sim %s (%s)", + mm_sim_get_path (self->mm_sim), + mm_sim_get_identifier (self->mm_sim)); + g_signal_emit(self, signals[SIM_NEEDS_UNLOCK], 0); +} + + +static void +fetch_modem_info (GsdWwanDevice *self) +{ + self->mm_modem = mm_object_get_modem (MM_OBJECT(self->mm_object)); + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_MM_MODEM]); + g_return_if_fail (self->mm_modem); + + g_debug ("Found modem %s (%s)", + mm_modem_get_path (self->mm_modem), + mm_modem_get_device (self->mm_modem)); + + if (mm_modem_get_state (self->mm_modem) != MM_MODEM_STATE_LOCKED) + return; + + /* The sim card will be valid as long as the modem exists */ + mm_modem_get_sim (self->mm_modem, NULL, (GAsyncReadyCallback)modem_get_sim_ready, self); +} + + +static void +gsd_wwan_device_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsdWwanDevice *self = GSD_WWAN_DEVICE (object); + + switch (prop_id) { + case PROP_MM_OBJECT: + self->mm_object = g_value_dup_object (value); + fetch_modem_info (self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsd_wwan_device_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsdWwanDevice *self = GSD_WWAN_DEVICE (object); + + switch (prop_id) { + case PROP_MM_OBJECT: + g_value_set_object (value, self->mm_object); + break; + case PROP_MM_MODEM: + g_value_set_object (value, self->mm_modem); + break; + case PROP_MM_SIM: + g_value_set_object (value, self->mm_sim); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsd_wwan_device_dispose (GObject *object) +{ + GsdWwanDevice *self = GSD_WWAN_DEVICE (object); + + g_clear_object (&self->mm_modem); + g_clear_object (&self->mm_sim); + g_clear_object (&self->mm_object); + + G_OBJECT_CLASS (gsd_wwan_device_parent_class)->dispose (object); +} + +static void +gsd_wwan_device_class_init (GsdWwanDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gsd_wwan_device_get_property; + object_class->set_property = gsd_wwan_device_set_property; + object_class->dispose = gsd_wwan_device_dispose; + + signals[SIM_NEEDS_UNLOCK] = + g_signal_new ("sim-needs-unlock", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + props[PROP_MM_OBJECT] = + g_param_spec_object ("mm-object", + "mm-object", + "The MMObject representing a modem", + MM_TYPE_OBJECT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + props[PROP_MM_MODEM] = + g_param_spec_object ("mm-modem", + "mm-modem", + "The MMModem interface object", + MM_TYPE_MODEM, + G_PARAM_READABLE | + G_PARAM_EXPLICIT_NOTIFY | + G_PARAM_STATIC_STRINGS); + props[PROP_MM_SIM] = + g_param_spec_object ("mm-sim", + "mm-sim", + "The MMSim interface object", + MM_TYPE_SIM, + G_PARAM_READABLE | + G_PARAM_EXPLICIT_NOTIFY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, PROP_LAST_PROP, props); +} + +static void +gsd_wwan_device_init (GsdWwanDevice *self) +{ +} + +GsdWwanDevice * +gsd_wwan_device_new (MMObject *object) +{ + return g_object_new (GSD_TYPE_WWAN_DEVICE, "mm-object", object, NULL); +} + + +MMObject * +gsd_wwan_device_get_mm_object (GsdWwanDevice *self) +{ + g_return_val_if_fail (GSD_IS_WWAN_DEVICE (self), NULL); + + return self->mm_object; +} + + +MMModem * +gsd_wwan_device_get_mm_modem (GsdWwanDevice *self) +{ + g_return_val_if_fail (GSD_IS_WWAN_DEVICE (self), NULL); + + return self->mm_modem; +} + + +MMSim * +gsd_wwan_device_get_mm_sim (GsdWwanDevice *self) +{ + g_return_val_if_fail (GSD_IS_WWAN_DEVICE (self), NULL); + + return self->mm_sim; +} + +gboolean +gsd_wwan_device_needs_unlock (GsdWwanDevice *self) +{ + g_return_val_if_fail (GSD_IS_WWAN_DEVICE (self), FALSE); + + return (mm_modem_get_state (self->mm_modem) == MM_MODEM_STATE_LOCKED && + self->mm_sim); +} diff --git a/plugins/wwan/gsd-wwan-device.h b/plugins/wwan/gsd-wwan-device.h new file mode 100644 index 00000000..a2fa903a --- /dev/null +++ b/plugins/wwan/gsd-wwan-device.h @@ -0,0 +1,38 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2019 Purism SPC + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Guido Günther <agx@sigxcpu.org> + * + */ + +# pragma once + +#include <glib-object.h> +#include <libmm-glib.h> + +G_BEGIN_DECLS + +#define GSD_TYPE_WWAN_DEVICE (gsd_wwan_device_get_type()) +G_DECLARE_FINAL_TYPE (GsdWwanDevice, gsd_wwan_device, GSD, WWAN_DEVICE, GObject) + +GsdWwanDevice *gsd_wwan_device_new (MMObject *object); +MMObject *gsd_wwan_device_get_mm_object (GsdWwanDevice *self); +MMModem *gsd_wwan_device_get_mm_modem (GsdWwanDevice *self); +MMSim *gsd_wwan_device_get_mm_sim (GsdWwanDevice *self); +gboolean gsd_wwan_device_needs_unlock (GsdWwanDevice *self); + +G_END_DECLS diff --git a/plugins/wwan/gsd-wwan-manager.c b/plugins/wwan/gsd-wwan-manager.c new file mode 100644 index 00000000..317218e5 --- /dev/null +++ b/plugins/wwan/gsd-wwan-manager.c @@ -0,0 +1,388 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2019 Purism SPC + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Guido Günther <agx@sigxcpu.org> + * + */ + +#include "config.h" + +#include <string.h> +#include <locale.h> + +#include <gio/gio.h> +#include <glib.h> +#include <glib/gi18n.h> + +#include <libmm-glib.h> + +#include "gnome-settings-profile.h" +#include "gsd-wwan-device.h" +#include "gsd-wwan-manager.h" +#include "gsd-wwan-pinentry.h" + + +struct _GsdWwanManager +{ + GObject parent; + + guint start_idle_id; + gboolean unlock; + GSettings *settings; + + GPtrArray *devices; + + MMManager *mm1; + gboolean mm1_running; +}; + +enum { + PROP_0, + PROP_UNLOCK_SIM, + PROP_LAST_PROP, +}; +static GParamSpec *props[PROP_LAST_PROP]; + +#define GSD_WWAN_SCHEMA_DIR "org.gnome.settings-daemon.plugins.wwan" +#define GSD_WWAN_SCHEMA_UNLOCK_SIM "unlock-sim" + +G_DEFINE_TYPE (GsdWwanManager, gsd_wwan_manager, G_TYPE_OBJECT) + +/* The plugin's manager object */ +static gpointer manager_object = NULL; + + +static void +unlock_sim_cb (GsdWwanManager *self, GsdWwanDevice *device) +{ + g_return_if_fail (GSD_IS_WWAN_MANAGER (self)); + g_return_if_fail (GSD_IS_WWAN_DEVICE (device)); + + if (!self->unlock) + return; + + gsd_wwan_pinentry_unlock_sim (device, NULL); +} + + +static gboolean +device_match_by_object (GsdWwanDevice *device, GDBusObject *object) +{ + g_return_val_if_fail (G_IS_DBUS_OBJECT (object), FALSE); + g_return_val_if_fail (GSD_IS_WWAN_DEVICE (device), FALSE); + + return object == G_DBUS_OBJECT (gsd_wwan_device_get_mm_object (device)); +} + + +static void +gsd_wwan_manager_cache_mm_object (GsdWwanManager *self, MMObject *obj) +{ + const gchar *modem_object_path; + GsdWwanDevice *device; + + modem_object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (obj)); + g_return_if_fail (modem_object_path); + + if (g_ptr_array_find_with_equal_func (self->devices, + obj, + (GEqualFunc) device_match_by_object, + NULL)) { + g_debug("Device %s already tracked", modem_object_path); + return; + } + + g_debug ("Tracking device at: %s", modem_object_path); + device = gsd_wwan_device_new (MM_OBJECT (obj)); + g_signal_connect_swapped (device, + "sim-needs-unlock", + G_CALLBACK (unlock_sim_cb), + self); + g_ptr_array_add (self->devices, device); +} + + +static void +object_added_cb (GsdWwanManager *self, GDBusObject *object, GDBusObjectManager *obj_manager) +{ + g_return_if_fail (GSD_IS_WWAN_MANAGER (self)); + g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER (obj_manager)); + + gsd_wwan_manager_cache_mm_object (self, MM_OBJECT(object)); +} + + +static void +object_removed_cb (GsdWwanManager *self, GDBusObject *object, GDBusObjectManager *obj_manager) +{ + guint index; + + g_return_if_fail (GSD_IS_WWAN_MANAGER (self)); + g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER (obj_manager)); + + if (g_ptr_array_find_with_equal_func (self->devices, + object, + (GEqualFunc) device_match_by_object, + &index)) { + g_ptr_array_remove_index_fast (self->devices, index); + } +} + + +static void +mm1_name_owner_changed_cb (GDBusObjectManagerClient *client, GParamSpec *pspec, GsdWwanManager *self) +{ + g_autofree gchar *name_owner = NULL; + + name_owner = g_dbus_object_manager_client_get_name_owner (client); + self->mm1_running = !!name_owner; + g_debug ("mm name owned: %d", self->mm1_running); + + if (!self->mm1_running) { + /* Drop all devices when MM goes away */ + if (self->devices->len) { + g_ptr_array_set_size (self->devices, 0); + } + return; + } +} + + +static void +get_all_modems (GsdWwanManager *self) +{ + GList *list, *l; + + g_return_if_fail (MM_IS_MANAGER (self->mm1)); + + list = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self->mm1)); + for (l = list; l != NULL; l = l->next) + gsd_wwan_manager_cache_mm_object (self, MM_OBJECT(l->data)); + g_list_free_full (list, g_object_unref); +} + + +static void +mm1_manager_new_cb (GDBusConnection *connection, GAsyncResult *res, GsdWwanManager *self) +{ + g_autoptr(GError) error = NULL; + + self->mm1 = mm_manager_new_finish (res, &error); + if (self->mm1) { + /* Listen for added/removed modems */ + g_signal_connect_object (self->mm1, + "object-added", + G_CALLBACK (object_added_cb), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (self->mm1, + "object-removed", + G_CALLBACK (object_removed_cb), + self, + G_CONNECT_SWAPPED); + + /* Listen for name owner changes */ + g_signal_connect (self->mm1, + "notify::name-owner", + G_CALLBACK (mm1_name_owner_changed_cb), + self); + + /* Handle all modems already known to MM */ + get_all_modems (self); + } else { + g_warning ("Error connecting to D-Bus: %s", error->message); + } +} + + +static void +set_modem_manager (GsdWwanManager *self) +{ + GDBusConnection *system_bus; + g_autoptr(GError) error = NULL; + + system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + if (system_bus) { + mm_manager_new (system_bus, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, + NULL, + (GAsyncReadyCallback) mm1_manager_new_cb, + self); + g_object_unref (system_bus); + } else { + g_warning ("Error connecting to system D-Bus: %s", error->message); + } +} + + +static gboolean +start_wwan_idle_cb (GsdWwanManager *self) +{ + g_debug ("Idle starting wwan manager"); + gnome_settings_profile_start (NULL); + + g_return_val_if_fail(GSD_IS_WWAN_MANAGER (self), FALSE); + self->settings = g_settings_new (GSD_WWAN_SCHEMA_DIR); + g_settings_bind (self->settings, "unlock-sim", self, "unlock-sim", G_SETTINGS_BIND_GET); + + set_modem_manager (self); + gnome_settings_profile_end (NULL); + self->start_idle_id = 0; + + return FALSE; +} + +gboolean +gsd_wwan_manager_start (GsdWwanManager *self, + GError **error) +{ + g_debug ("Starting wwan manager"); + g_return_val_if_fail(GSD_IS_WWAN_MANAGER (self), FALSE); + + gnome_settings_profile_start (NULL); + self->start_idle_id = g_idle_add ((GSourceFunc) start_wwan_idle_cb, self); + g_source_set_name_by_id (self->start_idle_id, "[gnome-settings-daemon] start_wwan_idle_cb"); + + gnome_settings_profile_end (NULL); + return TRUE; +} + +void +gsd_wwan_manager_stop (GsdWwanManager *self) +{ + g_debug ("Stopping wwan manager"); +} + + +static void +unlock_all (GsdWwanDevice *device, GsdWwanManager *self) +{ + if (gsd_wwan_device_needs_unlock (device)) + unlock_sim_cb (self, device); +} + + +static void +gsd_wwan_manager_set_unlock_sim (GsdWwanManager *self, gboolean unlock) +{ + if (self->unlock == unlock) + return; + + self->unlock = unlock; + + if (self->unlock) { + g_ptr_array_foreach (self->devices, + (GFunc) unlock_all, + self); + } + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_UNLOCK_SIM]); +} + + +static void +gsd_wwan_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsdWwanManager *self = GSD_WWAN_MANAGER (object); + + switch (prop_id) { + case PROP_UNLOCK_SIM: + gsd_wwan_manager_set_unlock_sim (self, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsd_wwan_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsdWwanManager *self = GSD_WWAN_MANAGER (object); + + switch (prop_id) { + case PROP_UNLOCK_SIM: + g_value_set_boolean (value, self->unlock); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsd_wwan_manager_dispose (GObject *object) +{ + GsdWwanManager *self = GSD_WWAN_MANAGER (object); + + if (self->mm1) { + self->mm1_running = FALSE; + g_clear_object (&self->mm1); + } + g_clear_pointer (&self->devices, g_ptr_array_unref); + g_clear_object (&self->settings); + + G_OBJECT_CLASS (gsd_wwan_manager_parent_class)->dispose (object); +} + +static void +gsd_wwan_manager_class_init (GsdWwanManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gsd_wwan_manager_get_property; + object_class->set_property = gsd_wwan_manager_set_property; + object_class->dispose = gsd_wwan_manager_dispose; + + props[PROP_UNLOCK_SIM] = + g_param_spec_boolean ("unlock-sim", + "unlock-sim", + "Whether to unlock new sims right away", + FALSE, + G_PARAM_READWRITE | + G_PARAM_EXPLICIT_NOTIFY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, PROP_LAST_PROP, props); +} + +static void +gsd_wwan_manager_init (GsdWwanManager *self) +{ + self->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); +} + + +GsdWwanManager * +gsd_wwan_manager_new (void) +{ + if (manager_object != NULL) { + g_object_ref (manager_object); + } else { + manager_object = g_object_new (GSD_TYPE_WWAN_MANAGER, NULL); + g_object_add_weak_pointer (manager_object, + (gpointer *) &manager_object); + } + + return GSD_WWAN_MANAGER (manager_object); +} diff --git a/plugins/wwan/gsd-wwan-manager.h b/plugins/wwan/gsd-wwan-manager.h new file mode 100644 index 00000000..127d3d21 --- /dev/null +++ b/plugins/wwan/gsd-wwan-manager.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2019 Purism SPC + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Guido Günther <agx@sigxcpu.org> + * + */ + +# pragma once + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GSD_TYPE_WWAN_MANAGER (gsd_wwan_manager_get_type()) +G_DECLARE_FINAL_TYPE (GsdWwanManager, gsd_wwan_manager, GSD, WWAN_MANAGER, GObject) + +GsdWwanManager * gsd_wwan_manager_new (void); +gboolean gsd_wwan_manager_start (GsdWwanManager *manager, + GError **error); +void gsd_wwan_manager_stop (GsdWwanManager *manager); + +G_END_DECLS diff --git a/plugins/wwan/gsd-wwan-pinentry.c b/plugins/wwan/gsd-wwan-pinentry.c new file mode 100644 index 00000000..2b86cb03 --- /dev/null +++ b/plugins/wwan/gsd-wwan-pinentry.c @@ -0,0 +1,296 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2019 Purism SPC + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Guido Günther <agx@sigxcpu.org> + * + */ + +#include <gio/gio.h> +#include <glib.h> +#include <glib/gi18n.h> + +#include <libmm-glib.h> + +#define GCR_API_SUBJECT_TO_CHANGE +#include <gcr/gcr-base.h> + +#include "gsd-wwan-device.h" +#include "gsd-wwan-pinentry.h" + + +static gchar * +get_sim_identifier (GsdWwanDevice *device) +{ + MMSim *sim = gsd_wwan_device_get_mm_sim (device); + char *identifier; + + identifier = mm_sim_dup_operator_name (sim); + if (identifier) + return identifier; + + identifier = mm_sim_dup_operator_identifier (sim); + if (identifier) + return identifier; + + identifier = mm_sim_dup_identifier (sim); + if (identifier) + return identifier; + + return NULL; +} + + +static GcrPrompt * +create_prompt (GsdWwanDevice *device, const char* msg) +{ + g_autoptr(GError) error = NULL; + GcrPrompt *prompt; + g_autofree gchar *identifier = NULL; + g_autofree gchar *description = NULL; + g_autofree gchar *warning = NULL; + const gchar *message = NULL; + MMModem *modem = gsd_wwan_device_get_mm_modem (device); + MMModemLock lock; + /* MM does not support g_autoptr */ + g_autoptr(GObject) retries = NULL; + guint num; + + identifier = get_sim_identifier (device); + g_return_val_if_fail (identifier, NULL); + g_debug ("Creating new PIN/PUK dialog for SIM %s", identifier); + + /* TODO: timeout */ + prompt = GCR_PROMPT (gcr_system_prompt_open (-1, NULL, &error)); + if (!prompt) { + /* timeout expired */ + if (error->code == GCR_SYSTEM_PROMPT_IN_PROGRESS) + g_warning ("The Gcr system prompter was already in use."); + else { + g_warning ("Couldn't create prompt for SIM PIN entry: %s", error->message); + } + return NULL; + } + + /* Set up the dialog */ + gcr_prompt_set_title (prompt, _("Unlock SIM card")); + gcr_prompt_set_continue_label (prompt, _("Unlock")); + gcr_prompt_set_cancel_label (prompt, _("Cancel")); + + lock = mm_modem_get_unlock_required (modem); + if (lock == MM_MODEM_LOCK_SIM_PIN) { + description = g_strdup_printf (_("Please provide the PIN for SIM card %s"), + identifier); + message = _("Enter PIN to unlock your SIM card"); + } else if (lock == MM_MODEM_LOCK_SIM_PUK) { + /* type = "PUK"; */ + g_warning ("Handling PUKs not yet supported"); + g_object_unref(prompt); + return NULL; + } else { + g_warning ("Unsupported lock type: %u", lock); + g_object_unref(prompt); + return NULL; + } + + if (!message || !description) { + g_object_unref(prompt); + g_return_val_if_fail (message && description, NULL); + } + + gcr_prompt_set_description (prompt, description); + gcr_prompt_set_message (prompt, message); + + retries = G_OBJECT(mm_modem_get_unlock_retries (modem)); + num = mm_unlock_retries_get (MM_UNLOCK_RETRIES (retries), lock); + + if (num != MM_UNLOCK_RETRIES_UNKNOWN) { + if (msg) { + /* msg is already localised */ + warning = g_strdup_printf (ngettext ("%2$s You have %1$u try left", + "%2$s You have %1$u tries left", num), + num, msg); + } else { + warning = g_strdup_printf (ngettext ("You have %u try left", + "You have %u tries left", num), + num); + } + } else if (msg) { + warning = g_strdup (msg); + } + + if (warning) + gcr_prompt_set_warning (prompt, warning); + + if (lock == MM_MODEM_LOCK_SIM_PIN) { + /* TODO + gcr_prompt_set_choice_label (prompt, _("Automatically unlock this SIM card")); + */ + } + return prompt; +} + + +static GcrPrompt * +create_confirm_prompt (GsdWwanDevice *device, const char* msg) +{ + g_autoptr(GError) error = NULL; + GcrPrompt *prompt; + g_autofree gchar *identifier = NULL; + /* MM does not support g_autoptr */ + g_autoptr(GObject) retries = NULL; + + identifier = get_sim_identifier (device); + g_return_val_if_fail (identifier, NULL); + g_debug ("Creating new confirm for SIM %s", identifier); + + /* TODO: timeout */ + prompt = GCR_PROMPT (gcr_system_prompt_open (-1, NULL, &error)); + if (!prompt) { + /* timeout expired */ + if (error->code == GCR_SYSTEM_PROMPT_IN_PROGRESS) + g_warning ("The Gcr system prompter was already in use."); + else { + g_warning ("Couldn't create prompt for SIM confirm: %s", error->message); + } + return NULL; + } + + /* Set up the dialog */ + gcr_prompt_set_title (prompt, _("SIM card unlock error")); + gcr_prompt_set_continue_label (prompt, _("OK")); + gcr_prompt_set_message (prompt, _("SIM card unlock error")); + gcr_prompt_set_description (prompt, msg); + return prompt; +} + + +static void +sim_send_pin_ready_cb (MMSim *sim, GAsyncResult *res, GsdWwanDevice *device) +{ + g_autoptr(GError) error = NULL; + const gchar *msg = NULL; + MMModem *modem = gsd_wwan_device_get_mm_modem (device); + + if (!mm_sim_send_pin_finish (sim, res, &error)) { + if (g_error_matches (error, + MM_MOBILE_EQUIPMENT_ERROR, + MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK)) { + g_warning ("Next entry will require the PUK."); + /* TODO: handle PUK as well */ + gsd_wwan_pinentry_unlock_sim_error (device,_("Too many incorrect PINs.")); + return; + } else { /* Report error and re-try PIN request */ + if (g_error_matches (error, + MM_MOBILE_EQUIPMENT_ERROR, + MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD)) { + msg = _("Wrong PIN code"); + } else { + g_dbus_error_strip_remote_error (error); + msg = error->message; + g_warning ("Got error '%s'", msg); + } + } + + g_warning ("Failed to send PIN to devid: '%s' simid: '%s' : %s", + mm_modem_get_device_identifier (modem), + mm_sim_get_identifier (sim), + error->message); + + gsd_wwan_pinentry_unlock_sim (device, msg); + return; + } else { + g_debug ("Succesfully unlocked %s", mm_sim_get_identifier (sim)); + } +} + + +static void +send_code_to_sim (GsdWwanDevice *device, const gchar *code, const gchar *new_pin) +{ + MMModem *modem = gsd_wwan_device_get_mm_modem (device); + MMSim *sim = gsd_wwan_device_get_mm_sim (device); + MMModemLock lock = mm_modem_get_unlock_required (modem); + + g_return_if_fail (code); + /* Send the code to ModemManager */ + if (lock == MM_MODEM_LOCK_SIM_PIN) { + mm_sim_send_pin (sim, code, + NULL, /* cancellable */ + (GAsyncReadyCallback)sim_send_pin_ready_cb, + device); + } else if (lock == MM_MODEM_LOCK_SIM_PUK) { + g_return_if_fail (new_pin); + g_assert_not_reached (); +#if 0 + mm_sim_send_puk (info->mm_sim, + code, /* puk */ + new_pin, /* new pin */ + NULL, /* cancellable */ + (GAsyncReadyCallback)sim_send_puk_ready_cb, + info); +#endif + } else { + /* We should never get a prompt for unsupported types */ + g_error ("Unhandled lock type %u", lock); + } +} + + +void +gsd_wwan_pinentry_unlock_sim (GsdWwanDevice *device, const char *error_msg) +{ + g_autoptr(GError) err = NULL; + const char *code; + GcrPrompt *prompt; + + prompt = create_prompt (device, error_msg); + g_return_if_fail (prompt); + code = gcr_prompt_password_run (prompt, NULL, &err); + + /* Close gcr_prompt as late as possible so the user has a + chance to see the spinner */ + if (err) { + g_warning ("Could not get PIN/PUK %s", err->message); + g_dbus_error_strip_remote_error (err); + + gsd_wwan_pinentry_unlock_sim_error (device, err->message); + } else if (code == NULL) { + g_debug ("Operation was cancelled"); + } else { + g_debug("Got PIN/PUK"); + send_code_to_sim (device, code, NULL); + } + gcr_prompt_close (prompt); + g_object_unref (prompt); +}; + + +void +gsd_wwan_pinentry_unlock_sim_error (GsdWwanDevice *device, const char *error_msg) +{ + g_autoptr(GError) err = NULL; + GcrPrompt *prompt; + + prompt = create_confirm_prompt (device, error_msg); + g_return_if_fail (prompt); + gcr_prompt_confirm_run (prompt, NULL, &err); + if (err) { + g_warning ("Error in confirm dialog %s", err->message); + } + gcr_prompt_close (prompt); + g_object_unref (prompt); +} diff --git a/plugins/wwan/gsd-wwan-pinentry.h b/plugins/wwan/gsd-wwan-pinentry.h new file mode 100644 index 00000000..a5a6c2c2 --- /dev/null +++ b/plugins/wwan/gsd-wwan-pinentry.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2019 Purism SPC + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Guido Günther <agx@sigxcpu.org> + * + */ + +#pragma once + +#include "gsd-wwan-device.h" +#include "gsd-wwan-manager.h" + +void gsd_wwan_pinentry_unlock_sim (GsdWwanDevice *device, const char *error_msg); +void gsd_wwan_pinentry_unlock_sim_error (GsdWwanDevice *device, const char *error_msg); diff --git a/plugins/wwan/main.c b/plugins/wwan/main.c new file mode 100644 index 00000000..c6adebda --- /dev/null +++ b/plugins/wwan/main.c @@ -0,0 +1,7 @@ +#define NEW gsd_wwan_manager_new +#define START gsd_wwan_manager_start +#define STOP gsd_wwan_manager_stop +#define MANAGER GsdWwanManager +#include "gsd-wwan-manager.h" + +#include "daemon-skeleton.h" diff --git a/plugins/wwan/meson.build b/plugins/wwan/meson.build new file mode 100644 index 00000000..81869b9f --- /dev/null +++ b/plugins/wwan/meson.build @@ -0,0 +1,22 @@ +sources = files( + 'gsd-wwan-device.c', + 'gsd-wwan-manager.c', + 'gsd-wwan-pinentry.c', + 'main.c' +) + +deps = plugins_deps + [gio_dep, gcr_base_dep, mm_glib_dep] + +cflags += ['-DGNOMECC_DATA_DIR="@0@"'.format(gsd_pkgdatadir)] + +executable( + 'gsd-' + plugin_name, + sources, + include_directories: [top_inc, common_inc], + dependencies: deps, + c_args: cflags, + install: true, + install_rpath: gsd_pkglibdir, + install_dir: gsd_libexecdir +) + diff --git a/plugins/wwan/org.gnome.SettingsDaemon.Wwan.desktop.in b/plugins/wwan/org.gnome.SettingsDaemon.Wwan.desktop.in new file mode 100644 index 00000000..33eb438c --- /dev/null +++ b/plugins/wwan/org.gnome.SettingsDaemon.Wwan.desktop.in @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=GNOME Settings Daemon's Wwan plugin +Exec=@libexecdir@/gsd-wwan +OnlyShowIn=GNOME; +NoDisplay=true +X-GNOME-Autostart-Phase=Initialization +X-GNOME-Autostart-Notify=true +X-GNOME-AutoRestart=true |