aboutsummaryrefslogtreecommitdiffstats
path: root/src/ucl/include/ucl.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/ucl/include/ucl.h')
-rw-r--r--src/ucl/include/ucl.h731
1 files changed, 731 insertions, 0 deletions
diff --git a/src/ucl/include/ucl.h b/src/ucl/include/ucl.h
new file mode 100644
index 000000000..1b8fa8631
--- /dev/null
+++ b/src/ucl/include/ucl.h
@@ -0,0 +1,731 @@
+/* Copyright (c) 2013, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RCL_H_
+#define RCL_H_
+
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include "config.h"
+
+#include "uthash.h"
+#include "utlist.h"
+#include "utstring.h"
+
+/**
+ * @file rcl.h
+ * RCL is an rspamd configuration language, which is a form of
+ * JSON with less strict rules that make it more comfortable for
+ * using as a configuration language
+ */
+
+/**
+ * XXX: Poorly named API functions, need to replace them with the appropriate
+ * named function. All API functions *must* use naming ucl_object_*. Usage of
+ * ucl_obj* should be avoided.
+ */
+#define ucl_object_todouble_safe ucl_obj_todouble_safe
+#define ucl_object_todouble ucl_obj_todouble
+#define ucl_object_tostring ucl_obj_tostring
+#define ucl_object_tostring_safe ucl_obj_tostring_safe
+#define ucl_object_tolstring ucl_obj_tolstring
+#define ucl_object_tolstring_safe ucl_obj_tolstring_safe
+#define ucl_object_toint ucl_obj_toint
+#define ucl_object_toint_safe ucl_obj_toint_safe
+#define ucl_object_toboolean ucl_obj_toboolean
+#define ucl_object_toboolean_safe ucl_obj_toboolean_safe
+#define ucl_object_find_key ucl_obj_get_key
+#define ucl_object_find_keyl ucl_obj_get_keyl
+#define ucl_object_unref ucl_obj_unref
+#define ucl_object_ref ucl_obj_ref
+#define ucl_object_free ucl_obj_free
+
+/**
+ * Memory allocation utilities
+ * UCL_ALLOC(size) - allocate memory for UCL
+ * UCL_FREE(size, ptr) - free memory of specified size at ptr
+ * Default: malloc and free
+ */
+#ifndef UCL_ALLOC
+#define UCL_ALLOC(size) malloc(size)
+#endif
+#ifndef UCL_FREE
+#define UCL_FREE(size, ptr) free(ptr)
+#endif
+
+enum ucl_error {
+ UCL_EOK = 0, //!< UCL_EOK
+ UCL_ESYNTAX, //!< UCL_ESYNTAX
+ UCL_EIO, //!< UCL_EIO
+ UCL_ESTATE, //!< UCL_ESTATE
+ UCL_ENESTED, //!< UCL_ENESTED
+ UCL_EMACRO, //!< UCL_EMACRO
+ UCL_ERECURSION,//!< UCL_ERECURSION
+ UCL_EINTERNAL, //!< UCL_EINTERNAL
+ UCL_ESSL //!< UCL_ESSL
+};
+
+enum ucl_type {
+ UCL_OBJECT = 0,
+ UCL_ARRAY,
+ UCL_INT,
+ UCL_FLOAT,
+ UCL_STRING,
+ UCL_BOOLEAN,
+ UCL_TIME,
+ UCL_USERDATA
+};
+
+enum ucl_emitter {
+ UCL_EMIT_JSON = 0,
+ UCL_EMIT_JSON_COMPACT,
+ UCL_EMIT_CONFIG,
+ UCL_EMIT_YAML
+};
+
+enum ucl_flags {
+ UCL_FLAG_KEY_LOWERCASE = 0x1,
+ UCL_FLAG_ZEROCOPY = 0x2
+};
+
+typedef struct ucl_object_s {
+ union {
+ int64_t iv; /**< int value of an object */
+ const char *sv; /**< string value of an object */
+ double dv; /**< double value of an object */
+ struct ucl_object_s *ov; /**< array or hash */
+ void* ud; /**< opaque user data */
+ } value;
+ enum ucl_type type; /**< real type */
+ int ref; /**< reference count */
+ size_t len; /**< size of an object */
+ struct ucl_object_s *next; /**< array handle */
+ struct ucl_object_s *prev; /**< array handle */
+ unsigned char* trash_stack[2]; /**< pointer to allocated chunks */
+ UT_hash_handle hh; /**< hash handle */
+} ucl_object_t;
+
+
+/**
+ * Copy and return a key of an object, returned key is zero-terminated
+ * @param obj CL object
+ * @return zero terminated key
+ */
+char* ucl_copy_key_trash (ucl_object_t *obj);
+
+/**
+ * Copy and return a string value of an object, returned key is zero-terminated
+ * @param obj CL object
+ * @return zero terminated string representation of object value
+ */
+char* ucl_copy_value_trash (ucl_object_t *obj);
+
+/**
+ * Creates a new object
+ * @return new object
+ */
+static inline ucl_object_t *
+ucl_object_new (void)
+{
+ ucl_object_t *new;
+ new = malloc (sizeof (ucl_object_t));
+ if (new != NULL) {
+ memset (new, 0, sizeof (ucl_object_t));
+ new->ref = 1;
+ }
+ return new;
+}
+
+/**
+ * String conversion flags
+ */
+enum ucl_string_flags {
+ UCL_STRING_ESCAPE = 0x1, /**< UCL_STRING_ESCAPE perform JSON escape */
+ UCL_STRING_TRIM = 0x2, /**< UCL_STRING_TRIM trim leading and trailing whitespaces */
+ UCL_STRING_PARSE_BOOLEAN = 0x4, /**< UCL_STRING_PARSE_BOOLEAN parse passed string and detect boolean */
+ UCL_STRING_PARSE_INT = 0x8, /**< UCL_STRING_PARSE_INT parse passed string and detect integer number */
+ UCL_STRING_PARSE_DOUBLE = 0x10, /**< UCL_STRING_PARSE_DOUBLE parse passed string and detect integer or float number */
+ UCL_STRING_PARSE_NUMBER = UCL_STRING_PARSE_INT|UCL_STRING_PARSE_DOUBLE , /**<
+ UCL_STRING_PARSE_NUMBER parse passed string and detect number */
+ UCL_STRING_PARSE = UCL_STRING_PARSE_BOOLEAN|UCL_STRING_PARSE_NUMBER /**<
+ UCL_STRING_PARSE parse passed string (and detect booleans and numbers) */
+};
+
+/**
+ * Convert any string to an ucl object making the specified transformations
+ * @param str fixed size or NULL terminated string
+ * @param len length (if len is zero, than str is treated as NULL terminated)
+ * @param flags conversion flags
+ * @return new object
+ */
+ucl_object_t * ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags flags);
+
+/**
+ * Create a UCL object from the specified string
+ * @param str NULL terminated string, will be json escaped
+ * @return new object
+ */
+static inline ucl_object_t *
+ucl_object_fromstring (const char *str)
+{
+ return ucl_object_fromstring_common (str, 0, UCL_STRING_ESCAPE);
+}
+
+/**
+ * Create a UCL object from the specified string
+ * @param str fixed size string, will be json escaped
+ * @param len length of a string
+ * @return new object
+ */
+static inline ucl_object_t *
+ucl_object_fromlstring (const char *str, size_t len)
+{
+ return ucl_object_fromstring_common (str, len, UCL_STRING_ESCAPE);
+}
+
+/**
+ * Create an object from an integer number
+ * @param iv number
+ * @return new object
+ */
+static inline ucl_object_t *
+ucl_object_fromint (int64_t iv)
+{
+ ucl_object_t *obj;
+
+ obj = ucl_object_new ();
+ if (obj != NULL) {
+ obj->type = UCL_INT;
+ obj->value.iv = iv;
+ }
+
+ return obj;
+}
+
+/**
+ * Create an object from a float number
+ * @param dv number
+ * @return new object
+ */
+static inline ucl_object_t *
+ucl_object_fromdouble (double dv)
+{
+ ucl_object_t *obj;
+
+ obj = ucl_object_new ();
+ if (obj != NULL) {
+ obj->type = UCL_FLOAT;
+ obj->value.dv = dv;
+ }
+
+ return obj;
+}
+
+/**
+ * Create an object from a boolean
+ * @param bv bool value
+ * @return new object
+ */
+static inline ucl_object_t *
+ucl_object_frombool (bool bv)
+{
+ ucl_object_t *obj;
+
+ obj = ucl_object_new ();
+ if (obj != NULL) {
+ obj->type = UCL_BOOLEAN;
+ obj->value.iv = bv;
+ }
+
+ return obj;
+}
+
+/**
+ * Insert a object 'elt' to the hash 'top' and associate it with key 'key'
+ * @param top destination object (will be created automatically if top is NULL)
+ * @param elt element to insert (must NOT be NULL)
+ * @param key key to associate with this object (either const or preallocated)
+ * @param keylen length of the key (or 0 for NULL terminated keys)
+ * @param copy_key make an internal copy of key
+ * @return new value of top object
+ */
+static inline ucl_object_t *
+ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt,
+ const char *key, size_t keylen, bool copy_key)
+{
+ ucl_object_t *found;
+
+ if (elt == NULL || key == NULL) {
+ return NULL;
+ }
+
+ if (top == NULL) {
+ top = ucl_object_new ();
+ top->type = UCL_OBJECT;
+ }
+ if (keylen == 0) {
+ keylen = strlen (key);
+ }
+
+ HASH_FIND (hh, top->value.ov, key, keylen, found);
+
+ if (!found) {
+ HASH_ADD_KEYPTR (hh, top->value.ov, key, keylen, elt);
+ }
+ DL_APPEND (found, elt);
+
+ if (copy_key) {
+ ucl_copy_key_trash (elt);
+ }
+
+ return top;
+}
+
+/**
+ * Append an element to the array object
+ * @param top destination object (will be created automatically if top is NULL)
+ * @param eltelement to append (must NOT be NULL)
+ * @return new value of top object
+ */
+static inline ucl_object_t *
+ucl_array_append (ucl_object_t *top, ucl_object_t *elt)
+{
+ if (elt == NULL) {
+ return NULL;
+ }
+
+ if (top == NULL) {
+ top = ucl_object_new ();
+ top->type = UCL_ARRAY;
+ }
+
+ DL_APPEND (top->value.ov, elt);
+
+ return top;
+}
+
+/**
+ * Append a element to another element forming an implicit array
+ * @param head head to append (may be NULL)
+ * @param elt new element
+ * @return new head if applicable
+ */
+static inline ucl_object_t *
+ucl_elt_append (ucl_object_t *head, ucl_object_t *elt)
+{
+ DL_APPEND (head, elt);
+ return head;
+}
+
+/**
+ * Converts an object to double value
+ * @param obj CL object
+ * @param target target double variable
+ * @return true if conversion was successful
+ */
+static inline bool
+ucl_obj_todouble_safe (ucl_object_t *obj, double *target)
+{
+ if (obj == NULL) {
+ return false;
+ }
+ switch (obj->type) {
+ case UCL_INT:
+ *target = obj->value.iv; /* Probaly could cause overflow */
+ break;
+ case UCL_FLOAT:
+ case UCL_TIME:
+ *target = obj->value.dv;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Unsafe version of \ref ucl_obj_todouble_safe
+ * @param obj CL object
+ * @return double value
+ */
+static inline double
+ucl_obj_todouble (ucl_object_t *obj)
+{
+ double result = 0.;
+
+ ucl_object_todouble_safe (obj, &result);
+ return result;
+}
+
+/**
+ * Converts an object to integer value
+ * @param obj CL object
+ * @param target target integer variable
+ * @return true if conversion was successful
+ */
+static inline bool
+ucl_obj_toint_safe (ucl_object_t *obj, int64_t *target)
+{
+ if (obj == NULL) {
+ return false;
+ }
+ switch (obj->type) {
+ case UCL_INT:
+ *target = obj->value.iv;
+ break;
+ case UCL_FLOAT:
+ case UCL_TIME:
+ *target = obj->value.dv; /* Loosing of decimal points */
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Unsafe version of \ref ucl_obj_toint_safe
+ * @param obj CL object
+ * @return int value
+ */
+static inline int64_t
+ucl_obj_toint (ucl_object_t *obj)
+{
+ int64_t result = 0;
+
+ ucl_object_toint_safe (obj, &result);
+ return result;
+}
+
+/**
+ * Converts an object to boolean value
+ * @param obj CL object
+ * @param target target boolean variable
+ * @return true if conversion was successful
+ */
+static inline bool
+ucl_obj_toboolean_safe (ucl_object_t *obj, bool *target)
+{
+ if (obj == NULL) {
+ return false;
+ }
+ switch (obj->type) {
+ case UCL_BOOLEAN:
+ *target = (obj->value.iv == true);
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Unsafe version of \ref ucl_obj_toboolean_safe
+ * @param obj CL object
+ * @return boolean value
+ */
+static inline bool
+ucl_obj_toboolean (ucl_object_t *obj)
+{
+ bool result = false;
+
+ ucl_object_toboolean_safe (obj, &result);
+ return result;
+}
+
+/**
+ * Converts an object to string value
+ * @param obj CL object
+ * @param target target string variable, no need to free value
+ * @return true if conversion was successful
+ */
+static inline bool
+ucl_obj_tostring_safe (ucl_object_t *obj, const char **target)
+{
+ if (obj == NULL) {
+ return false;
+ }
+
+ switch (obj->type) {
+ case UCL_STRING:
+ *target = ucl_copy_value_trash (obj);
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Unsafe version of \ref ucl_obj_tostring_safe
+ * @param obj CL object
+ * @return string value
+ */
+static inline const char *
+ucl_obj_tostring (ucl_object_t *obj)
+{
+ const char *result = NULL;
+
+ ucl_object_tostring_safe (obj, &result);
+ return result;
+}
+
+/**
+ * Convert any object to a string in JSON notation if needed
+ * @param obj CL object
+ * @return string value
+ */
+static inline const char *
+ucl_obj_tostring_forced (ucl_object_t *obj)
+{
+ return ucl_copy_value_trash (obj);
+}
+
+/**
+ * Return string as char * and len, string may be not zero terminated, more efficient that tostring as it
+ * allows zero-copy
+ * @param obj CL object
+ * @param target target string variable, no need to free value
+ * @param tlen target length
+ * @return true if conversion was successful
+ */
+static inline bool
+ucl_obj_tolstring_safe (ucl_object_t *obj, const char **target, size_t *tlen)
+{
+ if (obj == NULL) {
+ return false;
+ }
+ switch (obj->type) {
+ case UCL_STRING:
+ *target = obj->value.sv;
+ *tlen = obj->len;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Unsafe version of \ref ucl_obj_tolstring_safe
+ * @param obj CL object
+ * @return string value
+ */
+static inline const char *
+ucl_obj_tolstring (ucl_object_t *obj, size_t *tlen)
+{
+ const char *result = NULL;
+
+ ucl_object_tolstring_safe (obj, &result, tlen);
+ return result;
+}
+
+/**
+ * Return object identified by a key in the specified object
+ * @param obj object to get a key from (must be of type UCL_OBJECT)
+ * @param key key to search
+ * @return object matched the specified key or NULL if key is not found
+ */
+static inline ucl_object_t *
+ucl_obj_get_key (ucl_object_t *obj, const char *key)
+{
+ size_t keylen;
+ ucl_object_t *ret;
+
+ if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) {
+ return NULL;
+ }
+
+ keylen = strlen (key);
+ HASH_FIND (hh, obj->value.ov, key, keylen, ret);
+
+ return ret;
+}
+
+/**
+ * Return object identified by a fixed size key in the specified object
+ * @param obj object to get a key from (must be of type UCL_OBJECT)
+ * @param key key to search
+ * @param klen length of a key
+ * @return object matched the specified key or NULL if key is not found
+ */
+static inline ucl_object_t *
+ucl_obj_get_keyl (ucl_object_t *obj, const char *key, size_t klen)
+{
+ ucl_object_t *ret;
+
+ if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) {
+ return NULL;
+ }
+
+ HASH_FIND (hh, obj->value.ov, key, klen, ret);
+
+ return ret;
+}
+
+/**
+ * Returns a key of an object as a NULL terminated string
+ * @param obj CL object
+ * @return key or NULL if there is no key
+ */
+static inline const char *
+ucl_object_key (ucl_object_t *obj)
+{
+ return ucl_copy_key_trash (obj);
+}
+
+/**
+ * Returns a key of an object as a fixed size string (may be more efficient)
+ * @param obj CL object
+ * @param len target key length
+ * @return key pointer
+ */
+static inline const char *
+ucl_object_keyl (ucl_object_t *obj, size_t *len)
+{
+ *len = obj->hh.keylen;
+ return obj->hh.key;
+}
+
+/**
+ * Macro handler for a parser
+ * @param data the content of macro
+ * @param len the length of content
+ * @param ud opaque user data
+ * @param err error pointer
+ * @return true if macro has been parsed
+ */
+typedef bool (*ucl_macro_handler) (const unsigned char *data, size_t len, void* ud, UT_string **err);
+
+/* Opaque parser */
+struct ucl_parser;
+
+/**
+ * Creates new parser object
+ * @param pool pool to allocate memory from
+ * @return new parser object
+ */
+struct ucl_parser* ucl_parser_new (int flags);
+
+/**
+ * Register new handler for a macro
+ * @param parser parser object
+ * @param macro macro name (without leading dot)
+ * @param handler handler (it is called immediately after macro is parsed)
+ * @param ud opaque user data for a handler
+ */
+void ucl_parser_register_macro (struct ucl_parser *parser, const char *macro,
+ ucl_macro_handler handler, void* ud);
+
+/**
+ * Load new chunk to a parser
+ * @param parser parser structure
+ * @param data the pointer to the beginning of a chunk
+ * @param len the length of a chunk
+ * @param err if *err is NULL it is set to parser error
+ * @return true if chunk has been added and false in case of error
+ */
+bool ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
+ size_t len, UT_string **err);
+
+/**
+ * Load and add data from a file
+ * @param parser parser structure
+ * @param filename the name of file
+ * @param err if *err is NULL it is set to parser error
+ * @return true if chunk has been added and false in case of error
+ */
+bool ucl_parser_add_file (struct ucl_parser *parser, const char *filename,
+ UT_string **err);
+
+/**
+ * Get a top object for a parser
+ * @param parser parser structure
+ * @param err if *err is NULL it is set to parser error
+ * @return top parser object or NULL
+ */
+ucl_object_t* ucl_parser_get_object (struct ucl_parser *parser, UT_string **err);
+
+/**
+ * Free cl parser object
+ * @param parser parser object
+ */
+void ucl_parser_free (struct ucl_parser *parser);
+
+/**
+ * Free cl object
+ * @param obj cl object to free
+ */
+void ucl_obj_free (ucl_object_t *obj);
+
+/**
+ * Icrease reference count for an object
+ * @param obj object to ref
+ */
+static inline ucl_object_t *
+ucl_obj_ref (ucl_object_t *obj) {
+ obj->ref ++;
+ return obj;
+}
+
+/**
+ * Decrease reference count for an object
+ * @param obj object to unref
+ */
+static inline void
+ucl_obj_unref (ucl_object_t *obj) {
+ if (--obj->ref <= 0) {
+ ucl_obj_free (obj);
+ }
+}
+
+/**
+ * Emit object to a string
+ * @param obj object
+ * @param emit_type if type is UCL_EMIT_JSON then emit json, if type is
+ * UCL_EMIT_CONFIG then emit config like object
+ * @return dump of an object (must be freed after using) or NULL in case of error
+ */
+unsigned char *ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type);
+
+/**
+ * Add new public key to parser for signatures check
+ * @param parser parser object
+ * @param key PEM representation of a key
+ * @param len length of the key
+ * @param err if *err is NULL it is set to parser error
+ * @return true if a key has been successfully added
+ */
+bool ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len, UT_string **err);
+
+#endif /* RCL_H_ */