/*
 *  Copyright © 2012 Igalia S.L.
 *
 *  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, 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 "ephy-certificate-dialog.h"

#define GCR_API_SUBJECT_TO_CHANGE
#include <gcr/gcr.h>
#include <glib/gi18n.h>
#include <libsoup/soup.h>

/**
 * SECTION:ephy-certificate-dialog
 * @short_description: A dialog to show SSL certificate information
 *
 * #EphyCertificateDialog shows information about SSL certificates.
 */

enum
{
  PROP_0,
  PROP_ADDRESS,
  PROP_CERTIFICATE,
  PROP_TLS_ERRORS
};

struct _EphyCertificateDialogPrivate
{
  GtkWidget *icon;
  GtkWidget *title;
  GtkWidget *text;
};

G_DEFINE_TYPE (EphyCertificateDialog, ephy_certificate_dialog, GTK_TYPE_DIALOG)

static void
ephy_certificate_dialog_set_address (EphyCertificateDialog *dialog,
                                     const char *address)
{
  SoupURI *uri;

  uri = soup_uri_new (address);
  gtk_window_set_title (GTK_WINDOW (dialog), uri->host);
  soup_uri_free (uri);
}

static void
ephy_certificate_dialog_set_certificate (EphyCertificateDialog *dialog,
                                         GTlsCertificate *certificate)
{
  GcrCertificate *simple_certificate;
  GByteArray *certificate_data;
  GtkWidget *certificate_widget;
  GtkWidget *content_area;

  g_object_get (certificate, "certificate", &certificate_data, NULL);
  simple_certificate = gcr_simple_certificate_new ((const guchar *)certificate_data->data,
						   certificate_data->len);
  g_byte_array_unref (certificate_data);

  certificate_widget = GTK_WIDGET (gcr_certificate_widget_new (simple_certificate));
  g_object_unref (simple_certificate);

  content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
  gtk_box_pack_start (GTK_BOX (content_area), certificate_widget, TRUE, TRUE, 0);
  gtk_widget_show (certificate_widget);
}

static char *
get_error_messages_from_tls_errors (GTlsCertificateFlags tls_errors)
{
  GPtrArray *errors = g_ptr_array_new ();
  char *retval;

  if (tls_errors & G_TLS_CERTIFICATE_BAD_IDENTITY)
    g_ptr_array_add (errors, _("The certificate does not match the expected identity"));

  if (tls_errors & G_TLS_CERTIFICATE_EXPIRED)
    g_ptr_array_add (errors, _("The certificate has expired"));

  if (tls_errors & G_TLS_CERTIFICATE_UNKNOWN_CA)
    g_ptr_array_add (errors, _("The signing certificate authority is not known"));

  if (tls_errors & G_TLS_CERTIFICATE_GENERIC_ERROR)
    g_ptr_array_add (errors, _("The certificate contains errors"));

  if (tls_errors & G_TLS_CERTIFICATE_REVOKED)
    g_ptr_array_add (errors, _("The certificate has been revoked"));

  if (tls_errors & G_TLS_CERTIFICATE_INSECURE)
    g_ptr_array_add (errors, _("The certificate is signed using a weak signature algorithm"));

  if (tls_errors & G_TLS_CERTIFICATE_NOT_ACTIVATED)
    g_ptr_array_add (errors, _("The certificate activation time is still in the future"));

  if (errors->len == 1)
    retval = g_strdup (g_ptr_array_index (errors, 0));
  else {
    GString *message = g_string_new (NULL);
    guint i;

    for (i = 0; i < errors->len; i++) {
      g_string_append_printf (message, "• %s",
			      (char *)g_ptr_array_index (errors, i));
      if (i < errors->len - 1)
	g_string_append_c (message, '\n');
    }

    retval = g_string_free (message, FALSE);
  }

  g_ptr_array_free (errors, TRUE);

  return retval;
}

static void
ephy_certificate_dialog_set_tls_errors (EphyCertificateDialog *dialog,
                                        GTlsCertificateFlags tls_errors)
{
  EphyCertificateDialogPrivate *priv = dialog->priv;
  GIcon *icon;
  char *markup;

  icon = tls_errors == 0 ?
    g_themed_icon_new_with_default_fallbacks ("channel-secure-symbolic") :
    g_themed_icon_new_with_default_fallbacks ("channel-insecure-symbolic");
  gtk_image_set_from_gicon (GTK_IMAGE (priv->icon), icon, GTK_ICON_SIZE_DIALOG);
  g_object_unref (icon);

  markup = g_strdup_printf ("<span weight=\"bold\" size=\"large\">%s</span>",
			    tls_errors == 0 ?
			    _("The identity of this website has been verified") :
			    _("The identity of this website has not been verified"));
  gtk_label_set_markup (GTK_LABEL (priv->title), markup);
  g_free (markup);

  if (tls_errors) {
    char *text = get_error_messages_from_tls_errors (tls_errors);

    gtk_label_set_text (GTK_LABEL (priv->text), text);
    g_free (text);

    gtk_widget_show (priv->text);
  }
}

static void
ephy_certificate_dialog_set_property (GObject *object,
                                      guint prop_id,
                                      const GValue *value,
                                      GParamSpec *pspec)
{
  EphyCertificateDialog *dialog = EPHY_CERTIFICATE_DIALOG (object);

  switch (prop_id) {
  case PROP_ADDRESS:
    ephy_certificate_dialog_set_address (dialog, g_value_get_string (value));
    break;
  case PROP_CERTIFICATE:
    ephy_certificate_dialog_set_certificate (dialog, g_value_get_object (value));
    break;
  case PROP_TLS_ERRORS:
    ephy_certificate_dialog_set_tls_errors (dialog, g_value_get_flags (value));
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  }
}

