/*
 * Copyright (C) 2023 Tobias Brunner
 * Copyright (C) 2014 Martin Willi
 *
 * libvici.h is MIT-licensed to simplify reuse, but please note that libvici.c
 * is not, as it depends on the GPLv2 licensed libstrongswan.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * @defgroup libvici libvici
 * @{ @ingroup vici
 *
 * libvici is a low-level client library for the "Versatile IKE Control
 * Interface" protocol. While it uses libstrongswan and its thread-pool for
 * asynchronous message delivery, this interface does not directly depend on
 * libstrongswan interfaces and should be stable.
 *
 * This interface provides the following basic functions:
 *
 * - vici_init()/vici_deinit(): Library initialization functions
 * - vici_connect(): Connect to a vici service
 * - vici_disconnect(): Disconnect from a vici service
 *
 * Library initialization implicitly initializes libstrongswan and a small
 * thread pool.
 *
 * Connecting requires an uri, which is currently either a UNIX socket path
 * prefixed with unix://, or a hostname:port tuple prefixed with tcp://.
 * Passing NULL takes the system default socket path.
 *
 * After the connection has been established, request messages can be sent.
 * Only a single thread may operate on a single connection instance
 * simultaneously. To construct request messages, use the following functions:
 *
 * - vici_add_key_value() / vici_add_key_valuef(): Add key/value pairs
 * - vici_begin(): Start constructing a new request message
 * - vici_begin_section(): Open a new section to add contents to
 * - vici_end_section(): Close a previously opened session
 * - vici_begin_list(): Open a new list to add list items to
 * - vici_end_list(): Close a previously opened list
 * - vici_add_list_item() / vici_add_list_itemf(): Add list item
 *
 * Once the request message is complete, it can be sent or canceled with:
 *
 * - vici_submit()
 * - vici_free_req()
 *
 * If submitting a message is successful, a response message is returned. It
 * can be processed using the following functions:
 *
 * - vici_parse(): Parse content type
 * - vici_parse_name(): Parse name if content type provides one
 * - vici_parse_name_eq(): Parse name and check if matches string
 * - vici_parse_value() / vici_parse_value_str(): Parse value for content type
 * - vici_dump(): Dump a full response to a FILE stream
 * - vici_free_res(): Free response after use
 *
 * Usually vici_parse() is called in a loop, and depending on the returned
 * type the name and value can be inspected.
 *
 * To register or unregister for asynchronous event messages vici_register() is
 * used. The registered callback gets invoked by an asynchronous thread. To
 * parse the event message, the vici_parse*() functions can be used.
 * To get notified if the connection is closed by the vici service while waiting
 * for event messages, vici_on_close() may be used.
 */

#ifndef LIBVICI_H_
#define LIBVICI_H_

#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Opaque vici connection context.
 */
typedef struct vici_conn_t vici_conn_t;

/**
 * Opaque vici request message.
 */
typedef struct vici_req_t vici_req_t;

/**
 * Opaque vici response/event message.
 */
typedef struct vici_res_t vici_res_t;

/**
 * Vici parse result, as returned by vici_parse().
 */
typedef enum {
	/** encountered a section start, has a name */
	VICI_PARSE_BEGIN_SECTION,
	/** encountered a section end */
	VICI_PARSE_END_SECTION,
	/** encountered a list start, has a name */
	VICI_PARSE_BEGIN_LIST,
	/** encountered a list element, has a value */
	VICI_PARSE_LIST_ITEM,
	/** encountered a list end */
	VICI_PARSE_END_LIST,
	/** encountered a key/value pair, has a name and a value */
	VICI_PARSE_KEY_VALUE,
	/** encountered valid end of message */
	VICI_PARSE_END,
	/** parse error */
	VICI_PARSE_ERROR,
} vici_parse_t;

/**
 * Callback function invoked for received event messages.
 *
 * It is not allowed to call vici_submit() from this callback.
 *
 * @param user		user data, as passed to vici_connect
 * @param name		name of received event
 * @param msg		associated event message, destroyed by libvici
 */
typedef void (*vici_event_cb_t)(void *user, char *name, vici_res_t *msg);

/**
 * Callback function for key/value and list items, invoked by vici_parse_cb().
 *
 * @param user		user data, as passed to vici_parse_cb()
 * @param res		message currently parsing
 * @param name		name of key or list
 * @param value		value buffer
 * @param len		length of value buffer
 * @return			0 if parsed successfully
 */
typedef int	(*vici_parse_value_cb_t)(void *user, vici_res_t *res, char *name,
									 void *value, int len);

/**
 * Callback function for sections, invoked by vici_parse_cb().
 *
 * @param user		user data, as passed to vici_parse_cb()
 * @param res		message currently parsing
 * @param name		name of the section
 * @return			0 if parsed successfully
 */
typedef int (*vici_parse_section_cb_t)(void *user, vici_res_t *res, char *name);

