/*
 * SPDX-License-Identifier: LGPL-2.0-or-later
 */

#include "evolution-data-server-config.h"

#include <glib/gi18n-lib.h>

#include <libebackend/libebackend.h>
#include "trust-prompt.h"

/* Standard GObject macros */
#define E_TYPE_TRUST_PROMPT (e_trust_prompt_get_type ())
#define E_TRUST_PROMPT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_TRUST_PROMPT, ETrustPrompt))

typedef struct _ETrustPrompt ETrustPrompt;
typedef struct _ETrustPromptClass ETrustPromptClass;

struct _ETrustPrompt {
	EUserPrompterServerExtension parent;
};

struct _ETrustPromptClass {
	EUserPrompterServerExtensionClass parent_class;
};

/* Module Entry Points */
void e_module_load (GTypeModule *type_module);
void e_module_unload (GTypeModule *type_module);

/* Forward Declarations */
GType e_trust_prompt_get_type (void);

G_DEFINE_DYNAMIC_TYPE (
	ETrustPrompt,
	e_trust_prompt,
	E_TYPE_USER_PROMPTER_SERVER_EXTENSION)

#define TRUST_PROMPT_DIALOG "ETrustPrompt::trust-prompt"

/* dialog definitions */

/* ETrustPrompt::trust-prompt
 * The dialog expects these parameters:
 *    "host" - host from which the certificate is received
 *    "markup" - markup for the trust prompt, if not set, then "SSL/TLS certificate for '<b>host</b>' is not trusted. Do you wish to accept it?" is used
 *    "certificate" - a base64-encoded DER certificate, for which ask on trust
 *    "certificate-errors" - a hexa-decimal integer (as string) corresponding to GTlsCertificateFlags
 *
 * Result of the dialog is:
 *    0 - reject
 *    1 - accept permanently
 *    2 - accept temporarily
 *   -1 - user didn't choose any of the above
 *
 * The dialog doesn't provide any additional values in the response.
 */

static gchar *
cert_errors_to_reason (GTlsCertificateFlags flags)
{
	struct _convert_table {
		GTlsCertificateFlags flag;
		const gchar *description;
	} convert_table[] = {
		{ G_TLS_CERTIFICATE_UNKNOWN_CA,
		  N_("The signing certificate authority is not known.") },
		{ G_TLS_CERTIFICATE_BAD_IDENTITY,
		  N_("The certificate does not match the expected identity of the site that it was retrieved from.") },
		{ G_TLS_CERTIFICATE_NOT_ACTIVATED,
		  N_("The certificate’s activation time is still in the future.") },
		{ G_TLS_CERTIFICATE_EXPIRED,
		  N_("The certificate has expired.") },
		{ G_TLS_CERTIFICATE_REVOKED,
		  N_("The certificate has been revoked according to the connection’s certificate revocation list.") },
		{ G_TLS_CERTIFICATE_INSECURE,
		  N_("The certificate’s algorithm is considered insecure.") }
	};

	GString *reason = g_string_new ("");
	gint ii;

	for (ii = 0; ii < G_N_ELEMENTS (convert_table); ii++) {
		if ((flags & convert_table[ii].flag) != 0) {
			if (reason->len > 0)
				g_string_append_c (reason, '\n');

			g_string_append (reason, _(convert_table[ii].description));
		}
	}

	return g_string_free (reason, FALSE);
}

static gboolean
trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension,
                                gint prompt_id,
                                const ENamedParameters *parameters)
{
	const gchar *host, *markup, *base64_cert, *cert_errs_str;
	gchar *reason;
	gint64 cert_errs;
	gboolean success;

	g_return_val_if_fail (extension != NULL, FALSE);
	g_return_val_if_fail (parameters != NULL, FALSE);

	host = e_named_parameters_get (parameters, "host");
	markup = e_named_parameters_get (parameters, "markup");
	base64_cert = e_named_parameters_get (parameters, "certificate");
	cert_errs_str = e_named_parameters_get (parameters, "certificate-errors");

	g_return_val_if_fail (host != NULL, FALSE);
	g_return_val_if_fail (base64_cert != NULL, FALSE);
	g_return_val_if_fail (cert_errs_str != NULL, FALSE);

	cert_errs = g_ascii_strtoll (cert_errs_str, NULL, 16);
	reason = cert_errors_to_reason (cert_errs);

	success = trust_prompt_show (extension, prompt_id, host, markup, base64_cert, reason);

	g_free (reason);

	return success;
}

static void
trust_prompt_register_dialogs (EExtension *extension,
                               EUserPrompterServer *server)
{
	e_user_prompter_server_register (server, extension, TRUST_PROMPT_DIALOG);
}

static gboolean
trust_prompt_prompt (EUserPrompterServerExtension *extension,
                     gint prompt_id,
                     const gchar *dialog_name,
                     const ENamedParameters *parameters)
{
	if (g_strcmp0 (dialog_name, TRUST_PROMPT_DIALOG) == 0)
		return trust_prompt_show_trust_prompt (extension, prompt_id, parameters);

	return FALSE;
}

static void
e_trust_prompt_class_init (ETrustPromptClass *class)
{
	EUserPrompterServerExtensionClass *server_extension_class;

	server_extension_class = E_USER_PROMPTER_SERVER_EXTENSION_CLASS (class);
	server_extension_class->register_dialogs = trust_prompt_register_dialogs;
	server_extension_class->prompt = trust_prompt_prompt;
}

static void
e_trust_prompt_class_finalize (ETrustPromptClass *class)
{
}

static void
e_trust_prompt_init (ETrustPrompt *trust_prompt)
{
}

G_MODULE_EXPORT void
e_module_load (GTypeModule *type_module)
{
	e_trust_prompt_register_type (type_module);
}

G_MODULE_EXPORT void
e_module_unload (GTypeModule *type_module)
{
}

