/* realmd -- Realm configuration service
 *
 * Copyright 2012 Red Hat Inc
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2 of the licence or (at
 * your option) any later version.
 *
 * See the included COPYING file for more information.
 *
 * Author: Stef Walter <stefw@gnome.org>
 */

#include "config.h"

#include "realm-adcli-enroll.h"
#include "realm-command.h"
#include "realm-daemon.h"
#include "realm-diagnostics.h"
#include "realm-errors.h"
#include "realm-ini-config.h"
#include "realm-options.h"
#include "realm-settings.h"

static void
on_join_process (GObject *source,
                 GAsyncResult *result,
                 gpointer user_data)
{
	GTask *task = G_TASK (user_data);
	GError *error = NULL;
	GString *output = NULL;
	gint status;

	status = realm_command_run_finish (result, &output, &error);
	if (error == NULL && status != 0) {
		switch (status) {
		case 2: /* ADCLI_ERR_UNEXPECTED */
			g_set_error (&error, REALM_ERROR, REALM_ERROR_INTERNAL,
			             "Internal unexpected error joining the domain");
			break;
		case 6: /* ADCLI_ERR_CREDENTIALS */
			g_set_error (&error, REALM_ERROR, REALM_ERROR_AUTH_FAILED,
			             "Insufficient permissions to join the domain");
			break;
		default:
			g_set_error (&error, REALM_ERROR, REALM_ERROR_FAILED,
			             "Failed to join the domain");
			break;
		}
	}

	if (error == NULL) {
		g_task_return_boolean (task, TRUE);

	} else {
		g_task_return_error (task, error);
	}

	if (output)
		g_string_free (output, TRUE);
	g_object_unref (task);
}

void
realm_adcli_enroll_join_async (RealmDisco *disco,
                               RealmCredential *cred,
                               GVariant *options,
                               GDBusMethodInvocation *invocation,
                               GAsyncReadyCallback callback,
                               gpointer user_data)
{
	gchar *environ[] = { "LANG=C", NULL };
	GInetAddress *address;
	const gchar *computer_ou;
	GTask *task;
	GBytes *input = NULL;
	const gchar *upn;
	GPtrArray *args;
	const gchar *os;
	gchar *ccache_arg = NULL;
	gchar *upn_arg = NULL;
	gchar *server_arg = NULL;

	g_return_if_fail (cred != NULL);
	g_return_if_fail (disco != NULL);
	g_return_if_fail (invocation != NULL);

	task = g_task_new (NULL, NULL, callback, user_data);
	args = g_ptr_array_new ();

	/* Use our custom smb.conf */
	g_ptr_array_add (args, (gpointer)realm_settings_path ("adcli"));
	g_ptr_array_add (args, "join");
	g_ptr_array_add (args, "--verbose");
	g_ptr_array_add (args, "--domain");
	g_ptr_array_add (args, (gpointer)disco->domain_name);
	g_ptr_array_add (args, "--domain-realm");
	g_ptr_array_add (args, (gpointer)disco->kerberos_realm);

	if (G_IS_INET_SOCKET_ADDRESS (disco->server_address)) {
		address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (disco->server_address));
		server_arg = g_inet_address_to_string (address);
		if (server_arg) {
			g_ptr_array_add (args, "--domain-controller");
			g_ptr_array_add (args, server_arg);
		}

	} else if (disco->explicit_server) {
		g_ptr_array_add (args, "--domain-controller");
		g_ptr_array_add (args, (gpointer)disco->explicit_server);
	}

	if (disco->explicit_netbios) {
		realm_diagnostics_info (invocation, "Joining using a truncated netbios name: %s",
		                        disco->explicit_netbios);
		g_ptr_array_add (args, "--computer-name");
		g_ptr_array_add (args, disco->explicit_netbios);
	}

	computer_ou = realm_options_computer_ou (options, disco->domain_name);
	if (computer_ou) {
		g_ptr_array_add (args, "--computer-ou");
		g_ptr_array_add (args, (gpointer)computer_ou);
	}

	os = realm_settings_value ("active-directory", "os-name");
	if (os != NULL && !g_str_equal (os, "")) {
		g_ptr_array_add (args, "--os-name");
		g_ptr_array_add (args, (gpointer)os);
	}

	os = realm_settings_value ("active-directory", "os-version");
	if (os != NULL && !g_str_equal (os, "")) {
		g_ptr_array_add (args, "--os-version");
		g_ptr_array_add (args, (gpointer)os);
	}

	switch (cred->type) {
	case REALM_CREDENTIAL_AUTOMATIC:
		g_ptr_array_add (args, "--login-type");
		g_ptr_array_add (args, "computer");
		g_ptr_array_add (args, "--no-password");
		break;
	case REALM_CREDENTIAL_CCACHE:
		g_ptr_array_add (args, "--login-type");
		g_ptr_array_add (args, "user");
		ccache_arg = g_strdup_printf ("--login-ccache=%s", cred->x.ccache.file);
		g_ptr_array_add (args, ccache_arg);
		break;
	case REALM_CREDENTIAL_PASSWORD:
		input = g_bytes_ref (cred->x.password.value);
		g_ptr_array_add (args, "--login-type");
		g_ptr_array_add (args, "user");
		g_ptr_array_add (args, "--login-user");
		g_ptr_array_add (args, cred->x.password.name);
		g_ptr_array_add (args, "--stdin-password");
		break;
	case REALM_CREDENTIAL_SECRET:
		input = g_bytes_ref (cred->x.secret.value);
		g_ptr_array_add (args, "--login-type");
		g_ptr_array_add (args, "computer");
		g_ptr_array_add (args, "--stdin-password");
		break;
	}

	upn = realm_options_user_principal (options, disco->domain_name);
	if (upn) {
		if (g_str_equal (upn, "")) {
			g_ptr_array_add (args, "--user-principal");
		} else {
			upn_arg = g_strdup_printf ("--user-principal=%s", upn);
			g_ptr_array_add (args, upn_arg);
		}
	}

	g_ptr_array_add (args, NULL);

	realm_command_runv_async ((gchar **)args->pdata, environ, input,
	                          invocation, on_join_process,
	                          g_object_ref (task));

	g_ptr_array_free (args, TRUE);
	g_object_unref (task);

	if (input)
		g_bytes_unref (input);

	free (ccache_arg);
	free (upn_arg);
	free (server_arg);
}

