/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Copyright © 2014, 2015 Igalia S.L. * * This file is part of Epiphany. * * Epiphany 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 3 of the License, or * (at your option) any later version. * * Epiphany 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 Epiphany. If not, see . */ #include "config.h" #include "ephy-security-popover.h" #include #include "ephy-certificate-dialog.h" #include "ephy-embed-shell.h" #include "ephy-lib-type-builtins.h" #include "ephy-permissions-manager.h" #include "ephy-settings.h" #include "ephy-uri-helpers.h" /** * SECTION:ephy-security-popover * @short_description: A popover to show basic TLS connection information * * #EphySecurityPopover shows basic information about a TLS connection * and allows opening #EphyCertificateDialog for more detailed information. It * can also be used to show that a connection does not use TLS at all. */ enum { PROP_0, PROP_ADDRESS, PROP_CERTIFICATE, PROP_SECURITY_LEVEL, PROP_TLS_ERRORS, LAST_PROP }; static GParamSpec *obj_properties[LAST_PROP]; struct _EphySecurityPopover { GtkPopover parent_instance; char *address; char *hostname; guint permission_pos; GtkWidget *lock_image; GtkWidget *host_label; GtkWidget *security_label; GtkWidget *ad_combobox; GtkWidget *notification_combobox; GtkWidget *save_password_combobox; GtkWidget *access_location_combobox; GtkWidget *access_microphone_combobox; GtkWidget *access_webcam_combobox; GtkWidget *autoplay_combobox; GtkWidget *grid; GTlsCertificate *certificate; GTlsCertificateFlags tls_errors; EphySecurityLevel security_level; }; G_DEFINE_TYPE (EphySecurityPopover, ephy_security_popover, GTK_TYPE_POPOVER) static void set_permission_ads_combobox_state (EphyPermissionsManager *permissions_manager, gint permission_id, gchar *origin, GtkWidget *widget) { GSettings *web_settings = ephy_settings_get (EPHY_PREFS_WEB_SCHEMA); EphyPermission permission; permission = ephy_permissions_manager_get_permission (permissions_manager, permission_id, origin); switch (permission) { case EPHY_PERMISSION_UNDECIDED: gtk_combo_box_set_active (GTK_COMBO_BOX (widget), g_settings_get_boolean (web_settings, EPHY_PREFS_WEB_ENABLE_ADBLOCK)); break; case EPHY_PERMISSION_DENY: gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 1); break; case EPHY_PERMISSION_PERMIT: gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0); break; } } static void set_permission_combobox_state (EphyPermissionsManager *permissions_manager, gint permission_id, gchar *origin, GtkWidget *widget) { EphyPermission permission; permission = ephy_permissions_manager_get_permission (permissions_manager, permission_id, origin); switch (permission) { case EPHY_PERMISSION_PERMIT: gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0); break; case EPHY_PERMISSION_DENY: gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 1); break; case EPHY_PERMISSION_UNDECIDED: gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 2); break; } } static void ephy_security_popover_set_address (EphySecurityPopover *popover, const char *address) { EphyPermissionsManager *permissions_manager; g_autoptr (GUri) uri = NULL; g_autofree gchar *origin = NULL; g_autofree gchar *uri_text = NULL; uri = g_uri_parse (address, G_URI_FLAGS_NONE, NULL); uri_text = g_markup_printf_escaped ("%s", g_uri_get_host (uri)); /* Label when clicking the lock icon on a secure page. %s is the website's hostname. */ gtk_label_set_markup (GTK_LABEL (popover->host_label), uri_text); popover->address = g_strdup (address); popover->hostname = g_strdup (g_uri_get_host (uri)); origin = ephy_uri_to_security_origin (address); if (!origin) return; permissions_manager = ephy_embed_shell_get_permissions_manager (ephy_embed_shell_get_default ()); set_permission_ads_combobox_state (permissions_manager, EPHY_PERMISSION_TYPE_SHOW_ADS, origin, popover->ad_combobox); set_permission_combobox_state (permissions_manager, EPHY_PERMISSION_TYPE_SHOW_NOTIFICATIONS, origin, popover->notification_combobox); set_permission_combobox_state (permissions_manager, EPHY_PERMISSION_TYPE_SAVE_PASSWORD, origin, popover->save_password_combobox); set_permission_combobox_state (permissions_manager, EPHY_PERMISSION_TYPE_ACCESS_LOCATION, origin, popover->access_location_combobox); set_permission_combobox_state (permissions_manager, EPHY_PERMISSION_TYPE_ACCESS_MICROPHONE, origin, popover->access_microphone_combobox); set_permission_combobox_state (permissions_manager, EPHY_PERMISSION_TYPE_ACCESS_WEBCAM, origin, popover->access_webcam_combobox); set_permission_combobox_state (permissions_manager, EPHY_PERMISSION_TYPE_AUTOPLAY_POLICY, origin, popover->autoplay_combobox); } static void ephy_security_popover_set_certificate (EphySecurityPopover *popover, GTlsCertificate *certificate) { if (certificate) popover->certificate = g_object_ref (certificate); } static void ephy_security_popover_set_security_level (EphySecurityPopover *popover, EphySecurityLevel security_level) { GIcon *icon; g_autofree gchar *address_text = NULL; g_autofree gchar *label_text = NULL; popover->security_level = security_level; address_text = g_markup_printf_escaped ("%s", popover->hostname); gtk_label_set_markup (GTK_LABEL (popover->host_label), address_text); switch (security_level) { case EPHY_SECURITY_LEVEL_UNACCEPTABLE_CERTIFICATE: /* Label in certificate popover when site is untrusted. %s is a URL. */ label_text = g_strdup_printf (_("This web site’s digital identification is not trusted. " "You may have connected to an attacker pretending to be %s."), address_text); gtk_label_set_markup (GTK_LABEL (popover->security_label), label_text); break; case EPHY_SECURITY_LEVEL_NO_SECURITY: /* Label in certificate popover when site uses HTTP. %s is a URL. */ label_text = g_strdup_printf (_("This site has no security. An attacker could see any information " "you send, or control the content that you see.")); gtk_label_set_markup (GTK_LABEL (popover->security_label), label_text); break; case EPHY_SECURITY_LEVEL_MIXED_CONTENT: gtk_label_set_text (GTK_LABEL (popover->security_label), /* Label in certificate popover when site sends mixed content. */ _("This web site did not properly secure your connection.")); break; case EPHY_SECURITY_LEVEL_STRONG_SECURITY: gtk_label_set_text (GTK_LABEL (popover->security_label), /* Label in certificate popover on secure sites. */ _("Your connection seems to be secure.")); break; case EPHY_SECURITY_LEVEL_TO_BE_DETERMINED: case EPHY_SECURITY_LEVEL_LOCAL_PAGE: default: g_assert_not_reached (); } icon = g_themed_icon_new_with_default_fallbacks (ephy_security_level_to_icon_name (security_level)); gtk_image_set_from_gicon (GTK_IMAGE (popover->lock_image), icon, GTK_ICON_SIZE_BUTTON); g_object_unref (icon); } static void certificate_button_clicked_cb (GtkButton *button, gpointer user_data) { EphySecurityPopover *popover = EPHY_SECURITY_POPOVER (user_data); GtkWidget *dialog; dialog = ephy_certificate_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (popover))), popover->address, popover->certificate, popover->tls_errors, popover->security_level); gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); gtk_popover_popdown (GTK_POPOVER (popover)); gtk_widget_show (dialog); } static void ephy_security_popover_constructed (GObject *object) { EphySecurityPopover *popover = EPHY_SECURITY_POPOVER (object); GtkWidget *certificate_button; G_OBJECT_CLASS (ephy_security_popover_parent_class)->constructed (object); if (!popover->certificate) return; certificate_button = gtk_button_new_with_mnemonic (_("_View Certificate…")); gtk_widget_set_halign (certificate_button, GTK_ALIGN_END); gtk_widget_set_margin_top (certificate_button, 5); gtk_widget_set_receives_default (certificate_button, FALSE); gtk_widget_show (certificate_button); g_signal_connect (certificate_button, "clicked", G_CALLBACK (certificate_button_clicked_cb), popover); gtk_grid_attach (GTK_GRID (popover->grid), certificate_button, 1, 2, 1, 1); } static void ephy_security_popover_dispose (GObject *object) { EphySecurityPopover *popover = EPHY_SECURITY_POPOVER (object); g_clear_object (&popover->certificate); G_OBJECT_CLASS (ephy_security_popover_parent_class)->dispose (object); } static void ephy_security_popover_finalize (GObject *object) { EphySecurityPopover *popover = EPHY_SECURITY_POPOVER (object); g_free (popover->address); g_free (popover->hostname); G_OBJECT_CLASS (ephy_security_popover_parent_class)->finalize (object); } static void ephy_security_popover_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { EphySecurityPopover *popover = EPHY_SECURITY_POPOVER (object); switch (prop_id) { case PROP_ADDRESS: ephy_security_popover_set_address (popover, g_value_get_string (value)); break; case PROP_CERTIFICATE: ephy_security_popover_set_certificate (popover, g_value_get_object (value)); break; case PROP_SECURITY_LEVEL: ephy_security_popover_set_security_level (popover, g_value_get_enum (value)); break; case PROP_TLS_ERRORS: popover->tls_errors = g_value_get_flags (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void ephy_security_popover_get_preferred_width (GtkWidget *widget, gint *minimum_width, gint *natural_width) { GTK_WIDGET_CLASS (ephy_security_popover_parent_class)->get_preferred_width (widget, minimum_width, natural_width); if (*natural_width > 360) *natural_width = MAX (360, *minimum_width); } static void ephy_security_popover_class_init (EphySecurityPopoverClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->constructed = ephy_security_popover_constructed; object_class->dispose = ephy_security_popover_dispose; object_class->finalize = ephy_security_popover_finalize; object_class->set_property = ephy_security_popover_set_property; widget_class->get_preferred_width = ephy_security_popover_get_preferred_width; /** * EphySecurityPopover:address: * * The address of the website. */ obj_properties[PROP_ADDRESS] = g_param_spec_string ("address", "Address", "The address of the website", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * EphySecurityPopover:certificate: * * The certificate of the website. */ obj_properties[PROP_CERTIFICATE] = g_param_spec_object ("certificate", "Certificate", "The certificate of the website, if HTTPS", G_TYPE_TLS_CERTIFICATE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * EphySecurityPopover:tls-errors: * * Indicates issues with the security of the website. */ obj_properties[PROP_TLS_ERRORS] = g_param_spec_flags ("tls-errors", "TLS Errors", "Issues with the security of the website, if HTTPS", G_TYPE_TLS_CERTIFICATE_FLAGS, 0, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * EphySecurityPopover:security-level: * * The state of the lock displayed in the address bar. */ obj_properties[PROP_SECURITY_LEVEL] = g_param_spec_enum ("security-level", "Security Level", "Determines what type of information to display", EPHY_TYPE_SECURITY_LEVEL, EPHY_SECURITY_LEVEL_TO_BE_DETERMINED, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, LAST_PROP, obj_properties); } static gboolean on_ad_combobox_changed (GtkComboBox *widget, EphySecurityPopover *popover) { GSettings *web_settings = ephy_settings_get (EPHY_PREFS_WEB_SCHEMA); EphyPermissionsManager *permissions_manager; EphyPermission permission = EPHY_PERMISSION_UNDECIDED; gboolean global_flag = g_settings_get_boolean (web_settings, EPHY_PREFS_WEB_ENABLE_ADBLOCK); g_autofree gchar *origin = NULL; gboolean state = gtk_combo_box_get_active (widget) == 1; origin = ephy_uri_to_security_origin (popover->address); if (!origin) return FALSE; permissions_manager = ephy_embed_shell_get_permissions_manager (ephy_embed_shell_get_default ()); if (global_flag != state) permission = state ? EPHY_PERMISSION_DENY : EPHY_PERMISSION_PERMIT; ephy_permissions_manager_set_permission (permissions_manager, EPHY_PERMISSION_TYPE_SHOW_ADS, origin, permission); return FALSE; } static void handle_permission_combobox_changed (EphySecurityPopover *popover, gint action, EphyPermissionType permission_type) { EphyPermissionsManager *permissions_manager; EphyPermission permission; g_autofree gchar *origin = NULL; origin = ephy_uri_to_security_origin (popover->address); if (!origin) return; permissions_manager = ephy_embed_shell_get_permissions_manager (ephy_embed_shell_get_default ()); switch (action) { case 0: permission = EPHY_PERMISSION_PERMIT; break; default: case 1: permission = EPHY_PERMISSION_DENY; break; case 2: permission = EPHY_PERMISSION_UNDECIDED; break; } ephy_permissions_manager_set_permission (permissions_manager, permission_type, origin, permission); } static void on_notification_combobox_changed (GtkComboBox *box, EphySecurityPopover *popover) { handle_permission_combobox_changed (popover, gtk_combo_box_get_active (box), EPHY_PERMISSION_TYPE_SHOW_NOTIFICATIONS); } static void on_save_password_combobox_changed (GtkComboBox *box, EphySecurityPopover *popover) { handle_permission_combobox_changed (popover, gtk_combo_box_get_active (box), EPHY_PERMISSION_TYPE_SAVE_PASSWORD); } static void on_access_location_combobox_changed (GtkComboBox *box, EphySecurityPopover *popover) { handle_permission_combobox_changed (popover, gtk_combo_box_get_active (box), EPHY_PERMISSION_TYPE_ACCESS_LOCATION); } static void on_access_microphone_combobox_changed (GtkComboBox *box, EphySecurityPopover *popover) { handle_permission_combobox_changed (popover, gtk_combo_box_get_active (box), EPHY_PERMISSION_TYPE_ACCESS_MICROPHONE); } static void on_access_webcam_combobox_changed (GtkComboBox *box, EphySecurityPopover *popover) { handle_permission_combobox_changed (popover, gtk_combo_box_get_active (box), EPHY_PERMISSION_TYPE_ACCESS_WEBCAM); } static void on_autoplay_policy_combobox_changed (GtkComboBox *box, EphySecurityPopover *popover) { handle_permission_combobox_changed (popover, gtk_combo_box_get_active (box), EPHY_PERMISSION_TYPE_AUTOPLAY_POLICY); } static GtkWidget * add_permission_combobox (EphySecurityPopover *popover, const gchar *name, gpointer callback, GtkSizeGroup *size_group, gboolean no_ask, const gchar *third_option_name) { GtkWidget *widget; GtkWidget *hbox; GtkWidget *tmp; hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); gtk_grid_attach (GTK_GRID (popover->grid), hbox, 0, popover->permission_pos++, 2, 1); tmp = gtk_label_new (name); gtk_label_set_xalign (GTK_LABEL (tmp), 0.0); gtk_widget_set_hexpand (tmp, TRUE); gtk_box_pack_start (GTK_BOX (hbox), tmp, FALSE, TRUE, 0); widget = gtk_combo_box_text_new (); gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), _("Allow")); gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), _("Deny")); if (!no_ask) { const gchar *name = third_option_name == NULL ? _("Ask") : third_option_name; gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), _(name)); } gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, TRUE, 0); g_signal_connect (widget, "changed", G_CALLBACK (callback), popover); gtk_size_group_add_widget (size_group, widget); return widget; } static void ephy_security_popover_init (EphySecurityPopover *popover) { GtkWidget *permissions; GtkWidget *box; g_autoptr (GtkSizeGroup) combo_box_size_group = NULL; g_autofree char *label = g_strdup_printf ("%s", _("Permissions")); popover->grid = gtk_grid_new (); gtk_grid_set_column_spacing (GTK_GRID (popover->grid), 12); gtk_grid_set_row_spacing (GTK_GRID (popover->grid), 6); gtk_widget_set_margin_top (popover->grid, 10); gtk_widget_set_margin_bottom (popover->grid, 10); gtk_widget_set_margin_start (popover->grid, 10); gtk_widget_set_margin_end (popover->grid, 10); box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_widget_set_halign (box, GTK_ALIGN_CENTER); popover->lock_image = gtk_image_new (); gtk_box_pack_start (GTK_BOX (box), popover->lock_image, FALSE, TRUE, 0); popover->host_label = gtk_label_new (NULL); gtk_label_set_line_wrap (GTK_LABEL (popover->host_label), TRUE); gtk_label_set_line_wrap_mode (GTK_LABEL (popover->host_label), PANGO_WRAP_WORD_CHAR); gtk_label_set_xalign (GTK_LABEL (popover->host_label), 0.0); gtk_box_pack_start (GTK_BOX (box), popover->host_label, FALSE, TRUE, 0); popover->security_label = gtk_label_new (NULL); gtk_label_set_line_wrap (GTK_LABEL (popover->security_label), TRUE); gtk_label_set_xalign (GTK_LABEL (popover->security_label), 0.0); gtk_grid_attach (GTK_GRID (popover->grid), box, 0, 0, 2, 1); gtk_grid_attach (GTK_GRID (popover->grid), popover->security_label, 0, 1, 2, 1); gtk_grid_attach (GTK_GRID (popover->grid), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL), 0, 3, 2, 1); /* Permissions */ permissions = gtk_label_new (NULL); gtk_label_set_markup (GTK_LABEL (permissions), label); gtk_label_set_xalign (GTK_LABEL (permissions), 0.0); gtk_grid_attach (GTK_GRID (popover->grid), permissions, 0, 4, 2, 1); popover->permission_pos = 5; combo_box_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); popover->ad_combobox = add_permission_combobox (popover, _("Advertisements"), on_ad_combobox_changed, combo_box_size_group, TRUE, NULL); popover->notification_combobox = add_permission_combobox (popover, _("Notifications"), on_notification_combobox_changed, combo_box_size_group, FALSE, NULL); popover->save_password_combobox = add_permission_combobox (popover, _("Password saving"), on_save_password_combobox_changed, combo_box_size_group, FALSE, NULL); popover->access_location_combobox = add_permission_combobox (popover, _("Location access"), on_access_location_combobox_changed, combo_box_size_group, FALSE, NULL); popover->access_microphone_combobox = add_permission_combobox (popover, _("Microphone access"), on_access_microphone_combobox_changed, combo_box_size_group, FALSE, NULL); popover->access_webcam_combobox = add_permission_combobox (popover, _("Webcam access"), on_access_webcam_combobox_changed, combo_box_size_group, FALSE, NULL); popover->autoplay_combobox = add_permission_combobox (popover, _("Media autoplay"), on_autoplay_policy_combobox_changed, combo_box_size_group, FALSE, _("Without Sound")); gtk_container_add (GTK_CONTAINER (popover), popover->grid); gtk_widget_show_all (popover->grid); } GtkWidget * ephy_security_popover_new (GtkWidget *relative_to, const char *address, GTlsCertificate *certificate, GTlsCertificateFlags tls_errors, EphySecurityLevel security_level) { g_assert (address != NULL); return GTK_WIDGET (g_object_new (EPHY_TYPE_SECURITY_POPOVER, "address", address, "certificate", certificate, "relative-to", relative_to, "security-level", security_level, "tls-errors", tls_errors, NULL)); }