// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2007 - 2018 Red Hat, Inc.
 * Copyright (C) 2008 Novell, Inc.
 */

#include "nm-default.h"

#include "nm-settings-plugin.h"

#include "nm-utils.h"
#include "nm-core-internal.h"

#include "nm-settings-connection.h"

/*****************************************************************************/

enum {
	UNMANAGED_SPECS_CHANGED,
	UNRECOGNIZED_SPECS_CHANGED,

	LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

G_DEFINE_TYPE (NMSettingsPlugin, nm_settings_plugin, G_TYPE_OBJECT)

/*****************************************************************************/

int
nm_settings_plugin_cmp_by_priority (const NMSettingsPlugin *a,
                                    const NMSettingsPlugin *b,
                                    const GSList *plugin_list)
{
	nm_assert (NM_IS_SETTINGS_PLUGIN (a));
	nm_assert (NM_IS_SETTINGS_PLUGIN (b));

	if (a != b) {
		int idx_a = g_slist_index ((GSList *) plugin_list, a);
		int idx_b = g_slist_index ((GSList *) plugin_list, b);

		/* the plugins must be found in the list. */
		nm_assert (idx_a >= 0);
		nm_assert (idx_b >= 0);

		/* plugins that appear first in @plugin_list have higher priority.
		 * That means: smaller index -> higher priority. Reverse sort. */
		NM_CMP_DIRECT (idx_b, idx_a);
	}

	return 0;
}

/*****************************************************************************/

GSList *
nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *self)
{
	NMSettingsPluginClass *klass;

	g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);

	klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
	if (!klass->get_unmanaged_specs)
		return NULL;
	return klass->get_unmanaged_specs (self);
}

GSList *
nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self)
{
	NMSettingsPluginClass *klass;

	g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);

	klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
	if (!klass->get_unrecognized_specs)
		return NULL;
	return klass->get_unrecognized_specs (self);
}

void
nm_settings_plugin_reload_connections (NMSettingsPlugin *self,
                                       NMSettingsPluginConnectionLoadCallback callback,
                                       gpointer user_data)
{
	NMSettingsPluginClass *klass;

	g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self));
	g_return_if_fail (callback);

	klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
	if (klass->reload_connections)
		klass->reload_connections (self, callback, user_data);
}

NMSettingsPluginConnectionLoadEntry *
nm_settings_plugin_create_connection_load_entries (const char *const*filenames,
                                                   gsize *out_len)
{
	NMSettingsPluginConnectionLoadEntry *entries;
	gsize len;
	gsize i;

	len = NM_PTRARRAY_LEN (filenames);
	if (len == 0) {
		*out_len = 0;
		return NULL;
	}

	entries = g_new (NMSettingsPluginConnectionLoadEntry, len);
	for (i = 0; i < len; i++) {
		entries[i] = (NMSettingsPluginConnectionLoadEntry) {
			.filename = filenames[i],
			.error    = NULL,
			.handled  = FALSE,
		};
	}

	*out_len = len;
	return entries;
}

void
nm_settings_plugin_load_connections (NMSettingsPlugin *self,
                                     NMSettingsPluginConnectionLoadEntry *entries,
                                     gsize n_entries,
                                     NMSettingsPluginConnectionLoadCallback callback,
                                     gpointer user_data)
{
	NMSettingsPluginClass *klass;

	g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self));

	klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
	if (klass->load_connections)
		klass->load_connections (self, entries, n_entries, callback, user_data);
}

void
nm_settings_plugin_load_connections_done (NMSettingsPlugin *self)
{
	NMSettingsPluginClass *klass;

	g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self));

	klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
	if (klass->load_connections_done)
		klass->load_connections_done (self);
}

gboolean
nm_settings_plugin_add_connection (NMSettingsPlugin *self,
                                   NMConnection *connection,
                                   NMSettingsStorage **out_storage,
                                   NMConnection **out_connection,
                                   GError **error)
{
	NMSettingsPluginClass *klass;

	g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE);
	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);

