[PATCH 5/6] nm-applet: monitor mounts/unmounts, ask for auto-mount



---
 src/applet.c                                 |  441 +++++++++++++++++++
 src/applet.h                                 |    6 +


diff --git a/src/applet.c b/src/applet.c
index e6a3e2e..b56516f 100644
--- a/src/applet.c
+++ b/src/applet.c
@@ -52,6 +52,7 @@
 #include <nm-device-wimax.h>
 #include <nm-utils.h>
 #include <nm-connection.h>
+#include <nm-remote-connection.h>
 #include <nm-vpn-connection.h>
 #include <nm-setting-connection.h>
 #include <nm-setting-wired.h>
@@ -61,6 +62,7 @@
 #include <nm-setting-cdma.h>
 #include <nm-setting-bluetooth.h>
 #include <nm-setting-vpn.h>
+#include <nm-setting-resources.h>
 #include <nm-active-connection.h>
 #include <nm-secret-agent.h>

@@ -3447,6 +3449,13 @@ static void finalize (GObject *object)
 {
     NMApplet *applet = NM_APPLET (object);

+    // Disconnect & free volume monitoring
+    g_signal_handler_disconnect(applet->volume_monitor,
+            applet->mount_removed_handler_id);
+    g_signal_handler_disconnect(applet->volume_monitor,
+            applet->mount_added_handler_id);
+    g_object_unref(applet->volume_monitor);
+
     g_slice_free (NMADeviceClass, applet->wired_class);
     g_slice_free (NMADeviceClass, applet->wifi_class);
     g_slice_free (NMADeviceClass, applet->gsm_class);
@@ -3512,6 +3521,424 @@ static void finalize (GObject *object)
     G_OBJECT_CLASS (nma_parent_class)->finalize (object);
 }

