static void
perform_verification (EmpathyTLSVerifier *self,
        GcrCertificateChain *chain)
{
  gboolean ret = FALSE;
  EmpTLSCertificateRejectReason reason =
    EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
  gnutls_x509_crt_t *list, *anchors;
  guint n_list, n_anchors;
  guint verify_output;
  gint res;
  gint i;
  gboolean matched = FALSE;
  EmpathyTLSVerifierPriv *priv = GET_PRIV (self);

  DEBUG ("Performing verification");
  debug_certificate_chain (chain);

  list = anchors = NULL;
  n_list = n_anchors = 0;

  /*
   * If the first certificate is an pinned certificate then we completely
   * ignore the rest of the verification process.
   */
  if (gcr_certificate_chain_get_status (chain) == GCR_CERTIFICATE_CHAIN_PINNED)
    {
      DEBUG ("Found pinned certificate for %s", priv->hostname);
      complete_verification (self);
      goto out;
  }

  build_certificate_list_for_gnutls (chain, &list, &n_list,
          &anchors, &n_anchors);
  if (list == NULL || n_list == 0) {
      g_warn_if_reached ();
      abort_verification (self, EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN);
      goto out;
  }

  verify_output = 0;
  res = gnutls_x509_crt_list_verify (list, n_list, anchors, n_anchors,
           NULL, 0, 0, &verify_output);
  ret = verification_output_to_reason (res, verify_output, &reason);

  DEBUG ("Certificate verification gave result %d with reason %u", ret,
          reason);

  if (!ret) {
      abort_verification (self, reason);
      goto out;
  }

  /* now check if the certificate matches one of the reference identities. */
  if (priv->reference_identities != NULL)
    {
      for (i = 0, matched = FALSE; priv->reference_identities[i] != NULL; ++i)
        {
          if (gnutls_x509_crt_check_hostname (list[0],
                  priv->reference_identities[i]) == 1)
            {
              matched = TRUE;
              break;
            }
        }
    }

  if (!matched)
    {
      gchar *certified_hostname;

      certified_hostname = empathy_get_x509_certificate_hostname (list[0]);
      tp_asv_set_string (priv->details,
          "expected-hostname", priv->hostname);
      tp_asv_set_string (priv->details,
          "certificate-hostname", certified_hostname);

      DEBUG ("Hostname mismatch: got %s but expected %s",
          certified_hostname, priv->hostname);

      g_free (certified_hostname);
      abort_verification (self,
              EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
      goto out;
    }

  DEBUG ("Hostname matched");
  complete_verification (self);

 out:
  free_certificate_list_for_gnutls (list, n_list);
  free_certificate_list_for_gnutls (anchors, n_anchors);
}
void
empathy_tls_certificate_store_ca (EmpathyTLSCertificate *self)
{
  GArray *last_cert;
  gnutls_x509_crt_t cert;
  gnutls_datum_t datum = { NULL, 0 };
  gsize exported_len;
  guchar *exported_cert = NULL;
  gint res, offset;
  gchar *user_certs_dir = NULL, *filename = NULL, *path = NULL;
  gchar *hostname = NULL;
  GError *error = NULL;
  EmpathyTLSCertificatePriv *priv = GET_PRIV (self);

  last_cert = g_ptr_array_index (priv->cert_data, priv->cert_data->len - 1);
  datum.data = (guchar *) last_cert->data;
  datum.size = last_cert->len;

  gnutls_x509_crt_init (&cert);
  gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_DER);

  /* make sure it's self-signed, otherwise it's not a CA */
  if (gnutls_x509_crt_check_issuer (cert, cert) <= 0)
    {
      DEBUG ("Can't import the CA, as it's not self-signed");
      gnutls_x509_crt_deinit (cert);

      return;
    }

  if (gnutls_x509_crt_get_ca_status (cert, NULL) <= 0)
    {
      DEBUG ("Can't import the CA, it's not a valid CA certificate");
      gnutls_x509_crt_deinit (cert);

      goto out;
    }

  exported_len = get_exported_size (cert);
  exported_cert = g_malloc (sizeof (guchar) * exported_len);

  res = gnutls_x509_crt_export (cert, GNUTLS_X509_FMT_PEM,
      exported_cert, &exported_len);

  if (res < 0)
    {
      DEBUG ("Failed to export the CA certificate; GnuTLS returned %d,"
          "and should be %lu bytes long", res, (gulong) exported_len);
      gnutls_x509_crt_deinit (cert);

      goto out;
    }

  hostname = empathy_get_x509_certificate_hostname (cert);

  if (hostname == NULL)
    hostname = g_strdup ("ca");

  gnutls_x509_crt_deinit (cert);

  /* write the file */
  user_certs_dir = g_build_filename (g_get_user_config_dir (),
      "telepathy", "certs", NULL);

  res = g_mkdir_with_parents (user_certs_dir, S_IRWXU | S_IRWXG);

  if (res < 0)
    {
      DEBUG ("Failed to create the user certificate directory: %s",
          g_strerror (errno));

      goto out;
    }

  offset = 0;

  do
    {
      g_free (path);

      if (offset == 0)
        filename = g_strdup_printf ("cert-%s", hostname);
      else
        filename = g_strdup_printf ("cert-%s-%d", hostname, offset);

      path = g_build_filename (user_certs_dir, filename, NULL);

      offset++;
      g_free (filename);
    }
  while (g_file_test (path, G_FILE_TEST_EXISTS));

  DEBUG ("Will save to %s", path);

  g_file_set_contents (path, (const gchar *) exported_cert, exported_len,
      &error);

  if (error != NULL)
    {
      DEBUG ("Can't save the CA certificate to %s: %s",
          path, error->message);

      g_error_free (error);
    }

 out:
  g_free (path);
  g_free (exported_cert);
  g_free (user_certs_dir);
  g_free (hostname);
}