static void
ephy_certificate_dialog_class_init (EphyCertificateDialogClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->set_property = ephy_certificate_dialog_set_property;

  /**
   * EphyCertificateDialog:address:
   *
   * The address of the website.
   */
  g_object_class_install_property (object_class,
				   PROP_ADDRESS,
				   g_param_spec_string ("address",
							"Address",
							"The address of the website",
							NULL,
							G_PARAM_WRITABLE |
							G_PARAM_CONSTRUCT_ONLY |
							G_PARAM_STATIC_NAME |
							G_PARAM_STATIC_NICK |
							G_PARAM_STATIC_BLURB));

  /**
   * EphyCertificateDialog:certificate:
   *
   * The certificate of the website.
   */
  g_object_class_install_property (object_class,
				   PROP_CERTIFICATE,
				   g_param_spec_object ("certificate",
							"Certificate",
							"The certificate of the website",
							G_TYPE_TLS_CERTIFICATE,
							G_PARAM_WRITABLE |
							G_PARAM_CONSTRUCT_ONLY |
							G_PARAM_STATIC_NAME |
							G_PARAM_STATIC_NICK |
							G_PARAM_STATIC_BLURB));

  /**
   * EphyCertificateDialog:tls-errors:
   *
   * The verification errors on the TLS certificate.
   */
  g_object_class_install_property (object_class,
				   PROP_TLS_ERRORS,
				   g_param_spec_flags ("tls-errors",
						       "TLS Errors",
						       "The verification errors on the TLS certificate",
						       G_TYPE_TLS_CERTIFICATE_FLAGS,
						       0,
						       G_PARAM_WRITABLE |
						       G_PARAM_CONSTRUCT_ONLY |
						       G_PARAM_STATIC_NAME |
						       G_PARAM_STATIC_NICK |
						       G_PARAM_STATIC_BLURB));

  g_type_class_add_private (object_class, sizeof (EphyCertificateDialogPrivate));
}

static void
ephy_certificate_dialog_init (EphyCertificateDialog *dialog)
{
  GtkWidget *vbox, *hbox;
  GtkWidget *content_area, *action_area;
  EphyCertificateDialogPrivate *priv;

  dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
					      EPHY_TYPE_CERTIFICATE_DIALOG,
					      EphyCertificateDialogPrivate);
  priv = dialog->priv;

  gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
  gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE);

  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);

  priv->icon = gtk_image_new ();
  gtk_widget_set_halign (priv->icon, GTK_ALIGN_CENTER);
  gtk_widget_set_valign (priv->icon, GTK_ALIGN_START);
  gtk_box_pack_start (GTK_BOX (hbox), priv->icon, FALSE, FALSE, 0);
  gtk_widget_show (priv->icon);

  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);

  priv->title = gtk_label_new (NULL);
  gtk_label_set_use_markup (GTK_LABEL (priv->title), TRUE);
  gtk_label_set_line_wrap  (GTK_LABEL (priv->title), TRUE);
  gtk_label_set_selectable (GTK_LABEL (priv->title), TRUE);
  gtk_widget_set_halign (priv->title, GTK_ALIGN_START);
  gtk_widget_set_valign (priv->title, GTK_ALIGN_START);
  gtk_misc_set_alignment (GTK_MISC (priv->title), 0.0, 0.0);
  gtk_box_pack_start (GTK_BOX (vbox), priv->title, FALSE, FALSE, 0);
  gtk_widget_show (priv->title);

  priv->text = gtk_label_new (NULL);
  gtk_label_set_line_wrap  (GTK_LABEL (priv->text), TRUE);
  gtk_label_set_selectable (GTK_LABEL (priv->text), TRUE);
  gtk_widget_set_halign (priv->text, GTK_ALIGN_START);
  gtk_widget_set_valign (priv->text, GTK_ALIGN_START);
  gtk_misc_set_alignment (GTK_MISC (priv->text), 0.0, 0.0);
  gtk_box_pack_start (GTK_BOX (vbox), priv->text, TRUE, TRUE, 0);

  gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
  gtk_widget_show (vbox);

  content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
  gtk_box_set_spacing (GTK_BOX (content_area), 14);
  gtk_box_pack_start (GTK_BOX (content_area), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

  action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
  gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
  gtk_box_set_spacing (GTK_BOX (action_area), 6);
}

GtkWidget *
ephy_certificate_dialog_new (GtkWindow *parent,
                             const char *address,
                             GTlsCertificate *certificate,
                             GTlsCertificateFlags tls_errors)
{
  GtkWidget *dialog;

  g_return_val_if_fail (address != NULL, NULL);
  g_return_val_if_fail (G_IS_TLS_CERTIFICATE (certificate), NULL);

  dialog = GTK_WIDGET (g_object_new (EPHY_TYPE_CERTIFICATE_DIALOG,
				     "address", address,
				     "certificate", certificate,
				     "tls-errors", tls_errors,
				     NULL));
  gtk_dialog_add_buttons (GTK_DIALOG (dialog),
			  GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
			  NULL);
  if (parent)
    gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);

  return dialog;
}