gboolean
realm_adcli_enroll_join_finish (GAsyncResult *result,
                                GError **error)
{
	g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
	return g_task_propagate_boolean (G_TASK (result), error);
}

void
realm_adcli_enroll_delete_async (RealmDisco *disco,
                                 RealmCredential *cred,
                                 GVariant *options,
                                 GDBusMethodInvocation *invocation,
                                 GAsyncReadyCallback callback,
                                 gpointer user_data)
{
	gchar *environ[] = { "LANG=C", NULL };
	GInetAddress *address;
	GTask *task;
	GBytes *input = NULL;
	GPtrArray *args;
	gchar *ccache_arg = NULL;
	gchar *server_arg = NULL;

	g_return_if_fail (cred != NULL);
	g_return_if_fail (disco != NULL);
	g_return_if_fail (invocation != NULL);

	task = g_task_new (NULL, NULL, callback, user_data);
	args = g_ptr_array_new ();

	/* Use our custom smb.conf */
	g_ptr_array_add (args, (gpointer)realm_settings_path ("adcli"));
	g_ptr_array_add (args, "delete-computer");
	g_ptr_array_add (args, "--verbose");
	g_ptr_array_add (args, "--domain");
	g_ptr_array_add (args, (gpointer)disco->domain_name);
	g_ptr_array_add (args, "--domain-realm");
	g_ptr_array_add (args, (gpointer)disco->kerberos_realm);

	if (G_IS_INET_SOCKET_ADDRESS (disco->server_address)) {
		address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (disco->server_address));
		server_arg = g_inet_address_to_string (address);
		if (server_arg) {
			g_ptr_array_add (args, "--domain-controller");
			g_ptr_array_add (args, server_arg);
		}

	} else if (disco->explicit_server) {
		g_ptr_array_add (args, "--domain-controller");
		g_ptr_array_add (args, (gpointer)disco->explicit_server);
	}

	switch (cred->type) {
	case REALM_CREDENTIAL_AUTOMATIC:
	case REALM_CREDENTIAL_SECRET:
		g_return_if_reached ();
		break;
	case REALM_CREDENTIAL_CCACHE:
		ccache_arg = g_strdup_printf ("--login-ccache=%s", cred->x.ccache.file);
		g_ptr_array_add (args, ccache_arg);
		break;
	case REALM_CREDENTIAL_PASSWORD:
		input = g_bytes_ref (cred->x.password.value);
		g_ptr_array_add (args, "--login-user");
		g_ptr_array_add (args, cred->x.password.name);
		g_ptr_array_add (args, "--stdin-password");
		break;
	}

	g_ptr_array_add (args, NULL);

	realm_command_runv_async ((gchar **)args->pdata, environ, input,
	                          invocation, on_join_process,
	                          g_object_ref (task));

	g_ptr_array_free (args, TRUE);
	g_object_unref (task);

	if (input)
		g_bytes_unref (input);

	free (ccache_arg);
	g_free (server_arg);
}

gboolean
realm_adcli_enroll_delete_finish (GAsyncResult *result,
                                  GError **error)
{
	g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
	return g_task_propagate_boolean (G_TASK (result), error);
}
