summaryrefslogtreecommitdiffstats
path: root/panels/sharing
diff options
context:
space:
mode:
Diffstat (limited to 'panels/sharing')
-rw-r--r--panels/sharing/cc-gnome-remote-desktop.c124
-rw-r--r--panels/sharing/cc-gnome-remote-desktop.h36
-rw-r--r--panels/sharing/cc-media-sharing.c146
-rw-r--r--panels/sharing/cc-media-sharing.h27
-rw-r--r--panels/sharing/cc-remote-login-helper.c68
-rw-r--r--panels/sharing/cc-remote-login.c306
-rw-r--r--panels/sharing/cc-remote-login.h30
-rw-r--r--panels/sharing/cc-sharing-networks.c520
-rw-r--r--panels/sharing/cc-sharing-networks.h39
-rw-r--r--panels/sharing/cc-sharing-networks.ui31
-rw-r--r--panels/sharing/cc-sharing-panel.c1600
-rw-r--r--panels/sharing/cc-sharing-panel.h33
-rw-r--r--panels/sharing/cc-sharing-panel.ui537
-rw-r--r--panels/sharing/cc-systemd-service.c236
-rw-r--r--panels/sharing/cc-systemd-service.h33
-rw-r--r--panels/sharing/cc-tls-certificate.c511
-rw-r--r--panels/sharing/cc-tls-certificate.h63
-rw-r--r--panels/sharing/file-share-properties.c55
-rw-r--r--panels/sharing/file-share-properties.h22
-rw-r--r--panels/sharing/gnome-sharing-panel.desktop.in.in15
-rw-r--r--panels/sharing/gsd-sharing-enums.h31
-rw-r--r--panels/sharing/icons/meson.build4
-rw-r--r--panels/sharing/icons/scalable/org.gnome.Settings-sharing-symbolic.svg9
-rw-r--r--panels/sharing/meson.build114
-rw-r--r--panels/sharing/org.gnome.SettingsDaemon.Sharing.xml19
-rw-r--r--panels/sharing/org.gnome.controlcenter.remote-login-helper.policy.in.in22
-rw-r--r--panels/sharing/sharing.css4
-rw-r--r--panels/sharing/sharing.gresource.xml8
28 files changed, 4643 insertions, 0 deletions
diff --git a/panels/sharing/cc-gnome-remote-desktop.c b/panels/sharing/cc-gnome-remote-desktop.c
new file mode 100644
index 0000000..ef7bc53
--- /dev/null
+++ b/panels/sharing/cc-gnome-remote-desktop.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "config.h"
+
+#include "cc-gnome-remote-desktop.h"
+
+const SecretSchema *
+cc_grd_rdp_credentials_get_schema (void)
+{
+ static const SecretSchema grd_rdp_credentials_schema = {
+ .name = "org.gnome.RemoteDesktop.RdpCredentials",
+ .flags = SECRET_SCHEMA_NONE,
+ .attributes = {
+ { "credentials", SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { "NULL", 0 },
+ },
+ };
+
+ return &grd_rdp_credentials_schema;
+}
+
+void
+cc_grd_store_rdp_credentials (const gchar *username,
+ const gchar *password,
+ GCancellable *cancellable)
+{
+ GVariantBuilder builder;
+ char *credentials;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&builder, "{sv}", "username", g_variant_new_string (username));
+ g_variant_builder_add (&builder, "{sv}", "password", g_variant_new_string (password));
+ credentials = g_variant_print (g_variant_builder_end (&builder), TRUE);
+
+ secret_password_store_sync (CC_GRD_RDP_CREDENTIALS_SCHEMA,
+ SECRET_COLLECTION_DEFAULT,
+ "GNOME Remote Desktop RDP credentials",
+ credentials,
+ NULL, NULL,
+ NULL);
+
+ g_free (credentials);
+}
+
+gchar *
+cc_grd_lookup_rdp_username (GCancellable *cancellable)
+{
+ g_autoptr(GError) error = NULL;
+ gchar *username = NULL;
+ g_autofree gchar *secret;
+ GVariant *variant = NULL;
+
+ secret = secret_password_lookup_sync (CC_GRD_RDP_CREDENTIALS_SCHEMA,
+ cancellable, &error,
+ NULL);
+ if (error) {
+ g_warning ("Failed to get username: %s", error->message);
+ return NULL;
+ }
+
+ if (secret == NULL) {
+ g_debug ("No RDP credentials available");
+ return NULL;
+ }
+
+ variant = g_variant_parse (NULL, secret, NULL, NULL, &error);
+ if (variant == NULL) {
+ g_warning ("Invalid credentials format in the keyring: %s", error->message);
+ return NULL;
+ }
+
+ g_variant_lookup (variant, "username", "&s", &username);
+
+ return username;
+}
+
+gchar *
+cc_grd_lookup_rdp_password (GCancellable *cancellable)
+{
+ g_autoptr(GError) error = NULL;
+ g_autofree gchar *secret;
+ gchar *password = NULL;
+ GVariant *variant = NULL;
+
+ secret = secret_password_lookup_sync (CC_GRD_RDP_CREDENTIALS_SCHEMA,
+ cancellable, &error,
+ NULL);
+ if (error) {
+ g_warning ("Failed to get password: %s", error->message);
+ return NULL;
+ }
+
+ if (secret == NULL) {
+ g_debug ("No RDP credentials available");
+ return NULL;
+ }
+
+ variant = g_variant_parse (NULL, secret, NULL, NULL, &error);
+ if (variant == NULL) {
+ g_warning ("Invalid credentials format in the keyring: %s", error->message);
+ return NULL;
+ }
+
+ g_variant_lookup (variant, "password", "&s", &password);
+
+ return password;
+}
diff --git a/panels/sharing/cc-gnome-remote-desktop.h b/panels/sharing/cc-gnome-remote-desktop.h
new file mode 100644
index 0000000..592da34
--- /dev/null
+++ b/panels/sharing/cc-gnome-remote-desktop.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#pragma once
+
+#include <libsecret/secret.h>
+
+G_BEGIN_DECLS
+
+const SecretSchema * cc_grd_rdp_credentials_get_schema (void);
+#define CC_GRD_RDP_CREDENTIALS_SCHEMA cc_grd_rdp_credentials_get_schema ()
+
+void cc_grd_store_rdp_credentials (const gchar *username,
+ const gchar *password,
+ GCancellable *cancellable);
+
+gchar * cc_grd_lookup_rdp_username (GCancellable *cancellable);
+gchar * cc_grd_lookup_rdp_password (GCancellable *cancellable);
+
+G_END_DECLS
diff --git a/panels/sharing/cc-media-sharing.c b/panels/sharing/cc-media-sharing.c
new file mode 100644
index 0000000..d0f12aa
--- /dev/null
+++ b/panels/sharing/cc-media-sharing.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2013 Intel, Inc
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Thomas Wood <thomas.wood@intel.com>
+ *
+ */
+
+#include "cc-media-sharing.h"
+
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+#include <glib/gstdio.h>
+
+static GKeyFile*
+cc_media_sharing_open_key_file (void)
+{
+ g_autofree gchar *path = NULL;
+ GKeyFile *file;
+
+ file = g_key_file_new ();
+
+ path = g_build_filename (g_get_user_config_dir (), "rygel.conf", NULL);
+
+ if (!g_key_file_load_from_file (file, path,
+ G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
+ NULL))
+ {
+ g_autofree gchar *sysconf_path = NULL;
+ sysconf_path = g_build_filename (SYSCONFDIR, "rygel.conf", NULL);
+ g_key_file_load_from_file (file, sysconf_path,
+ G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
+ NULL);
+ }
+
+ return file;
+}
+
+void
+cc_media_sharing_get_preferences (gchar ***folders)
+{
+ g_autoptr(GKeyFile) file = NULL;
+
+ file = cc_media_sharing_open_key_file ();
+
+ if (folders)
+ {
+ gsize length;
+ GPtrArray *array;
+ GStrv str_list;
+ g_auto(GStrv) orig_list = NULL;
+
+ str_list = g_key_file_get_string_list (file, "MediaExport", "uris",
+ &length, NULL);
+ orig_list = str_list;
+ array = g_ptr_array_new ();
+
+ while (str_list && *str_list)
+ {
+ const char *dir;
+
+ if (g_str_equal (*str_list, "@MUSIC@"))
+ dir = g_get_user_special_dir (G_USER_DIRECTORY_MUSIC);
+ else if (g_str_equal (*str_list, "@VIDEOS@"))
+ dir = g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS);
+ else if (g_str_equal (*str_list, "@PICTURES@"))
+ dir = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
+ else
+ dir = g_strdup (*str_list);
+
+ if (dir != NULL)
+ g_ptr_array_add (array, g_strdup (dir));
+
+ str_list++;
+ }
+
+ g_ptr_array_add (array, NULL);
+
+ *folders = (char **) g_ptr_array_free (array, FALSE);
+ }
+}
+
+void
+cc_media_sharing_set_preferences (gchar **folders)
+{
+ g_autoptr(GKeyFile) file = NULL;
+ gchar **str_list;
+ g_autofree gchar *path = NULL;
+ gsize length;
+ g_autofree gchar *data = NULL;
+
+ file = cc_media_sharing_open_key_file ();
+
+ g_key_file_set_boolean (file, "general", "upnp-enabled", TRUE);
+ g_key_file_set_boolean (file, "Tracker", "enabled", FALSE);
+ g_key_file_set_boolean (file, "Tracker3", "enabled", FALSE);
+ g_key_file_set_boolean (file, "MediaExport", "enabled", TRUE);
+
+ str_list = folders;
+ length = 0;
+
+ while (str_list && *str_list)
+ {
+ if (g_strcmp0 (*str_list, g_get_user_special_dir (G_USER_DIRECTORY_MUSIC)) == 0)
+ {
+ g_free (*str_list);
+ *str_list = g_strdup ("@MUSIC@");
+ }
+
+ if (g_strcmp0 (*str_list, g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS)) == 0)
+ {
+ g_free (*str_list);
+ *str_list = g_strdup ("@VIDEOS@");
+ }
+
+ if (g_strcmp0 (*str_list, g_get_user_special_dir (G_USER_DIRECTORY_PICTURES)) == 0)
+ {
+ g_free (*str_list);
+ *str_list = g_strdup ("@PICTURES@");
+ }
+
+ str_list++;
+ length++;
+ }
+
+ g_key_file_set_string_list (file, "MediaExport", "uris", (const gchar**) folders, length);
+
+ data = g_key_file_to_data (file, NULL, NULL);
+
+ path = g_build_filename (g_get_user_config_dir (), "rygel.conf", NULL);
+
+ g_file_set_contents (path, data, -1, NULL);
+}
diff --git a/panels/sharing/cc-media-sharing.h b/panels/sharing/cc-media-sharing.h
new file mode 100644
index 0000000..7923db0
--- /dev/null
+++ b/panels/sharing/cc-media-sharing.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 Intel, Inc
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Thomas Wood <thomas.wood@intel.com>
+ *
+ */
+
+#pragma once
+
+#include <glib.h>
+
+void cc_media_sharing_get_preferences (gchar ***folders);
+void cc_media_sharing_set_preferences (gchar **folders);
diff --git a/panels/sharing/cc-remote-login-helper.c b/panels/sharing/cc-remote-login-helper.c
new file mode 100644
index 0000000..26e527f
--- /dev/null
+++ b/panels/sharing/cc-remote-login-helper.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 Intel, Inc
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Thomas Wood <thomas.wood@intel.com>
+ *
+ */
+
+#include "cc-systemd-service.h"
+
+#ifndef SSHD_SERVICE
+#define SSHD_SERVICE "sshd.service"
+#endif
+
+int
+main (int argc,
+ char **argv)
+{
+ if (argc < 2)
+ return 1;
+
+ if (argv[1] == NULL)
+ return 1;
+
+ if (g_str_equal (argv[1], "enable"))
+ {
+ g_autoptr(GError) error = NULL;
+
+ if (!cc_enable_service (SSHD_SERVICE, G_BUS_TYPE_SYSTEM, &error))
+ {
+ g_critical ("Failed to enable %s: %s", SSHD_SERVICE, error->message);
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ return EXIT_SUCCESS;
+ }
+ }
+ else if (g_str_equal (argv[1], "disable"))
+ {
+ g_autoptr(GError) error = NULL;
+
+ if (!cc_disable_service (SSHD_SERVICE, G_BUS_TYPE_SYSTEM, &error))
+ {
+ g_critical ("Failed to enable %s: %s", SSHD_SERVICE, error->message);
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ return EXIT_SUCCESS;
+ }
+ }
+
+ return 1;
+}
diff --git a/panels/sharing/cc-remote-login.c b/panels/sharing/cc-remote-login.c
new file mode 100644
index 0000000..d185f7d
--- /dev/null
+++ b/panels/sharing/cc-remote-login.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2013 Intel, Inc
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Thomas Wood <thomas.wood@intel.com>
+ *
+ */
+#include "cc-remote-login.h"
+#include <gio/gio.h>
+
+#ifndef SSHD_SERVICE
+#define SSHD_SERVICE "sshd.service"
+#endif
+
+typedef struct
+{
+ GtkSwitch *gtkswitch;
+ GtkWidget *button;
+ GCancellable *cancellable;
+} CallbackData;
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (CallbackData, g_free)
+
+static void
+set_switch_state (GtkSwitch *gtkswitch,
+ gboolean active)
+{
+ if (gtk_switch_get_active (gtkswitch) != active)
+ {
+ g_object_set_data (G_OBJECT (gtkswitch), "set-from-dbus",
+ GINT_TO_POINTER (1));
+ gtk_switch_set_active (gtkswitch, active);
+ }
+ gtk_widget_set_sensitive (GTK_WIDGET (gtkswitch), TRUE);
+}
+
+static void
+active_state_ready_callback (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(CallbackData) callback_data = user_data;
+ g_autoptr(GVariant) active_variant = NULL;
+ g_autoptr(GVariant) child_variant = NULL;
+ g_autoptr(GVariant) tmp_variant = NULL;
+ const gchar *active_state;
+ gboolean active;
+ g_autoptr(GError) error = NULL;
+
+ active_variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+ result, &error);
+
+ if (!active_variant)
+ {
+ /* print a warning if there was an error but the operation was not
+ * cancelled */
+ if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Error getting remote login state: %s", error->message);
+
+ /* the switch will be remain insensitive, since the current state could
+ * not be determined */
+ return;
+ }
+
+ child_variant = g_variant_get_child_value (active_variant, 0);
+ tmp_variant = g_variant_get_variant (child_variant);
+ active_state = g_variant_get_string (tmp_variant, NULL);
+
+ active = g_str_equal (active_state, "active");
+
+ /* set the switch to the correct state */
+ if (callback_data->gtkswitch)
+ set_switch_state (callback_data->gtkswitch, active);
+}
+
+static void
+path_ready_callback (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(CallbackData) callback_data = user_data;
+ g_autoptr(GVariant) path_variant = NULL;
+ g_autoptr(GVariant) child_variant = NULL;
+ const gchar *object_path;
+ g_autoptr(GError) error = NULL;
+
+ path_variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+ result, &error);
+
+ if (!path_variant)
+ {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ /* this may fail if systemd or remote login service is not available */
+ g_debug ("Error getting remote login state: %s", error->message);
+
+ /* hide the remote login button, since the service is not available */
+ if (callback_data->button)
+ gtk_widget_hide (callback_data->button);
+
+ return;
+ }
+
+ child_variant = g_variant_get_child_value (path_variant, 0);
+ object_path = g_variant_get_string (child_variant, NULL);
+
+ g_dbus_connection_call (G_DBUS_CONNECTION (source_object),
+ "org.freedesktop.systemd1",
+ object_path,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)",
+ "org.freedesktop.systemd1.Unit",
+ "ActiveState"),
+ (GVariantType*) "(v)",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ callback_data->cancellable,
+ active_state_ready_callback,
+ callback_data);
+ g_steal_pointer (&callback_data);
+}
+
+static void
+state_ready_callback (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(CallbackData) callback_data = user_data;
+ g_autoptr(GVariant) state_variant = NULL;
+ g_autoptr(GVariant) child_variant = NULL;
+ const gchar *state_string;
+ g_autoptr(GError) error = NULL;
+
+ state_variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+ result, &error);
+ if (!state_variant)
+ {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ /* this may fail if systemd or remote login service is not available */
+ g_debug ("Error getting remote login state: %s", error->message);
+
+ /* hide the remote login button, since the service is not available */
+ if (callback_data->button)
+ gtk_widget_hide (callback_data->button);
+
+ return;
+ }
+
+ child_variant = g_variant_get_child_value (state_variant, 0);
+ state_string = g_variant_get_string (child_variant, NULL);
+
+ if (g_str_equal (state_string, "enabled"))
+ {
+ /* service is enabled, so check whether it is running or not */
+ g_dbus_connection_call (G_DBUS_CONNECTION (source_object),
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnit",
+ g_variant_new ("(s)", SSHD_SERVICE),
+ (GVariantType*) "(o)",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ callback_data->cancellable,
+ path_ready_callback,
+ callback_data);
+ g_steal_pointer (&callback_data);
+ }
+ else if (g_str_equal (state_string, "disabled"))
+ {
+ /* service is available, but is currently disabled */
+ set_switch_state (callback_data->gtkswitch, FALSE);
+ }
+ else
+ {
+ /* unknown state */
+ g_warning ("Unknown state %s for %s", state_string, SSHD_SERVICE);
+ }
+}
+
+static void
+bus_ready_callback (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(CallbackData) callback_data = user_data;
+ g_autoptr(GDBusConnection) connection = NULL;
+ g_autoptr(GError) error = NULL;
+
+ connection = g_bus_get_finish (result, &error);
+
+ if (!connection)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Error getting remote login state: %s", error->message);
+
+ return;
+ }
+
+ g_dbus_connection_call (connection,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnitFileState",
+ g_variant_new ("(s)", SSHD_SERVICE),
+ (GVariantType*) "(s)",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ callback_data->cancellable,
+ state_ready_callback,
+ callback_data);
+ g_steal_pointer (&callback_data);
+}
+
+void
+cc_remote_login_get_enabled (GCancellable *cancellable,
+ GtkSwitch *gtkswitch,
+ GtkWidget *button)
+{
+ CallbackData *callback_data;
+
+ /* disable the switch until the current state is known */
+ gtk_widget_set_sensitive (GTK_WIDGET (gtkswitch), FALSE);
+
+ callback_data = g_new (CallbackData, 1);
+ callback_data->gtkswitch = gtkswitch;
+ callback_data->button = button;
+ callback_data->cancellable = cancellable;
+
+ g_bus_get (G_BUS_TYPE_SYSTEM, callback_data->cancellable,
+ bus_ready_callback, callback_data);
+}
+
+static gint std_err;
+
+static void
+child_watch_func (GPid pid,
+ gint status,
+ gpointer user_data)
+{
+ g_autoptr(CallbackData) callback_data = user_data;
+ if (status != 0)
+ {
+ g_warning ("Error enabling or disabling remote login service");
+
+ /* make sure the switch reflects the current status */
+ cc_remote_login_get_enabled (callback_data->cancellable, callback_data->gtkswitch, NULL);
+ }
+ g_spawn_close_pid (pid);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (callback_data->gtkswitch), TRUE);
+}
+
+void
+cc_remote_login_set_enabled (GCancellable *cancellable,
+ GtkSwitch *gtkswitch)
+{
+ gchar *command[] = { "pkexec", LIBEXECDIR "/cc-remote-login-helper", NULL,
+ NULL };
+ g_autoptr(GError) error = NULL;
+ GPid pid;
+ CallbackData *callback_data;
+
+ if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (gtkswitch), "set-from-dbus")) == 1)
+ {
+ g_object_set_data (G_OBJECT (gtkswitch), "set-from-dbus", NULL);
+ return;
+ }
+
+ if (gtk_switch_get_active (gtkswitch))
+ command[2] = "enable";
+ else
+ command[2] = "disable";
+
+ gtk_widget_set_sensitive (GTK_WIDGET (gtkswitch), FALSE);
+
+ g_spawn_async_with_pipes (NULL, command, NULL,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL,
+ NULL, &pid, NULL, NULL, &std_err, &error);
+
+ callback_data = g_new0 (CallbackData, 1);
+ callback_data->gtkswitch = gtkswitch;
+ callback_data->cancellable = cancellable;
+
+ g_child_watch_add (pid, child_watch_func, callback_data);
+
+ if (error)
+ g_error ("Error running cc-remote-login-helper: %s", error->message);
+}
diff --git a/panels/sharing/cc-remote-login.h b/panels/sharing/cc-remote-login.h
new file mode 100644
index 0000000..c23ddcf
--- /dev/null
+++ b/panels/sharing/cc-remote-login.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 Intel, Inc
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Thomas Wood <thomas.wood@intel.com>
+ *
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+void cc_remote_login_get_enabled (GCancellable *cancellable,
+ GtkSwitch *gtkswitch,
+ GtkWidget *button);
+void cc_remote_login_set_enabled (GCancellable *cancellable,
+ GtkSwitch *gtkswitch);
diff --git a/panels/sharing/cc-sharing-networks.c b/panels/sharing/cc-sharing-networks.c
new file mode 100644
index 0000000..0ebbbfe
--- /dev/null
+++ b/panels/sharing/cc-sharing-networks.c
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2014 Bastien Nocera <hadess@hadess.net>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "config.h"
+
+#include <adwaita.h>
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+
+#include "cc-sharing-networks.h"
+#include "org.gnome.SettingsDaemon.Sharing.h"
+#include "gsd-sharing-enums.h"
+
+struct _CcSharingNetworks {
+ GtkBox parent_instance;
+
+ GtkWidget *listbox;
+
+ GtkWidget *current_row;
+ GtkWidget *current_icon;
+ GtkWidget *current_switch;
+
+ GtkWidget *no_network_row;
+
+ char *service_name;
+ GsdSharing *proxy;
+ CcSharingStatus status;
+
+ GList *networks; /* list of CcSharingNetwork */
+};
+
+
+G_DEFINE_TYPE (CcSharingNetworks, cc_sharing_networks, GTK_TYPE_BOX)
+
+enum {
+ PROP_0,
+ PROP_PROXY,
+ PROP_SERVICE_NAME,
+ PROP_STATUS
+};
+
+static void cc_sharing_networks_class_init (CcSharingNetworksClass *klass);
+static void cc_sharing_networks_init (CcSharingNetworks *self);
+static void cc_sharing_networks_finalize (GObject *object);
+
+static void cc_sharing_update_networks_box (CcSharingNetworks *self);
+
+typedef struct {
+ char *uuid;
+ char *network_name;
+ char *carrier_type;
+} CcSharingNetwork;
+
+static void
+cc_sharing_network_free (gpointer data)
+{
+ CcSharingNetwork *net = data;
+
+ g_free (net->uuid);
+ g_free (net->network_name);
+ g_free (net->carrier_type);
+ g_free (net);
+}
+
+static void
+cc_sharing_networks_update_status (CcSharingNetworks *self)
+{
+ CcSharingStatus status;
+
+ if (self->networks == NULL)
+ status = CC_SHARING_STATUS_OFF;
+ else if (gtk_widget_is_visible (self->current_switch) &&
+ gtk_switch_get_active (GTK_SWITCH (self->current_switch)))
+ status = CC_SHARING_STATUS_ACTIVE;
+ else
+ status = CC_SHARING_STATUS_ENABLED;
+
+ if (status != self->status) {
+ self->status = status;
+ g_object_notify (G_OBJECT (self), "status");
+ }
+}
+
+static void
+cc_sharing_update_networks (CcSharingNetworks *self)
+{
+ g_autoptr(GVariant) networks = NULL;
+ char *uuid, *network_name, *carrier_type;
+ GVariantIter iter;
+ g_autoptr(GError) error = NULL;
+
+ g_list_free_full (self->networks, cc_sharing_network_free);
+ self->networks = NULL;
+
+ if (!gsd_sharing_call_list_networks_sync (self->proxy, self->service_name, &networks, NULL, &error)) {
+ g_warning ("couldn't list networks: %s", error->message);
+ g_dbus_proxy_set_cached_property (G_DBUS_PROXY (self->proxy),
+ "SharingStatus",
+ g_variant_new_uint32 (GSD_SHARING_STATUS_OFFLINE));
+ return;
+ }
+
+ g_variant_iter_init (&iter, networks);
+ while (g_variant_iter_next (&iter, "(sss)", &uuid, &network_name, &carrier_type)) {
+ CcSharingNetwork *net;
+
+ net = g_new0 (CcSharingNetwork, 1);
+ net->uuid = uuid;
+ net->network_name = network_name;
+ net->carrier_type = carrier_type;
+ self->networks = g_list_prepend (self->networks, net);
+ }
+ self->networks = g_list_reverse (self->networks);
+}
+
+static void
+cc_sharing_networks_remove_network (CcSharingNetworks *self,
+ GtkWidget *button)
+{
+ GtkWidget *row;
+ g_autoptr(GError) error = NULL;
+ gboolean ret;
+ const char *uuid;
+
+ row = g_object_get_data (G_OBJECT (button), "row");
+ uuid = g_object_get_data (G_OBJECT (row), "uuid");
+
+ ret = gsd_sharing_call_disable_service_sync (self->proxy,
+ self->service_name,
+ uuid,
+ NULL,
+ &error);
+ if (!ret)
+ g_warning ("Failed to remove service %s: %s",
+ self->service_name, error->message);
+
+ cc_sharing_update_networks (self);
+ cc_sharing_update_networks_box (self);
+}
+
+static gboolean
+cc_sharing_networks_enable_network (CcSharingNetworks *self,
+ gboolean state,
+ GtkSwitch *widget)
+{
+ g_autoptr(GError) error = NULL;
+ gboolean ret;
+
+ if (state) {
+ ret = gsd_sharing_call_enable_service_sync (self->proxy,
+ self->service_name,
+ NULL,
+ &error);
+ } else {
+ ret = gsd_sharing_call_disable_service_sync (self->proxy,
+ self->service_name,
+ gsd_sharing_get_current_network (self->proxy),
+ NULL,
+ &error);
+ }
+
+ if (ret) {
+ gtk_switch_set_state (widget, state);
+ } else {
+ g_warning ("Failed to %s service %s: %s", state ? "enable" : "disable",
+ self->service_name, error->message);
+ g_signal_handlers_block_by_func (widget,
+ cc_sharing_networks_enable_network, self);
+ gtk_switch_set_active (widget, !state);
+ g_signal_handlers_unblock_by_func (widget,
+ cc_sharing_networks_enable_network, self);
+ }
+
+ cc_sharing_update_networks (self);
+ cc_sharing_networks_update_status (self);
+
+ return TRUE;
+}
+
+static GtkWidget *
+cc_sharing_networks_new_row (const char *uuid,
+ const char *network_name,
+ const char *carrier_type,
+ CcSharingNetworks *self)
+{
+ GtkWidget *row, *w;
+ const char *icon_name;
+
+ row = adw_action_row_new ();
+
+ if (g_strcmp0 (carrier_type, "802-11-wireless") == 0) {
+ icon_name = "network-wireless-offline-symbolic";
+ } else if (g_strcmp0 (carrier_type, "802-3-ethernet") == 0) {
+ icon_name = "network-wired-disconnected-symbolic";
+ } else {
+ icon_name = "network-wired-symbolic";
+ }
+
+ adw_action_row_set_icon_name (ADW_ACTION_ROW (row), icon_name);
+ adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self->current_row), network_name);
+
+ /* Remove button */
+ w = gtk_button_new_from_icon_name ("window-close-symbolic");
+ gtk_widget_add_css_class (w, "flat");
+ gtk_widget_set_valign (w, GTK_ALIGN_CENTER);
+ gtk_accessible_update_property (GTK_ACCESSIBLE (w),
+ GTK_ACCESSIBLE_PROPERTY_LABEL, _("Remove"),
+ -1);
+ adw_action_row_add_suffix (ADW_ACTION_ROW (row), w);
+ g_signal_connect_object (G_OBJECT (w), "clicked",
+ G_CALLBACK (cc_sharing_networks_remove_network), self, G_CONNECT_SWAPPED);
+ g_object_set_data (G_OBJECT (w), "row", row);
+
+ g_object_set_data_full (G_OBJECT (row), "uuid", g_strdup (uuid), g_free);
+
+ return row;
+}
+
+static GtkWidget *
+cc_sharing_networks_new_current_row (CcSharingNetworks *self)
+{
+ GtkWidget *row, *w;
+
+ row = adw_action_row_new ();
+
+ /* Icon */
+ w = gtk_image_new_from_icon_name ("image-missing");
+ adw_action_row_add_prefix (ADW_ACTION_ROW (row), w);
+ self->current_icon = w;
+
+ w = gtk_switch_new ();
+ gtk_widget_set_valign (w, GTK_ALIGN_CENTER);
+ adw_action_row_add_suffix (ADW_ACTION_ROW (row), w);
+ g_signal_connect_object (G_OBJECT (w), "state-set",
+ G_CALLBACK (cc_sharing_networks_enable_network), self, G_CONNECT_SWAPPED);
+ self->current_switch = w;
+ g_object_set_data (G_OBJECT (w), "row", row);
+
+ return row;
+}
+
+static GtkWidget *
+cc_sharing_networks_new_no_network_row (CcSharingNetworks *self)
+{
+ GtkWidget *row, *box, *w;
+
+ row = gtk_list_box_row_new ();
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), box);
+
+ /* Label */
+ w = gtk_label_new (_("No networks selected for sharing"));
+ gtk_widget_set_hexpand (w, TRUE);
+ gtk_widget_set_halign (w, GTK_ALIGN_CENTER);
+ gtk_style_context_add_class (gtk_widget_get_style_context (w), "dim-label");
+ gtk_box_append (GTK_BOX (box), w);
+
+ return row;
+}
+
+static void
+cc_sharing_update_networks_box (CcSharingNetworks *self)
+{
+ GtkWidget *child;
+ gboolean current_visible;
+ const char *current_network;
+ GList *l;
+
+ child = gtk_widget_get_first_child (self->listbox);
+ while (child) {
+ GtkWidget *next = gtk_widget_get_next_sibling (child);
+ if (child != self->current_row && child != self->no_network_row)
+ gtk_list_box_remove (GTK_LIST_BOX (self->listbox), child);
+ child = next;
+ }
+
+ current_network = gsd_sharing_get_current_network (self->proxy);
+
+ if (current_network != NULL &&
+ !g_str_equal (current_network, "")) {
+ gboolean available;
+ const char *carrier_type, *icon_name, *current_network_name;
+
+ gtk_widget_show (self->current_row);
+ current_visible = TRUE;
+
+ /* Network name */
+ g_object_set_data_full (G_OBJECT (self->current_row),
+ "uuid", g_strdup (current_network), g_free);
+ current_network_name = gsd_sharing_get_current_network_name (self->proxy);
+ if (ADW_IS_PREFERENCES_ROW (self->current_row))
+ adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self->current_row), current_network_name);
+
+ /* Icon */
+ carrier_type = gsd_sharing_get_carrier_type (self->proxy);
+ if (g_strcmp0 (carrier_type, "802-11-wireless") == 0) {
+ icon_name = "network-wireless-signal-excellent-symbolic";
+ } else if (g_strcmp0 (carrier_type, "802-3-ethernet") == 0) {
+ icon_name = "network-wired-symbolic";
+ } else {
+ icon_name = "network-wired-symbolic";
+ }
+ gtk_image_set_from_icon_name (GTK_IMAGE (self->current_icon), icon_name);
+
+ /* State */
+ available = gsd_sharing_get_sharing_status (self->proxy) == GSD_SHARING_STATUS_AVAILABLE;
+ gtk_widget_set_sensitive (self->current_switch, available);
+ //FIXME add a subtitle explaining why it's disabled
+ } else {
+ gtk_widget_hide (self->current_row);
+ current_visible = FALSE;
+ }
+
+ for (l = self->networks; l != NULL; l = l->next) {
+ CcSharingNetwork *net = l->data;
+ GtkWidget *row;
+
+ if (g_strcmp0 (net->uuid, current_network) == 0) {
+ g_signal_handlers_block_by_func (self->current_switch,
+ cc_sharing_networks_enable_network, self);
+ gtk_switch_set_state (GTK_SWITCH (self->current_switch), TRUE);
+ g_signal_handlers_unblock_by_func (self->current_switch,
+ cc_sharing_networks_enable_network, self);
+ continue;
+ }
+
+ row = cc_sharing_networks_new_row (net->uuid,
+ net->network_name,
+ net->carrier_type,
+ self);
+ gtk_widget_show (row);
+ gtk_list_box_insert (GTK_LIST_BOX (self->listbox), row, -1);
+ }
+
+ if (self->networks == NULL &&
+ !current_visible) {
+ gtk_widget_show (self->no_network_row);
+ } else {
+ gtk_widget_hide (self->no_network_row);
+ }
+
+ cc_sharing_networks_update_status (self);
+}
+
+static void
+current_network_changed (CcSharingNetworks *self)
+{
+ cc_sharing_update_networks (self);
+ cc_sharing_update_networks_box (self);
+}
+
+static void
+cc_sharing_networks_constructed (GObject *object)
+{
+ CcSharingNetworks *self;
+
+ G_OBJECT_CLASS (cc_sharing_networks_parent_class)->constructed (object);
+
+ self = CC_SHARING_NETWORKS (object);
+
+ self->current_row = cc_sharing_networks_new_current_row (self);
+ gtk_list_box_insert (GTK_LIST_BOX (self->listbox), self->current_row, -1);
+ g_object_set_data (G_OBJECT (self), "switch", self->current_switch);
+
+ self->no_network_row = cc_sharing_networks_new_no_network_row (self);
+ gtk_list_box_insert (GTK_LIST_BOX (self->listbox), self->no_network_row, -1);
+
+ cc_sharing_update_networks (self);
+ cc_sharing_update_networks_box (self);
+
+ g_signal_connect_object (self->proxy, "notify::current-network",
+ G_CALLBACK (current_network_changed), self, G_CONNECT_SWAPPED);
+}
+
+static void
+cc_sharing_networks_init (CcSharingNetworks *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+GtkWidget *
+cc_sharing_networks_new (GDBusProxy *proxy,
+ const char *service_name)
+{
+ g_return_val_if_fail (GSD_IS_SHARING (proxy), NULL);
+ g_return_val_if_fail (service_name != NULL, NULL);
+
+ return GTK_WIDGET (g_object_new (CC_TYPE_SHARING_NETWORKS,
+ "proxy", proxy,
+ "service-name", service_name,
+ NULL));
+}
+
+static void
+cc_sharing_networks_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CcSharingNetworks *self;
+
+ self = CC_SHARING_NETWORKS (object);
+
+ switch (prop_id) {
+ case PROP_SERVICE_NAME:
+ self->service_name = g_value_dup_string (value);
+ break;
+ case PROP_PROXY:
+ self->proxy = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_sharing_networks_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CcSharingNetworks *self;
+
+ self = CC_SHARING_NETWORKS (object);
+
+ switch (prop_id) {
+ case PROP_STATUS:
+ g_value_set_uint (value, self->status);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_sharing_networks_finalize (GObject *object)
+{
+ CcSharingNetworks *self;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CC_IS_SHARING_NETWORKS (object));
+
+ self = CC_SHARING_NETWORKS (object);
+
+ g_return_if_fail (self != NULL);
+
+ g_clear_object (&self->proxy);
+ g_clear_pointer (&self->service_name, g_free);
+
+ if (self->networks != NULL) {
+ g_list_free_full (self->networks, cc_sharing_network_free);
+ self->networks = NULL;
+ }
+
+ G_OBJECT_CLASS (cc_sharing_networks_parent_class)->finalize (object);
+}
+
+
+static void
+cc_sharing_networks_class_init (CcSharingNetworksClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->set_property = cc_sharing_networks_set_property;
+ object_class->get_property = cc_sharing_networks_get_property;
+ object_class->finalize = cc_sharing_networks_finalize;
+ object_class->constructed = cc_sharing_networks_constructed;
+
+ g_object_class_install_property (object_class,
+ PROP_PROXY,
+ g_param_spec_object ("proxy",
+ "proxy",
+ "proxy",
+ GSD_TYPE_SHARING_PROXY,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class,
+ PROP_SERVICE_NAME,
+ g_param_spec_string ("service-name",
+ "service-name",
+ "service-name",
+ NULL,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class,
+ PROP_STATUS,
+ g_param_spec_uint ("status",
+ "status",
+ "status",
+ CC_SHARING_STATUS_UNSET, CC_SHARING_STATUS_ACTIVE + 1, CC_SHARING_STATUS_OFF,
+ G_PARAM_READABLE));
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/control-center/sharing/cc-sharing-networks.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, CcSharingNetworks, listbox);
+}
+
+/*
+ * vim: sw=2 ts=8 cindent noai bs=2
+ */
diff --git a/panels/sharing/cc-sharing-networks.h b/panels/sharing/cc-sharing-networks.h
new file mode 100644
index 0000000..f44567e
--- /dev/null
+++ b/panels/sharing/cc-sharing-networks.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 Bastien Nocera <hadess@hadess.net>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_SHARING_NETWORKS (cc_sharing_networks_get_type ())
+G_DECLARE_FINAL_TYPE (CcSharingNetworks, cc_sharing_networks, CC, SHARING_NETWORKS, GtkBox)
+
+typedef enum {
+ CC_SHARING_STATUS_UNSET,
+ CC_SHARING_STATUS_OFF,
+ CC_SHARING_STATUS_ENABLED,
+ CC_SHARING_STATUS_ACTIVE
+} CcSharingStatus;
+
+GtkWidget * cc_sharing_networks_new (GDBusProxy *proxy,
+ const char *service_name);
+
+G_END_DECLS
diff --git a/panels/sharing/cc-sharing-networks.ui b/panels/sharing/cc-sharing-networks.ui
new file mode 100644
index 0000000..5703441
--- /dev/null
+++ b/panels/sharing/cc-sharing-networks.ui
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.18.1 -->
+<interface>
+ <requires lib="gtk+" version="3.10"/>
+ <template class="CcSharingNetworks" parent="GtkBox">
+ <property name="margin_start">0</property>
+ <property name="margin_end">0</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="spacing">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="label15">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Networks</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame" id="shared-networks-frame">
+ <property name="hexpand">True</property>
+ <child>
+ <object class="GtkListBox" id="listbox">
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/panels/sharing/cc-sharing-panel.c b/panels/sharing/cc-sharing-panel.c
new file mode 100644
index 0000000..1ac744c
--- /dev/null
+++ b/panels/sharing/cc-sharing-panel.c
@@ -0,0 +1,1600 @@
+/*
+ * Copyright (C) 2013 Intel, Inc
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Thomas Wood <thomas.wood@intel.com>
+ *
+ */
+
+#include "cc-sharing-panel.h"
+#include "cc-hostname-entry.h"
+#include "cc-list-row.h"
+
+#include "cc-sharing-resources.h"
+#include "cc-remote-login.h"
+#include "file-share-properties.h"
+#include "cc-media-sharing.h"
+#include "cc-sharing-networks.h"
+#include "cc-gnome-remote-desktop.h"
+#include "cc-tls-certificate.h"
+#include "cc-systemd-service.h"
+#include "org.gnome.SettingsDaemon.Sharing.h"
+
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/wayland/gdkwayland.h>
+#endif
+#include <glib/gi18n.h>
+
+#define GCR_API_SUBJECT_TO_CHANGE
+#include <gcr/gcr-base.h>
+
+#include <pwquality.h>
+
+#include <config.h>
+
+#include <unistd.h>
+#include <pwd.h>
+
+static void cc_sharing_panel_setup_label_with_hostname (CcSharingPanel *self, GtkWidget *label);
+static GtkWidget *cc_sharing_panel_new_media_sharing_row (const char *uri_or_path,
+ CcSharingPanel *self);
+
+#define FILE_SHARING_SCHEMA_ID "org.gnome.desktop.file-sharing"
+#define GNOME_REMOTE_DESKTOP_SCHEMA_ID "org.gnome.desktop.remote-desktop"
+#define GNOME_REMOTE_DESKTOP_RDP_SCHEMA_ID "org.gnome.desktop.remote-desktop.rdp"
+
+#define REMOTE_DESKTOP_STORE_CREDENTIALS_TIMEOUT_S 1
+
+#define REMOTE_DESKTOP_SERVICE "gnome-remote-desktop.service"
+
+struct _CcSharingPanel
+{
+ CcPanel parent_instance;
+
+ GtkWidget *hostname_entry;
+ GtkWidget *main_list_box;
+ GtkWidget *master_switch;
+ GtkWidget *media_sharing_dialog;
+ GtkWidget *media_sharing_headerbar;
+ GtkWidget *media_sharing_row;
+ GtkWidget *media_sharing_switch;
+ GtkWidget *personal_file_sharing_dialog;
+ GtkWidget *personal_file_sharing_grid;
+ GtkWidget *personal_file_sharing_headerbar;
+ GtkWidget *personal_file_sharing_label;
+ GtkWidget *personal_file_sharing_password_entry;
+ GtkWidget *personal_file_sharing_password_label;
+ GtkWidget *personal_file_sharing_require_password_switch;
+ GtkWidget *personal_file_sharing_row;
+ GtkWidget *personal_file_sharing_switch;
+ GtkWidget *remote_login_dialog;
+ GtkWidget *remote_login_label;
+ GtkWidget *remote_login_row;
+ GtkWidget *remote_login_switch;
+
+ GtkWidget *remote_control_switch;
+ GtkWidget *remote_control_checkbutton;
+ GtkWidget *remote_desktop_toast_overlay;
+ GtkWidget *remote_desktop_password_entry;
+ GtkWidget *remote_desktop_password_copy;
+ GtkWidget *remote_desktop_username_entry;
+ GtkWidget *remote_desktop_username_copy;
+ GtkWidget *remote_desktop_dialog;
+ GtkWidget *remote_desktop_device_name_label;
+ GtkWidget *remote_desktop_device_name_copy;
+ GtkWidget *remote_desktop_address_label;
+ GtkWidget *remote_desktop_address_copy;
+ GtkWidget *remote_desktop_row;
+ GtkWidget *remote_desktop_switch;
+ GtkWidget *remote_desktop_verify_encryption;
+ GtkWidget *remote_desktop_fingerprint_dialog;
+ GtkWidget *remote_desktop_fingerprint_left;
+ GtkWidget *remote_desktop_fingerprint_right;
+
+ GtkWidget *shared_folders_grid;
+ GtkWidget *shared_folders_listbox;
+
+ GDBusProxy *sharing_proxy;
+
+ guint remote_desktop_name_watch;
+ guint remote_desktop_store_credentials_id;
+ GTlsCertificate *remote_desktop_certificate;
+};
+
+CC_PANEL_REGISTER (CcSharingPanel, cc_sharing_panel)
+
+#define OFF_IF_VISIBLE(x, y) { if (gtk_widget_is_visible(x) && (y) != NULL && gtk_widget_is_sensitive(y)) gtk_switch_set_active (GTK_SWITCH(y), FALSE); }
+
+static gboolean store_remote_desktop_credentials_timeout (gpointer user_data);
+
+static void
+cc_sharing_panel_master_switch_notify (CcSharingPanel *self)
+{
+ gboolean active;
+
+ active = gtk_switch_get_active (GTK_SWITCH (self->master_switch));
+
+ if (!active)
+ {
+ /* disable all services if the master switch is not active */
+ OFF_IF_VISIBLE(self->media_sharing_row, self->media_sharing_switch);
+ OFF_IF_VISIBLE(self->personal_file_sharing_row, self->personal_file_sharing_switch);
+ OFF_IF_VISIBLE(self->remote_desktop_row, self->remote_desktop_switch);
+
+ gtk_switch_set_active (GTK_SWITCH (self->remote_login_switch), FALSE);
+ }
+
+ gtk_widget_set_sensitive (self->main_list_box, active);
+}
+
+static void
+cc_sharing_panel_dispose (GObject *object)
+{
+ CcSharingPanel *self = CC_SHARING_PANEL (object);
+
+ if (self->remote_desktop_name_watch)
+ g_bus_unwatch_name (self->remote_desktop_name_watch);
+ self->remote_desktop_name_watch = 0;
+
+ if (self->media_sharing_dialog)
+ {
+ gtk_window_destroy (GTK_WINDOW (self->media_sharing_dialog));
+ self->media_sharing_dialog = NULL;
+ }
+
+ if (self->personal_file_sharing_dialog)
+ {
+ gtk_window_destroy (GTK_WINDOW (self->personal_file_sharing_dialog));
+ self->personal_file_sharing_dialog = NULL;
+ }
+
+ if (self->remote_login_dialog)
+ {
+ gtk_window_destroy (GTK_WINDOW (self->remote_login_dialog));
+ self->remote_login_dialog = NULL;
+ }
+
+ if (self->remote_desktop_dialog)
+ {
+ gtk_window_destroy (GTK_WINDOW (self->remote_desktop_dialog));
+ self->remote_desktop_dialog = NULL;
+ }
+
+ g_clear_object (&self->sharing_proxy);
+
+ if (self->remote_desktop_store_credentials_id)
+ {
+ g_clear_handle_id (&self->remote_desktop_store_credentials_id,
+ g_source_remove);
+ store_remote_desktop_credentials_timeout (self);
+ }
+
+ G_OBJECT_CLASS (cc_sharing_panel_parent_class)->dispose (object);
+}
+
+static const char *
+cc_sharing_panel_get_help_uri (CcPanel *panel)
+{
+ return "help:gnome-help/prefs-sharing";
+}
+
+static void
+remote_desktop_show_encryption_fingerprint (CcSharingPanel *self)
+{
+ g_autoptr(GByteArray) der = NULL;
+ g_autoptr(GcrCertificate) gcr_cert = NULL;
+ g_autofree char *fingerprint = NULL;
+ g_auto(GStrv) fingerprintv = NULL;
+ g_autofree char *left_string = NULL;
+ g_autofree char *right_string = NULL;
+
+ g_return_if_fail (self->remote_desktop_certificate);
+
+ g_object_get (self->remote_desktop_certificate,
+ "certificate", &der, NULL);
+ gcr_cert = gcr_simple_certificate_new (der->data, der->len);
+ if (!gcr_cert)
+ {
+ g_warning ("Failed to load GCR TLS certificate representation");
+ return;
+ }
+
+ fingerprint = gcr_certificate_get_fingerprint_hex (gcr_cert, G_CHECKSUM_SHA256);
+
+ fingerprintv = g_strsplit (fingerprint, " ", -1);
+ g_return_if_fail (g_strv_length (fingerprintv) == 32);
+
+ left_string = g_strdup_printf (
+ "%s:%s:%s:%s\n"
+ "%s:%s:%s:%s\n"
+ "%s:%s:%s:%s\n"
+ "%s:%s:%s:%s\n",
+ fingerprintv[0], fingerprintv[1], fingerprintv[2], fingerprintv[3],
+ fingerprintv[8], fingerprintv[9], fingerprintv[10], fingerprintv[11],
+ fingerprintv[16], fingerprintv[17], fingerprintv[18], fingerprintv[19],
+ fingerprintv[24], fingerprintv[25], fingerprintv[26], fingerprintv[27]);
+
+ right_string = g_strdup_printf (
+ "%s:%s:%s:%s\n"
+ "%s:%s:%s:%s\n"
+ "%s:%s:%s:%s\n"
+ "%s:%s:%s:%s\n",
+ fingerprintv[4], fingerprintv[5], fingerprintv[6], fingerprintv[7],
+ fingerprintv[12], fingerprintv[13], fingerprintv[14], fingerprintv[15],
+ fingerprintv[20], fingerprintv[21], fingerprintv[22], fingerprintv[23],
+ fingerprintv[28], fingerprintv[29], fingerprintv[30], fingerprintv[31]);
+
+ gtk_label_set_label (GTK_LABEL (self->remote_desktop_fingerprint_left),
+ left_string);
+ gtk_label_set_label (GTK_LABEL (self->remote_desktop_fingerprint_right),
+ right_string);
+
+ gtk_window_set_transient_for (GTK_WINDOW (self->remote_desktop_fingerprint_dialog),
+ GTK_WINDOW (self->remote_desktop_dialog));
+ gtk_window_present (GTK_WINDOW (self->remote_desktop_fingerprint_dialog));
+}
+
+static void
+cc_sharing_panel_class_init (CcSharingPanelClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
+
+ object_class->dispose = cc_sharing_panel_dispose;
+
+ panel_class->get_help_uri = cc_sharing_panel_get_help_uri;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/sharing/cc-sharing-panel.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, hostname_entry);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, shared_folders_grid);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, master_switch);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, main_list_box);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, media_sharing_dialog);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, media_sharing_headerbar);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, media_sharing_row);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, personal_file_sharing_dialog);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, personal_file_sharing_grid);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, personal_file_sharing_headerbar);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, personal_file_sharing_label);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, personal_file_sharing_password_entry);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, personal_file_sharing_password_label);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, personal_file_sharing_require_password_switch);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, personal_file_sharing_row);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_login_dialog);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_login_label);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_login_row);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_login_switch);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_dialog);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_toast_overlay);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_switch);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_control_switch);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_username_entry);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_username_copy);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_password_entry);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_password_copy);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_device_name_label);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_device_name_copy);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_address_label);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_address_copy);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_verify_encryption);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_fingerprint_dialog);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_row);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_fingerprint_left);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, remote_desktop_fingerprint_right);
+ gtk_widget_class_bind_template_child (widget_class, CcSharingPanel, shared_folders_listbox);
+
+ gtk_widget_class_bind_template_callback (widget_class, remote_desktop_show_encryption_fingerprint);
+
+ g_type_ensure (CC_TYPE_LIST_ROW);
+ g_type_ensure (CC_TYPE_HOSTNAME_ENTRY);
+}
+
+static void
+cc_sharing_panel_run_dialog (CcSharingPanel *self,
+ GtkWidget *dialog)
+{
+ GtkWidget *parent;
+
+ /* ensure labels with the hostname are updated if the hostname has changed */
+ cc_sharing_panel_setup_label_with_hostname (self,
+ self->remote_desktop_address_label);
+ cc_sharing_panel_setup_label_with_hostname (self, self->remote_login_label);
+ cc_sharing_panel_setup_label_with_hostname (self,
+ self->personal_file_sharing_label);
+
+
+ parent = cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self)));
+
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent));
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+cc_sharing_panel_main_list_box_row_activated (CcSharingPanel *self,
+ GtkListBoxRow *row)
+{
+ GtkWidget *dialog;
+
+ if (row == GTK_LIST_BOX_ROW (self->media_sharing_row))
+ dialog = self->media_sharing_dialog;
+ else if (row == GTK_LIST_BOX_ROW (self->personal_file_sharing_row))
+ dialog = self->personal_file_sharing_dialog;
+ else if (row == GTK_LIST_BOX_ROW (self->remote_login_row))
+ dialog = self->remote_login_dialog;
+ else if (row == GTK_LIST_BOX_ROW (self->remote_desktop_row))
+ dialog = self->remote_desktop_dialog;
+ else
+ return;
+
+ gtk_list_box_select_row (GTK_LIST_BOX (self->main_list_box), NULL);
+
+ cc_sharing_panel_run_dialog (self, dialog);
+}
+
+static gboolean
+cc_sharing_panel_switch_to_label_transform_func (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ CcSharingPanel *self)
+{
+ gboolean active;
+
+ if (!G_VALUE_HOLDS_BOOLEAN (source_value))
+ return FALSE;
+
+ if (!G_VALUE_HOLDS_STRING (target_value))
+ return FALSE;
+
+ active = g_value_get_boolean (source_value);
+
+ if (active)
+ g_value_set_string (target_value, C_("service is enabled", "On"));
+ else
+ g_value_set_string (target_value, C_("service is disabled", "Off"));
+
+ /* ensure the master switch is active if one of the services is active */
+ if (active)
+ gtk_switch_set_active (GTK_SWITCH (self->master_switch), TRUE);
+
+ return TRUE;
+}
+
+static gboolean
+cc_sharing_panel_networks_to_label_transform_func (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ CcSharingPanel *self)
+{
+ CcSharingStatus status;
+
+ if (!G_VALUE_HOLDS_UINT (source_value))
+ return FALSE;
+
+ if (!G_VALUE_HOLDS_STRING (target_value))
+ return FALSE;
+
+ status = g_value_get_uint (source_value);
+
+ switch (status) {
+ case CC_SHARING_STATUS_OFF:
+ g_value_set_string (target_value, C_("service is disabled", "Off"));
+ break;
+ case CC_SHARING_STATUS_ENABLED:
+ g_value_set_string (target_value, C_("service is enabled", "Enabled"));
+ break;
+ case CC_SHARING_STATUS_ACTIVE:
+ g_value_set_string (target_value, C_("service is active", "Active"));
+ break;
+ default:
+ return FALSE;
+ }
+
+ /* ensure the master switch is active if one of the services is active */
+ if (status != CC_SHARING_STATUS_OFF)
+ gtk_switch_set_active (GTK_SWITCH (self->master_switch), TRUE);
+
+ return TRUE;
+}
+
+static void
+cc_sharing_panel_bind_switch_to_label (CcSharingPanel *self,
+ GtkWidget *gtkswitch,
+ GtkWidget *row)
+{
+ g_object_bind_property_full (gtkswitch, "active", row, "secondary-label",
+ G_BINDING_SYNC_CREATE,
+ (GBindingTransformFunc) cc_sharing_panel_switch_to_label_transform_func,
+ NULL, self, NULL);
+}
+
+static void
+cc_sharing_panel_bind_networks_to_label (CcSharingPanel *self,
+ GtkWidget *networks,
+ GtkWidget *list_row)
+{
+ g_object_bind_property_full (networks, "status", list_row, "secondary-label",
+ G_BINDING_SYNC_CREATE,
+ (GBindingTransformFunc) cc_sharing_panel_networks_to_label_transform_func,
+ NULL, self, NULL);
+}
+
+static void
+cc_sharing_panel_bind_switch_to_widgets (GtkWidget *gtkswitch,
+ GtkWidget *first_widget,
+ ...)
+{
+ va_list w;
+ GtkWidget *widget;
+
+ va_start (w, first_widget);
+
+ g_object_bind_property (gtkswitch, "active", first_widget,
+ "sensitive", G_BINDING_SYNC_CREATE);
+
+ while ((widget = va_arg (w, GtkWidget*)))
+ {
+ g_object_bind_property (gtkswitch, "active", widget,
+ "sensitive", G_BINDING_SYNC_CREATE);
+ }
+
+ va_end (w);
+}
+
+static void
+on_add_folder_dialog_response_cb (GtkDialog *dialog,
+ gint response,
+ CcSharingPanel *self)
+{
+ g_autofree gchar *folder = NULL;
+ g_autoptr(GFile) file = NULL;
+ GtkWidget *child;
+ gboolean matching = FALSE;
+ gint n_rows = 0;
+
+ if (response != GTK_RESPONSE_ACCEPT)
+ goto bail;
+
+ file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+ folder = g_file_get_uri (file);
+ if (!folder || g_str_equal (folder, ""))
+ goto bail;
+
+ g_debug ("Trying to add %s", folder);
+
+ for (child = gtk_widget_get_first_child (self->shared_folders_listbox);
+ child;
+ child = gtk_widget_get_next_sibling (child))
+ {
+ const char *string;
+
+ string = g_object_get_data (G_OBJECT (child), "path");
+ matching = (g_strcmp0 (string, folder) == 0);
+
+ if (matching)
+ {
+ g_debug ("Found a duplicate for %s", folder);
+ break;
+ }
+
+ n_rows++;
+ }
+
+ if (!matching)
+ {
+ GtkWidget *row = cc_sharing_panel_new_media_sharing_row (folder, self);
+
+ gtk_list_box_insert (GTK_LIST_BOX (self->shared_folders_listbox),
+ row,
+ n_rows - 1);
+ }
+
+bail:
+ gtk_window_destroy (GTK_WINDOW (dialog));
+}
+
+static void
+cc_sharing_panel_add_folder (CcSharingPanel *self,
+ GtkListBoxRow *row)
+{
+ GtkWidget *dialog;
+
+ if (!GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "is-add")))
+ return;
+
+ dialog = gtk_file_chooser_dialog_new (_("Choose a Folder"),
+ GTK_WINDOW (self->media_sharing_dialog),
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ g_signal_connect_object (dialog,
+ "response",
+ G_CALLBACK (on_add_folder_dialog_response_cb),
+ self,
+ 0);
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+cc_sharing_panel_remove_folder (CcSharingPanel *self,
+ GtkButton *button)
+{
+ GtkWidget *row;
+
+ row = g_object_get_data (G_OBJECT (button), "row");
+ gtk_list_box_remove (GTK_LIST_BOX (self->shared_folders_listbox), row);
+}
+
+static void
+cc_sharing_panel_media_sharing_dialog_response (CcSharingPanel *self,
+ gint reponse_id)
+{
+ g_autoptr(GPtrArray) folders = NULL;
+ GtkWidget *child;
+
+ folders = g_ptr_array_new_with_free_func (g_free);
+
+ for (child = gtk_widget_get_first_child (self->shared_folders_listbox);
+ child;
+ child = gtk_widget_get_next_sibling (child))
+ {
+ const char *folder;
+
+ folder = g_object_get_data (G_OBJECT (child), "path");
+ if (folder == NULL)
+ continue;
+ g_ptr_array_add (folders, g_strdup (folder));
+ }
+
+ g_ptr_array_add (folders, NULL);
+
+ cc_media_sharing_set_preferences ((gchar **) folders->pdata);
+}
+
+#define ICON_NAME_FOLDER "folder-symbolic"
+#define ICON_NAME_FOLDER_DESKTOP "user-desktop-symbolic"
+#define ICON_NAME_FOLDER_DOCUMENTS "folder-documents-symbolic"
+#define ICON_NAME_FOLDER_DOWNLOAD "folder-download-symbolic"
+#define ICON_NAME_FOLDER_MUSIC "folder-music-symbolic"
+#define ICON_NAME_FOLDER_PICTURES "folder-pictures-symbolic"
+#define ICON_NAME_FOLDER_PUBLIC_SHARE "folder-publicshare-symbolic"
+#define ICON_NAME_FOLDER_TEMPLATES "folder-templates-symbolic"
+#define ICON_NAME_FOLDER_VIDEOS "folder-videos-symbolic"
+#define ICON_NAME_FOLDER_SAVED_SEARCH "folder-saved-search-symbolic"
+
+static GIcon *
+special_directory_get_gicon (GUserDirectory directory)
+{
+#define ICON_CASE(x) \
+ case G_USER_DIRECTORY_ ## x: \
+ return g_themed_icon_new_with_default_fallbacks (ICON_NAME_FOLDER_ ## x);
+
+ switch (directory)
+ {
+ ICON_CASE (DESKTOP);
+ ICON_CASE (DOCUMENTS);
+ ICON_CASE (DOWNLOAD);
+ ICON_CASE (MUSIC);
+ ICON_CASE (PICTURES);
+ ICON_CASE (PUBLIC_SHARE);
+ ICON_CASE (TEMPLATES);
+ ICON_CASE (VIDEOS);
+
+ default:
+ return g_themed_icon_new_with_default_fallbacks (ICON_NAME_FOLDER);
+ }
+
+#undef ICON_CASE
+}
+
+static GtkWidget *
+cc_sharing_panel_new_media_sharing_row (const char *uri_or_path,
+ CcSharingPanel *self)
+{
+ GtkWidget *row, *w;
+ GUserDirectory dir = G_USER_N_DIRECTORIES;
+ g_autoptr(GIcon) icon = NULL;
+ guint i;
+ g_autofree gchar *basename = NULL;
+ g_autofree gchar *path = NULL;
+ g_autoptr(GFile) file = NULL;
+
+ file = g_file_new_for_commandline_arg (uri_or_path);
+ path = g_file_get_path (file);
+
+ row = adw_action_row_new ();
+
+ /* Find the icon and create it */
+ for (i = 0; i < G_USER_N_DIRECTORIES; i++)
+ {
+ if (g_strcmp0 (path, g_get_user_special_dir (i)) == 0)
+ {
+ dir = i;
+ break;
+ }
+ }
+
+ icon = special_directory_get_gicon (dir);
+ adw_action_row_add_prefix (ADW_ACTION_ROW (row),
+ gtk_image_new_from_gicon (icon));
+
+ /* Label */
+ basename = g_filename_display_basename (path);
+ adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), basename);
+
+ /* Remove button */
+ w = gtk_button_new_from_icon_name ("window-close-symbolic");
+ gtk_widget_add_css_class (w, "flat");
+ gtk_widget_set_valign (w, GTK_ALIGN_CENTER);
+ gtk_accessible_update_property (GTK_ACCESSIBLE (w),
+ GTK_ACCESSIBLE_PROPERTY_LABEL, _("Remove"),
+ -1);
+ adw_action_row_add_suffix (ADW_ACTION_ROW (row), w);
+ g_signal_connect_object (G_OBJECT (w), "clicked",
+ G_CALLBACK (cc_sharing_panel_remove_folder), self, G_CONNECT_SWAPPED);
+ g_object_set_data (G_OBJECT (w), "row", row);
+
+ g_object_set_data_full (G_OBJECT (row), "path", g_steal_pointer (&path), g_free);
+
+ return row;
+}
+
+static GtkWidget *
+cc_sharing_panel_new_add_media_sharing_row (CcSharingPanel *self)
+{
+ GtkWidget *row, *box, *w;
+
+ row = gtk_list_box_row_new ();
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), box);
+
+ w = gtk_image_new_from_icon_name ("list-add-symbolic");
+ gtk_widget_set_hexpand (w, TRUE);
+ gtk_widget_set_margin_top (w, 12);
+ gtk_widget_set_margin_bottom (w, 12);
+ gtk_accessible_update_property (GTK_ACCESSIBLE (w),
+ GTK_ACCESSIBLE_PROPERTY_LABEL, _("Add"),
+ -1);
+ gtk_box_append (GTK_BOX (box), w);
+
+ g_object_set_data (G_OBJECT (w), "row", row);
+
+ g_object_set_data (G_OBJECT (row), "is-add", GINT_TO_POINTER (1));
+
+ return row;
+}
+
+static GtkWidget *
+create_switch_with_bindings (GtkSwitch *from)
+{
+ GtkWidget *new_switch = gtk_switch_new ();
+
+ g_object_bind_property (from, "visible", new_switch, "visible", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (from, "state", new_switch, "state", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (from, "active", new_switch, "active", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_bind_property (from, "sensitive", new_switch, "sensitive", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+ return new_switch;
+}
+
+static void
+cc_sharing_panel_setup_media_sharing_dialog (CcSharingPanel *self)
+{
+ g_auto(GStrv) folders = NULL;
+ GStrv list;
+ GtkWidget *row, *networks, *w;
+ g_autofree gchar *path = NULL;
+
+ path = g_find_program_in_path ("rygel");
+ if (path == NULL)
+ {
+ gtk_widget_hide (self->media_sharing_row);
+ return;
+ }
+
+ g_signal_connect_object (self->media_sharing_dialog, "response",
+ G_CALLBACK (cc_sharing_panel_media_sharing_dialog_response),
+ self, G_CONNECT_SWAPPED);
+
+ cc_media_sharing_get_preferences (&folders);
+
+ list = folders;
+ while (list && *list)
+ {
+ row = cc_sharing_panel_new_media_sharing_row (*list, self);
+ gtk_list_box_insert (GTK_LIST_BOX (self->shared_folders_listbox), row, -1);
+ list++;
+ }
+
+ row = cc_sharing_panel_new_add_media_sharing_row (self);
+ gtk_list_box_append (GTK_LIST_BOX (self->shared_folders_listbox), row);
+
+ g_signal_connect_object (self->shared_folders_listbox, "row-activated",
+ G_CALLBACK (cc_sharing_panel_add_folder), self, G_CONNECT_SWAPPED);
+
+ networks = cc_sharing_networks_new (self->sharing_proxy, "rygel");
+ gtk_grid_attach (GTK_GRID (self->shared_folders_grid), networks, 0, 4, 2, 1);
+
+ w = create_switch_with_bindings (GTK_SWITCH (g_object_get_data (G_OBJECT (networks), "switch")));
+ gtk_accessible_update_property (GTK_ACCESSIBLE (w),
+ GTK_ACCESSIBLE_PROPERTY_LABEL, _("Enable media sharing"),
+ -1);
+ gtk_header_bar_pack_start (GTK_HEADER_BAR (self->media_sharing_headerbar), w);
+ self->media_sharing_switch = w;
+
+ cc_sharing_panel_bind_networks_to_label (self, networks,
+ self->media_sharing_row);
+}
+
+static void
+cc_sharing_panel_setup_label (CcSharingPanel *self,
+ GtkWidget *label,
+ const gchar *hostname)
+{
+ g_autofree gchar *text = NULL;
+
+ if (label == self->personal_file_sharing_label)
+ {
+ g_autofree gchar *url = g_strdup_printf ("<a href=\"dav://%s\">dav://%s</a>", hostname, hostname);
+ /* TRANSLATORS: %s is replaced with a link to a dav://<hostname> URL */
+ text = g_strdup_printf (_("File Sharing allows you to share your Public folder with others on your current network using: %s"), url);
+ }
+ else if (label == self->remote_login_label)
+ {
+ g_autofree gchar *command = g_strdup_printf ("<a href=\"ssh %s\">ssh %s</a>", hostname, hostname);
+ /* TRANSLATORS: %s is replaced with a link to a "ssh <hostname>" command to run */
+ text = g_strdup_printf (_("When remote login is enabled, remote users can connect using the Secure Shell command:\n%s"), command);
+ }
+ else if (label == self->remote_desktop_address_label)
+ {
+ text = g_strdup_printf ("ms-rd://%s", hostname);
+ }
+ else
+ g_assert_not_reached ();
+
+ gtk_label_set_label (GTK_LABEL (label), text);
+}
+
+typedef struct
+{
+ CcSharingPanel *panel;
+ GtkWidget *label;
+} GetHostNameData;
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GetHostNameData, g_free);
+
+static void
+cc_sharing_panel_get_host_name_fqdn_done (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GDBusConnection *connection = G_DBUS_CONNECTION (object);
+ g_autoptr(GetHostNameData) data = user_data;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GVariant) variant = NULL;
+ const gchar *fqdn;
+
+ variant = g_dbus_connection_call_finish (connection, res, &error);
+
+ if (variant == NULL)
+ {
+ /* Avahi service may not be available */
+ g_debug ("Error calling GetHostNameFqdn: %s", error->message);
+
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_autofree gchar *hostname = NULL;
+
+ hostname = cc_hostname_entry_get_hostname (CC_HOSTNAME_ENTRY (data->panel->hostname_entry));
+
+ cc_sharing_panel_setup_label (data->panel, data->label, hostname);
+ }
+
+ return;
+ }
+
+ g_variant_get (variant, "(&s)", &fqdn);
+
+ cc_sharing_panel_setup_label (data->panel, data->label, fqdn);
+}
+
+static void
+cc_sharing_panel_bus_ready (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autoptr(GDBusConnection) connection = NULL;
+ g_autoptr(GetHostNameData) data = user_data;
+ g_autoptr(GError) error = NULL;
+
+ connection = g_bus_get_finish (res, &error);
+
+ if (connection == NULL)
+ {
+ g_warning ("Could not connect to system bus: %s", error->message);
+
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_autofree gchar *hostname = NULL;
+
+ hostname = cc_hostname_entry_get_hostname (CC_HOSTNAME_ENTRY (data->panel->hostname_entry));
+
+ cc_sharing_panel_setup_label (data->panel, data->label, hostname);
+ }
+
+ return;
+ }
+
+ g_dbus_connection_call (connection,
+ "org.freedesktop.Avahi",
+ "/",
+ "org.freedesktop.Avahi.Server",
+ "GetHostNameFqdn",
+ NULL,
+ (GVariantType*)"(s)",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ cc_panel_get_cancellable (CC_PANEL (data->panel)),
+ cc_sharing_panel_get_host_name_fqdn_done,
+ data);
+ g_steal_pointer (&data);
+}
+
+
+static void
+cc_sharing_panel_setup_label_with_hostname (CcSharingPanel *self,
+ GtkWidget *label)
+
+{
+ GetHostNameData *get_hostname_data;
+
+ /* set the hostname */
+ get_hostname_data = g_new (GetHostNameData, 1);
+ get_hostname_data->panel = self;
+ get_hostname_data->label = label;
+ g_bus_get (G_BUS_TYPE_SYSTEM,
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ cc_sharing_panel_bus_ready,
+ get_hostname_data);
+}
+
+static gboolean
+file_sharing_get_require_password (GValue *value,
+ GVariant *variant,
+ gpointer user_data)
+{
+ if (g_str_equal (g_variant_get_string (variant, NULL), "always"))
+ g_value_set_boolean (value, TRUE);
+ else
+ g_value_set_boolean (value, FALSE);
+
+ return TRUE;
+}
+
+static GVariant *
+file_sharing_set_require_password (const GValue *value,
+ const GVariantType *type,
+ gpointer user_data)
+{
+ if (g_value_get_boolean (value))
+ return g_variant_new_string ("always");
+ else
+ return g_variant_new_string ("never");
+}
+
+static void
+file_sharing_password_changed (GtkEntry *entry)
+{
+ file_share_write_out_password (gtk_editable_get_text (GTK_EDITABLE (entry)));
+}
+
+static void
+cc_sharing_panel_setup_personal_file_sharing_dialog (CcSharingPanel *self)
+{
+ GSettings *settings;
+ GtkWidget *networks, *w;
+
+ cc_sharing_panel_bind_switch_to_widgets (self->personal_file_sharing_require_password_switch,
+ self->personal_file_sharing_password_entry,
+ self->personal_file_sharing_password_label,
+ NULL);
+
+ cc_sharing_panel_setup_label_with_hostname (self,
+ self->personal_file_sharing_label);
+
+ /* the password cannot be read, so just make sure the entry is not empty */
+ gtk_editable_set_text (GTK_EDITABLE (self->personal_file_sharing_password_entry),
+ "password");
+
+ settings = g_settings_new (FILE_SHARING_SCHEMA_ID);
+ g_settings_bind_with_mapping (settings, "require-password",
+ self->personal_file_sharing_require_password_switch,
+ "active",
+ G_SETTINGS_BIND_DEFAULT,
+ file_sharing_get_require_password,
+ file_sharing_set_require_password, NULL, NULL);
+
+ g_signal_connect (self->personal_file_sharing_password_entry,
+ "notify::text", G_CALLBACK (file_sharing_password_changed),
+ NULL);
+
+ networks = cc_sharing_networks_new (self->sharing_proxy, "gnome-user-share-webdav");
+ gtk_grid_attach (GTK_GRID (self->personal_file_sharing_grid), networks, 0, 3, 2, 1);
+
+ w = create_switch_with_bindings (GTK_SWITCH (g_object_get_data (G_OBJECT (networks), "switch")));
+ gtk_accessible_update_property (GTK_ACCESSIBLE (w),
+ GTK_ACCESSIBLE_PROPERTY_LABEL, _("Enable personal media sharing"),
+ -1);
+ gtk_header_bar_pack_start (GTK_HEADER_BAR (self->personal_file_sharing_headerbar), w);
+ self->personal_file_sharing_switch = w;
+
+ cc_sharing_panel_bind_networks_to_label (self,
+ networks,
+ self->personal_file_sharing_row);
+}
+
+static void
+remote_login_switch_activate (CcSharingPanel *self)
+{
+ cc_remote_login_set_enabled (cc_panel_get_cancellable (CC_PANEL (self)), GTK_SWITCH (self->remote_login_switch));
+}
+
+static void
+cc_sharing_panel_setup_remote_login_dialog (CcSharingPanel *self)
+{
+ cc_sharing_panel_bind_switch_to_label (self, self->remote_login_switch,
+ self->remote_login_row);
+
+ cc_sharing_panel_setup_label_with_hostname (self, self->remote_login_label);
+
+ g_signal_connect_object (self->remote_login_switch, "notify::active",
+ G_CALLBACK (remote_login_switch_activate), self, G_CONNECT_SWAPPED);
+ gtk_widget_set_sensitive (self->remote_login_switch, FALSE);
+
+ cc_remote_login_get_enabled (cc_panel_get_cancellable (CC_PANEL (self)),
+ GTK_SWITCH (self->remote_login_switch),
+ self->remote_login_row);
+}
+
+static gboolean
+cc_sharing_panel_check_schema_available (CcSharingPanel *self,
+ const gchar *schema_id)
+{
+ GSettingsSchemaSource *source;
+ g_autoptr(GSettingsSchema) schema = NULL;
+
+ source = g_settings_schema_source_get_default ();
+ if (!source)
+ return FALSE;
+
+ schema = g_settings_schema_source_lookup (source, schema_id, TRUE);
+ if (!schema)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+store_remote_desktop_credentials_timeout (gpointer user_data)
+{
+ CcSharingPanel *self = CC_SHARING_PANEL (user_data);
+ const char *username, *password;
+
+ username = gtk_editable_get_text (GTK_EDITABLE (self->remote_desktop_username_entry));
+ password = gtk_editable_get_text (GTK_EDITABLE (self->remote_desktop_password_entry));
+
+ if (username && password)
+ {
+ cc_grd_store_rdp_credentials (username, password,
+ cc_panel_get_cancellable (CC_PANEL (self)));
+ }
+
+ self->remote_desktop_store_credentials_id = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+remote_desktop_credentials_changed (CcSharingPanel *self)
+{
+ g_clear_handle_id (&self->remote_desktop_store_credentials_id,
+ g_source_remove);
+
+ self->remote_desktop_store_credentials_id =
+ g_timeout_add_seconds (REMOTE_DESKTOP_STORE_CREDENTIALS_TIMEOUT_S,
+ store_remote_desktop_credentials_timeout,
+ self);
+}
+
+static gboolean
+is_remote_desktop_enabled (CcSharingPanel *self)
+{
+ g_autoptr(GSettings) rdp_settings = NULL;
+
+ rdp_settings = g_settings_new (GNOME_REMOTE_DESKTOP_RDP_SCHEMA_ID);
+
+ if (!g_settings_get_boolean (rdp_settings, "enable"))
+ return FALSE;
+
+ return cc_is_service_active (REMOTE_DESKTOP_SERVICE, G_BUS_TYPE_SESSION);
+}
+
+static void
+enable_gnome_remote_desktop_service (CcSharingPanel *self)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (is_remote_desktop_enabled (self))
+ return;
+
+ if (!cc_enable_service (REMOTE_DESKTOP_SERVICE,
+ G_BUS_TYPE_SESSION,
+ &error))
+ g_warning ("Failed to enable remote desktop service: %s", error->message);
+}
+
+static void
+disable_gnome_remote_desktop_service (CcSharingPanel *self)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GSettings) rdp_settings = NULL;
+
+ rdp_settings = g_settings_new (GNOME_REMOTE_DESKTOP_RDP_SCHEMA_ID);
+
+ g_settings_set_boolean (rdp_settings, "enable", FALSE);
+
+ if (!cc_disable_service (REMOTE_DESKTOP_SERVICE,
+ G_BUS_TYPE_SESSION,
+ &error))
+ g_warning ("Failed to enable remote desktop service: %s", error->message);
+}
+
+static void
+calc_default_tls_paths (char **out_dir_path,
+ char **out_cert_path,
+ char **out_key_path)
+{
+ g_autofree char *dir_path = NULL;
+
+ dir_path = g_strdup_printf ("%s/gnome-remote-desktop",
+ g_get_user_data_dir ());
+
+ if (out_cert_path)
+ *out_cert_path = g_strdup_printf ("%s/rdp-tls.crt", dir_path);
+ if (out_key_path)
+ *out_key_path = g_strdup_printf ("%s/rdp-tls.key", dir_path);
+
+ if (out_dir_path)
+ *out_dir_path = g_steal_pointer (&dir_path);
+}
+
+static void
+set_tls_certificate (CcSharingPanel *self,
+ GTlsCertificate *tls_certificate)
+{
+ g_set_object (&self->remote_desktop_certificate,
+ tls_certificate);
+ gtk_widget_set_sensitive (self->remote_desktop_verify_encryption, TRUE);
+}
+
+static void
+on_certificate_generated (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CcSharingPanel *self;
+ g_autoptr(GTlsCertificate) tls_certificate = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autofree char *cert_path = NULL;
+ g_autofree char *key_path = NULL;
+ g_autoptr(GSettings) rdp_settings = NULL;
+
+ tls_certificate = bonsai_tls_certificate_new_generate_finish (res, &error);
+ if (!tls_certificate)
+ {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ g_warning ("Failed to generate TLS certificate: %s", error->message);
+ return;
+ }
+
+ self = CC_SHARING_PANEL (user_data);
+
+ calc_default_tls_paths (NULL, &cert_path, &key_path);
+
+ rdp_settings = g_settings_new (GNOME_REMOTE_DESKTOP_RDP_SCHEMA_ID);
+
+ g_settings_set_string (rdp_settings, "tls-cert", cert_path);
+ g_settings_set_string (rdp_settings, "tls-key", key_path);
+
+ set_tls_certificate (self, tls_certificate);
+
+ enable_gnome_remote_desktop_service (self);
+}
+
+static void
+enable_gnome_remote_desktop (CcSharingPanel *self)
+{
+ g_autofree char *dir_path = NULL;
+ g_autofree char *cert_path = NULL;
+ g_autofree char *key_path = NULL;
+ g_autoptr(GFile) dir = NULL;
+ g_autoptr(GFile) cert_file = NULL;
+ g_autoptr(GFile) key_file = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GSettings) rdp_settings = NULL;
+
+ rdp_settings = g_settings_new (GNOME_REMOTE_DESKTOP_RDP_SCHEMA_ID);
+
+ g_settings_set_boolean (rdp_settings, "enable", TRUE);
+
+ cert_path = g_settings_get_string (rdp_settings, "tls-cert");
+ key_path = g_settings_get_string (rdp_settings, "tls-key");
+ if (strlen (cert_path) > 0 &&
+ strlen (key_path) > 0)
+ {
+ g_autoptr(GTlsCertificate) tls_certificate = NULL;
+
+ tls_certificate = g_tls_certificate_new_from_file (cert_path, &error);
+ if (tls_certificate)
+ {
+ set_tls_certificate (self, tls_certificate);
+
+ enable_gnome_remote_desktop_service (self);
+ return;
+ }
+
+ g_warning ("Configured TLS certificate invalid: %s", error->message);
+ return;
+ }
+
+ calc_default_tls_paths (&dir_path, &cert_path, &key_path);
+
+ dir = g_file_new_for_path (dir_path);
+ if (!g_file_query_exists (dir, NULL))
+ {
+ if (!g_file_make_directory_with_parents (dir, NULL, &error))
+ {
+ g_warning ("Failed to create remote desktop certificate directory: %s",
+ error->message);
+ return;
+ }
+ }
+
+ cert_file = g_file_new_for_path (cert_path);
+ key_file = g_file_new_for_path (key_path);
+
+ if (g_file_query_exists (cert_file, NULL) &&
+ g_file_query_exists (key_file, NULL))
+ {
+ g_autoptr(GTlsCertificate) tls_certificate = NULL;
+
+ tls_certificate = g_tls_certificate_new_from_file (cert_path, &error);
+ if (tls_certificate)
+ {
+ g_settings_set_string (rdp_settings, "tls-cert", cert_path);
+ g_settings_set_string (rdp_settings, "tls-key", key_path);
+
+ set_tls_certificate (self, tls_certificate);
+
+ enable_gnome_remote_desktop_service (self);
+ return;
+ }
+
+ g_warning ("Existing TLS certificate invalid: %s", error->message);
+ return;
+ }
+
+ bonsai_tls_certificate_new_generate_async (cert_path,
+ key_path,
+ "US",
+ "GNOME",
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ on_certificate_generated,
+ self);
+}
+
+static void
+on_remote_desktop_state_changed (GtkWidget *widget,
+ GParamSpec *pspec,
+ CcSharingPanel *self)
+{
+ if (gtk_switch_get_active (GTK_SWITCH (widget)))
+ enable_gnome_remote_desktop (self);
+ else
+ disable_gnome_remote_desktop_service (self);
+}
+
+static char *
+get_hostname (void)
+{
+ g_autoptr(GDBusConnection) bus = NULL;
+ g_autoptr(GVariant) res = NULL;
+ g_autoptr(GVariant) inner = NULL;
+ g_autoptr(GError) error = NULL;
+ const char *hostname;
+
+ bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (bus == NULL)
+ {
+ g_warning ("Failed to get system bus connection: %s", error->message);
+ return NULL;
+ }
+ res = g_dbus_connection_call_sync (bus,
+ "org.freedesktop.hostname1",
+ "/org/freedesktop/hostname1",
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)",
+ "org.freedesktop.hostname1",
+ "PrettyHostname"),
+ (GVariantType*)"(v)",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ if (res == NULL)
+ {
+ g_warning ("Getting pretty hostname failed: %s", error->message);
+ return NULL;
+ }
+
+ g_variant_get (res, "(v)", &inner);
+ hostname = g_variant_get_string (inner, NULL);
+ if (g_strcmp0 (hostname, "") != 0)
+ return g_strdup (hostname);
+
+ g_clear_pointer (&inner, g_variant_unref);
+ g_clear_pointer (&res, g_variant_unref);
+
+ res = g_dbus_connection_call_sync (bus,
+ "org.freedesktop.hostname1",
+ "/org/freedesktop/hostname1",
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)",
+ "org.freedesktop.hostname1",
+ "Hostname"),
+ (GVariantType*)"(v)",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ if (res == NULL)
+ {
+ g_warning ("Getting hostname failed: %s", error->message);
+ return NULL;
+ }
+
+ g_variant_get (res, "(v)", &inner);
+ return g_variant_dup_string (inner, NULL);
+}
+
+static void
+add_toast (CcSharingPanel *self,
+ const char *message)
+{
+ adw_toast_overlay_add_toast (ADW_TOAST_OVERLAY (self->remote_desktop_toast_overlay),
+ adw_toast_new (message));
+}
+
+static void
+on_device_name_copy_clicked (GtkButton *button,
+ CcSharingPanel *self)
+{
+ GtkLabel *label = GTK_LABEL (self->remote_desktop_device_name_label);
+
+ gdk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button)),
+ gtk_label_get_text (label));
+ add_toast (self, _("Device name copied"));
+}
+
+static void
+on_device_address_copy_clicked (GtkButton *button,
+ CcSharingPanel *self)
+{
+ GtkLabel *label = GTK_LABEL (self->remote_desktop_address_label);
+
+ gdk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button)),
+ gtk_label_get_text (label));
+ add_toast (self, _("Device address copied"));
+}
+
+static void
+on_username_copy_clicked (GtkButton *button,
+ CcSharingPanel *self)
+{
+ GtkEditable *editable = GTK_EDITABLE (self->remote_desktop_username_entry);
+
+ gdk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button)),
+ gtk_editable_get_text (editable));
+ add_toast (self, _("Username copied"));
+}
+
+static void
+on_password_copy_clicked (GtkButton *button,
+ CcSharingPanel *self)
+{
+ GtkEditable *editable = GTK_EDITABLE (self->remote_desktop_password_entry);
+
+ gdk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button)),
+ gtk_editable_get_text (editable));
+ add_toast (self, _("Password copied"));
+}
+
+static pwquality_settings_t *
+get_pwq (void)
+{
+ static pwquality_settings_t *settings;
+
+ if (settings == NULL)
+ {
+ gchar *err = NULL;
+ gint rv = 0;
+
+ settings = pwquality_default_settings ();
+ pwquality_set_int_value (settings, PWQ_SETTING_MAX_SEQUENCE, 4);
+
+ rv = pwquality_read_config (settings, NULL, (gpointer)&err);
+ if (rv < 0)
+ {
+ g_warning ("Failed to read pwquality configuration: %s\n",
+ pwquality_strerror (NULL, 0, rv, err));
+ pwquality_free_settings (settings);
+
+ /* Load just default settings in case of failure. */
+ settings = pwquality_default_settings ();
+ pwquality_set_int_value (settings, PWQ_SETTING_MAX_SEQUENCE, 4);
+ }
+ }
+
+ return settings;
+}
+
+static char *
+pw_generate (void)
+{
+ char *res;
+ int rv;
+
+ rv = pwquality_generate (get_pwq (), 0, &res);
+
+ if (rv < 0) {
+ g_warning ("Password generation failed: %s\n",
+ pwquality_strerror (NULL, 0, rv, NULL));
+ return NULL;
+ }
+
+ return res;
+}
+
+static void
+cc_sharing_panel_setup_remote_desktop_dialog (CcSharingPanel *self)
+{
+ const gchar *username = NULL;
+ const gchar *password = NULL;
+ g_autoptr(GSettings) rdp_settings = NULL;
+ g_autofree char *hostname = NULL;
+
+ cc_sharing_panel_bind_switch_to_label (self, self->remote_desktop_switch,
+ self->remote_desktop_row);
+
+ cc_sharing_panel_setup_label_with_hostname (self, self->remote_desktop_address_label);
+
+ rdp_settings = g_settings_new (GNOME_REMOTE_DESKTOP_RDP_SCHEMA_ID);
+
+ g_settings_bind (rdp_settings,
+ "view-only",
+ self->remote_control_switch,
+ "active",
+ G_SETTINGS_BIND_DEFAULT | G_SETTINGS_BIND_INVERT_BOOLEAN);
+ g_object_bind_property (self->remote_desktop_switch, "state",
+ self->remote_control_switch, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ hostname = get_hostname ();
+ gtk_label_set_label (GTK_LABEL (self->remote_desktop_device_name_label),
+ hostname);
+
+ username = cc_grd_lookup_rdp_username (cc_panel_get_cancellable (CC_PANEL (self)));
+ password = cc_grd_lookup_rdp_password (cc_panel_get_cancellable (CC_PANEL (self)));
+ if (username != NULL)
+ gtk_editable_set_text (GTK_EDITABLE (self->remote_desktop_username_entry), username);
+ if (password != NULL)
+ gtk_editable_set_text (GTK_EDITABLE (self->remote_desktop_password_entry), password);
+
+ g_signal_connect_swapped (self->remote_desktop_username_entry,
+ "notify::text",
+ G_CALLBACK (remote_desktop_credentials_changed),
+ self);
+ g_signal_connect_swapped (self->remote_desktop_password_entry,
+ "notify::text",
+ G_CALLBACK (remote_desktop_credentials_changed),
+ self);
+ if (username == NULL)
+ {
+ struct passwd *pw = getpwuid (getuid ());
+ if (pw != NULL)
+ {
+ gtk_editable_set_text (GTK_EDITABLE (self->remote_desktop_username_entry),
+ pw->pw_name);
+ }
+ else
+ {
+ g_warning ("Failed to get username: %s", g_strerror (errno));
+ }
+ }
+
+ if (password == NULL)
+ {
+ char * pw = pw_generate ();
+ if (pw)
+ gtk_editable_set_text (GTK_EDITABLE (self->remote_desktop_password_entry),
+ pw );
+ }
+ g_signal_connect (self->remote_desktop_device_name_copy,
+ "clicked", G_CALLBACK (on_device_name_copy_clicked),
+ self);
+ g_signal_connect (self->remote_desktop_address_copy,
+ "clicked", G_CALLBACK (on_device_address_copy_clicked),
+ self);
+ g_signal_connect (self->remote_desktop_username_copy,
+ "clicked", G_CALLBACK (on_username_copy_clicked),
+ self);
+ g_signal_connect (self->remote_desktop_password_copy,
+ "clicked", G_CALLBACK (on_password_copy_clicked),
+ self);
+
+ g_signal_connect (self->remote_desktop_switch, "notify::state",
+ G_CALLBACK (on_remote_desktop_state_changed), self);
+
+ if (is_remote_desktop_enabled (self))
+ {
+ gtk_switch_set_active (GTK_SWITCH (self->remote_desktop_switch),
+ TRUE);
+ }
+}
+
+static void
+remote_desktop_name_appeared (GDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ gpointer user_data)
+{
+ CcSharingPanel *self = CC_SHARING_PANEL (user_data);
+
+ g_bus_unwatch_name (self->remote_desktop_name_watch);
+ self->remote_desktop_name_watch = 0;
+
+ cc_sharing_panel_setup_remote_desktop_dialog (self);
+ gtk_widget_show (self->remote_desktop_row);
+}
+
+static void
+check_remote_desktop_available (CcSharingPanel *self)
+{
+ if (!cc_sharing_panel_check_schema_available (self, GNOME_REMOTE_DESKTOP_SCHEMA_ID))
+ return;
+
+ if (!cc_sharing_panel_check_schema_available (self, GNOME_REMOTE_DESKTOP_RDP_SCHEMA_ID))
+ return;
+
+ self->remote_desktop_name_watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
+ "org.gnome.Mutter.RemoteDesktop",
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ remote_desktop_name_appeared,
+ NULL,
+ self,
+ NULL);
+}
+
+static void
+sharing_proxy_ready (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CcSharingPanel *self;
+ GDBusProxy *proxy;
+ g_autoptr(GError) error = NULL;
+
+ proxy = G_DBUS_PROXY (gsd_sharing_proxy_new_for_bus_finish (res, &error));
+ if (!proxy) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to get sharing proxy: %s", error->message);
+ return;
+ }
+
+ self = CC_SHARING_PANEL (user_data);
+ self->sharing_proxy = proxy;
+
+ /* media sharing */
+ cc_sharing_panel_setup_media_sharing_dialog (self);
+
+ /* personal file sharing */
+ if (cc_sharing_panel_check_schema_available (self, FILE_SHARING_SCHEMA_ID))
+ cc_sharing_panel_setup_personal_file_sharing_dialog (self);
+ else
+ gtk_widget_hide (self->personal_file_sharing_row);
+
+ /* remote login */
+ cc_sharing_panel_setup_remote_login_dialog (self);
+
+ /* screen sharing */
+ check_remote_desktop_available (self);
+ gtk_widget_hide (self->remote_desktop_row);
+}
+
+static void
+cc_sharing_panel_init (CcSharingPanel *self)
+{
+ g_autoptr(GtkCssProvider) provider = NULL;
+
+ g_resources_register (cc_sharing_get_resource ());
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ g_signal_connect_object (self->main_list_box, "row-activated",
+ G_CALLBACK (cc_sharing_panel_main_list_box_row_activated), self, G_CONNECT_SWAPPED);
+
+ g_signal_connect (self->media_sharing_dialog, "response",
+ G_CALLBACK (gtk_widget_hide), NULL);
+ g_signal_connect (self->personal_file_sharing_dialog, "response",
+ G_CALLBACK (gtk_widget_hide), NULL);
+ g_signal_connect (self->remote_login_dialog, "response",
+ G_CALLBACK (gtk_widget_hide), NULL);
+ g_signal_connect (self->remote_desktop_dialog, "response",
+ G_CALLBACK (gtk_widget_hide), NULL);
+
+ gtk_list_box_set_activate_on_single_click (GTK_LIST_BOX (self->main_list_box),
+ TRUE);
+
+ /* start the panel in the disabled state */
+ gtk_switch_set_active (GTK_SWITCH (self->master_switch), FALSE);
+ gtk_widget_set_sensitive (self->main_list_box, FALSE);
+ g_signal_connect_object (self->master_switch, "notify::active",
+ G_CALLBACK (cc_sharing_panel_master_switch_notify), self, G_CONNECT_SWAPPED);
+
+ gsd_sharing_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.gnome.SettingsDaemon.Sharing",
+ "/org/gnome/SettingsDaemon/Sharing",
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ sharing_proxy_ready,
+ self);
+
+ /* make sure the hostname entry isn't focused by default */
+ g_signal_connect_swapped (self, "map", G_CALLBACK (gtk_widget_grab_focus),
+ self->main_list_box);
+
+ provider = gtk_css_provider_new ();
+ gtk_css_provider_load_from_resource (provider,
+ "/org/gnome/control-center/sharing/sharing.css");
+ gtk_style_context_add_provider_for_display (gdk_display_get_default (),
+ GTK_STYLE_PROVIDER (provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+}
+
+CcSharingPanel *
+cc_sharing_panel_new (void)
+{
+ return g_object_new (CC_TYPE_SHARING_PANEL, NULL);
+}
diff --git a/panels/sharing/cc-sharing-panel.h b/panels/sharing/cc-sharing-panel.h
new file mode 100644
index 0000000..b8b69ab
--- /dev/null
+++ b/panels/sharing/cc-sharing-panel.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Intel, Inc
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Thomas Wood <thomas.wood@intel.com>
+ *
+ */
+
+#pragma once
+
+#include <shell/cc-shell.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_SHARING_PANEL (cc_sharing_panel_get_type ())
+G_DECLARE_FINAL_TYPE (CcSharingPanel, cc_sharing_panel, CC, SHARING_PANEL, CcPanel)
+
+CcSharingPanel *cc_sharing_panel_new (void);
+
+G_END_DECLS
diff --git a/panels/sharing/cc-sharing-panel.ui b/panels/sharing/cc-sharing-panel.ui
new file mode 100644
index 0000000..e02ca68
--- /dev/null
+++ b/panels/sharing/cc-sharing-panel.ui
@@ -0,0 +1,537 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="CcSharingPanel" parent="CcPanel">
+
+ <child type="titlebar-end">
+ <object class="GtkSwitch" id="master_switch">
+ <property name="valign">center</property>
+ <accessibility>
+ <property name="label" translatable="yes">Sharing</property>
+ </accessibility>
+ </object>
+ </child>
+
+ <child type="content">
+ <object class="GtkScrolledWindow" id="sharing_panel">
+ <property name="hscrollbar-policy">never</property>
+ <child>
+ <object class="AdwClamp">
+ <property name="margin_top">32</property>
+ <property name="margin_bottom">32</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <child>
+ <object class="GtkBox">
+ <property name="hexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="margin_bottom">12</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Computer Name</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">hostname_entry</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="CcHostnameEntry" id="hostname_entry">
+ <property name="margin_bottom">32</property>
+ <property name="invisible_char">●</property>
+ <accessibility>
+ <relation name="labelled-by">label6</relation>
+ </accessibility>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBox" id="main_list_box">
+ <property name="selection-mode">none</property>
+ <style>
+ <class name="boxed-list" />
+ </style>
+ <child>
+ <object class="CcListRow" id="personal_file_sharing_row">
+ <property name="show-arrow">True</property>
+ <property name="use-underline">True</property>
+ <property name="title" translatable="yes">_File Sharing</property>
+ </object>
+ </child>
+ <child>
+ <object class="CcListRow" id="remote_desktop_row">
+ <property name="show-arrow">True</property>
+ <property name="use-underline">True</property>
+ <property name="title" translatable="yes">Remote _Desktop</property>
+ </object>
+ </child>
+ <child>
+ <object class="CcListRow" id="media_sharing_row">
+ <property name="show-arrow">True</property>
+ <property name="use-underline">True</property>
+ <property name="title" translatable="yes">_Media Sharing</property>
+ </object>
+ </child>
+ <child>
+ <object class="CcListRow" id="remote_login_row">
+ <property name="show-arrow">True</property>
+ <property name="use-underline">True</property>
+ <property name="title" translatable="yes">_Remote Login</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkDialog" id="personal_file_sharing_dialog">
+ <property name="title" translatable="yes">File Sharing</property>
+ <property name="resizable">False</property>
+ <property name="use_header_bar">1</property>
+ <property name="modal">True</property>
+ <child>
+ <object class="GtkBox">
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="spacing">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkGrid" id="personal_file_sharing_grid">
+ <property name="margin_start">12</property>
+ <property name="margin_end">6</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="personal_file_sharing_label">
+ <property name="xalign">0</property>
+ <property name="label">File Sharing allows you to share your Public folder with others on your current network using: &lt;a href="dav://%s"&gt;dav://%s&lt;/a&gt;</property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ <property name="max-width-chars">36</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkGrid" id="require_password_grid">
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">6</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ <child>
+ <object class="GtkLabel" id="label17">
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">_Require Password</property>
+ <property name="use_markup">True</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">personal_file_sharing_require_password_switch</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="personal_file_sharing_password_label">
+ <property name="halign">start</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">_Password</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">personal_file_sharing_password_entry</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="personal_file_sharing_require_password_switch">
+ <property name="halign">end</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="personal_file_sharing_password_entry">
+ <property name="visibility">False</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="input_purpose">password</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="titlebar">
+ <object class="GtkHeaderBar" id="personal_file_sharing_headerbar">
+ <property name="show_title_buttons">True</property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkDialog" id="remote_login_dialog">
+ <property name="title" translatable="yes">Remote Login</property>
+ <property name="resizable">False</property>
+ <property name="use_header_bar">1</property>
+ <property name="modal">True</property>
+ <child>
+ <object class="GtkBox">
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="spacing">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="remote_login_label">
+ <property name="xalign">0</property>
+ <property name="label">When remote login is enabled, remote users can connect using the Secure Shell command:
+&lt;a href="ssh %s"&gt;ssh %s&lt;/a&gt;</property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ <property name="max-width-chars">36</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <style>
+ <class name="frame"/>
+ <class name="view"/>
+ </style>
+ <child>
+ <object class="GtkBox">
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Remote Login</property>
+ <property name="hexpand">True</property>
+ <property name="xalign">0</property>
+ <property name="mnemonic_widget">remote_login_switch</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="remote_login_switch">
+ <property name="halign">end</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ </object>
+ <object class="GtkDialog" id="remote_desktop_dialog">
+ <property name="title" translatable="yes">Remote Desktop</property>
+ <property name="modal">True</property>
+ <property name="default-width">640</property>
+ <child type="titlebar">
+ <object class="GtkHeaderBar">
+ <property name="show_title_buttons">True</property>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwToastOverlay" id="remote_desktop_toast_overlay">
+ <child>
+ <object class="AdwPreferencesPage">
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="description" translatable="yes">Remote desktop allows viewing and controlling your desktop from another computer.</property>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Remote Desktop</property>
+ <property name="subtitle" translatable="yes">Enable or disable remote desktop connections to this computer.</property>
+ <property name="activatable_widget">remote_desktop_switch</property>
+ <child type="suffix">
+ <object class="GtkSwitch" id="remote_desktop_switch">
+ <property name="state">False</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Remote Control</property>
+ <property name="subtitle" translatable="yes">Allows remote connections to control the screen.</property>
+ <property name="activatable_widget">remote_control_switch</property>
+ <child type="suffix">
+ <object class="GtkSwitch" id="remote_control_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">How to Connect</property>
+ <property name="description" translatable="yes">Connect to this computer using the device name or remote desktop address.</property>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Device Name</property>
+ <child type="suffix">
+ <object class="GtkBox">
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkLabel" id="remote_desktop_device_name_label">
+ <property name="selectable">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="remote_desktop_device_name_copy">
+ <property name="valign">center</property>
+ <property name="icon-name">edit-copy-symbolic</property>
+ <accessibility>
+ <property name="label" translatable="yes">Copy</property>
+ </accessibility>
+ <style>
+ <class name="flat"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Remote Desktop Address</property>
+ <child type="suffix">
+ <object class="GtkBox">
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkLabel" id="remote_desktop_address_label">
+ <property name="selectable">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="remote_desktop_address_copy">
+ <property name="valign">center</property>
+ <property name="icon-name">edit-copy-symbolic</property>
+ <accessibility>
+ <property name="label" translatable="yes">Copy</property>
+ </accessibility>
+ <style>
+ <class name="flat"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Authentication</property>
+ <property name="description" translatable="yes">The user name and password are required to connect to this computer.</property>
+
+ <child>
+ <object class="AdwEntryRow" id="remote_desktop_username_entry">
+ <property name="title" translatable="yes">User Name</property>
+ <child type="suffix">
+ <object class="GtkButton" id="remote_desktop_username_copy">
+ <property name="valign">center</property>
+ <property name="icon-name">edit-copy-symbolic</property>
+ <accessibility>
+ <property name="label" translatable="yes">Copy</property>
+ </accessibility>
+ <style>
+ <class name="flat"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPasswordEntryRow" id="remote_desktop_password_entry">
+ <property name="title" translatable="yes">Password</property>
+ <child type="suffix">
+ <object class="GtkButton" id="remote_desktop_password_copy">
+ <property name="valign">center</property>
+ <property name="icon-name">edit-copy-symbolic</property>
+ <accessibility>
+ <property name="label" translatable="yes">Copy</property>
+ </accessibility>
+ <style>
+ <class name="flat"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkButton" id="remote_desktop_verify_encryption">
+ <property name="sensitive">False</property>
+ <property name="halign">end</property>
+ <property name="margin-top">12</property>
+ <property name="label" translatable="yes">Verify Encryption</property>
+ <signal name="clicked" handler="remote_desktop_show_encryption_fingerprint" swapped="yes"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+
+ <object class="AdwWindow" id="remote_desktop_fingerprint_dialog">
+ <property name="default-width">360</property>
+ <property name="default-height">360</property>
+ <property name="hide-on-close">True</property>
+ <property name="modal">True</property>
+
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkWindowControls">
+ <property name="halign">end</property>
+ <property name="side">end</property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwStatusPage">
+ <property name="title" translatable="yes">Encryption Fingerprint</property>
+ <property name="description" translatable="yes">The encryption fingerprint can be seen in connecting clients and should be identical.</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="halign">center</property>
+ <style>
+ <class name="tls-cert-verification" />
+ </style>
+ <child>
+ <object class="GtkLabel" id="remote_desktop_fingerprint_left">
+ <style>
+ <class name="tls-cert-fingerprint" />
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="remote_desktop_fingerprint_right">
+ <style>
+ <class name="tls-cert-fingerprint" />
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+
+ <object class="GtkDialog" id="media_sharing_dialog">
+ <property name="title" translatable="yes">Media Sharing</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <child>
+ <object class="GtkBox">
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="spacing">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkGrid" id="shared_folders_grid">
+ <property name="margin_start">12</property>
+ <property name="margin_end">6</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label13">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Share music, photos and videos over the network.</property>
+ <property name="max-width-chars">36</property>
+ <property name="wrap">True</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label15">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Folders</property>
+ <property name="mnemonic_widget">shared_folders_listbox</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ <property name="column-span">2</property>
+ </layout>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBox" id="shared_folders_listbox">
+ <property name="selection-mode">none</property>
+ <style>
+ <class name="boxed-list" />
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">3</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="titlebar">
+ <object class="GtkHeaderBar" id="media_sharing_headerbar">
+ <property name="show_title_buttons">True</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/panels/sharing/cc-systemd-service.c b/panels/sharing/cc-systemd-service.c
new file mode 100644
index 0000000..d2b6346
--- /dev/null
+++ b/panels/sharing/cc-systemd-service.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2013 Intel, Inc
+ * Copyright (C) 2022 Red Hat, Inc
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Thomas Wood <thomas.wood@intel.com>
+ *
+ */
+
+#include "cc-systemd-service.h"
+
+gboolean
+cc_is_service_active (const char *service,
+ GBusType bus_type)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GDBusConnection) connection = NULL;
+ g_autoptr(GVariant) unit_path_variant = NULL;
+ g_autofree char *unit_path = NULL;
+ g_autoptr(GVariant) active_state_prop = NULL;
+ g_autoptr(GVariant) active_state_variant = NULL;
+ const char *active_state = NULL;
+ g_autoptr(GVariant) unit_state_prop = NULL;
+ g_autoptr(GVariant) unit_state_variant = NULL;
+ const char *unit_state = NULL;
+
+ connection = g_bus_get_sync (bus_type, NULL, &error);
+ if (!connection)
+ {
+ g_warning ("Failed connecting to D-Bus system bus: %s", error->message);
+ return FALSE;
+ }
+
+ unit_path_variant =
+ g_dbus_connection_call_sync (connection,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnit",
+ g_variant_new ("(s)",
+ service),
+ (GVariantType *) "(o)",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ NULL);
+
+ if (!unit_path_variant)
+ return FALSE;
+ g_variant_get_child (unit_path_variant, 0, "o", &unit_path);
+
+ active_state_prop =
+ g_dbus_connection_call_sync (connection,
+ "org.freedesktop.systemd1",
+ unit_path,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)",
+ "org.freedesktop.systemd1.Unit",
+ "ActiveState"),
+ (GVariantType *) "(v)",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ if (!active_state_prop)
+ {
+ g_warning ("Failed to get service active state: %s", error->message);
+ return FALSE;
+ }
+ g_variant_get_child (active_state_prop, 0, "v", &active_state_variant);
+ active_state = g_variant_get_string (active_state_variant, NULL);
+
+ if (g_strcmp0 (active_state, "active") != 0 &&
+ g_strcmp0 (active_state, "activating") != 0)
+ return FALSE;
+
+ unit_state_prop =
+ g_dbus_connection_call_sync (connection,
+ "org.freedesktop.systemd1",
+ unit_path,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)",
+ "org.freedesktop.systemd1.Unit",
+ "UnitFileState"),
+ (GVariantType *) "(v)",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ if (!unit_state_prop)
+ {
+ g_warning ("Failed to get service active state: %s", error->message);
+ return FALSE;
+ }
+ g_variant_get_child (unit_state_prop, 0, "v", &unit_state_variant);
+ unit_state = g_variant_get_string (unit_state_variant, NULL);
+
+ if (g_strcmp0 (unit_state, "enabled") == 0 ||
+ g_strcmp0 (unit_state, "static") == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+gboolean
+cc_enable_service (const char *service,
+ GBusType bus_type,
+ GError **error)
+{
+ g_autoptr(GDBusConnection) connection = NULL;
+ g_autoptr(GVariant) start_result = NULL;
+ g_autoptr(GVariant) enable_result = NULL;
+ const char *service_list[] = { service, NULL };
+
+ connection = g_bus_get_sync (bus_type, NULL, error);
+ if (!connection)
+ {
+ g_prefix_error_literal (error, "Failed connecting to D-Bus system bus: ");
+ return FALSE;
+ }
+
+ start_result = g_dbus_connection_call_sync (connection,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartUnit",
+ g_variant_new ("(ss)",
+ service,
+ "replace"),
+ (GVariantType *) "(o)",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ error);
+
+ if (!start_result)
+ {
+ g_prefix_error_literal (error, "Failed to start service: ");
+ return FALSE;
+ }
+
+ enable_result = g_dbus_connection_call_sync (connection,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "EnableUnitFiles",
+ g_variant_new ("(^asbb)",
+ service_list,
+ FALSE, FALSE),
+ (GVariantType *) "(ba(sss))",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ error);
+
+ if (!enable_result)
+ {
+ g_prefix_error_literal (error, "Failed to enable service: ");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+cc_disable_service (const char *service,
+ GBusType bus_type,
+ GError **error)
+{
+ g_autoptr(GDBusConnection) connection = NULL;
+ g_autoptr(GVariant) stop_result = NULL;
+ g_autoptr(GVariant) disable_result = NULL;
+ const char *service_list[] = { service, NULL };
+
+ connection = g_bus_get_sync (bus_type, NULL, error);
+ if (!connection)
+ {
+ g_prefix_error_literal (error, "Failed connecting to D-Bus system bus: ");
+ return FALSE;
+ }
+
+ stop_result = g_dbus_connection_call_sync (connection,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StopUnit",
+ g_variant_new ("(ss)", service, "replace"),
+ (GVariantType *) "(o)",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ error);
+ if (!stop_result)
+ {
+ g_prefix_error_literal (error, "Failed to stop service: ");
+ return FALSE;
+ }
+
+ disable_result = g_dbus_connection_call_sync (connection,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "DisableUnitFiles",
+ g_variant_new ("(^asb)", service_list, FALSE,
+ FALSE),
+ (GVariantType *) "(a(sss))",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ error);
+
+ if (!stop_result)
+ {
+ g_prefix_error_literal (error, "Failed to disable service: ");
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/panels/sharing/cc-systemd-service.h b/panels/sharing/cc-systemd-service.h
new file mode 100644
index 0000000..89b1d9a
--- /dev/null
+++ b/panels/sharing/cc-systemd-service.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 Red Hat, Inc
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+gboolean cc_is_service_active (const char *service,
+ GBusType bus_type);
+
+gboolean cc_enable_service (const char *service,
+ GBusType bus_type,
+ GError **error);
+
+gboolean cc_disable_service (const char *service,
+ GBusType bus_type,
+ GError **error);
diff --git a/panels/sharing/cc-tls-certificate.c b/panels/sharing/cc-tls-certificate.c
new file mode 100644
index 0000000..3b616f8
--- /dev/null
+++ b/panels/sharing/cc-tls-certificate.c
@@ -0,0 +1,511 @@
+/* cc-tls-certificate.c
+ *
+ * Copyright 2018 Christian Hergert <chergert@redhat.com>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <glib/gstdio.h>
+#include <gnutls/x509.h>
+
+#include "cc-tls-certificate.h"
+
+#define DEFAULT_KEY_SIZE 4096
+#define DEFAULT_EXPIRATION (60L*60L*24L*2L*365L)
+
+static void
+_gnutls_datum_clear (gnutls_datum_t *datum)
+{
+ if (datum->data != NULL)
+ gnutls_free (datum->data);
+}
+
+static void
+_gnutls_crt_free (gnutls_x509_crt_t *cert)
+{
+ if (cert != NULL)
+ gnutls_x509_crt_deinit (*cert);
+}
+
+static void
+_gnutls_privkey_free (gnutls_x509_privkey_t *privkey)
+{
+ if (privkey != NULL)
+ gnutls_x509_privkey_deinit (*privkey);
+}
+
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (gnutls_datum_t, _gnutls_datum_clear)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (gnutls_x509_crt_t, _gnutls_crt_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (gnutls_x509_privkey_t, _gnutls_privkey_free)
+
+typedef struct
+{
+ gchar *public_key_path;
+ gchar *private_key_path;
+ gchar *c;
+ gchar *cn;
+} GenerateData;
+
+static void
+generate_data_free (GenerateData *data)
+{
+ g_clear_pointer (&data->public_key_path, g_free);
+ g_clear_pointer (&data->private_key_path, g_free);
+ g_clear_pointer (&data->c, g_free);
+ g_clear_pointer (&data->cn, g_free);
+ g_slice_free (GenerateData, data);
+}
+
+static gboolean
+make_directory_parent (const gchar *path,
+ GError **error)
+{
+ g_autofree gchar *dir = NULL;
+
+ g_assert (path != NULL);
+ g_assert (error != NULL);
+
+ dir = g_path_get_dirname (path);
+
+ if (g_mkdir_with_parents (dir, 0750) == -1)
+ {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ g_io_error_from_errno (errno),
+ g_strerror (errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+bonsai_tls_certificate_generate_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GenerateData *data = task_data;
+ g_autoptr(GTlsCertificate) certificate = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(gnutls_x509_crt_t) certptr = NULL;
+ g_autoptr(gnutls_x509_privkey_t) privkeyptr = NULL;
+ g_auto(gnutls_datum_t) pubkey_data = {0};
+ g_auto(gnutls_datum_t) privkey_data = {0};
+ g_autofree char *dn = NULL;
+ gnutls_x509_privkey_t privkey;
+ gnutls_x509_crt_t cert;
+ guint32 serial = 1;
+ int gtlsret = 0;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (source_object == NULL);
+ g_assert (data != NULL);
+ g_assert (data->public_key_path != NULL);
+ g_assert (data->private_key_path != NULL);
+ g_assert (data->c != NULL);
+ g_assert (data->cn != NULL);
+
+ if (!make_directory_parent (data->public_key_path, &error) ||
+ !make_directory_parent (data->private_key_path, &error))
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ /*
+ * From the GnuTLS documentation:
+ *
+ * To be consistent with the X.509/PKIX specifications the provided serial
+ * should be a big-endian positive number (i.e. it's leftmost bit should be
+ * zero).
+ */
+ serial = GUINT32_TO_BE (serial);
+
+#define HANDLE_FAILURE(x) \
+ G_STMT_START { \
+ gtlsret = x; \
+ if (gtlsret != GNUTLS_E_SUCCESS) \
+ goto failure; \
+ } G_STMT_END
+
+ HANDLE_FAILURE (gnutls_x509_crt_init (&cert));
+ certptr = &cert;
+ HANDLE_FAILURE (gnutls_x509_crt_set_version (cert, 3));
+ HANDLE_FAILURE (gnutls_x509_crt_set_activation_time (cert, time (NULL)));
+ dn = g_strdup_printf ("C=%s,CN=%s", data->c, data->cn);
+ HANDLE_FAILURE (gnutls_x509_crt_set_dn (cert, dn, NULL));
+ HANDLE_FAILURE (gnutls_x509_crt_set_serial (cert, &serial, sizeof serial));
+ /* 5 years. We'll figure out key rotation in that time... */
+ HANDLE_FAILURE (gnutls_x509_crt_set_expiration_time (cert, time (NULL) + DEFAULT_EXPIRATION));
+ HANDLE_FAILURE (gnutls_x509_privkey_init (&privkey));
+ privkeyptr = &privkey;
+ HANDLE_FAILURE (gnutls_x509_privkey_generate (privkey, GNUTLS_PK_RSA, DEFAULT_KEY_SIZE, 0));
+ HANDLE_FAILURE (gnutls_x509_crt_set_key (cert, privkey));
+ HANDLE_FAILURE (gnutls_x509_crt_sign (cert, cert, privkey));
+ HANDLE_FAILURE (gnutls_x509_crt_export2 (cert, GNUTLS_X509_FMT_PEM, &pubkey_data));
+ if (!g_file_set_contents (data->public_key_path, (char *)pubkey_data.data, pubkey_data.size, &error))
+ goto failure;
+
+ HANDLE_FAILURE (gnutls_x509_privkey_export2 (privkey, GNUTLS_X509_FMT_PEM, &privkey_data));
+ if (!g_file_set_contents (data->private_key_path, (char*)privkey_data.data, privkey_data.size, &error))
+ goto failure;
+
+#undef HANDLE_FAILURE
+
+ if ((certificate = g_tls_certificate_new_from_files (data->public_key_path, data->private_key_path, &error)))
+ {
+ g_task_return_pointer (task, g_steal_pointer (&certificate), g_object_unref);
+ return;
+ }
+
+failure:
+
+ if (error != NULL)
+ g_task_return_error (task, g_steal_pointer (&error));
+ else if (gtlsret != 0)
+ g_task_return_new_error (task,
+ G_TLS_ERROR,
+ G_TLS_ERROR_MISC,
+ "GnuTLS Error: %s",
+ gnutls_strerror (gtlsret));
+ else
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Failed to generate TLS certificate pair");
+}
+
+void
+bonsai_tls_certificate_new_generate_async (const gchar *public_key_path,
+ const gchar *private_key_path,
+ const gchar *c,
+ const gchar *cn,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ GenerateData *data;
+
+ g_return_if_fail (public_key_path != NULL);
+ g_return_if_fail (private_key_path != NULL);
+ g_return_if_fail (c != NULL);
+ g_return_if_fail (cn != NULL);
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (NULL, cancellable, callback, user_data);
+ g_task_set_source_tag (task, bonsai_tls_certificate_new_generate_async);
+
+ data = g_slice_new0 (GenerateData);
+ data->public_key_path = g_strdup (public_key_path);
+ data->private_key_path = g_strdup (private_key_path);
+ data->c = g_strdup (c);
+ data->cn = g_strdup (cn);
+ g_task_set_task_data (task, data, (GDestroyNotify)generate_data_free);
+
+ g_task_run_in_thread (task, bonsai_tls_certificate_generate_worker);
+}
+
+GTlsCertificate *
+bonsai_tls_certificate_new_generate_finish (GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+GTlsCertificate *
+bonsai_tls_certificate_new_generate (const gchar *public_key_path,
+ const gchar *private_key_path,
+ const gchar *c,
+ const gchar *cn,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GTask) task = NULL;
+ GenerateData *data;
+
+ g_return_val_if_fail (public_key_path != NULL, NULL);
+ g_return_val_if_fail (private_key_path != NULL, NULL);
+ g_return_val_if_fail (c != NULL, NULL);
+ g_return_val_if_fail (cn != NULL, NULL);
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
+
+ task = g_task_new (NULL, cancellable, NULL, NULL);
+ g_task_set_source_tag (task, bonsai_tls_certificate_new_generate);
+
+ data = g_slice_new0 (GenerateData);
+ data->public_key_path = g_strdup (public_key_path);
+ data->private_key_path = g_strdup (private_key_path);
+ data->c = g_strdup (c);
+ data->cn = g_strdup (cn);
+ g_task_set_task_data (task, data, (GDestroyNotify)generate_data_free);
+
+ bonsai_tls_certificate_generate_worker (task, NULL, data, cancellable);
+
+ return g_task_propagate_pointer (task, error);
+}
+
+gchar *
+bonsai_tls_certificate_get_hash (GTlsCertificate *cert)
+{
+ g_autoptr(GByteArray) bytesarray = NULL;
+ g_autoptr(GChecksum) checksum = NULL;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE (cert), NULL);
+
+ g_object_get (cert, "certificate", &bytesarray, NULL);
+
+ checksum = g_checksum_new (G_CHECKSUM_SHA256);
+ g_checksum_update (checksum, bytesarray->data, bytesarray->len);
+
+ return g_ascii_strdown (g_checksum_get_string (checksum), -1);
+}
+
+typedef struct
+{
+ gchar *public_key_path;
+ gchar *private_key_path;
+ gchar *c;
+ gchar *cn;
+} NewFromFilesOrGenerate;
+
+static void
+new_from_files_or_generate_free (gpointer data)
+{
+ NewFromFilesOrGenerate *state = data;
+
+ g_clear_pointer (&state->public_key_path, g_free);
+ g_clear_pointer (&state->private_key_path, g_free);
+ g_clear_pointer (&state->c, g_free);
+ g_clear_pointer (&state->cn, g_free);
+ g_free (state);
+}
+
+static void
+bonsai_tls_certificate_new_from_files_or_generate_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ NewFromFilesOrGenerate *state = task_data;
+ g_autoptr(GTlsCertificate) certificate = NULL;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_assert (state != NULL);
+ g_assert (state->public_key_path != NULL);
+ g_assert (state->private_key_path != NULL);
+
+ /* Generate new public/private key for server if we need one.
+ * Ideally, we would generate something signed by a real CA
+ * for the user. But since this is "private cloud" oriented,
+ * we should be fine for now.
+ */
+ if (!g_file_test (state->public_key_path, G_FILE_TEST_EXISTS) ||
+ !g_file_test (state->private_key_path, G_FILE_TEST_EXISTS))
+ certificate = bonsai_tls_certificate_new_generate (state->public_key_path,
+ state->private_key_path,
+ state->c,
+ state->cn,
+ cancellable,
+ &error);
+ else
+ certificate = g_tls_certificate_new_from_files (state->public_key_path,
+ state->private_key_path,
+ &error);
+
+ if (certificate == NULL)
+ g_task_return_error (task, g_steal_pointer (&error));
+ else
+ g_task_return_pointer (task, g_steal_pointer (&certificate), g_object_unref);
+}
+
+/**
+ * bonsai_tls_certificate_new_from_files_or_generate_async:
+ * @public_key_path: the path to the public key file
+ * @private_key_path: the path to the private key file
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @callback: a callback to execute upon completion
+ * @user_data: closure data for @callback
+ *
+ * Asynchronously requests that a certificate is loaded, or generate one if it
+ * does not yet exist. The generated certificate is a self-signed certificate.
+ *
+ * Since: 0.2
+ */
+void
+bonsai_tls_certificate_new_from_files_or_generate_async (const gchar *public_key_path,
+ const gchar *private_key_path,
+ const gchar *c,
+ const gchar *cn,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ NewFromFilesOrGenerate state;
+
+ g_assert (public_key_path != NULL);
+ g_assert (private_key_path != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ state.public_key_path = g_strdup (public_key_path);
+ state.private_key_path = g_strdup (private_key_path);
+ state.c = g_strdup (c);
+ state.cn = g_strdup (cn);
+
+ task = g_task_new (NULL, cancellable, callback, user_data);
+ g_task_set_source_tag (task, bonsai_tls_certificate_new_from_files_or_generate_async);
+ g_task_set_task_data (task, g_memdup2 (&state, sizeof state), new_from_files_or_generate_free);
+ g_task_run_in_thread (task, bonsai_tls_certificate_new_from_files_or_generate_worker);
+}
+
+/**
+ * bonsai_tls_certificate_new_from_files_or_generate_finish:
+ * @result: a #GAsyncResult provided to callback
+ * @error: a location for a #GError, or %NULL
+ *
+ * Completes a request to
+ * bonsai_tls_certificate_new_from_files_or_generate_async() which will
+ * either load a #GTlsCertificate for the files if they exist, or generate
+ * a new self-signed certificate in their place.
+ *
+ * Returns: (transfer none): a #GTlsCertificate or %NULL and @error is set.
+ *
+ * Since: 0.2
+ */
+GTlsCertificate *
+bonsai_tls_certificate_new_from_files_or_generate_finish (GAsyncResult *result,
+ GError **error)
+{
+ g_assert (G_IS_TASK (result));
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+/**
+ * bonsai_tls_certificate_new_from_files_or_generate:
+ * @public_key_path: the path to the public key
+ * @private_key_path: the path to the private key
+ * @c: the C for the certificate
+ * @cn: the CN for the certificate
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @error: the location for the error
+ *
+ * Loads a certificate or generates a new self-signed certificate in
+ * it's place.
+ *
+ * Returns: (transfer full): a #GTlsCertificate or %NULL and @error is set
+ *
+ * Since: 0.2
+ */
+GTlsCertificate *
+bonsai_tls_certificate_new_from_files_or_generate (const gchar *public_key_path,
+ const gchar *private_key_path,
+ const gchar *c,
+ const gchar *cn,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsCertificate *ret;
+
+ g_return_val_if_fail (public_key_path != NULL, NULL);
+ g_return_val_if_fail (private_key_path != NULL, NULL);
+ g_return_val_if_fail (c != NULL, NULL);
+ g_return_val_if_fail (cn != NULL, NULL);
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
+
+ if (!(ret = g_tls_certificate_new_from_files (public_key_path, private_key_path, NULL)))
+ ret = bonsai_tls_certificate_new_generate (public_key_path,
+ private_key_path,
+ c,
+ cn,
+ cancellable,
+ error);
+
+ return g_steal_pointer (&ret);
+}
+
+/**
+ * bonsai_tls_certificate_new_for_user:
+ * @public_key_path: the path to the public key
+ * @private_key_path: the path to the private key
+ *
+ * This is a simplified form to create a new certificate or load a previously
+ * created certificate for the current user.
+ *
+ * Returns: (transfer none): a #GTlsCertificate or %NULL and @error is set.
+ *
+ * Since: 0.2
+ */
+GTlsCertificate *
+bonsai_tls_certificate_new_for_user (GCancellable *cancellable,
+ GError **error)
+{
+ g_autofree gchar *public_key_path = NULL;
+ g_autofree gchar *private_key_path = NULL;
+
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
+
+ public_key_path = g_build_filename (g_get_user_config_dir (), "bonsai", "public.key", NULL);
+ private_key_path = g_build_filename (g_get_user_config_dir (), "bonsai", "private.key", NULL);
+
+ return bonsai_tls_certificate_new_from_files_or_generate (public_key_path,
+ private_key_path,
+ "US",
+ "GNOME",
+ cancellable,
+ error);
+}
+
+gboolean
+bonsai_is_tls_hash (const gchar *hash)
+{
+ guint len = 0;
+
+ if (hash == NULL)
+ return FALSE;
+
+ for (; *hash; hash++)
+ {
+ if (len == 64)
+ return FALSE;
+
+ switch (*hash)
+ {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ len++;
+ break;
+
+ default:
+ return FALSE;
+ }
+ }
+
+ return len == 64;
+}
+
diff --git a/panels/sharing/cc-tls-certificate.h b/panels/sharing/cc-tls-certificate.h
new file mode 100644
index 0000000..9b9a8d5
--- /dev/null
+++ b/panels/sharing/cc-tls-certificate.h
@@ -0,0 +1,63 @@
+/* cc-tls-certificate.h
+ *
+ * Copyright 2018 Christian Hergert <chergert@redhat.com>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+gboolean bonsai_is_tls_hash (const gchar *hash);
+GTlsCertificate *bonsai_tls_certificate_new_generate (const gchar *public_key_path,
+ const gchar *private_key_path,
+ const gchar *c,
+ const gchar *cn,
+ GCancellable *cancellable,
+ GError **error);
+void bonsai_tls_certificate_new_generate_async (const gchar *public_key_path,
+ const gchar *private_key_path,
+ const gchar *c,
+ const gchar *cn,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GTlsCertificate *bonsai_tls_certificate_new_generate_finish (GAsyncResult *result,
+ GError **error);
+gchar *bonsai_tls_certificate_get_hash (GTlsCertificate *cert);
+GTlsCertificate *bonsai_tls_certificate_new_from_files_or_generate (const gchar *public_key_path,
+ const gchar *private_key_path,
+ const gchar *c,
+ const gchar *cn,
+ GCancellable *cancellable,
+ GError **error);
+void bonsai_tls_certificate_new_from_files_or_generate_async (const gchar *public_key_path,
+ const gchar *private_key_path,
+ const gchar *c,
+ const gchar *cn,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GTlsCertificate *bonsai_tls_certificate_new_from_files_or_generate_finish (GAsyncResult *result,
+ GError **error);
+GTlsCertificate *bonsai_tls_certificate_new_for_user (GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
diff --git a/panels/sharing/file-share-properties.c b/panels/sharing/file-share-properties.c
new file mode 100644
index 0000000..dcf741c
--- /dev/null
+++ b/panels/sharing/file-share-properties.c
@@ -0,0 +1,55 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+
+/*
+ * Copyright (C) 2004 Red Hat, Inc.
+ *
+ * Nautilus 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.
+ *
+ * Nautilus 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Authors: Alexander Larsson <alexl@redhat.com>
+ *
+ */
+
+#include "file-share-properties.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+
+#define REALM "Please log in as the user guest"
+#define USER "guest"
+
+void
+file_share_write_out_password (const char *password)
+{
+ g_autofree gchar *to_hash = NULL;
+ g_autofree gchar *ascii_digest = NULL;
+ g_autofree gchar *line = NULL;
+ g_autofree gchar *filename = NULL;
+ FILE *file;
+
+ to_hash = g_strdup_printf ("%s:%s:%s", USER, REALM, password);
+ ascii_digest = g_compute_checksum_for_string (G_CHECKSUM_MD5, to_hash, strlen (to_hash));
+ line = g_strdup_printf ("%s:%s:%s\n", USER, REALM, ascii_digest);
+
+ filename = g_build_filename (g_get_user_config_dir (), "user-share", "passwd", NULL);
+
+ file = fopen (filename, "w");
+ if (file != NULL) {
+ fwrite (line, strlen (line), 1, file);
+ fclose (file);
+ }
+}
diff --git a/panels/sharing/file-share-properties.h b/panels/sharing/file-share-properties.h
new file mode 100644
index 0000000..b29d23f
--- /dev/null
+++ b/panels/sharing/file-share-properties.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2013 Intel, Inc
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#pragma once
+
+void file_share_write_out_password (const char *password);
diff --git a/panels/sharing/gnome-sharing-panel.desktop.in.in b/panels/sharing/gnome-sharing-panel.desktop.in.in
new file mode 100644
index 0000000..8dc340c
--- /dev/null
+++ b/panels/sharing/gnome-sharing-panel.desktop.in.in
@@ -0,0 +1,15 @@
+[Desktop Entry]
+Name=Sharing
+Comment=Control what you want to share with others
+Exec=gnome-control-center sharing
+# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+Icon=org.gnome.Settings-sharing-symbolic
+Terminal=false
+Type=Application
+NoDisplay=true
+StartupNotify=true
+Categories=GNOME;GTK;Settings;DesktopSettings;X-GNOME-Settings-Panel;X-GNOME-AccountSettings;
+OnlyShowIn=GNOME;Unity;
+X-GNOME-Settings-Panel=sharing
+# Translators: Search terms to find the Sharing panel. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
+Keywords=share;sharing;ssh;host;name;remote;desktop;media;audio;video;pictures;photos;movies;server;renderer;
diff --git a/panels/sharing/gsd-sharing-enums.h b/panels/sharing/gsd-sharing-enums.h
new file mode 100644
index 0000000..d117845
--- /dev/null
+++ b/panels/sharing/gsd-sharing-enums.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2014 Bastien Nocera <hadess@hadess.net>
+ *
+ * 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/>.
+ *
+ */
+
+#pragma once
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GSD_SHARING_STATUS_OFFLINE,
+ GSD_SHARING_STATUS_DISABLED_MOBILE_BROADBAND,
+ GSD_SHARING_STATUS_DISABLED_LOW_SECURITY,
+ GSD_SHARING_STATUS_AVAILABLE
+} GsdSharingStatus;
+
+G_END_DECLS
diff --git a/panels/sharing/icons/meson.build b/panels/sharing/icons/meson.build
new file mode 100644
index 0000000..4ee7e38
--- /dev/null
+++ b/panels/sharing/icons/meson.build
@@ -0,0 +1,4 @@
+install_data(
+ 'scalable/org.gnome.Settings-sharing-symbolic.svg',
+ install_dir: join_paths(control_center_icondir, 'hicolor', 'scalable', 'apps')
+)
diff --git a/panels/sharing/icons/scalable/org.gnome.Settings-sharing-symbolic.svg b/panels/sharing/icons/scalable/org.gnome.Settings-sharing-symbolic.svg
new file mode 100644
index 0000000..acd455b
--- /dev/null
+++ b/panels/sharing/icons/scalable/org.gnome.Settings-sharing-symbolic.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <g fill="#2e3436">
+ <path d="m 6 8 c 0 1.65625 -1.34375 3 -3 3 s -3 -1.34375 -3 -3 s 1.34375 -3 3 -3 s 3 1.34375 3 3 z m 0 0"/>
+ <path d="m 12.554688 2.105469 l -11.789063 5.894531 l 11.789063 5.894531 l 0.894531 -1.789062 l -8.210938 -4.105469 l 8.210938 -4.105469 z m 0 0"/>
+ <path d="m 16 3 c 0 1.65625 -1.34375 3 -3 3 s -3 -1.34375 -3 -3 s 1.34375 -3 3 -3 s 3 1.34375 3 3 z m 0 0"/>
+ <path d="m 16 13 c 0 1.65625 -1.34375 3 -3 3 s -3 -1.34375 -3 -3 s 1.34375 -3 3 -3 s 3 1.34375 3 3 z m 0 0"/>
+ </g>
+</svg>
diff --git a/panels/sharing/meson.build b/panels/sharing/meson.build
new file mode 100644
index 0000000..c674cfb
--- /dev/null
+++ b/panels/sharing/meson.build
@@ -0,0 +1,114 @@
+panels_list += cappletname
+desktop = 'gnome-@0@-panel.desktop'.format(cappletname)
+
+desktop_in = configure_file(
+ input: desktop + '.in.in',
+ output: desktop + '.in',
+ configuration: desktop_conf
+)
+
+i18n.merge_file(
+ type: 'desktop',
+ input: desktop_in,
+ output: desktop,
+ po_dir: po_dir,
+ install: true,
+ install_dir: control_center_desktopdir
+)
+
+polkit_conf = configuration_data()
+polkit_conf.set('libexecdir', control_center_libexecdir)
+
+polkit = 'org.gnome.controlcenter.remote-login-helper.policy'
+
+polkit_in = configure_file(
+ input: polkit + '.in.in',
+ output: polkit + '.in',
+ configuration: polkit_conf
+)
+
+i18n.merge_file(
+ input: polkit_in,
+ output: polkit,
+ po_dir: po_dir,
+ install: true,
+ install_dir: join_paths(control_center_datadir, 'polkit-1', 'actions')
+)
+
+sources = files(
+ 'cc-sharing-panel.c',
+ 'cc-media-sharing.c',
+ 'cc-remote-login.c',
+ 'cc-sharing-networks.c',
+ 'cc-gnome-remote-desktop.c',
+ 'cc-tls-certificate.c',
+ 'cc-systemd-service.c',
+ 'file-share-properties.c',
+)
+
+resource_data = files(
+ 'cc-sharing-panel.ui',
+ 'cc-sharing-networks.ui',
+)
+
+sources += gnome.compile_resources(
+ 'cc-' + cappletname + '-resources',
+ cappletname + '.gresource.xml',
+ c_name: 'cc_' + cappletname,
+ dependencies: resource_data,
+ export: true
+)
+
+settings_daemon = 'org.gnome.SettingsDaemon'
+gdbus = settings_daemon + '.Sharing'
+
+sources += gnome.gdbus_codegen(
+ gdbus,
+ gdbus + '.xml',
+ interface_prefix: settings_daemon + '.',
+ namespace: 'Gsd'
+)
+
+cflags += [
+ '-DLIBEXECDIR="@0@"'.format(control_center_libexecdir),
+ '-DSYSCONFDIR="@0@"'.format(control_center_sysconfdir)
+]
+
+libsecret_dep = dependency('libsecret-1')
+gnutls_dep = dependency('gnutls')
+
+panels_libs += static_library(
+ cappletname,
+ sources: sources,
+ include_directories: [ top_inc, common_inc ],
+ dependencies: [
+ common_deps,
+ libsecret_dep,
+ gnutls_dep,
+ gcr_dep,
+ pwquality_dep,
+ ],
+ c_args: cflags
+)
+
+name = 'cc-remote-login-helper'
+
+deps = [
+ gio_dep,
+ glib_dep,
+]
+
+executable(
+ name,
+ sources: [
+ name + '.c',
+ 'cc-systemd-service.c',
+ ],
+ include_directories: top_inc,
+ dependencies: deps,
+ c_args: cflags,
+ install: true,
+ install_dir: control_center_libexecdir
+)
+
+subdir('icons')
diff --git a/panels/sharing/org.gnome.SettingsDaemon.Sharing.xml b/panels/sharing/org.gnome.SettingsDaemon.Sharing.xml
new file mode 100644
index 0000000..1710c3e
--- /dev/null
+++ b/panels/sharing/org.gnome.SettingsDaemon.Sharing.xml
@@ -0,0 +1,19 @@
+<node name="/org/gnome/SettingsDaemon/Sharing">
+ <interface name='org.gnome.SettingsDaemon.Sharing'>
+ <property name='CurrentNetworkName' type='s' access='read'/>
+ <property name='CurrentNetwork' type='s' access='read'/>
+ <property name='CarrierType' type='s' access='read'/>
+ <property name='SharingStatus' type='u' access='read'/>
+ <method name='EnableService'>
+ <arg name='service_name' direction='in' type='s'/>
+ </method>
+ <method name='DisableService'>
+ <arg name='service_name' direction='in' type='s'/>
+ <arg name='network' direction='in' type='s'/>
+ </method>
+ <method name='ListNetworks'>
+ <arg name='service_name' direction='in' type='s'/>
+ <arg name='networks' direction='out' type='a(sss)'/>
+ </method>
+ </interface>
+</node>
diff --git a/panels/sharing/org.gnome.controlcenter.remote-login-helper.policy.in.in b/panels/sharing/org.gnome.controlcenter.remote-login-helper.policy.in.in
new file mode 100644
index 0000000..7fabac2
--- /dev/null
+++ b/panels/sharing/org.gnome.controlcenter.remote-login-helper.policy.in.in
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+<policyconfig>
+
+ <vendor>The GNOME Project</vendor>
+ <vendor_url>http://www.gnome.org/</vendor_url>
+
+ <action id="org.gnome.controlcenter.remote-login-helper">
+ <description>Enable or disable remote login</description>
+ <message>Authentication is required to enable or disable remote login</message>
+ <defaults>
+ <allow_any>no</allow_any>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.exec.path">@libexecdir@/cc-remote-login-helper</annotate>
+ </action>
+
+</policyconfig>
+
diff --git a/panels/sharing/sharing.css b/panels/sharing/sharing.css
new file mode 100644
index 0000000..cf18ab3
--- /dev/null
+++ b/panels/sharing/sharing.css
@@ -0,0 +1,4 @@
+.tls-cert-fingerprint {
+ font-family: monospace;
+ margin: 10px;
+}
diff --git a/panels/sharing/sharing.gresource.xml b/panels/sharing/sharing.gresource.xml
new file mode 100644
index 0000000..275f884
--- /dev/null
+++ b/panels/sharing/sharing.gresource.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/control-center/sharing">
+ <file preprocess="xml-stripblanks">cc-sharing-panel.ui</file>
+ <file preprocess="xml-stripblanks">cc-sharing-networks.ui</file>
+ <file>sharing.css</file>
+ </gresource>
+</gresources>