diff --git a/includes/orcania.h b/includes/orcania.h new file mode 100644 index 0000000000000000000000000000000000000000..c1f3599c082446a747f30e1c1cba0b256b6fea84 --- /dev/null +++ b/includes/orcania.h @@ -0,0 +1,215 @@ +/** + * + * Orcania library + * + * Different functions for different purposes but that can be shared between + * other projects + * + */ + +#ifndef __ORCANIA_H__ +#define __ORCANIA_H__ + +#ifndef U_DISABLE_JANSSON +#include <jansson.h> +#endif + +#define ORCANIA_VERSION 1.1 +/** + * char * str_replace(const char * source, char * old, char * new) + * replace all occurences of old by new in the string source + * return a char * with the new value + * return NULL on error + * returned value must be free'd after use + */ +char * str_replace(const char * source, const char * str_old, const char * str_new); + +/** + * o_strdup + * a modified strdup function that don't crash when source is NULL, instead return NULL + * Returned value must be free'd after use + */ +char * o_strdup(const char * source); + +/** + * o_strndup + * a modified strndup function that don't crash when source is NULL, instead return NULL + * Returned value must be free'd after use + */ +char * o_strndup(const char * source, size_t len); + +/** + * o_strcmp + * a modified strcmp function that don't crash when p1 is NULL or p2 us NULL + */ +int o_strcmp(const char * p1, const char * p2); + +/** + * o_strncmp + * a modified strncmp function that don't crash when p1 is NULL or p2 us NULL + */ +int o_strncmp(const char * p1, const char * p2, size_t n); + +/** + * o_strcpy + * a modified strcpy function that don't crash when p1 is NULL or p2 us NULL + */ +char * o_strcpy(char * p1, const char * p2); + +/** + * o_strncpy + * a modified strncpy function that don't crash when p1 is NULL or p2 us NULL + */ +char * o_strncpy(char * p1, const char * p2, size_t n); + +/** + * o_strcasecmp + * a modified strcasecmp function that don't crash when p1 is NULL or p2 us NULL + */ +int o_strcasecmp(const char * p1, const char * p2); + +/** + * o_strncasecmp + * a modified strncasecmp function that don't crash when p1 is NULL or p2 us NULL + */ +int o_strncasecmp(const char * p1, const char * p2, size_t n); + +/** + * o_strstr + * a modified strstr function that don't crash when haystack is NULL or needle us NULL + */ +char * o_strstr(const char * haystack, const char * needle); + +/** + * o_strnstr + * a modified strnstr function that don't crash when haystack is NULL or needle us NULL + */ +char * o_strnstr(const char * haystack, const char * needle, size_t len); + +/** + * o_strcasestr + * a modified strcasestr function that don't crash when haystack is NULL or needle us NULL + */ +char * o_strcasestr(const char * haystack, const char * needle); + +/** + * o_strchr + * a modified strchr function that don't crash when haystack is NULL + */ +char * o_strchr(const char * haystack, int c); + +/** + * o_strrchr + * a modified strrchr function that don't crash when haystack is NULL + */ +char * o_strrchr(const char * haystack, int c); + +/** + * o_strlen + * a modified version of strlen that don't crash when s is NULL + */ +size_t o_strlen(const char * s); + +/** + * char * msprintf(const char * message, ...) + * Implementation of sprintf that return a malloc'd char * with the string construction + * because life is too short to use 3 lines instead of 1 + * but don't forget to free the returned value after use! + */ +char * msprintf(const char * message, ...); + +/** + * Split a string into an array of strings using separator string + * return the number of elements to be returned, 0 on error + * if return_array is not NULL, set the returned array in it + * return_array is an array of char * ending with a NULL value + * return_array must be free'd after use + * you can use free_string_array to free return_array + */ +int split_string(const char * string, const char * separator, char *** return_array); + +/** + * Clean an array of strings + */ +void free_string_array(char ** array); + +/** + * Check if an array of string has a specified value + */ +int string_array_has_value(const char ** array, const char * needle); + +/** + * Check if an array of string has a specified trimmed value + */ +int string_array_has_trimmed_value(const char ** array, const char * needle); + +/** + * Remove string of beginning and ending whitespaces + */ +char * trimwhitespace(char * str); + +#ifndef U_DISABLE_JANSSON +/** + * json_t * json_search(json_t * haystack, json_t * needle) + * jansson library addon + * This function could be removed if y pull request is accepted in jansson official repository: + * https://github.com/akheron/jansson/pull/265 + * Look for an occurence of needle within haystack + * If needle is present in haystack, return the reference to the json_t * that is equal to needle + * If needle is not found, return NULL + */ +json_t * json_search(json_t * haystack, json_t * needle); + +/** + * Check if the result json object has a "result" element that is equal to value + */ +int check_result_value(json_t * result, const int value); +#endif + +/** + * Memory functions + */ +/* C89 allows these to be macros */ +#undef malloc +#undef realloc +#undef free + +typedef void *(*o_malloc_t)(size_t); +typedef void *(*o_realloc_t)(void *, size_t); +typedef void (*o_free_t)(void *); + +void * o_malloc(size_t size); +void * o_realloc(void * ptr, size_t size); +void o_free(void * ptr); +void o_set_alloc_funcs(o_malloc_t malloc_fn, o_realloc_t realloc_fn, o_free_t free_fn); +void o_get_alloc_funcs(o_malloc_t * malloc_fn, o_realloc_t * realloc_fn, o_free_t * free_fn); + +/** + * Base64 encode and decode functions + */ + +/** + * o_base64_encode - Base64 encode + * @src: Data to be encoded + * @len: Length of the data to be encoded + * @out: Pointer to output variable + * @out_len: Pointer to output length variable + * Returns: 1 on success, 0 on failure + * + * The nul terminator is not included in out_len. + */ +int o_base64_encode(const unsigned char * src, size_t len, unsigned char * out, size_t * out_len); + +/** + * o_base64_decode - Base64 decode + * @src: Data to be decoded + * @len: Length of the data to be decoded + * @out: Pointer to output variable + * @out_len: Pointer to output length variable + * Returns: 1 on success, 0 on failure + * + * The nul terminator is not included in out_len. + */ +int o_base64_decode(const unsigned char *src, size_t len, unsigned char * out, size_t * out_len); + +#endif diff --git a/includes/ulfius.h b/includes/ulfius.h new file mode 100644 index 0000000000000000000000000000000000000000..f7da3eb9c5a2874d83eec9e41de161e5e9b40df3 --- /dev/null +++ b/includes/ulfius.h @@ -0,0 +1,1224 @@ +/** + * + * Ulfius Framework + * + * REST framework library + * + * ulfius.h: public structures and functions declarations + * + * Copyright 2015-2017 Nicolas Mora <mail@babelouest.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef __ULFIUS_H__ +#define __ULFIUS_H__ + +/** External dependencies **/ +#include <sys/poll.h> +#include <microhttpd.h> + +#if (MHD_VERSION < 0x00095300) && !defined(U_DISABLE_WEBSOCKET) + #define U_DISABLE_WEBSOCKET +#endif + +/** Angharad libraries **/ +#include <yder.h> +#include <orcania.h> + +#ifndef U_DISABLE_JANSSON +#include <jansson.h> +#endif + +#define ULFIUS_STREAM_BLOCK_SIZE_DEFAULT 1024 +#define U_STREAM_END MHD_CONTENT_READER_END_OF_STREAM +#define U_STREAM_ERROR MHD_CONTENT_READER_END_WITH_ERROR +#define U_STREAM_SIZE_UNKOWN MHD_SIZE_UNKNOWN + +#define U_OK 0 // No error +#define U_ERROR 1 // Error +#define U_ERROR_MEMORY 2 // Error in memory allocation +#define U_ERROR_PARAMS 3 // Error in input parameters +#define U_ERROR_LIBMHD 4 // Error in libmicrohttpd execution +#define U_ERROR_LIBCURL 5 // Error in libcurl execution +#define U_ERROR_NOT_FOUND 6 // Something was not found + +#define ULFIUS_VERSION 2.1 + +#define U_CALLBACK_CONTINUE 0 +#define U_CALLBACK_COMPLETE 1 +#define U_CALLBACK_UNAUTHORIZED 2 +#define U_CALLBACK_ERROR 3 + +/************* + * Structures + *************/ + +/** + * struct _u_map + */ +struct _u_map { + int nb_values; + char ** keys; + char ** values; + size_t * lengths; +}; + +/** + * struct _u_cookie + * the structure containing the response cookie parameters + */ +struct _u_cookie { + char * key; + char * value; + char * expires; + uint max_age; + char * domain; + char * path; + int secure; + int http_only; +}; + +/** + * + * Structure of request parameters + * + * Contains request data + * http_protocol: http protocol used (1.0 or 1.1) + * http_verb: http method (GET, POST, PUT, DELETE, etc.), use '*' to match all http methods + * http_url: url used to call this callback function or full url to call when used in a ulfius_send_http_request + * proxy: proxy address to use for outgoing connections, used by ulfius_send_http_request + * check_server_certificate: do not check server certificate and hostname if false (default true), used by ulfius_send_http_request + * timeout connection timeout used by ulfius_send_http_request, default is 0 + * client_address: IP address of the client + * auth_basic_user: basic authtication username + * auth_basic_password: basic authtication password + * map_url: map containing the url variables, both from the route and the ?key=value variables + * map_header: map containing the header variables + * map_cookie: map containing the cookie variables + * map_post_body: map containing the post body variables (if available) + * binary_body: pointer to raw body + * binary_body_length: length of raw body + * + */ +struct _u_request { + char * http_protocol; + char * http_verb; + char * http_url; + char * proxy; + int check_server_certificate; + long timeout; + struct sockaddr * client_address; + char * auth_basic_user; + char * auth_basic_password; + struct _u_map * map_url; + struct _u_map * map_header; + struct _u_map * map_cookie; + struct _u_map * map_post_body; + void * binary_body; + size_t binary_body_length; +}; + +/** + * + * Structure of response parameters + * + * Contains response data that must be set by the user + * status: HTTP status code (200, 404, 500, etc) + * protocol: HTTP Protocol sent + * map_header: map containing the header variables + * nb_cookies: number of cookies sent + * map_cookie: array of cookies sent + * auth_realm: realm to send to the client on authenticationb failed + * binary_body: a void * containing a raw binary content + * binary_body_length: the length of the binary_body + * stream_callback: callback function to stream data in response body + * stream_callback_free: callback function to free data allocated for streaming + * stream_size: size of the streamed data (U_STREAM_SIZE_UNKOWN if unknown) + * stream_block_size: size of each block to be streamed, set according to your system + * stream_user_data: user defined data that will be available in your callback stream functions + * websocket_handle: handle for websocket extension + * shared_data: any data shared between callback functions, must be allocated and freed by the callback functions + * + */ +struct _u_response { + long status; + char * protocol; + struct _u_map * map_header; + unsigned int nb_cookies; + struct _u_cookie * map_cookie; + char * auth_realm; + void * binary_body; + size_t binary_body_length; + ssize_t (* stream_callback) (void * stream_user_data, uint64_t offset, char * out_buf, size_t max); + void (* stream_callback_free) (void * stream_user_data); + uint64_t stream_size; + size_t stream_block_size; + void * stream_user_data; + void * websocket_handle; + void * shared_data; +}; + +/** + * + * Structure of an endpoint + * + * Contains all informations needed for an endpoint + * http_method: http verb (GET, POST, PUT, etc.) in upper case + * url_prefix: prefix for the url (optional) + * url_format: string used to define the endpoint format + * separate words with / + * to define a variable in the url, prefix it with @ or : + * example: /test/resource/:name/elements + * on an url_format that ends with '*', the rest of the url will not be tested + * priority: endpoint priority in descending order (0 is the higher priority) + * callback_function: a pointer to a function that will be executed each time the endpoint is called + * you must declare the function as described. + * user_data: a pointer to a data or a structure that will be available in callback_function + * + */ +struct _u_endpoint { + char * http_method; + char * url_prefix; + char * url_format; + uint priority; + int (* callback_function)(const struct _u_request * request, // Input parameters (set by the framework) + struct _u_response * response, // Output parameters (set by the user) + void * user_data); + void * user_data; +}; + +/** + * + * Structure of an instance + * + * Contains the needed data for an ulfius instance to work + * + * mhd_daemon: pointer to the libmicrohttpd daemon + * status: status of the current instance, status are U_STATUS_STOP, U_STATUS_RUNNING or U_STATUS_ERROR + * port: port number to listen to + * bind_address: ip address to listen to (optional) + * nb_endpoints: Number of available endpoints + * default_auth_realm: Default realm on authentication error + * endpoint_list: List of available endpoints + * default_endpoint: Default endpoint if no other endpoint match the current url + * default_headers: Default headers that will be added to all response->map_header + * max_post_param_size: maximum size for a post parameter, 0 means no limit, default 0 + * max_post_body_size: maximum size for the entire post body, 0 means no limit, default 0 + * + */ +struct _u_instance { + struct MHD_Daemon * mhd_daemon; + int status; + uint port; + struct sockaddr_in * bind_address; + int nb_endpoints; + char * default_auth_realm; + struct _u_endpoint * endpoint_list; + struct _u_endpoint * default_endpoint; + struct _u_map * default_headers; + size_t max_post_param_size; + size_t max_post_body_size; + void * websocket_handler; +}; + +/** + * Structures used to facilitate data manipulations (internal) + */ +struct connection_info_struct { + struct MHD_PostProcessor * post_processor; + int has_post_processor; + int callback_first_iteration; + struct _u_request * request; + size_t max_post_param_size; + struct _u_map map_url_initial; +}; + +/********************************** + * Instance functions declarations + **********************************/ + +/** + * ulfius_init_instance + * + * Initialize a struct _u_instance * with default values + * return U_OK on success + */ +int ulfius_init_instance(struct _u_instance * u_instance, uint port, struct sockaddr_in * bind_address, const char * default_auth_realm); + +/** + * ulfius_clean_instance + * + * Clean memory allocated by a struct _u_instance * + */ +void ulfius_clean_instance(struct _u_instance * u_instance); + +/** + * ulfius_start_framework + * Initializes the framework and run the webservice based on the parameters given + * + * u_instance: pointer to a struct _u_instance that describe its port and bind address + * return U_OK on success + */ +int ulfius_start_framework(struct _u_instance * u_instance); + +/** + * ulfius_start_secure_framework + * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection + * + * u_instance: pointer to a struct _u_instance that describe its port and bind address + * key_pem: private key for the server + * cert_pem: server certificate + * return U_OK on success + */ +int ulfius_start_secure_framework(struct _u_instance * u_instance, const char * key_pem, const char * cert_pem); + +/** + * ulfius_stop_framework + * + * Stop the webservice + * u_instance: pointer to a struct _u_instance that describe its port and bind address + * return U_OK on success + */ +int ulfius_stop_framework(struct _u_instance * u_instance); + +/*********************************** + * Endpoints functions declarations + ***********************************/ + +/** + * Add a struct _u_endpoint * to the specified u_instance + * Can be done during the execution of the webservice for injection + * u_instance: pointer to a struct _u_instance that describe its port and bind address + * u_endpoint: pointer to a struct _u_endpoint that will be copied in the u_instance endpoint_list + * return U_OK on success + */ +int ulfius_add_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint); + +/** + * Add a struct _u_endpoint * to the specified u_instance with its values specified + * Can be done during the execution of the webservice for injection + * u_instance: pointer to a struct _u_instance that describe its port and bind address + * http_method: http verb (GET, POST, PUT, etc.) in upper case + * url_prefix: prefix for the url (optional) + * url_format: string used to define the endpoint format + * separate words with / + * to define a variable in the url, prefix it with @ or : + * example: /test/resource/:name/elements + * on an url_format that ends with '*', the rest of the url will not be tested + * priority: endpoint priority in descending order (0 is the higher priority) + * callback_function: a pointer to a function that will be executed each time the endpoint is called + * you must declare the function as described. + * user_data: a pointer to a data or a structure that will be available in callback_function + * return U_OK on success + */ +int ulfius_add_endpoint_by_val(struct _u_instance * u_instance, + const char * http_method, + const char * url_prefix, + const char * url_format, + uint priority, + int (* callback_function)(const struct _u_request * request, // Input parameters (set by the framework) + struct _u_response * response, // Output parameters (set by the user) + void * user_data), + void * user_data); + +/** + * Add a struct _u_endpoint * list to the specified u_instance + * Can be done during the execution of the webservice for injection + * u_instance: pointer to a struct _u_instance that describe its port and bind address + * u_endpoint_list: pointer to an array of struct _u_endpoint ending with a ulfius_empty_endpoint() that will be copied in the u_instance endpoint_list + * return U_OK on success + */ +int ulfius_add_endpoint_list(struct _u_instance * u_instance, const struct _u_endpoint ** u_endpoint_list); + +/** + * Remove a struct _u_endpoint * from the specified u_instance + * Can be done during the execution of the webservice for injection + * u_instance: pointer to a struct _u_instance that describe its port and bind address + * u_endpoint: pointer to a struct _u_endpoint that will be removed in the u_instance endpoint_list + * The parameters _u_endpoint.http_method, _u_endpoint.url_prefix and _u_endpoint.url_format are strictly compared for the match + * If no endpoint is found, return U_ERROR_NOT_FOUND + * return U_OK on success + */ +int ulfius_remove_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint); + +/** + * ulfius_set_default_endpoint + * Set the default endpoint + * This endpoint will be called if no endpoint match the url called + * u_instance: pointer to a struct _u_instance that describe its port and bind address + * auth_function: a pointer to a function that will be executed prior to the callback for authentication + * you must declare the function as described. + * auth_data: a pointer to a data or a structure that will be available in auth_function + * auth_realm: realm value for authentication + * callback_function: a pointer to a function that will be executed each time the endpoint is called + * you must declare the function as described. + * user_data: a pointer to a data or a structure that will be available in callback_function + * to remove a default endpoint, call ulfius_set_default_endpoint with NULL parameter for callback_function + * return U_OK on success + */ +int ulfius_set_default_endpoint(struct _u_instance * u_instance, + int (* callback_function)(const struct _u_request * request, struct _u_response * response, void * user_data), + void * user_data); + +/** + * Remove a struct _u_endpoint * from the specified u_instance + * using the specified values used to identify an endpoint + * Can be done during the execution of the webservice for injection + * u_instance: pointer to a struct _u_instance that describe its port and bind address + * http_method: http_method used by the endpoint + * url_prefix: url_prefix used by the endpoint + * url_format: url_format used by the endpoint + * The parameters _u_endpoint.http_method, _u_endpoint.url_prefix and _u_endpoint.url_format are strictly compared for the match + * If no endpoint is found, return U_ERROR_NOT_FOUND + * return U_OK on success + */ +int ulfius_remove_endpoint_by_val(struct _u_instance * u_instance, const char * http_method, const char * url_prefix, const char * url_format); + +/** + * ulfius_empty_endpoint + * return an empty endpoint that goes at the end of an endpoint list + */ +const struct _u_endpoint * ulfius_empty_endpoint(); + +/** + * ulfius_copy_endpoint + * return a copy of an endpoint with duplicate values + */ +int ulfius_copy_endpoint(struct _u_endpoint * dest, const struct _u_endpoint * source); + +/** + * u_copy_endpoint_list + * return a copy of an endpoint list with duplicate values + * returned value must be free'd after use + */ +struct _u_endpoint * ulfius_duplicate_endpoint_list(const struct _u_endpoint * endpoint_list); + +/** + * ulfius_clean_endpoint + * free allocated memory by an endpoint + */ +void ulfius_clean_endpoint(struct _u_endpoint * endpoint); + +/** + * ulfius_clean_endpoint_list + * free allocated memory by an endpoint list + */ +void ulfius_clean_endpoint_list(struct _u_endpoint * endpoint_list); + +/** + * ulfius_equals_endpoints + * Compare 2 endpoints and return true if their method, prefix and format are the same or if both are NULL + */ +int ulfius_equals_endpoints(const struct _u_endpoint * endpoint1, const struct _u_endpoint * endpoint2); + +#ifndef U_DISABLE_CURL +/******************************************** + * Requests/Responses functions declarations + ********************************************/ + +/** + * ulfius_send_http_request + * Send a HTTP request and store the result into a _u_response + * return U_OK on success + */ +int ulfius_send_http_request(const struct _u_request * request, struct _u_response * response); + +/** + * ulfius_send_http_streaming_request + * Send a HTTP request and store the result into a _u_response + * Except for the body which will be available using write_body_function in the write_body_data + * return U_OK on success + */ +int ulfius_send_http_streaming_request(const struct _u_request * request, struct _u_response * response, size_t (* write_body_function)(void * contents, size_t size, size_t nmemb, void * user_data), void * write_body_data); + +/** + * ulfius_send_smtp_email + * Send an email using libcurl + * email is plain/text and UTF8 charset + * host: smtp server host name + * port: tcp port number (optional, 0 for default) + * use_tls: true if the connection is tls secured + * verify_certificate: true if you want to disable the certificate verification on a tls server + * user: connection user name (optional, NULL: no user name) + * password: connection password (optional, NULL: no password) + * from: from address (mandatory) + * to: to recipient address (mandatory) + * cc: cc recipient address (optional, NULL: no cc) + * bcc: bcc recipient address (optional, NULL: no bcc) + * subject: email subject (mandatory) + * mail_body: email body (mandatory) + * return U_OK on success + */ +int ulfius_send_smtp_email(const char * host, + const int port, + const int use_tls, + const int verify_certificate, + const char * user, + const char * password, + const char * from, + const char * to, + const char * cc, + const char * bcc, + const char * subject, + const char * mail_body); +#endif + +/** + * ulfius_add_cookie_to_header + * add a cookie to the cookie map + * return U_OK on success + */ +int ulfius_add_cookie_to_response(struct _u_response * response, const char * key, const char * value, const char * expires, const uint max_age, + const char * domain, const char * path, const int secure, const int http_only); + +/** + * ulfius_add_header_to_response + * add a header to the response + * return U_OK on success + */ +int ulfius_add_header_to_response(struct _u_response * response, const char * key, const char * value); + +/** + * ulfius_set_string_body_response + * Add a string body to a response + * body must end with a '\0' character + * return U_OK on success + */ +int ulfius_set_string_body_response(struct _u_response * response, const uint status, const char * body); + +/** + * ulfius_set_binary_body_response + * Add a binary body to a response + * return U_OK on success + */ +int ulfius_set_binary_body_response(struct _u_response * response, const uint status, const char * body, const size_t length); + +/** + * ulfius_set_empty_body_response + * Set an empty response with only a status + * return U_OK on success + */ +int ulfius_set_empty_body_response(struct _u_response * response, const uint status); + +/** + * ulfius_set_stream_response + * Set an stream response with a status + * return U_OK on success + */ +int ulfius_set_stream_response(struct _u_response * response, + const uint status, + ssize_t (* stream_callback) (void * stream_user_data, uint64_t offset, char * out_buf, size_t max), + void (* stream_callback_free) (void * stream_user_data), + uint64_t stream_size, + size_t stream_block_size, + void * stream_user_data); + +/** + * ulfius_init_request + * Initialize a request structure by allocating inner elements + * return U_OK on success + */ +int ulfius_init_request(struct _u_request * request); + +/** + * ulfius_clean_request + * clean the specified request's inner elements + * user must free the parent pointer if needed after clean + * or use ulfius_clean_request_full + * return U_OK on success + */ +int ulfius_clean_request(struct _u_request * request); + +/** + * ulfius_clean_request_full + * clean the specified request and all its elements + * return U_OK on success + */ +int ulfius_clean_request_full(struct _u_request * request); + +/** + * ulfius_init_response + * Initialize a response structure by allocating inner elements + * return U_OK on success + */ +int ulfius_init_response(struct _u_response * response); + +/** + * ulfius_clean_response + * clean the specified response's inner elements + * user must free the parent pointer if needed after clean + * or use ulfius_clean_response_full + * return U_OK on success + */ +int ulfius_clean_response(struct _u_response * response); + +/** + * ulfius_clean_response_full + * clean the specified response and all its elements + * return U_OK on success + */ +int ulfius_clean_response_full(struct _u_response * response); + +/** + * ulfius_copy_response + * Copy the source response elements into the des response + * return U_OK on success + */ +int ulfius_copy_response(struct _u_response * dest, const struct _u_response * source); + +/** + * ulfius_clean_cookie + * clean the cookie's elements + * return U_OK on success + */ +int ulfius_clean_cookie(struct _u_cookie * cookie); + +/** + * Copy the cookie source elements into dest elements + * return U_OK on success + */ +int ulfius_copy_cookie(struct _u_cookie * dest, const struct _u_cookie * source); + +/** + * create a new request based on the source elements + * returned value must be cleaned after use + */ +struct _u_request * ulfius_duplicate_request(const struct _u_request * request); + +/** + * create a new response based on the source elements + * return value must be cleaned after use + */ +struct _u_response * ulfius_duplicate_response(const struct _u_response * response); + +#ifndef U_DISABLE_JANSSON +/** + * ulfius_get_json_body_request + * Get JSON structure from the request body if the request is valid + */ +json_t * ulfius_get_json_body_request(const struct _u_request * request, json_error_t * json_error); + +/** + * ulfius_set_json_body_request + * Add a json_t body to a request + * return U_OK on success + */ +int ulfius_set_json_body_request(struct _u_request * request, json_t * body); + +/** + * ulfius_set_json_body_response + * Add a json_t body to a response + * return U_OK on success + */ +int ulfius_set_json_body_response(struct _u_response * response, const uint status, const json_t * body); + +/** + * ulfius_get_json_body_response + * Get JSON structure from the response body if the request is valid + */ +json_t * ulfius_get_json_body_response(struct _u_response * response, json_error_t * json_error); +#endif + +/************************************************************************ + * _u_map declarations * + * _u_map is a simple map structure that handles sets of key/value maps * + ************************************************************************/ + +/** + * initialize a struct _u_map + * this function MUST be called after a declaration or allocation + * return U_OK on success + */ +int u_map_init(struct _u_map * map); + +/** + * free the struct _u_map's inner components + * return U_OK on success + */ +int u_map_clean(struct _u_map * u_map); + +/** + * free the struct _u_map and its components + * return U_OK on success + */ +int u_map_clean_full(struct _u_map * u_map); + +/** + * free an enum return by functions u_map_enum_keys or u_map_enum_values + * return U_OK on success + */ +int u_map_clean_enum(char ** array); + +/** + * returns an array containing all the keys in the struct _u_map + * return an array of char * ending with a NULL element + */ +const char ** u_map_enum_keys(const struct _u_map * u_map); + +/** + * returns an array containing all the values in the struct _u_map + * return an array of char * ending with a NULL element + */ +const char ** u_map_enum_values(const struct _u_map * u_map); + +/** + * return true if the sprcified u_map contains the specified key + * false otherwise + * search is case sensitive + */ +int u_map_has_key(const struct _u_map * u_map, const char * key); + +/** + * return true if the sprcified u_map contains the specified value + * false otherwise + * search is case sensitive + */ +int u_map_has_value(const struct _u_map * u_map, const char * value); + +/** + * return true if the sprcified u_map contains the specified value up until the specified length + * false otherwise + * search is case sensitive + */ +int u_map_has_value_binary(const struct _u_map * u_map, const char * value, size_t length); + +/** + * return true if the sprcified u_map contains the specified key + * false otherwise + * search is case insensitive + */ +int u_map_has_key_case(const struct _u_map * u_map, const char * key); + +/** + * return true if the sprcified u_map contains the specified value + * false otherwise + * search is case insensitive + */ +int u_map_has_value_case(const struct _u_map * u_map, const char * value); + +/** + * add the specified key/value pair into the specified u_map + * if the u_map already contains a pair with the same key, replace the value + * return U_OK on success + */ +int u_map_put(struct _u_map * u_map, const char * key, const char * value); + +/** + * add the specified key/binary value pair into the specified u_map + * if the u_map already contains a pair with the same key, + * replace the value at the specified offset with the specified length + * return U_OK on success + */ +int u_map_put_binary(struct _u_map * u_map, const char * key, const char * value, uint64_t offset, size_t length); + +/** + * get the value length corresponding to the specified key in the u_map + * return -1 if no match found + * search is case sensitive + */ +ssize_t u_map_get_length(const struct _u_map * u_map, const char * key); + +/** + * get the value length corresponding to the specified key in the u_map + * return -1 if no match found + * search is case insensitive + */ +ssize_t u_map_get_case_length(const struct _u_map * u_map, const char * key); + +/** + * get the value corresponding to the specified key in the u_map + * return NULL if no match found + * search is case sensitive + */ +const char * u_map_get(const struct _u_map * u_map, const char * key); + +/** + * get the value corresponding to the specified key in the u_map + * return NULL if no match found + * search is case insensitive + */ +const char * u_map_get_case(const struct _u_map * u_map, const char * key); + +/** + * remove an pair key/value that has the specified key + * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise + */ +int u_map_remove_from_key(struct _u_map * u_map, const char * key); + +/** + * remove all pairs key/value that has the specified key (case insensitive search) + * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise + */ +int u_map_remove_from_key_case(struct _u_map * u_map, const char * key); + +/** + * remove all pairs key/value that has the specified value + * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise + */ +int u_map_remove_from_value(struct _u_map * u_map, const char * value); + +/** + * remove all pairs key/value that has the specified value (case insensitive search) + * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise + */ +int u_map_remove_from_value_case(struct _u_map * u_map, const char * value); + +/** + * remove all pairs key/value that has the specified value up until the specified length + * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise + */ +int u_map_remove_from_value_binary(struct _u_map * u_map, const char * key, size_t length); + +/** + * remove the pair key/value at the specified index + * return U_OK on success, U_NOT_FOUND if index is out of bound, error otherwise + */ +int u_map_remove_at(struct _u_map * u_map, const int index); + +/** + * Create an exact copy of the specified struct _u_map + * return a reference to the copy, NULL otherwise + * returned value must be free'd after use + */ +struct _u_map * u_map_copy(const struct _u_map * source); + +/** + * Copy all key/values pairs of source into dest + * If key is already present in dest, it's overwritten + * return U_OK on success, error otherwise + */ +int u_map_copy_into(struct _u_map * dest, const struct _u_map * source); + +/** + * Return the number of key/values pair in the specified struct _u_map + * Return -1 on error + */ +int u_map_count(const struct _u_map * source); + +/** + * Empty a struct u_map of all its elements + * return U_OK on success, error otherwise + */ +int u_map_empty(struct _u_map * u_map); + +#ifndef U_DISABLE_WEBSOCKET +/********************************** + * Websocket functions declarations + **********************************/ + +#define U_WEBSOCKET_MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define U_WEBSOCKET_UPGRADE_VALUE "websocket" +#define U_WEBSOCKET_BAD_REQUEST_BODY "Error in websocket handshake, wrong parameters" +#define U_WEBSOCKET_USEC_WAIT 50 +#define WEBSOCKET_MAX_CLOSE_TRY 10 + +#define U_WEBSOCKET_BIT_FIN 0x80 +#define U_WEBSOCKET_HAS_MASK 0x80 +#define U_WEBSOCKET_LEN_MASK 0x7F +#define U_WEBSOCKET_OPCODE_CONTINUE 0x00 +#define U_WEBSOCKET_OPCODE_TEXT 0x01 +#define U_WEBSOCKET_OPCODE_BINARY 0x01 +#define U_WEBSOCKET_OPCODE_CLOSE 0x08 +#define U_WEBSOCKET_OPCODE_PING 0x09 +#define U_WEBSOCKET_OPCODE_PONG 0x0A +#define U_WEBSOCKET_OPCODE_CLOSED 0xFD +#define U_WEBSOCKET_OPCODE_ERROR 0xFE +#define U_WEBSOCKET_OPCODE_NONE 0xFF + +/** + * Websocket manager structure + * contains among other things the socket + * the status (open, closed), and the list of incoming and outcoming messages + * Used on public callback functions + */ +struct _websocket_manager { + struct _websocket_message_list * message_list_incoming; + struct _websocket_message_list * message_list_outcoming; + int connected; + int closing; + int manager_closed; + MHD_socket sock; + pthread_mutex_t read_lock; + pthread_mutex_t write_lock; + struct pollfd fds; +}; + +/** + * websocket message structure + * contains all the data of a websocket message + * and the timestamp of when it was sent of received + */ +struct _websocket_message { + time_t datestamp; + uint8_t opcode; + uint8_t has_mask; + uint8_t mask[4]; + uint64_t data_len; + char * data; +}; + +struct _websocket_message_list { + struct _websocket_message ** list; + size_t len; +}; + +/** + * websocket structure + * contains all the data of the websocket + */ +struct _websocket { + struct _u_instance * instance; + struct _u_request * request; + char * websocket_protocol; + char * websocket_extensions; + void (* websocket_manager_callback) (const struct _u_request * request, + struct _websocket_manager * websocket_manager, + void * websocket_manager_user_data); + void * websocket_manager_user_data; + void (* websocket_incoming_message_callback) (const struct _u_request * request, + struct _websocket_manager * websocket_manager, + const struct _websocket_message * message, + void * websocket_incoming_user_data); + void * websocket_incoming_user_data; + void (* websocket_onclose_callback) (const struct _u_request * request, + struct _websocket_manager * websocket_manager, + void * websocket_onclose_user_data); + void * websocket_onclose_user_data; + int tls; + struct _websocket_manager * websocket_manager; + struct MHD_UpgradeResponseHandle * urh; +}; + +/** + * Set a websocket in the response + * You must set at least websocket_manager_callback or websocket_incoming_message_callback + * @Parameters + * response: struct _u_response to send back the websocket initialization, mandatory + * websocket_protocol: list of protocols, separated by a comma, or NULL if all protocols are accepted + * websocket_extensions: list of extensions, separated by a comma, or NULL if all extensions are accepted + * websocket_manager_callback: callback function called right after the handshake acceptance, optional + * websocket_manager_user_data: any data that will be given to the websocket_manager_callback, optional + * websocket_incoming_message_callback: callback function called on each incoming complete message, optional + * websocket_incoming_user_data: any data that will be given to the websocket_incoming_message_callback, optional + * websocket_onclose_callback: callback function called right before closing the websocket, must be complete for the websocket to close + * websocket_onclose_user_data: any data that will be given to the websocket_onclose_callback, optional + * @Return value: U_OK on success + */ +int ulfius_set_websocket_response(struct _u_response * response, + const char * websocket_protocol, + const char * websocket_extensions, + void (* websocket_manager_callback) (const struct _u_request * request, + struct _websocket_manager * websocket_manager, + void * websocket_manager_user_data), + void * websocket_manager_user_data, + void (* websocket_incoming_message_callback) (const struct _u_request * request, + struct _websocket_manager * websocket_manager, + const struct _websocket_message * message, + void * websocket_incoming_user_data), + void * websocket_incoming_user_data, + void (* websocket_onclose_callback) (const struct _u_request * request, + struct _websocket_manager * websocket_manager, + void * websocket_onclose_user_data), + void * websocket_onclose_user_data); + +/** + * Send a message in the websocket + * Return U_OK on success + */ +int ulfius_websocket_send_message(struct _websocket_manager * websocket_manager, + const uint8_t opcode, + const uint64_t data_len, + const char * data); + +/** + * Return the first message of the message list + * Return NULL if message_list has no message + * Returned value must be cleared after use + */ +struct _websocket_message * ulfius_websocket_pop_first_message(struct _websocket_message_list * message_list); + +/** + * Clear data of a websocket message + */ +void ulfius_clear_websocket_message(struct _websocket_message * message); + +#endif + +/** + * free data allocated by ulfius functions + */ +void u_free(void * data); + +/********************************** + * Internal functions declarations + **********************************/ + +/** + * ulfius_validate_instance + * return true if u_instance has valid parameters, false otherwise + */ +int ulfius_validate_instance(const struct _u_instance * u_instance); +/** + * ulfius_is_valid_endpoint + * return true if the endpoind has valid parameters + */ +int ulfius_is_valid_endpoint(const struct _u_endpoint * endpoint, int to_delete); + +/** + * ulfius_validate_endpoint_list + * return true if endpoint_list has valid parameters, false otherwise + */ +int ulfius_validate_endpoint_list(const struct _u_endpoint * endpoint_list, int nb_endpoints); + +/** + * ulfius_webservice_dispatcher + * function executed by libmicrohttpd every time an HTTP call is made + * return MHD_NO on error + */ +int ulfius_webservice_dispatcher (void *cls, struct MHD_Connection *connection, + const char *url, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls); +/** + * mhd_iterate_post_data + * function used to iterate post parameters + * return MHD_NO on error + */ +int mhd_iterate_post_data (void *coninfo_cls, enum MHD_ValueKind kind, const char *key, + const char *filename, const char *content_type, + const char *transfer_encoding, const char *data, uint64_t off, + size_t size); + +/** + * mhd_request_completed + * function used to clean data allocated after a web call is complete + */ +void mhd_request_completed (void *cls, struct MHD_Connection *connection, + void **con_cls, enum MHD_RequestTerminationCode toe); + +/** + * ulfius_split_url + * return an array of char based on the url words + * returned value must be u_free'd after use + */ +char ** ulfius_split_url(const char * prefix, const char * url); + +/** + * Sort an array of struct _u_endpoint * using bubble sort algorithm + */ +void sort_endpoint_list (struct _u_endpoint ** endpoint_list, int length); + +/** + * ulfius_endpoint_match + * return the endpoint array matching the url called with the proper http method + * the returned array always has its last value to NULL + * return NULL on memory error + */ +struct _u_endpoint ** ulfius_endpoint_match(const char * method, const char * url, struct _u_endpoint * endpoint_list); + +/** + * ulfius_url_format_match + * return true if splitted_url matches splitted_url_format + * false otherwise + */ +int ulfius_url_format_match(const char ** splitted_url, const char ** splitted_url_format); + +/** + * ulfius_parse_url + * fills map with the keys/values defined in the url that are described in the endpoint format url + * return U_OK on success + */ +int ulfius_parse_url(const char * url, const struct _u_endpoint * endpoint, struct _u_map * map); + +/** + * ulfius_set_response_header + * adds headers defined in the response_map_header to the response + * return the number of added headers, -1 on error + */ +int ulfius_set_response_header(struct MHD_Response * response, const struct _u_map * response_map_header); + +/** + * ulfius_set_response_cookie + * adds cookies defined in the response_map_cookie + * return the number of added headers, -1 on error + */ +int ulfius_set_response_cookie(struct MHD_Response * mhd_response, const struct _u_response * response); + +/** + * Add a cookie in the cookie map as defined in the RFC 6265 + * Returned value must be u_free'd after use + */ +char * ulfius_get_cookie_header(const struct _u_cookie * cookie); + +/** Macro values **/ +#define ULFIUS_URL_SEPARATOR "/" +#define ULFIUS_HTTP_ENCODING_JSON "application/json" +#define ULFIUS_HTTP_HEADER_CONTENT "Content-Type" +#define ULFIUS_HTTP_NOT_FOUND_BODY "Resource not found" +#define ULFIUS_HTTP_ERROR_BODY "Server Error" + +#define ULFIUS_COOKIE_ATTRIBUTE_EXPIRES "Expires" +#define ULFIUS_COOKIE_ATTRIBUTE_MAX_AGE "Max-Age" +#define ULFIUS_COOKIE_ATTRIBUTE_DOMAIN "Domain" +#define ULFIUS_COOKIE_ATTRIBUTE_PATH "Path" +#define ULFIUS_COOKIE_ATTRIBUTE_SECURE "Secure" +#define ULFIUS_COOKIE_ATTRIBUTE_HTTPONLY "HttpOnly" + +#define ULFIUS_POSTBUFFERSIZE 1024 + +#define U_STATUS_STOP 0 +#define U_STATUS_RUNNING 1 +#define U_STATUS_ERROR 2 + +#ifndef U_DISABLE_WEBSOCKET + +/** + * websocket_manager_callback: callback function for working with the websocket + * websocket_manager_user_data: user-defined data that will be handled to websocket_manager_callback + * websocket_incoming_message_callback: callback function that will be called every time a message arrives from the client in the websocket + * websocket_incoming_user_data: user-defined data that will be handled to websocket_incoming_message_callback + * websocket_onclose_callback: callback function that will be called if the websocket is open while the program calls ulfius_stop_framework + * websocket_onclose_user_data: user-defined data that will be handled to websocket_onclose_callback + */ +struct _websocket_handle { + char * websocket_protocol; + char * websocket_extensions; + void (* websocket_manager_callback) (const struct _u_request * request, + struct _websocket_manager * websocket_manager, + void * websocket_manager_user_data); + void * websocket_manager_user_data; + void (* websocket_incoming_message_callback) (const struct _u_request * request, + struct _websocket_manager * websocket_manager, + const struct _websocket_message * message, + void * websocket_incoming_user_data); + void * websocket_incoming_user_data; + void (* websocket_onclose_callback) (const struct _u_request * request, + struct _websocket_manager * websocket_manager, + void * websocket_onclose_user_data); + void * websocket_onclose_user_data; +}; + +struct _websocket_handler { + size_t nb_websocket_active; + struct _websocket ** websocket_active; + pthread_mutex_t websocket_close_lock; + pthread_cond_t websocket_close_cond; + int pthread_init; +}; + +/** + * Websocket callback function for MHD + * Starts the websocket manager if set, + * then sets a listening message loop + * Complete the callback when the websocket is closed + * The websocket can be closed by the client, the manager, the program, or on network disconnect + */ +void ulfius_start_websocket_cb (void *cls, + struct MHD_Connection *connection, + void *con_cls, + const char *extra_in, + size_t extra_in_size, + MHD_socket sock, + struct MHD_UpgradeResponseHandle *urh); + +/** + * Workaround to make sure a message, as long as it can be is complete sent + */ +void ulfius_websocket_send_all(MHD_socket sock, const uint8_t * data, size_t len); + +/** + * Centralise socket reading in this function + * so if options or check must be done, it's done here instead of each read call + */ +size_t ulfius_websocket_recv_all(MHD_socket sock, uint8_t * data, size_t len); + +/** + * Generates a handhshake answer from the key given in parameter + */ +int ulfius_generate_handshake_answer(const char * key, char * out_digest); + +/** + * Close the websocket + */ +int ulfius_close_websocket(struct _websocket * websocket); + +/** + * Return a match list between two list of items + * If match is NULL, then return source duplicate + * Returned value must be u_free'd after use + */ +char * ulfius_check_list_match(const char * source, const char * match); + +/** + * Initialize a websocket message list + * Return U_OK on success + */ +int ulfius_init_websocket_message_list(struct _websocket_message_list * message_list); + +/** + * Clear data of a websocket message list + */ +void ulfius_clear_websocket_message_list(struct _websocket_message_list * message_list); + +/** + * Append a message in a message list + * Return U_OK on success + */ +int ulfius_push_websocket_message(struct _websocket_message_list * message_list, struct _websocket_message * message); + +/** + * Clear data of a websocket + */ +int ulfius_clear_websocket(struct _websocket * websocket); + +/** + * Clear data of a websocket_manager + */ +void ulfius_clear_websocket_manager(struct _websocket_manager * websocket_manager); + +/** + * Read and parse a new message from the websocket + * Return the opcode of the new websocket, U_WEBSOCKET_OPCODE_NONE if no message arrived, or U_WEBSOCKET_OPCODE_ERROR on error + * Sets the new message in the message variable if available + */ +int ulfius_read_incoming_message(struct _websocket_manager * websocket_manager, struct _websocket_message ** message); + +/** + * Run the websocket manager in a separated detached thread + */ +void * ulfius_thread_websocket_manager_run(void * args); + +/** + * Send a message in the websocket without lock + * Return U_OK on success + */ +int ulfius_websocket_send_message_nolock(struct _websocket_manager * websocket_manager, + const uint8_t opcode, + const uint64_t data_len, + const char * data); + + /** + * Add a websocket in the list of active websockets of the instance + */ +int ulfius_instance_add_websocket_active(struct _u_instance * instance, struct _websocket * websocket); + +/** + * Remove a websocket from the list of active websockets of the instance + */ +int ulfius_instance_remove_websocket_active(struct _u_instance * instance, struct _websocket * websocket); +#endif // U_DISABLE_WEBSOCKET + +#endif // __ULFIUS_H__ diff --git a/includes/yder.h b/includes/yder.h new file mode 100644 index 0000000000000000000000000000000000000000..a2defa4660a0dd62637dfb14d2a72a9448d7600b --- /dev/null +++ b/includes/yder.h @@ -0,0 +1,60 @@ +/** + * + * Yder Framework + * + * Logging framework library + * + * yder.h: structures and functions declarations + * + * Copyright 2015-2017 Nicolas Mora <mail@babelouest.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef __YDER_H__ +#define __YDER_H__ + +#define YDER_VERSION 2.0 + +#define Y_LOG_MODE_NONE 0x0000 +#define Y_LOG_MODE_CONSOLE 0x00F0 +#define Y_LOG_MODE_SYSLOG 0x0F00 +#define Y_LOG_MODE_FILE 0xF000 +#define Y_LOG_MODE_CURRENT 0xFFFF + +#define Y_LOG_LEVEL_NONE 0x0000 +#define Y_LOG_LEVEL_ERROR 0x000F +#define Y_LOG_LEVEL_WARNING 0x00F0 +#define Y_LOG_LEVEL_INFO 0x0F00 +#define Y_LOG_LEVEL_DEBUG 0xF000 +#define Y_LOG_LEVEL_CURRENT 0xFFFF + +/** + * Initialize logging with mode and level parameters, specify a log file if needed + * Return true on success, false on error + */ +int y_init_logs(const char * app, const unsigned long init_mode, const unsigned long init_level, const char * init_log_file, const char * message); + +/** + * Close the logs + */ +int y_close_logs(); + +/** + * Log a message using current parameters + */ +void y_log_message(const unsigned long type, const char * message, ...); + +#endif // __YDER_H__ diff --git a/libraries/liborcania.so b/libraries/liborcania.so new file mode 100755 index 0000000000000000000000000000000000000000..e41e68d5d809996e5df81680ac48cb89549f71f8 Binary files /dev/null and b/libraries/liborcania.so differ diff --git a/libraries/liborcania.so.1.1 b/libraries/liborcania.so.1.1 new file mode 100755 index 0000000000000000000000000000000000000000..e41e68d5d809996e5df81680ac48cb89549f71f8 Binary files /dev/null and b/libraries/liborcania.so.1.1 differ diff --git a/libraries/libulfius.so b/libraries/libulfius.so new file mode 100755 index 0000000000000000000000000000000000000000..1f1731e0907d7bf1fbf51328565362fa510b4afc Binary files /dev/null and b/libraries/libulfius.so differ diff --git a/libraries/libulfius.so.2.1 b/libraries/libulfius.so.2.1 new file mode 100755 index 0000000000000000000000000000000000000000..1f1731e0907d7bf1fbf51328565362fa510b4afc Binary files /dev/null and b/libraries/libulfius.so.2.1 differ diff --git a/libraries/libyder.so b/libraries/libyder.so new file mode 100755 index 0000000000000000000000000000000000000000..623103e1ab2ff20bb9b4a740847b3f5c5fc8f374 Binary files /dev/null and b/libraries/libyder.so differ diff --git a/libraries/libyder.so.2.0 b/libraries/libyder.so.2.0 new file mode 100755 index 0000000000000000000000000000000000000000..623103e1ab2ff20bb9b4a740847b3f5c5fc8f374 Binary files /dev/null and b/libraries/libyder.so.2.0 differ