static gboolean
infinoted_plugin_certificate_auth_initialize(InfinotedPluginManager* manager,
                                             gpointer plugin_info,
                                             GError** error)
{
  InfinotedPluginCertificateAuth* plugin;
  InfCertificateCredentials* creds;
  GPtrArray* read_certs;
  int res;
  guint i;

  gnutls_x509_crt_t* sign_certs;
  InfCertificateChain* sign_chain;

  gnutls_x509_privkey_t super_key;
  InfCertUtilDescription desc;
  gnutls_x509_crt_t super_cert;
  InfAclAccountId super_id;
  gnutls_x509_crt_t chain[2];
  gboolean written;

  InfdDirectory* directory;
  InfBrowserIter iter;
  InfAclSheetSet sheet_set;
  InfAclSheet sheet;
  InfRequest* request;

  plugin = (InfinotedPluginCertificateAuth*)plugin_info;
  plugin->manager = manager;

  creds = infinoted_plugin_manager_get_credentials(manager);
  if(creds == NULL)
  {
    g_set_error(
      error,
      infinoted_plugin_certificate_auth_error_quark(),
      INFINOTED_PLUGIN_CERTIFICATE_AUTH_ERROR_NO_CREDENTIALS,
      "%s",
      _("The certificate-auth plugin can only be used when TLS is enabled "
        "and a server certificate has been set.")
    );

    return FALSE;
  }

  read_certs =
    inf_cert_util_read_certificate(plugin->ca_list_file, NULL, error);
  if(read_certs == NULL) return FALSE;

  if(read_certs->len == 0)
  {
    g_set_error(
      error,
      infinoted_plugin_certificate_auth_error_quark(),
      INFINOTED_PLUGIN_CERTIFICATE_AUTH_ERROR_NO_CAS,
      _("File \"%s\" does not contain any CA certificates"),
      plugin->ca_list_file
    );

    g_ptr_array_free(read_certs, TRUE);
    return FALSE;
  }

  plugin->n_cas = read_certs->len;
  plugin->cas = (gnutls_x509_crt_t*)g_ptr_array_free(read_certs, FALSE);

  res = gnutls_certificate_set_x509_trust(
    inf_certificate_credentials_get(creds),
    plugin->cas,
    plugin->n_cas
  );

  if(res < 0)
  {
    inf_gnutls_set_error(error, res);
    return FALSE;
  }

  if(plugin->ca_key_file != NULL)
  {
    plugin->ca_key =
      inf_cert_util_read_private_key(plugin->ca_key_file, error);
    if(plugin->ca_key == NULL)
      return FALSE;

    /* Walk through certificates and find the certificate that the key
     * belongs to. */
    for(i = 0; i < plugin->n_cas; ++i)
      if(inf_cert_util_check_certificate_key(plugin->cas[i], plugin->ca_key))
        break;

    if(i == plugin->n_cas)
    {
      gnutls_x509_privkey_deinit(plugin->ca_key);
      plugin->ca_key = NULL;

      g_set_error(
        error,
        infinoted_plugin_certificate_auth_error_quark(),
        INFINOTED_PLUGIN_CERTIFICATE_AUTH_ERROR_NO_CA_FOR_KEY,
        "%s",
        _("The given CA key does not match with any of the CA certificates")
      );

      return FALSE;
    }

    plugin->ca_key_index = i;

    /* Set the signing certificate of the directory, so that it can handle
     * account creation requests. Note that this takes ownership of the
     * certificate, so we take special care in the cleanup code in
     * infinoted_plugin_certificate_auth_deinitialize(). */
    sign_certs = g_malloc(sizeof(gnutls_x509_crt_t));
    sign_certs[0] = plugin->cas[plugin->ca_key_index];
    sign_chain = inf_certificate_chain_new(sign_certs, 1);

    infd_directory_set_certificate(
      infinoted_plugin_manager_get_directory(plugin->manager),
      plugin->ca_key,
      sign_chain
    );

    inf_certificate_chain_unref(sign_chain);
  }

  if(plugin->super_user != NULL)
  {
    if(plugin->ca_key == NULL)
    {
      g_set_error(
        error,
        infinoted_plugin_certificate_auth_error_quark(),
        INFINOTED_PLUGIN_CERTIFICATE_AUTH_ERROR_NO_CA_KEY,
        "%s",
        _("Cannot generate a superuser certificate without CA key")
      );

      return FALSE;
    }

    /* Create a private key and certificate for the super user. */
    infinoted_log_info(
      infinoted_plugin_manager_get_log(plugin->manager),
      _("Creating 4096-bit RSA private key for the super user account...")
    );

    super_key =
      inf_cert_util_create_private_key(GNUTLS_PK_RSA, 4096, error);
    if(super_key == NULL)
      return FALSE;

    desc.validity = 12 * 3600; /* 12 hours */
    desc.dn_common_name = "Super User";
    desc.san_dnsname = NULL;

    super_cert = inf_cert_util_create_signed_certificate(
      super_key,
      &desc,
      plugin->cas[plugin->ca_key_index],
      plugin->ca_key,
      error
    );

    if(super_cert == NULL)
    {
      gnutls_x509_privkey_deinit(super_key);
      return FALSE;
    }

    super_id = infd_directory_create_acl_account(
      infinoted_plugin_manager_get_directory(plugin->manager),
      _("Super User"),
      TRUE, /* transient */
      &super_cert,
      1,
      error
    );

    if(super_id == 0)
    {
      gnutls_x509_crt_deinit(super_cert);
      gnutls_x509_privkey_deinit(super_key);
      return FALSE;
    }

    plugin->super_id = super_id;

    chain[0] = super_cert;
    chain[1] = plugin->cas[plugin->ca_key_index];

    written = inf_cert_util_write_certificate_with_key(
      super_key,
      chain,
      2,
      plugin->super_user,
      error
    );

    gnutls_x509_crt_deinit(super_cert);
    gnutls_x509_privkey_deinit(super_key);

    if(written == FALSE)
      return FALSE;

    inf_browser_get_root(
      INF_BROWSER(infinoted_plugin_manager_get_directory(plugin->manager)),
      &iter
    );

    directory = infinoted_plugin_manager_get_directory(plugin->manager);

    sheet.account = super_id;
    sheet.mask = INF_ACL_MASK_ALL;
    infd_directory_get_support_mask(directory, &sheet.perms);
    sheet_set.n_sheets = 1;
    sheet_set.own_sheets = NULL;
    sheet_set.sheets = &sheet;

    request = inf_browser_set_acl(
      INF_BROWSER(directory),
      &iter,
      &sheet_set,
      infinoted_plugin_certificate_auth_set_acl_cb,
      plugin
    );

    if(request != NULL)
    {
      plugin->set_acl_request = request;
      g_object_ref(plugin->set_acl_request);
    }
  }

  return TRUE;
}
void Gobby::BrowserContextCommands::on_account_created(
	gnutls_x509_privkey_t key,
	InfCertificateChain* certificate,
	const InfAclAccount* account)
{
	InfBrowser* browser;
	g_object_get(G_OBJECT(m_dialog->gobj()), "browser", &browser, NULL);
	const InfAclAccount* own_account =
		inf_browser_get_acl_local_account(browser);
	const InfAclAccountId default_id =
		inf_acl_account_id_from_string("default");
	const bool is_default_account = (own_account->id == default_id);
	g_object_unref(browser);

	gnutls_x509_crt_t cert =
		inf_certificate_chain_get_own_certificate(certificate);
	gchar* name = inf_cert_util_get_dn_by_oid(
		cert, GNUTLS_OID_X520_COMMON_NAME, 0);
	const std::string cn = name;
	g_free(name);

	std::string host;
	if(INFC_IS_BROWSER(browser))
	{
		InfXmlConnection* xml =
			infc_browser_get_connection(INFC_BROWSER(browser));
		if(INF_IS_XMPP_CONNECTION(xml))
		{
			gchar* hostname;
			g_object_get(G_OBJECT(xml), "remote-hostname",
			             &hostname, NULL);
			host = hostname;
			g_free(hostname);
		}
	}

	if(host.empty())
		host = "local";

	gnutls_x509_crt_t* certs = inf_certificate_chain_get_raw(certificate);
	const unsigned int n_certs =
		inf_certificate_chain_get_n_certificates(certificate);

	std::auto_ptr<Gtk::MessageDialog> dlg;

	try
	{
		const std::string filename = make_certificate_filename(cn, host);

		GError* error = NULL;
		inf_cert_util_write_certificate_with_key(
			key, certs, n_certs, filename.c_str(), &error);

		if(error != NULL)
		{
			const std::string message = error->message;
			g_error_free(error);
			throw std::runtime_error(message);
		}

		if(is_default_account &&
		   (m_cert_manager.get_private_key() == NULL ||
		    m_cert_manager.get_certificates() == NULL))
		{
			m_preferences.security.key_file = filename;
			m_preferences.security.certificate_file = filename;

			dlg.reset(new Gtk::MessageDialog(
				m_parent, _("Account successfully created"),
				false, Gtk::MESSAGE_INFO,
				Gtk::BUTTONS_CLOSE));
			dlg->set_secondary_text(
				_("When re-connecting to the server, the "
				  "new account will be used."));
		}
		else
		{
			// TODO: Gobby should support multiple certificates
			dlg.reset(new Gtk::MessageDialog(
				m_parent, _("Account successfully created"),
				false, Gtk::MESSAGE_INFO,
				Gtk::BUTTONS_CLOSE));
			dlg->set_secondary_text(Glib::ustring::compose(
				_("The certificate has been stored at %1.\n\n"
				  "To login to this account, set the "
				  "certificate in Gobby's preferences "
				  "and re-connect to the server."),
				filename));
		}
	}
	catch(const std::exception& ex)
	{
		// This is actually a bit unfortunate, because the
		// account was actually created and we have a valid
		// certificate for it, but we cannot keep it...
		dlg.reset(new Gtk::MessageDialog(
			m_parent, _("Failed to create account"),
			false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE));
		dlg->set_secondary_text(Glib::ustring::compose(
			_("Could not save the certificate for the "
			  "account: %1"), ex.what()));
	}

	m_dialog = dlg;
	m_dialog->signal_response().connect(
		sigc::mem_fun(
			*this,
			&BrowserContextCommands::
				on_account_created_response));
	m_dialog->present();
}