// Unless explicitly stated otherwise all files in this repository are
// dual-licensed under the Apache-2.0 License or BSD-3-Clause License.
//
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2021 Datadog, Inc.

#ifndef pw_h
#define pw_h

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>

#define DDWAF_MAX_STRING_LENGTH 4096
#define DDWAF_MAX_MAP_DEPTH 20
#define DDWAF_MAX_ARRAY_LENGTH 256
#define DDWAF_RUN_TIMEOUT 5000

/**
 * @enum DDWAF_OBJ_TYPE
 *
 * Specifies the type of a ddwaf::object.
 **/
typedef enum
{
    DDWAF_OBJ_INVALID     = 0,
    /** Value shall be decoded as a int64_t (or int32_t on 32bits platforms). **/
    DDWAF_OBJ_SIGNED   = 1 << 0,
    /** Value shall be decoded as a uint64_t (or uint32_t on 32bits platforms). **/
    DDWAF_OBJ_UNSIGNED = 1 << 1,
    /** Value shall be decoded as a UTF-8 string of length nbEntries. **/
    DDWAF_OBJ_STRING   = 1 << 2,
    /** Value shall be decoded as an array of ddwaf_object of length nbEntries, each item having no parameterName. **/
    DDWAF_OBJ_ARRAY    = 1 << 3,
    /** Value shall be decoded as an array of ddwaf_object of length nbEntries, each item having a parameterName. **/
    DDWAF_OBJ_MAP      = 1 << 4,
} DDWAF_OBJ_TYPE;

/**
 * @enum DDWAF_RET_CODE
 *
 * Codes returned by ddwaf_run.
 **/
typedef enum
{
    DDWAF_ERR_INTERNAL     = -4,
    DDWAF_ERR_INVALID_OBJECT = -3,
    DDWAF_ERR_INVALID_ARGUMENT = -2,
    DDWAF_ERR_TIMEOUT      = -1,
    DDWAF_GOOD             = 0,
    DDWAF_MONITOR          = 1,
    DDWAF_BLOCK            = 2
} DDWAF_RET_CODE;

/**
 * @enum DDWAF_LOG_LEVEL
 *
 * Internal WAF log levels, to be used when setting the minimum log level and cb.
 **/
typedef enum
{
    DDWAF_LOG_TRACE,
    DDWAF_LOG_DEBUG,
    DDWAF_LOG_INFO,
    DDWAF_LOG_WARN,
    DDWAF_LOG_ERROR,
    DDWAF_LOG_OFF,
} DDWAF_LOG_LEVEL;

typedef struct _ddwaf_handle* ddwaf_handle;
typedef struct _ddwaf_context* ddwaf_context;
typedef struct _ddwaf_object ddwaf_object;
typedef struct _ddwaf_config ddwaf_config;
typedef struct _ddwaf_result ddwaf_result;
typedef struct _ddwaf_version ddwaf_version;

/**
 * @struct ddwaf_object
 *
 * Generic object used to pass data and rules to the WAF.
 **/
struct _ddwaf_object
{
    const char* parameterName;
    uint64_t parameterNameLength;
    // uintValue should be at least as wide as the widest type on the platform.
    union
    {
        const char* stringValue;
        uint64_t uintValue;
        int64_t intValue;
        const ddwaf_object* array;
    };
    uint64_t nbEntries;
    DDWAF_OBJ_TYPE type;
};

/**
 * @struct ddwaf_config
 *
 * Configuration to be provided to the WAF
 **/
struct _ddwaf_config
{
    /** Maximum length of ddwaf::object arrays. */
    uint64_t maxArrayLength;
    /** Maximum depth of ddwaf::object maps. */
    uint64_t maxMapDepth;
    /** Maximum size of the rule run time store. **/
    int32_t maxTimeStore;
};

/**
 * @struct ddwaf_result
 *
 * Structure containing the result of a WAF run.
 **/