/**
 * Callback function invoked if the connection is closed by the vici service.
 *
 * @param user		user data, as passed to vici_on_close()
 */
typedef void (*vici_close_cb_t)(void *user);

/**
 * Open a new vici connection.
 *
 * On error, NULL is returned and errno is set appropriately.
 *
 * @param uri		URI to connect to, NULL to use system default
 * @return			opaque vici connection context, NULL on error
 */
vici_conn_t* vici_connect(char *uri);

/**
 * Close a vici connection.
 *
 * @param conn		connection context
 */
void vici_disconnect(vici_conn_t *conn);

/**
 * Begin a new vici message request.
 *
 * This function always succeeds.
 *
 * @param name		name of request command
 * @return			request message, to add contents
 */
vici_req_t* vici_begin(char *name);

/**
 * Begin a new section in a vici request message.
 *
 * @param req		request message to create a new section in
 * @param name		name of section to create
 */
void vici_begin_section(vici_req_t *req, char *name);

/**
 * End a previously opened section.
 *
 * @param req		request message to close an open section in
 */
void vici_end_section(vici_req_t *req);

/**
 * Add a key/value pair, using an as-is blob as value.
 *
 * @param req		request message to add key/value pair to
 * @param key		key name of key/value pair
 * @param buf		pointer to blob to add as value
 * @param len		length of value blob to add
 */
void vici_add_key_value(vici_req_t *req, char *key, void *buf, int len);

/**
 * Add a key/value pair, setting value from a printf() format string.
 *
 * @param req		request message to add key/value pair to
 * @param key		key name of key/value pair
 * @param fmt		format string for value
 * @param ...		arguments to format string
 */
void vici_add_key_valuef(vici_req_t *req, char *key, char *fmt, ...);

/**
 * Begin a list in a request message.
 *
 * After starting a list, only list items can be added until the list gets
 * closed by vici_end_list().
 *
 * @param req		request message to begin list in
 * @param name		name of list to begin
 */
void vici_begin_list(vici_req_t *req, char *name);

/**
 * Add a list item to a currently open list, using an as-is blob.
 *
 * @param req		request message to add list item to
 * @param buf		pointer to blob to add as value
 * @param len		length of value blob to add
 */
void vici_add_list_item(vici_req_t *req, void *buf, int len);

/**
 * Add a list item to a currently open list, using a printf() format string.
 *
 * @param req		request message to add list item to
 * @param fmt		format string to create value from
 * @param ...		arguments to format string
 */
void vici_add_list_itemf(vici_req_t *req, char *fmt, ...);

/**
 * End a previously opened list in a request message.
 *
 * @param req		request message to end list in
 */
void vici_end_list(vici_req_t *req);

/**
 * Submit a request message, and wait for response.
 *
 * The request messages gets cleaned up by this call and gets invalid.
 * On error, NULL is returned an errno is set to:
 * - EINVAL if the request is invalid/incomplete
 * - ENOENT if the command is unknown
 * - EBADMSG if the response is invalid
 * - Any other IO related errno
 *
 * @param req		request message to send
 * @param conn		connection context to send message over
 * @return			response message, NULL on error
 */
vici_res_t* vici_submit(vici_req_t *req, vici_conn_t *conn);

/**
 * Cancel a request message started.
 *
 * If a request created by vici_begin() does not get submitted using
 * vici_submit(), it has to get freed using this call.
 *
 * @param req		request message to clean up
 */
void vici_free_req(vici_req_t *req);

/**
 * Dump a message text representation to a FILE stream.
 *
 * On error, errno is set to:
 * - EBADMSG if the message is invalid
 *
 * @param res		response message to dump
 * @param label		a label to print for this message
 * @param pretty	use pretty print with indentation
 * @param out		FILE to dump to
 * @return			0 if dumped complete message, 1 on error
 */
int vici_dump(vici_res_t *res, char *label, int pretty, FILE *out);

/**
 * Parse next element from a vici response message.
 *
 * @param res		response message to parse
 * @return			parse result
 */
vici_parse_t vici_parse(vici_res_t *res);

/**
 * Parse name tag / key of a previously parsed element.
 *
 * This call is valid only after vici_parse() returned VICI_PARSE_KEY_VALUE,
 * VICI_PARSE_BEGIN_SECTION or VICI_PARSE_BEGIN_LIST.
 *
 * The string is valid until vici_free_res() is called.
 *
 * On error, errno is set to:
 *- EINVAL if not in valid parser state
 *
 * @param res		response message to parse
 * @return			name tag / key, NULL on error
 */
char* vici_parse_name(vici_res_t *res);

/**
 * Compare name tag / key of a previously parsed element.
 *
 * This call is valid only after vici_parse() returned VICI_PARSE_KEY_VALUE,
 * VICI_PARSE_BEGIN_SECTION or VICI_PARSE_BEGIN_LIST.
 *
 * @param res		response message to parse
 * @param name		string to compare
 * @return			1 if name equals, 0 if not
 */
