/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2016 Red Hat, Inc.
 */

#ifndef __NM_VPN_EDITOR_PLUGIN_CALL_H__
#define __NM_VPN_EDITOR_PLUGIN_CALL_H__

/* This header is an internal, header-only file that can be copied to
 * other projects to call well-known service functions on VPN plugins.
 *
 * This uses the NMVpnEditorPluginVT and allows a user (nm-applet)
 * to directly communicate with a VPN plugin using API that is newer
 * then the current libnm version. That is, it allows to call to a VPN
 * plugin bypassing libnm. */

#include <NetworkManager.h>

/* we make use of other internal header files, you need those too. */
#include "libnm-glib-aux/nm-macros-internal.h"

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

/**
 * NMVpnEditorPluginServiceFlags:
 * @NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_NONE: no flags
 * @NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_CAN_ADD: whether the plugin can
 *   add a new connection for the given service-type.
 **/
typedef enum /*< skip >*/ {
    NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_NONE    = 0x00,
    NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_CAN_ADD = 0x01,
} NMVpnEditorPluginServiceFlags;

struct _NMVpnEditorPluginVT {
    gboolean (*fcn_get_service_info)(NMVpnEditorPlugin             *plugin,
                                     const char                    *service_type,
                                     char                         **out_short_name,
                                     char                         **out_pretty_name,
                                     char                         **out_description,
                                     NMVpnEditorPluginServiceFlags *out_flags);
    char **(*fcn_get_service_add_details)(NMVpnEditorPlugin *plugin, const char *service_name);
    gboolean (*fcn_get_service_add_detail)(NMVpnEditorPlugin *plugin,
                                           const char        *service_type,
                                           const char        *add_detail,
                                           char             **out_pretty_name,
                                           char             **out_description,
                                           char             **out_add_detail_key,
                                           char             **out_add_detail_val,
                                           guint             *out_flags);
};

/*****************************************************************************
 * Call
 *
 * The following wrap the calling of generic functions for a VPN plugin.
 * They are used by callers (for example nm-connection-editor).
 *****************************************************************************/

static inline gboolean
nm_vpn_editor_plugin_get_service_info(NMVpnEditorPlugin             *plugin,
                                      const char                    *service_type,
                                      char                         **out_short_name,
                                      char                         **out_pretty_name,
                                      char                         **out_description,
                                      NMVpnEditorPluginServiceFlags *out_flags)
{
    NMVpnEditorPluginVT vt;
    gs_free char       *short_name_local  = NULL;
    gs_free char       *pretty_name_local = NULL;
    gs_free char       *description_local = NULL;
    guint               flags_local       = 0;

    g_return_val_if_fail(NM_IS_VPN_EDITOR_PLUGIN(plugin), FALSE);
    g_return_val_if_fail(service_type, FALSE);

    nm_vpn_editor_plugin_get_vt(plugin, &vt, sizeof(vt));
    if (!vt.fcn_get_service_info
        || !vt.fcn_get_service_info(plugin,
                                    service_type,
                                    out_short_name ? &short_name_local : NULL,
                                    out_pretty_name ? &pretty_name_local : NULL,
                                    out_description ? &description_local : NULL,
                                    out_flags ? &flags_local : NULL))
        return FALSE;
    NM_SET_OUT(out_short_name, g_steal_pointer(&short_name_local));
    NM_SET_OUT(out_pretty_name, g_steal_pointer(&pretty_name_local));
    NM_SET_OUT(out_description, g_steal_pointer(&description_local));
    NM_SET_OUT(out_flags, flags_local);
    return TRUE;
}

static inline char **
nm_vpn_editor_plugin_get_service_add_details(NMVpnEditorPlugin *plugin, const char *service_name)
{
    NMVpnEditorPluginVT vt;
    char              **details = NULL;

    g_return_val_if_fail(NM_IS_VPN_EDITOR_PLUGIN(plugin), NULL);
    g_return_val_if_fail(service_name, NULL);

    nm_vpn_editor_plugin_get_vt(plugin, &vt, sizeof(vt));
    if (vt.fcn_get_service_add_details)
        details = vt.fcn_get_service_add_details(plugin, service_name);
    if (!details)
        return nm_strv_empty_new();
    return details;
}

static inline gboolean
nm_vpn_editor_plugin_get_service_add_detail(NMVpnEditorPlugin *plugin,
                                            const char        *service_type,
                                            const char        *add_detail,
                                            char             **out_pretty_name,
                                            char             **out_description,
                                            char             **out_add_detail_key,
                                            char             **out_add_detail_val,
                                            guint             *out_flags)
{
    NMVpnEditorPluginVT vt;
    gs_free char       *pretty_name_local    = NULL;
    gs_free char       *description_local    = NULL;
    gs_free char       *add_detail_key_local = NULL;
    gs_free char       *add_detail_val_local = NULL;
    guint               flags_local          = 0;

    g_return_val_if_fail(NM_IS_VPN_EDITOR_PLUGIN(plugin), FALSE);
    g_return_val_if_fail(service_type, FALSE);
    g_return_val_if_fail(add_detail, FALSE);

    nm_vpn_editor_plugin_get_vt(plugin, &vt, sizeof(vt));
    if (!vt.fcn_get_service_add_detail
        || !vt.fcn_get_service_add_detail(plugin,
                                          service_type,
                                          add_detail,
                                          out_pretty_name ? &pretty_name_local : NULL,
                                          out_description ? &description_local : NULL,
                                          out_add_detail_key ? &add_detail_key_local : NULL,
                                          out_add_detail_val ? &add_detail_val_local : NULL,
                                          out_flags ? &flags_local : NULL))
        return FALSE;
    NM_SET_OUT(out_pretty_name, g_steal_pointer(&pretty_name_local));
    NM_SET_OUT(out_description, g_steal_pointer(&description_local));
    NM_SET_OUT(out_add_detail_key, g_steal_pointer(&add_detail_key_local));
    NM_SET_OUT(out_add_detail_val, g_steal_pointer(&add_detail_val_local));
    NM_SET_OUT(out_flags, flags_local);
    return TRUE;
}

/*****************************************************************************
 * Implementation
 *
 * The following glue code can be used to implement calls in a VPN plugin.
 *****************************************************************************/

#define NM_VPN_EDITOR_PLUGIN_VT_DEFINE(vt_name, get_vt, ...)                                \
    static const NMVpnEditorPluginVT  vt_name = {__VA_ARGS__};                              \
    static const NMVpnEditorPluginVT *get_vt(NMVpnEditorPlugin *plugin, gsize *out_vt_size) \
    {                                                                                       \
        nm_assert(NM_IS_VPN_EDITOR_PLUGIN(plugin));                                         \
        nm_assert(out_vt_size);                                                             \
                                                                                            \
        *out_vt_size = sizeof(vt_name);                                                     \
        return &vt_name;                                                                    \
    }

#endif /* __NM_VPN_EDITOR_PLUGIN_CALL_H__ */