struct _ddwaf_result
{
    /** Run result action **/
    DDWAF_RET_CODE action;
    /** Run result in JSON format **/
    const char* data;
    /** Performance data in JSON format **/
    const char* perfData;
    /** Total run time in microseconds **/
    uint32_t perfTotalRuntime;
};

/**
 * @ddwaf_version
 *
 * Structure containing the version of the WAF following semver.
 **/
struct _ddwaf_version
{
    uint16_t major;
    uint16_t minor;
    uint16_t patch;
};

/**
 * @typedef ddwaf_object_free_fn
 *
 * Type of the function to free ddwaf::objects.
 **/
typedef void (*ddwaf_object_free_fn)(ddwaf_object *object);

/**
 * @typedef ddwaf_log_cb
 *
 * Callback that powerwaf will call to relay messages to the binding.
 *
 * @param level The logging level.
 * @param function The native function that emitted the message. (nonnull)
 * @param file The file of the native function that emmitted the message. (nonnull)
 * @param line The line where the message was emmitted.
 * @param message The size of the logging message. NUL-terminated
 * @param message_len The length of the logging message (excluding NUL terminator).
 */
typedef void (*ddwaf_log_cb)(
    DDWAF_LOG_LEVEL level, const char* function, const char* file, unsigned line,
    const char* message, uint64_t message_len);

/**
 * ddwaf_init
 *
 * Initialize a ddwaf instance
 *
 * @param rule ddwaf::object containing the patterns to be used by the WAF. (nonnull)
 * @param config Optional configuration of the WAF. (nullable)
 *
 * @return Handle to the WAF instance.
 **/
ddwaf_handle ddwaf_init(const ddwaf_object *rule, const ddwaf_config* config);

/**
 * ddwaf_destroy
 *
 * Destroy a WAF instance.
 *
 * @param Handle to the WAF instance.
 */
void ddwaf_destroy(ddwaf_handle handle);

/**
 * ddwaf_required_addresses
 *
 * Get a list of required (root) addresses. The memory is owned by the WAF and
 * should not be freed.
 *
 * @param Handle to the WAF instance.
 * @param size Output parameter in which the size will be returned. The value of
 *             size will be 0 if the return value is nullptr.
 * @return NULL if error, otherwise a pointer to an array with size elements.
 **/
const char* const* ddwaf_required_addresses(const ddwaf_handle handle, uint32_t *size);
/**
 * ddwaf_context_init
 *
 * Context object to perform matching using the provided WAF instance.
 *
 * @param handle Handle of the WAF instance containing the ruleset definition. (nonnull)
 * @param obj_free Function to free the ddwaf::object provided to the context
 *                 during calls to ddwaf_run. If the value of this function is
 *                 NULL, the objects will not be freed. By default the value of
 *                 this parameter should be ddwaf_object_free.
 *
 * @return Handle to the context instance.
 *
 * @note The WAF instance needs to be valid for the lifetime of the context.
 **/
ddwaf_context ddwaf_context_init(const ddwaf_handle handle, ddwaf_object_free_fn obj_free);

/**
 * ddwaf_run
 *
 * Perform a matching operation on the provided data
 *
 * @param context WAF context to be used in this run, this will determine the
 *                ruleset which will be used and it will also ensure that
 *                parameters are taken into account across runs (nonnull)
 * @param data Data on which to perform the pattern matching. This data will be
 *             stored by the context and used across multiple calls to this
 *             function. Once the context is destroyed, the used-defined free
 *             function will be used to free the data provided. Note that the
 *             data passed must be valid until the destruction of the context.
 *             (nonull)
 * @param result Structure containing the result of the operation. (nullable)
 * @param timeout Maximum time budget in microseconds.
 *
 * @return Return code of the operation, also contained in the result structure.
 * @error DDWAF_ERR_INVALID_ARGUMENT The context is invalid, the data will not
 *                                   be freed.
 * @error DDWAF_ERR_INVALID_OBJECT The data provided didn't match the desired
 *                                 structure or contained invalid objects, the
 *                                 data will be freed by this function.
 * @error DDWAF_ERR_TIMEOUT The operation timed out, the data will be owned by
 *                          the context and freed during destruction.
 * @error DDWAF_ERR_INTERNAL There was an unexpected error and the operation did
 *                           not succeed. The state of the WAF is undefined if
 *                           this error is produced and the ownership of the
 *                           data is unknown. The result structure will not be
 *                           filled if this error occurs.
 **/