int vici_parse_name_eq(vici_res_t *res, char *name);

/**
 * Parse value of a previously parsed element, as a blob.
 *
 * This call is valid only after vici_parse() returned VICI_PARSE_KEY_VALUE or
 * VICI_PARSE_LIST_ITEM.
 *
 * The string is valid until vici_free_res() is called.
 *
 * On error, errno is set to:
 * - EINVAL if not in valid parser state
 *
 * @param res		response message to parse
 * @param len		pointer receiving value length
 * @return			pointer to value, NULL on error
 */
void* vici_parse_value(vici_res_t *res, int *len);

/**
 * Parse value of a previously parsed element, as a string.
 *
 * This call is valid only after vici_parse() returned VICI_PARSE_KEY_VALUE or
 * VICI_PARSE_LIST_ITEM.
 *
 * This call is successful only if the value contains no non-printable
 * characters. The string is valid until vici_free_res() is called.
 *
 * On error, errno is set to:
 * - EBADMSG if value is not a printable string
 * - EINVAL if not in valid parser state
 *
 * @param res		response message to parse
 * @return			value as string, NULL on error
 */
char* vici_parse_value_str(vici_res_t *res);

/**
 * Parse a complete message with callbacks.
 *
 * Any of the callbacks may be NULL to skip this kind of item. Callbacks are
 * invoked for the current section level only. To descent into sections, call
 * vici_parse_cb() from within a section callback.
 *
 * On error, errno is set to:
 * - EBADMSG if message encoding invalid
 * - Any other errno set by the invoked callbacks
 *
 * @param res		message to parse
 * @param section	callback invoked for each section
 * @param kv		callback invoked for key/value pairs
 * @param li		callback invoked for list items
 * @param user		user data to pass to callbacks
 * @return			0 if parsing successful
 */
int vici_parse_cb(vici_res_t *res, vici_parse_section_cb_t section,
				  vici_parse_value_cb_t kv, vici_parse_value_cb_t li,
				  void *user);

/*
 * Find a blob value in a message for a given key.
 *
 * Sections can be selected by prefixing them separated by dots.
 *
 * @param res		response message to parse
 * @param len		length of returned object
 * @param fmt		printf format string of key and sections
 * @param ...		arguments to format string
 * @return			blob value, having *len bytes, NULL if not found
 */
void *vici_find(vici_res_t *res, int *len, char *fmt, ...);

/**
 * Find a string value in a message for a given key.
 *
 * Sections can be selected by prefixing them separated by dots.
 *
 * @param res		response message to parse
 * @param def		default value, if key not found
 * @param fmt		printf format string of key and sections
 * @param ...		arguments to format string
 * @return			string, def if not found
 */
char* vici_find_str(vici_res_t *res, char *def, char *fmt, ...);

/**
 * Find an integer value in a message for a given key.
 *
 * Sections can be selected by prefixing them separated by dots.
 *
 * @param res		response message to parse
 * @param def		default value, if key not found
 * @param fmt		printf format string of key and sections
 * @param ...		arguments to format string
 * @return			integer value, def if not found
 */
int vici_find_int(vici_res_t *res, int def, char *fmt, ...);

/**
 * Clean up a received response message.
 *
 * Event messages get cleaned up by the library, it is not allowed to call
 * vici_free_res() from within a vici_event_cb_t.
 *
 * @param res		response message to free
 */
void vici_free_res(vici_res_t *res);

/**
 * (Un-)Register for events of a given kind.
 *
 * Events callbacks get invoked by a different thread from the libstrongswan
 * thread pool.
 * On failure, errno is set to:
 * - ENOENT if the event name is unknown
 * - EBADMSG if the response is invalid
 * - Any other IO related errno
 *
 * @param conn		connection context
 * @param name		name of event messages to register to
 * @param cb		callback function to register, NULL to unregister
 * @param user		user data passed to callback invocations
 * @return			0 if registered successfully
 */
int vici_register(vici_conn_t *conn, char *name, vici_event_cb_t cb, void *user);

/**
 * (Un-)Register a callback that's invoked if the connection is closed by the
 * vici service.
 *
 * Primarily useful when listening for events via vici_register(). The callback
 * gets invoked by a different thread from the libstrongswan thread pool.
 *
 * @param conn		connection context
 * @param cb		callback function to register, NULL to unregister
 * @param user		user data passed to callback invocation
 */
void vici_on_close(vici_conn_t *conn, vici_close_cb_t cb, void *user);

/**
 * Initialize libvici before first time use.
 */
void vici_init();

/**
 * Deinitialize libvici after use.
 */
void vici_deinit();

#ifdef __cplusplus
}
#endif

#endif /** LIBVICI_H_ @}*/