+/**
+ * @user_data the uri of the network drive
+ */
+static void
+commit_changes_cb (NMRemoteConnection *connection,
+                                              GError *error,
+                                              gpointer user_data)
+{
+    // Clear secrets from memory
+    nm_setting_clear_secrets ( NM_SETTING (
+ nm_connection_get_setting_wireless_security(&connection->parent)));
+
+    if (error == NULL)
+    {
+        g_message("Changed auto-mounting for %s with connection %s",
+ (char*) user_data, nm_connection_get_id(&connection->parent));
+    }
+    else
+    {
+ g_warning("Failed to change auto-mounting for %s with connection %s: %s", + (char*) user_data, nm_connection_get_id(&connection->parent),
+                error->message);
+        g_error_free(error);
+    }
+
+    g_free(user_data);
+}
+
+/**
+ * @user_data the uri of the network drive
+ */
+static void
+get_secrets_cb (NMRemoteConnection *connection,
+                GHashTable *secrets,
+                GError *error,
+                gpointer user_data)
+{
+    GHashTable *settings, *settings_secrets;
+    GHashTableIter iter;
+    gpointer key, value;
+    NMSettingWirelessSecurity *sec;
+
+    if (error)
+    {
+ g_warning("Error getting secrets for connection %s while changing auto-mount %s for %s:",
+                nm_connection_get_id(&connection->parent),
+                (char *) user_data,
+                error->message);
+        g_free(user_data);
+        return;
+    }
+
+    // Merge wireless security settings with acquired secrets
+    settings = nm_connection_to_hash (&connection->parent,
+            NM_SETTING_HASH_FLAG_ALL);
+ settings = g_hash_table_lookup(settings, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + settings_secrets = g_hash_table_lookup(secrets, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+    g_hash_table_iter_init (&iter, settings_secrets);
+    while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+        if (g_hash_table_lookup(settings, key) != NULL) continue;
+        g_hash_table_insert (settings, key, value);
+    }
+
+    // Re-add merged settings to connection
+ sec = NM_SETTING_WIRELESS_SECURITY (nm_setting_new_from_hash(NM_TYPE_SETTING_WIRELESS_SECURITY, settings));
+    nm_connection_add_setting(&connection->parent, NM_SETTING(sec));
+
+    // Commit settings
+    nm_remote_connection_commit_changes(NM_REMOTE_CONNECTION (connection),
+            commit_changes_cb, user_data);
+}
+
+static void
+autoconnect_cb (NotifyNotification *notify,
+                             gchar *id,
+                             gpointer user_data)
+{
+    NMConnection *connection = NM_CONNECTION(user_data);
+    NMSettingResources *setting =
+            nm_connection_get_setting_resources (connection);
+
+    // Assure connection has resources settings
+    if (!NM_IS_SETTING_RESOURCES(setting))
+    {
+        setting = NM_SETTING_RESOURCES (nm_setting_resources_new ());
+        nm_connection_add_setting (connection, NM_SETTING (setting));
+    }
+
+    // Add to connection's automount list
+    if (!nm_setting_resources_add_network_drive(setting, id))
+    {
+        g_error("Failed to add auto-mounting for %s with connection %s",
+                id, nm_connection_get_id(connection));
+        return;
+    }
+
+    // Secrets need to be acquired & explicitly re-added to the connection
+    // so they don't get lost
+    // The g_strdup(id) is required as id is free'd for some reason
+ // TODO: Not just wireless / only wireless if the connection is wireless
+    nm_remote_connection_get_secrets (
+            NM_REMOTE_CONNECTION (connection),
+            NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+            get_secrets_cb,
+            (gpointer) g_strdup(id));
+}
+
+static void
+mount_added_cb (GVolumeMonitor *volume_monitor,
+        GMount         *mount,
+        gpointer        user_data)
+{
+    NotifyNotification *notify;
+    GError *error = NULL;
+    char *escaped;
+    NMApplet *applet = NM_APPLET (user_data);
+    gboolean is_network_scheme = FALSE;
+    const char **allowed_schemes;
+    GSList *iter;
+    char *scheme, *uri, *tmp;
+    GFile *file;
+    GSList *active_networks = NULL;
+    const GPtrArray *active_list;
+    int i;
+
+    // Only continue if notification server allows actions,
+    // as the notification window is pointless otherwise
+    g_return_if_fail (applet != NULL);
+    g_return_if_fail (applet_notify_server_has_actions ());
+    g_return_if_fail (gtk_status_icon_is_embedded (applet->status_icon));
+
+    // Only continue with mounts that are auto-mountable
+    i = 0;
+    allowed_schemes = nm_setting_resources_get_allowed_schemes();
+    scheme = g_file_get_uri_scheme(g_mount_get_default_location(mount));
+    while ((!is_network_scheme) &&
+            (scheme != NULL) &&
+            (allowed_schemes[i] != NULL))
+    {
+        if (strcasecmp (allowed_schemes[i], scheme) == 0)
+            is_network_scheme = TRUE;
+        i++;
+    }
+    if (!is_network_scheme) return;
+
+    // Clean uri from trailing slash
+    file = g_mount_get_default_location(mount);
+    uri = g_file_get_uri(file);
+    if (g_str_has_suffix(uri, "/"))
+    {
+        tmp = g_strndup(uri, strlen(uri) - 1);
+        g_free(uri);
+        uri = tmp;
+    }
+    g_object_unref(file);
+
+    // Create list of active network connections to choose from in
+    // notification popup
+    // TODO: Check if this works with active VPN connections
+    active_list = nm_client_get_active_connections (applet->nm_client);
+    for (i = 0; active_list && (i < active_list->len); i++) {
+        NMActiveConnection *active =
+                NM_ACTIVE_CONNECTION (g_ptr_array_index (active_list, i));
+        NMConnection *connection =
+                applet_get_connection_for_active (applet, active);
+        NMSettingResources *settings =
+                nm_connection_get_setting_resources (connection);
+
+        // Only check supported connection types (e.g. configuring without
+        // messing secrets; supported in connection editor etc.)
+ if (!nm_connection_is_type (connection, NM_SETTING_WIRELESS_SETTING_NAME))
+            continue;
+
+ // Filter out if this mount is already configured for this connection
+        if (NM_IS_SETTING_RESOURCES (settings))
+        {
+            if (nm_setting_resources_has_network_drive (settings, uri))
+            {
+ g_debug("%s is already known on current active network %s; "
+                        "not asking for automount",
+                        uri, nm_connection_get_id (connection));
+                continue;
+            }
+        }
+        else
+        {
+ g_debug("Connection %s does not have any resources (for auto-mounting) configured",
+                    nm_connection_get_id (connection));
+        }
+
+ // TODO IF VPN IS SUPPORTED: Filter out if this mount is already on *any* connection
+
+        active_networks = g_slist_append (active_networks, connection);
+    }
+
+    if (!active_networks)
+    {
+        g_free(uri);
+        return;
+    }
+
+    // Display notification window & ask whether to always mount this share
+ // Following based on applet_do_notify, but enhanced for more than one action
+    applet_clear_notify (applet);
+    tmp = g_mount_get_name(mount);
+    if (g_slist_length(active_networks) == 1)
+        escaped = utils_escape_notify_message (g_strdup_printf(
+ _("Do you want to mount\n%s\nautomatically when connected to\n%s?"),
+                tmp,
+ nm_connection_get_id((NMConnection *) g_slist_nth_data(active_networks, 0))));
+    else
+        escaped = utils_escape_notify_message (g_strdup_printf(
+                _("Do you want to mount\n%s\nautomatically?"),
+                tmp));
+    notify = notify_notification_new (_("Mount automatically?"),
+                                      escaped,
+                                      "nm-device-wired"
+#if HAVE_LIBNOTIFY_07
+                                      );
+#else
+                                      , NULL);
+#endif
+    g_free (tmp);
+    g_free (escaped);
+    applet->notification = notify;
+
+#if HAVE_LIBNOTIFY_07
+ notify_notification_set_hint (notify, "transient", g_variant_new_boolean (TRUE));
+#else
+ notify_notification_attach_to_status_icon (notify, applet->status_icon);
+#endif
+    notify_notification_set_urgency (notify, NOTIFY_URGENCY_LOW);
+    notify_notification_set_timeout (notify, NOTIFY_EXPIRES_DEFAULT);
+
+    // Add active networks to choose from
+    for (iter = active_networks; iter; iter = g_slist_next (iter)) {
+        NMConnection *connection = NM_CONNECTION(iter->data);
+
+        notify_notification_add_action (
+                notify,
+                uri,
+                (g_slist_length(active_networks) == 1) ?
+                        _("Mount automatically") :
+ g_strdup_printf (_("For %s"), nm_connection_get_id(connection)),
+                autoconnect_cb,
+                connection,
+                NULL);
+    }
+    g_slist_free(active_networks);
+
+    if (!notify_notification_show (notify, &error)) {
+        g_warning ("Failed to show notification: %s",
+                   error && error->message ? error->message : "(unknown)");
+        g_clear_error (&error);
+        g_free(uri);
+    }
+}
+
+
+static void
+remove_autoconnect_cb (NotifyNotification *notify,
+                             gchar *id,
+                             gpointer user_data)
+{
+    NMConnection *connection = NM_CONNECTION(user_data);
+    NMSettingResources *setting =
+            nm_connection_get_setting_resources (connection);
+
+    // Assure connection has resources settings
+    if (!NM_IS_SETTING_RESOURCES(setting))
+    {
+        setting = NM_SETTING_RESOURCES (nm_setting_resources_new ());
+        nm_connection_add_setting (connection, NM_SETTING (setting));
+    }
+
+    // Remove from connection's automount list
+    nm_setting_resources_remove_network_drive_by_uri(setting, id);
+
+    // Secrets need to be acquired & explicitly re-added to the connection
+    // so they don't get lost
+ // TODO: Not just wireless / only wireless if the connection is wireless
+    nm_remote_connection_get_secrets (NM_REMOTE_CONNECTION (connection),
+            NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+            get_secrets_cb,
+            (gpointer) g_strdup(id));
+}
+
+static void
+mount_removed_cb (GVolumeMonitor *volume_monitor,
+        GMount         *mount,
+        gpointer        user_data)
+{
+    NotifyNotification *notify;
+    GError *error = NULL;
+    char *escaped;
+    NMApplet *applet = NM_APPLET (user_data);
+    GSList *iter;
+    char *uri, *tmp;
+    GFile *file;
+    GSList *active_networks = NULL;
+    const GPtrArray *active_list;
+    int i;
+
+    // Only continue if notification server allows actions,
+    // as the notification window is pointless otherwise
+    g_return_if_fail (applet != NULL);
+    g_return_if_fail (applet_notify_server_has_actions ());
+    g_return_if_fail (gtk_status_icon_is_embedded (applet->status_icon));
+
+    // Clean uri
+    file = g_mount_get_default_location(mount);
+    uri = g_file_get_uri(file);
+    if (g_str_has_suffix(uri, "/"))
+    {
+        tmp = g_strndup(uri, strlen(uri) - 1);
+        g_free(uri);
+        uri = tmp;
+    }
+    g_object_unref(file);
+
+    // Only continue if unmount was caused by disconnection
+    if (applet->disconnecting)
+    {
+ g_debug("Network is disconnecting; not reacting to unmount of %s", uri);
+        g_free(uri);
+        return;
+    }
+
+    // Create list of active network connections that have this connection
+    // in their auto-mount lists
+    // TODO: Check if this works with active VPN connections
+    active_list = nm_client_get_active_connections (applet->nm_client);
+    for (i = 0; active_list && (i < active_list->len); i++) {
+        NMActiveConnection *active =
+                NM_ACTIVE_CONNECTION (g_ptr_array_index (active_list, i));
+        NMConnection *connection =
+                applet_get_connection_for_active (applet, active);
+        NMSettingResources *settings =
+                nm_connection_get_setting_resources (connection);
+
+        // Only check supported connection types (e.g. configuring without
+        // messing secrets; supported in connection editor etc.)
+ if (!nm_connection_is_type (connection, NM_SETTING_WIRELESS_SETTING_NAME))
+            continue;
+
+ // Filter out if this mount is already configured for this connection
+        if (NM_IS_SETTING_RESOURCES (settings))
+        {
+            if (nm_setting_resources_has_network_drive (settings, uri))
+            {
+ active_networks = g_slist_append (active_networks, connection);
+            }
+        }
+    }
+
+    if (!active_networks)
+    {
+        g_free(uri);
+        return;
+    }
+
+    // Display notification window & ask whether to always mount this share
+ // Following copy & paste from applet_do_notify, enhanced for more actions
+    applet_clear_notify (applet);
+    tmp = g_mount_get_name(mount);
+    if (g_slist_length(active_networks) == 1)
+        escaped = utils_escape_notify_message (g_strdup_printf(
+ _("You just unmounted\n%s\nDo you want to remove auto-mounting for\n%s?"), + tmp, nm_connection_get_id((NMConnection *) g_slist_nth_data(active_networks, 0))));
+    else
+        escaped = utils_escape_notify_message (g_strdup_printf(
+ _("You just unmounted\n%s\nDo you want to remove auto-mounting?"),
+                tmp));
+    notify = notify_notification_new (_("Also remove auto-mounting?"),
+                                      escaped,
+                                      "nm-device-wired"
+#if HAVE_LIBNOTIFY_07
+                                      );
+#else
+                                      , NULL);
+#endif
+    g_free (tmp);
+    g_free (escaped);
+    applet->notification = notify;
+
+#if HAVE_LIBNOTIFY_07
+ notify_notification_set_hint (notify, "transient", g_variant_new_boolean (TRUE));
+#else
+ notify_notification_attach_to_status_icon (notify, applet->status_icon);
+#endif
+    notify_notification_set_urgency (notify, NOTIFY_URGENCY_LOW);
+    notify_notification_set_timeout (notify, NOTIFY_EXPIRES_DEFAULT);
+
+    // Add active networks to choose from
+    for (iter = active_networks; iter; iter = g_slist_next (iter)) {
+        NMConnection *connection = NM_CONNECTION(iter->data);
+
+        notify_notification_add_action (
+                notify,
+                uri,
+                (g_slist_length(active_networks) == 1) ?
+                        _("Remove auto-mount") :
+ g_strdup_printf (_("For %s"), nm_connection_get_id(connection)),
+                remove_autoconnect_cb,
+                connection,
+                NULL);
+    }
+    g_slist_free(active_networks);
+
+    if (!notify_notification_show (notify, &error)) {
+        g_warning ("Failed to show notification: %s",
+                   error && error->message ? error->message : "(unknown)");
+        g_clear_error (&error);
+        g_free(uri);
+    }
+}
+
 static void nma_init (NMApplet *applet)
 {
     applet->animation_id = 0;
@@ -3519,6 +3946,20 @@ static void nma_init (NMApplet *applet)
     applet->icon_theme = NULL;
     applet->notification = NULL;
     applet->icon_size = 16;
+
+    // Connect volume monitoring
+    applet->volume_monitor = g_volume_monitor_get();
+    applet->mount_added_handler_id = g_signal_connect_after (
+            applet->volume_monitor,
+            "mount-added",
+            G_CALLBACK (mount_added_cb),
+            applet);
+    applet->mount_removed_handler_id = g_signal_connect_after (
+            applet->volume_monitor,
+            "mount-removed",
+            G_CALLBACK (mount_removed_cb),
+            applet);
+    applet->disconnecting = FALSE;
 }

 enum {
diff --git a/src/applet.h b/src/applet.h
index ab21ece..e39a643 100644
--- a/src/applet.h
+++ b/src/applet.h
@@ -175,6 +175,12 @@ typedef struct

     /* Tracker objects for secrets requests */
     GSList *        secrets_reqs;
+
+    // Mount-watching for auto-mount configuration dialogs
+    GVolumeMonitor* volume_monitor;
+    gulong            mount_added_handler_id;
+    gulong            mount_removed_handler_id;
+    gboolean        disconnecting;
 } NMApplet;

 typedef void (*AppletNewAutoConnectionCallback) (NMConnection *connection,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]