DDWAF_RET_CODE ddwaf_run(ddwaf_context context, ddwaf_object *data, ddwaf_result *result, uint64_t timeout);

/**
 * ddwaf_context_destroy
 *
 * Performs the destruction of the context, freeing the data passed to it through
 * ddwaf_run using the used-defined free function.
 *
 * @param context Context to destroy. (nonnull)
 **/
void ddwaf_context_destroy(ddwaf_context context);

/**
 * ddwaf_result_free
 *
 * Free a ddwaf_result structure.
 *
 * @param result Structure to free. (nonnull)
 **/
void ddwaf_result_free(ddwaf_result *result);

/**
 * ddwaf_object_invalid
 *
 * Creates an invalid object.
 *
 * @param object Object to perform the operation on. (nonnull)
 *
 * @return A pointer to the passed object or NULL if the operation failed.
 **/
ddwaf_object* ddwaf_object_invalid(ddwaf_object *object);

/**
 * ddwaf_object_string
 *
 * Creates an object from a string.
 *
 * @param object Object to perform the operation on. (nonnull)
 * @param string String to initialise the object with, this string will be copied
 *               and its length will be calculated using strlen(string). (nonnull)
 *
 * @return A pointer to the passed object or NULL if the operation failed.
 **/
ddwaf_object* ddwaf_object_string(ddwaf_object *object, const char *string);

/**
 * ddwaf_object_stringl
 *
 * Creates an object from a string and its length.
 *
 * @param object Object to perform the operation on. (nonnull)
 * @param string String to initialise the object with, this string will be
 *               copied. (nonnull)
 * @param length Length of the string.
 *
 * @return A pointer to the passed object or NULL if the operation failed.
 **/
ddwaf_object* ddwaf_object_stringl(ddwaf_object *object, const char *string, size_t length);

/**
 * ddwaf_object_stringl_nc
 *
 * Creates an object with the string pointer and length provided.
 *
 * @param object Object to perform the operation on. (nonnull)
 * @param string String pointer to initialise the object with.
 * @param length Length of the string.
 *
 * @return A pointer to the passed object or NULL if the operation failed.
 **/
ddwaf_object* ddwaf_object_stringl_nc(ddwaf_object *object, const char *string, size_t length);

/**
 * ddwaf_object_unsigned
 *
 * Creates an object using an unsigned integer (64-bit). The resulting object
 * will contain a string created using the integer provided. This is the
 * preferred method for passing an unsigned integer to the WAF.
 *
 * @param object Object to perform the operation on. (nonnull)
 * @param value Integer to initialise the object with.
 *
 * @return A pointer to the passed object or NULL if the operation failed.
 **/
ddwaf_object* ddwaf_object_unsigned(ddwaf_object *object, uint64_t value);

/**
 * ddwaf_object_signed
 *
 * Creates an object using a signed integer (64-bit). The resulting object
 * will contain a string created using the integer provided. This is the
 * preferred method for passing a signed integer to the WAF.
 *
 * @param object Object to perform the operation on. (nonnull)
 * @param value Integer to initialise the object with.
 *
 * @return A pointer to the passed object or NULL if the operation failed.
 **/
ddwaf_object* ddwaf_object_signed(ddwaf_object *object, int64_t value);

/**
 * ddwaf_object_unsigned_force
 *
 * Creates an object using an unsigned integer (64-bit). The resulting object
 * will contain an unsigned integer as opposed to a string.
 *
 * @param object Object to perform the operation on. (nonnull)
 * @param value Integer to initialise the object with.
 *
 * @return A pointer to the passed object or NULL if the operation failed.
 **/