#if NM_MORE_ASSERTS > 5
	nm_assert (nm_connection_verify (connection, NULL));
#endif

	NM_SET_OUT (out_storage, NULL);
	NM_SET_OUT (out_connection, NULL);

	klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
	if (!klass->add_connection) {
		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED,
		                     "settings plugin does not support adding connections");
		return FALSE;
	}
	return klass->add_connection (self,
	                              connection,
	                              out_storage,
	                              out_connection,
	                              error);
}

gboolean
nm_settings_plugin_update_connection (NMSettingsPlugin *self,
                                      NMSettingsStorage *storage,
                                      NMConnection *connection,
                                      NMSettingsStorage **out_storage,
                                      NMConnection **out_connection,
                                      GError **error)
{
	NMSettingsPluginClass *klass = NULL;

	g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE);
	g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (storage), FALSE);
	g_return_val_if_fail (nm_settings_storage_get_plugin (storage) == self, FALSE);
	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);

#if NM_MORE_ASSERTS > 5
	nm_assert (nm_connection_verify (connection, NULL));
	nm_assert (nm_streq (nm_connection_get_uuid (connection), nm_settings_storage_get_uuid (storage)));
#endif

	klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);

	NM_SET_OUT (out_storage, NULL);
	NM_SET_OUT (out_connection, NULL);

	if (!klass->update_connection) {
		g_set_error (error,
		             NM_SETTINGS_ERROR,
		             NM_SETTINGS_ERROR_NOT_SUPPORTED,
		             "settings plugin does not support modifying connections");
		return FALSE;
	}
	return klass->update_connection (self,
	                                 storage,
	                                 connection,
	                                 out_storage,
	                                 out_connection,
	                                 error);
}

gboolean
nm_settings_plugin_delete_connection (NMSettingsPlugin *self,
                                      NMSettingsStorage *storage,
                                      GError **error)
{
	NMSettingsPluginClass *klass = NULL;

	g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE);
	g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (storage), FALSE);
	g_return_val_if_fail (nm_settings_storage_get_plugin (storage) == self, FALSE);

	klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);

	if (!klass->delete_connection) {
		g_set_error (error,
		             NM_SETTINGS_ERROR,
		             NM_SETTINGS_ERROR_NOT_SUPPORTED,
		             "settings plugin does not support deleting connections");
		return FALSE;
	}

	return klass->delete_connection (self,
	                                 storage,
	                                 error);
}

/*****************************************************************************/

void
_nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *self)
{
	nm_assert (NM_IS_SETTINGS_PLUGIN (self));

	g_signal_emit (self, signals[UNMANAGED_SPECS_CHANGED], 0);
}

void
_nm_settings_plugin_emit_signal_unrecognized_specs_changed (NMSettingsPlugin *self)
{
	nm_assert (NM_IS_SETTINGS_PLUGIN (self));

	g_signal_emit (self, signals[UNRECOGNIZED_SPECS_CHANGED], 0);
}

/*****************************************************************************/

static void
nm_settings_plugin_init (NMSettingsPlugin *self)
{
}

static void
nm_settings_plugin_class_init (NMSettingsPluginClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	signals[UNMANAGED_SPECS_CHANGED] =
	    g_signal_new (NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED,
	                  G_OBJECT_CLASS_TYPE (object_class),
	                  G_SIGNAL_RUN_FIRST,
	                  0, NULL, NULL,
	                  g_cclosure_marshal_VOID__VOID,
	                  G_TYPE_NONE, 0);

	signals[UNRECOGNIZED_SPECS_CHANGED] =
	    g_signal_new (NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED,
	                  G_OBJECT_CLASS_TYPE (object_class),
	                  G_SIGNAL_RUN_FIRST,
	                  0, NULL, NULL,
	                  g_cclosure_marshal_VOID__VOID,
	                  G_TYPE_NONE, 0);
}