ddwaf_object* ddwaf_object_unsigned_force(ddwaf_object *object, uint64_t value);

/**
 * ddwaf_object_signed_force
 *
 * Creates an object using a signed integer (64-bit). The resulting object
 * will contain a signed integer as opposed to a string.
 *
 * @param object Object to perform the operation on. (nonnull)
 * @param value Integer to initialise the object with.
 *
 * @return A pointer to the passed object or NULL if the operation failed.
 **/
ddwaf_object* ddwaf_object_signed_force(ddwaf_object *object, int64_t value);

/**
 * ddwaf_object_array
 *
 * Creates an array object, for sequential storage.
 *
 * @param object Object to perform the operation on. (nonnull)
 *
 * @return A pointer to the passed object or NULL if the operation failed.
 **/
ddwaf_object* ddwaf_object_array(ddwaf_object *object);

/**
 * ddwaf_object_map
 *
 * Creates a map object, for key-value storage.
 *
 * @param object Object to perform the operation on. (nonnull)
 *
 * @return A pointer to the passed object or NULL if the operation failed.
 **/
ddwaf_object* ddwaf_object_map(ddwaf_object *object);

/**
 * ddwaf_object_array_add
 *
 * Inserts an object into an array object.
 *
 * @param array Array in which to insert the object. (nonnull)
 * @param object Object to insert into the array. (nonnull)
 *
 * @return The success or failure of the operation.
 **/
bool ddwaf_object_array_add(ddwaf_object *array, ddwaf_object *object);

/**
 * ddwaf_object_map_add
 *
 * Inserts an object into an map object, using a key.
 *
 * @param map Map in which to insert the object. (nonnull)
 * @param key The key for indexing purposes, this string will be copied and its
 *            length will be calcualted using strlen(key). (nonnull)
 * @param object Object to insert into the array. (nonnull)
 *
 * @return The success or failure of the operation.
 **/
bool ddwaf_object_map_add(ddwaf_object *map, const char *key, ddwaf_object *object);

/**
 * ddwaf_object_map_addl
 *
 * Inserts an object into an map object, using a key and its length.
 *
 * @param map Map in which to insert the object. (nonnull)
 * @param key The key for indexing purposes, this string will be copied (nonnull)
 * @param length Length of the key.
 * @param object Object to insert into the array. (nonnull)
 *
 * @return The success or failure of the operation.
 **/
bool ddwaf_object_map_addl(ddwaf_object *map, const char *key, size_t length, ddwaf_object *object);

/**
 * ddwaf_object_map_addl_nc
 *
 * Inserts an object into an map object, using a key and its length, but without
 * creating a copy of the key.
 *
 * @param map Map in which to insert the object. (nonnull)
 * @param key The key for indexing purposes, this string will be copied (nonnull)
 * @param length Length of the key.
 * @param object Object to insert into the array. (nonnull)
 *
 * @return The success or failure of the operation.
 **/
bool ddwaf_object_map_addl_nc(ddwaf_object *map, const char *key, size_t length, ddwaf_object *object);

/**
 * ddwaf_object_free
 *
 * @param object Object to free. (nonnull)
 **/
void ddwaf_object_free(ddwaf_object *object);

/**
 * ddwaf_get_version
 *
 * Return the version of the library
 *
 * @param version Version structure following semver
 **/
void ddwaf_get_version(ddwaf_version *version);

/**
 * ddwaf_set_log_cb
 *
 * Sets the callback to relay logging messages to the binding
 *
 * @param cb The callback to call, or NULL to stop relaying messages
 * @param min_level The minimum logging level for which to relay messages
 *
 * @return whether the operation succeeded or not
 **/
bool ddwaf_set_log_cb(ddwaf_log_cb cb, DDWAF_LOG_LEVEL min_level);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* pw_h */
